diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c index 11e607f6e2..8999491983 100644 --- a/daemons/pacemakerd/pacemakerd.c +++ b/daemons/pacemakerd/pacemakerd.c @@ -1,412 +1,347 @@ /* * 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 #include #include #include #include /* indirectly: CRM_EX_* */ #include /* cib_channel_ro */ #include #include #include #include #include #include #include static const char *pid_file = PCMK_RUN_DIR "/pacemaker.pid"; static void pcmk_ignore(int nsig) { crm_info("Ignoring signal %s (%d)", strsignal(nsig), nsig); } static void pcmk_sigquit(int nsig) { pcmk__panic(__func__); } -/* 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); - } - - } 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; -} - -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 -}; - static pcmk__cli_option_t long_options[] = { // long option, argument type, storage, short option, description, flags { "help", no_argument, NULL, '?', "\tThis text", pcmk__option_default }, { "version", no_argument, NULL, '$', "\tVersion information", pcmk__option_default }, { "verbose", no_argument, NULL, 'V', "\tIncrease debug output", pcmk__option_default }, { "shutdown", no_argument, NULL, 'S', "\tInstruct Pacemaker to shutdown on this machine", pcmk__option_default }, { "features", no_argument, NULL, 'F', "\tDisplay full version and list of features Pacemaker was built with", pcmk__option_default }, { "-spacer-", no_argument, NULL, '-', "\nAdditional Options:", pcmk__option_default }, { "foreground", no_argument, NULL, 'f', "\t(Ignored) Pacemaker always runs in the foreground", pcmk__option_default }, { "pid-file", required_argument, NULL, 'p', "\t(Ignored) Daemon pid file location", pcmk__option_default }, { "standby", no_argument, NULL, 's', "\tStart node in standby state", pcmk__option_default }, { 0, 0, 0, 0 } }; static void mcp_chown(const char *path, uid_t uid, gid_t gid) { int rc = chown(path, uid, gid); if (rc < 0) { crm_warn("Cannot change the ownership of %s to user %s and gid %d: %s", path, CRM_DAEMON_USER, gid, pcmk_strerror(errno)); } } static void create_pcmk_dirs(void) { uid_t pcmk_uid = 0; gid_t pcmk_gid = 0; const char *dirs[] = { CRM_PACEMAKER_DIR, // core/blackbox/scheduler/CIB files CRM_CORE_DIR, // core files CRM_BLACKBOX_DIR, // blackbox dumps PE_STATE_DIR, // scheduler inputs CRM_CONFIG_DIR, // the Cluster Information Base (CIB) // Don't build CRM_RSCTMP_DIR, pacemaker-execd will do it NULL }; if (pcmk_daemon_user(&pcmk_uid, &pcmk_gid) < 0) { crm_err("Cluster user %s does not exist, aborting Pacemaker startup", CRM_DAEMON_USER); crm_exit(CRM_EX_NOUSER); } // Used by some resource agents if ((mkdir(CRM_STATE_DIR, 0750) < 0) && (errno != EEXIST)) { crm_warn("Could not create directory " CRM_STATE_DIR ": %s", pcmk_rc_str(errno)); } else { mcp_chown(CRM_STATE_DIR, pcmk_uid, pcmk_gid); } for (int i = 0; dirs[i] != NULL; ++i) { int rc = pcmk__build_path(dirs[i], 0750); if (rc != pcmk_rc_ok) { crm_warn("Could not create directory %s: %s", dirs[i], pcmk_rc_str(rc)); } else { mcp_chown(dirs[i], pcmk_uid, pcmk_gid); } } } static void remove_core_file_limit(void) { struct rlimit cores; int rc = getrlimit(RLIMIT_CORE, &cores); if (rc < 0) { crm_warn("Cannot determine current maximum core file size: %s", strerror(errno)); return; } if ((cores.rlim_max == 0) && (geteuid() == 0)) { cores.rlim_max = RLIM_INFINITY; } else { crm_info("Maximum core file size is %llu bytes", (unsigned long long) cores.rlim_max); } cores.rlim_cur = cores.rlim_max; rc = setrlimit(RLIMIT_CORE, &cores); if (rc < 0) { crm_warn("Cannot raise system limit on core file size " "(consider doing so manually)"); } } int main(int argc, char **argv) { int flag; int argerr = 0; int option_index = 0; bool old_instance_connected = false; gboolean shutdown = FALSE; crm_ipc_t *old_instance = NULL; qb_ipcs_service_t *ipcs = NULL; crm_log_preinit(NULL, argc, argv); pcmk__set_cli_options(NULL, "[options]", long_options, "primary Pacemaker daemon that launches and " "monitors all subsidiary Pacemaker daemons"); mainloop_add_signal(SIGHUP, pcmk_ignore); mainloop_add_signal(SIGQUIT, pcmk_sigquit); while (1) { flag = pcmk__next_cli_option(argc, argv, &option_index, NULL); if (flag == -1) break; switch (flag) { case 'V': crm_bump_log_level(argc, argv); break; case 'f': /* Legacy */ break; case 'p': pid_file = optarg; break; case 's': pcmk__set_env_option("node_start_state", "standby"); break; case '$': case '?': pcmk__cli_help(flag, CRM_EX_OK); break; case 'S': shutdown = TRUE; break; case 'F': printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES); crm_exit(CRM_EX_OK); default: printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } if (argerr) { pcmk__cli_help('?', CRM_EX_USAGE); } setenv("LC_ALL", "C", 1); pcmk__set_env_option("mcp", "true"); crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE); crm_debug("Checking for existing Pacemaker instance"); old_instance = crm_ipc_new(CRM_SYSTEM_MCP, 0); old_instance_connected = crm_ipc_connect(old_instance); if (shutdown) { if (old_instance_connected) { crm_exit(request_shutdown(old_instance)); } else { crm_err("Could not request shutdown of existing " "Pacemaker instance: %s", strerror(errno)); crm_ipc_close(old_instance); crm_ipc_destroy(old_instance); crm_exit(CRM_EX_DISCONNECT); } } else if (old_instance_connected) { crm_ipc_close(old_instance); crm_ipc_destroy(old_instance); crm_err("Aborting start-up because active Pacemaker instance found"); crm_exit(CRM_EX_FATAL); } crm_ipc_close(old_instance); crm_ipc_destroy(old_instance); #ifdef SUPPORT_COROSYNC if (mcp_read_config() == FALSE) { crm_exit(CRM_EX_UNAVAILABLE); } #endif // OCF shell functions and cluster-glue need facility under different name { const char *facility = pcmk__env_option("logfacility"); if (facility && !pcmk__str_eq(facility, "none", pcmk__str_casei)) { setenv("HA_LOGFACILITY", facility, 1); } } crm_notice("Starting Pacemaker %s "CRM_XS" build=%s features:%s", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES); mainloop = g_main_loop_new(NULL, FALSE); remove_core_file_limit(); create_pcmk_dirs(); pcmk__serve_pacemakerd_ipc(&ipcs, &mcp_ipc_callbacks); #ifdef SUPPORT_COROSYNC /* Allows us to block shutdown */ if (!cluster_connect_cfg()) { crm_exit(CRM_EX_PROTOCOL); } #endif if (pcmk__locate_sbd() > 0) { setenv("PCMK_watchdog", "true", 1); running_with_sbd = TRUE; } else { setenv("PCMK_watchdog", "false", 1); } switch (find_and_track_existing_processes()) { case pcmk_rc_ok: break; case pcmk_rc_ipc_unauthorized: crm_exit(CRM_EX_CANTCREAT); default: crm_exit(CRM_EX_FATAL); }; mainloop_add_signal(SIGTERM, pcmk_shutdown); mainloop_add_signal(SIGINT, pcmk_shutdown); if ((running_with_sbd) && pcmk__get_sbd_sync_resource_startup()) { crm_notice("Waiting for startup-trigger from SBD."); pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_WAITPING; startup_trigger = mainloop_add_trigger(G_PRIORITY_HIGH, init_children_processes, NULL); } else { if (running_with_sbd) { crm_warn("Enabling SBD_SYNC_RESOURCE_STARTUP would (if supported " "by your SBD version) improve reliability of " "interworking between SBD & pacemaker."); } pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS; init_children_processes(NULL); } crm_notice("Pacemaker daemon successfully started and accepting connections"); g_main_loop_run(mainloop); if (ipcs) { crm_trace("Closing IPC server"); mainloop_del_ipc_server(ipcs); ipcs = NULL; } g_main_loop_unref(mainloop); #ifdef SUPPORT_COROSYNC cluster_disconnect_cfg(); #endif crm_exit(CRM_EX_OK); } diff --git a/daemons/pacemakerd/pacemakerd.h b/daemons/pacemakerd/pacemakerd.h index b3a802bf0e..3e618b57b7 100644 --- a/daemons/pacemakerd/pacemakerd.h +++ b/daemons/pacemakerd/pacemakerd.h @@ -1,54 +1,56 @@ /* * 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 #include #include #include #include #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); -void pcmkd_shutdown_corosync(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 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 dc84dd2a41..437087cd79 100644 --- a/daemons/pacemakerd/pcmkd_messages.c +++ b/daemons/pacemakerd/pcmkd_messages.c @@ -1,154 +1,219 @@ /* * 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 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); } } } 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); + } + + } 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 +};