diff --git a/daemons/pacemakerd/pacemakerd.h b/daemons/pacemakerd/pacemakerd.h index 73e48d0b1e..f6f108655f 100644 --- a/daemons/pacemakerd/pacemakerd.h +++ b/daemons/pacemakerd/pacemakerd.h @@ -1,48 +1,49 @@ /* * Copyright 2010-2021 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include typedef struct pcmk_child_s { pid_t pid; int start_seq; int respawn_count; gboolean respawn; const char *name; const char *uid; const char *command; const char *endpoint; /* IPC server name */ gboolean active_before_startup; } pcmk_child_t; #define SIZEOF(a) (sizeof(a) / sizeof(a[0])) #define MAX_RESPAWN 100 extern GMainLoop *mainloop; extern struct qb_ipcs_service_handlers mcp_ipc_callbacks; extern const char *pacemakerd_state; extern gboolean running_with_sbd; extern unsigned int shutdown_complete_state_reported_to; extern gboolean shutdown_complete_state_reported_client_closed; extern crm_trigger_t *shutdown_trigger; extern crm_trigger_t *startup_trigger; gboolean mcp_read_config(void); gboolean cluster_connect_cfg(void); gboolean cluster_disconnect_cfg(void); int find_and_track_existing_processes(void); gboolean init_children_processes(void *user_data); void pcmk_shutdown(int nsig); void pcmk_handle_ping_request(pcmk__client_t *c, xmlNode *msg, uint32_t id); +void pcmk_handle_shutdown_request(pcmk__client_t *c, xmlNode *msg, uint32_t id, uint32_t flags); void pcmkd_shutdown_corosync(void); crm_exit_t request_shutdown(crm_ipc_t *ipc); diff --git a/daemons/pacemakerd/pcmkd_messages.c b/daemons/pacemakerd/pcmkd_messages.c index 516647ab8c..4fd10b0fa0 100644 --- a/daemons/pacemakerd/pcmkd_messages.c +++ b/daemons/pacemakerd/pcmkd_messages.c @@ -1,223 +1,251 @@ /* * Copyright 2010-2021 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include "pacemakerd.h" #include #include #include #include #include #include #include void pcmk_handle_ping_request(pcmk__client_t *c, xmlNode *msg, uint32_t id) { const char *value = NULL; xmlNode *ping = NULL; xmlNode *reply = NULL; time_t pinged = time(NULL); const char *from = crm_element_value(msg, F_CRM_SYS_FROM); /* Pinged for status */ crm_trace("Pinged from %s.%s", crm_str(crm_element_value(msg, F_CRM_ORIGIN)), from?from:"unknown"); ping = create_xml_node(NULL, XML_CRM_TAG_PING); value = crm_element_value(msg, F_CRM_SYS_TO); crm_xml_add(ping, XML_PING_ATTR_SYSFROM, value); crm_xml_add(ping, XML_PING_ATTR_PACEMAKERDSTATE, pacemakerd_state); crm_xml_add_ll(ping, XML_ATTR_TSTAMP, (long long) pinged); crm_xml_add(ping, XML_PING_ATTR_STATUS, "ok"); reply = create_reply(msg, ping); free_xml(ping); if (reply) { if (pcmk__ipc_send_xml(c, id, reply, crm_ipc_server_event) != pcmk_rc_ok) { crm_err("Failed sending ping reply to client %s", pcmk__client_name(c)); } free_xml(reply); } else { crm_err("Failed building ping reply for client %s", pcmk__client_name(c)); } /* just proceed state on sbd pinging us */ if (from && strstr(from, "sbd")) { if (pcmk__str_eq(pacemakerd_state, XML_PING_ATTR_PACEMAKERDSTATE_SHUTDOWNCOMPLETE, pcmk__str_none)) { if (pcmk__get_sbd_sync_resource_startup()) { crm_notice("Shutdown-complete-state passed to SBD."); } shutdown_complete_state_reported_to = c->pid; } else if (pcmk__str_eq(pacemakerd_state, XML_PING_ATTR_PACEMAKERDSTATE_WAITPING, pcmk__str_none)) { crm_notice("Received startup-trigger from SBD."); pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS; mainloop_set_trigger(startup_trigger); } } } +void +pcmk_handle_shutdown_request(pcmk__client_t *c, xmlNode *msg, uint32_t id, uint32_t flags) +{ + xmlNode *shutdown = NULL; + xmlNode *reply = NULL; + + /* Only allow privileged users (i.e. root or hacluster) to shut down + * Pacemaker from the command line (or direct IPC), so that other users + * are forced to go through the CIB and have ACLs applied. + */ + bool allowed = pcmk_is_set(c->flags, pcmk__client_privileged); + + shutdown = create_xml_node(NULL, XML_CIB_ATTR_SHUTDOWN); + + if (allowed) { + crm_notice("Shutting down in response to IPC request %s from %s", + crm_element_value(msg, F_CRM_REFERENCE), + crm_element_value(msg, F_CRM_ORIGIN)); + crm_xml_add_int(shutdown, XML_LRM_ATTR_OPSTATUS, CRM_EX_OK); + } else { + crm_warn("Ignoring shutdown request from unprivileged client %s", + pcmk__client_name(c)); + crm_xml_add_int(shutdown, XML_LRM_ATTR_OPSTATUS, CRM_EX_INSUFFICIENT_PRIV); + } + + reply = create_reply(msg, shutdown); + free_xml(shutdown); + if (reply) { + if (pcmk__ipc_send_xml(c, id, reply, crm_ipc_server_event) != pcmk_rc_ok) { + crm_err("Failed sending shutdown reply to client %s", + pcmk__client_name(c)); + } + free_xml(reply); + } else { + crm_err("Failed building shutdown reply for client %s", + pcmk__client_name(c)); + } + + if (allowed) { + pcmk_shutdown(15); + } +} + static int32_t pcmk_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid) { crm_trace("Connection %p", c); if (pcmk__new_client(c, uid, gid) == NULL) { return -EIO; } return 0; } /* Error code means? */ static int32_t pcmk_ipc_closed(qb_ipcs_connection_t * c) { pcmk__client_t *client = pcmk__find_client(c); if (client == NULL) { return 0; } crm_trace("Connection %p", c); if (shutdown_complete_state_reported_to == client->pid) { shutdown_complete_state_reported_client_closed = TRUE; if (shutdown_trigger) { mainloop_set_trigger(shutdown_trigger); } } pcmk__free_client(client); return 0; } static void pcmk_ipc_destroy(qb_ipcs_connection_t * c) { crm_trace("Connection %p", c); pcmk_ipc_closed(c); } /* Exit code means? */ static int32_t pcmk_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size) { uint32_t id = 0; uint32_t flags = 0; const char *task = NULL; xmlNode *msg = NULL; pcmk__client_t *c = pcmk__find_client(qbc); CRM_CHECK(c != NULL, return 0); msg = pcmk__client_data2xml(c, data, &id, &flags); if (msg == NULL) { pcmk__ipc_send_ack(c, id, flags, "ack", CRM_EX_PROTOCOL); return 0; } task = crm_element_value(msg, F_CRM_TASK); if (pcmk__str_eq(task, CRM_OP_QUIT, pcmk__str_none)) { - /* Only allow privileged users (i.e. root or hacluster) to shut down - * Pacemaker from the command line (or direct IPC), so that other users - * are forced to go through the CIB and have ACLs applied. - */ - bool allowed = pcmk_is_set(c->flags, pcmk__client_privileged); - - if (allowed) { - crm_notice("Shutting down in response to IPC request %s from %s", - crm_element_value(msg, F_CRM_REFERENCE), - crm_element_value(msg, F_CRM_ORIGIN)); - pcmk__ipc_send_ack(c, id, flags, "ack", CRM_EX_OK); - pcmk_shutdown(15); - } else { - crm_warn("Ignoring shutdown request from unprivileged client %s", - pcmk__client_name(c)); - pcmk__ipc_send_ack(c, id, flags, "ack", CRM_EX_INSUFFICIENT_PRIV); - } + pcmk__ipc_send_ack(c, id, flags, "ack", CRM_EX_INDETERMINATE); + pcmk_handle_shutdown_request(c, msg, id, flags); } else if (pcmk__str_eq(task, CRM_OP_RM_NODE_CACHE, pcmk__str_none)) { crm_trace("Ignoring request from client %s to purge node " "because peer cache is not used", pcmk__client_name(c)); pcmk__ipc_send_ack(c, id, flags, "ack", CRM_EX_OK); } else if (pcmk__str_eq(task, CRM_OP_PING, pcmk__str_none)) { pcmk__ipc_send_ack(c, id, flags, "ack", CRM_EX_INDETERMINATE); pcmk_handle_ping_request(c, msg, id); } else { crm_debug("Unrecognized IPC command '%s' from client %s", crm_str(task), pcmk__client_name(c)); pcmk__ipc_send_ack(c, id, flags, "ack", CRM_EX_INVALID_PARAM); } free_xml(msg); return 0; } crm_exit_t request_shutdown(crm_ipc_t *ipc) { xmlNode *request = NULL; xmlNode *reply = NULL; int rc = 0; crm_exit_t status = CRM_EX_OK; request = create_request(CRM_OP_QUIT, NULL, NULL, CRM_SYSTEM_MCP, CRM_SYSTEM_MCP, NULL); if (request == NULL) { crm_err("Unable to create shutdown request"); // Probably memory error status = CRM_EX_TEMPFAIL; goto done; } crm_notice("Requesting shutdown of existing Pacemaker instance"); rc = crm_ipc_send(ipc, request, crm_ipc_client_response, 0, &reply); if (rc < 0) { crm_err("Could not send shutdown request"); status = crm_errno2exit(rc); goto done; } if ((rc == 0) || (reply == NULL)) { crm_err("Unrecognized response to shutdown request"); status = CRM_EX_PROTOCOL; goto done; } if ((crm_element_value_int(reply, "status", &rc) == 0) && (rc != CRM_EX_OK)) { crm_err("Shutdown request failed: %s", crm_exit_str(rc)); status = rc; goto done; } // Wait for pacemakerd to shut down IPC (with 30-minute timeout) status = CRM_EX_TIMEOUT; for (int i = 0; i < 900; ++i) { if (!crm_ipc_connected(ipc)) { status = CRM_EX_OK; break; } sleep(2); } done: free_xml(request); crm_ipc_close(ipc); crm_ipc_destroy(ipc); return status; } struct qb_ipcs_service_handlers mcp_ipc_callbacks = { .connection_accept = pcmk_ipc_accept, .connection_created = NULL, .msg_process = pcmk_ipc_dispatch, .connection_closed = pcmk_ipc_closed, .connection_destroyed = pcmk_ipc_destroy };