diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c index c3a5e1b66b..8ec9708634 100644 --- a/daemons/pacemakerd/pacemakerd.c +++ b/daemons/pacemakerd/pacemakerd.c @@ -1,341 +1,378 @@ /* * 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 /* indirectly: CRM_EX_* */ #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__); } 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)"); } } +static void +pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api, + enum pcmk_ipc_event event_type, crm_exit_t status, + void *event_data, void *user_data) +{ + pcmk_pacemakerd_api_reply_t *reply = event_data; + + switch (event_type) { + case pcmk_ipc_event_reply: + break; + + default: + return; + } + + if (status != CRM_EX_OK) { + fprintf(stderr, "Bad reply from pacemakerd: %s", crm_exit_str(status)); + return; + } + + if (reply->reply_type != pcmk_pacemakerd_reply_shutdown) { + fprintf(stderr, "Unknown reply type %d from pacemakerd", + reply->reply_type); + } +} + int main(int argc, char **argv) { + int rc = pcmk_rc_ok; int flag; int argerr = 0; int option_index = 0; bool old_instance_connected = false; gboolean shutdown = FALSE; - crm_ipc_t *old_instance = NULL; + pcmk_ipc_api_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); + + rc = pcmk_new_ipc_api(&old_instance, pcmk_ipc_pacemakerd); + if (old_instance == NULL) { + fprintf(stderr, "Could not connect to pacemakerd: %s", + pcmk_rc_str(rc)); + crm_exit(pcmk_rc2exitc(rc)); + } + + pcmk_register_ipc_callback(old_instance, pacemakerd_event_cb, NULL); + rc = pcmk_connect_ipc(old_instance, pcmk_ipc_dispatch_sync); + old_instance_connected = pcmk_ipc_is_connected(old_instance); if (shutdown) { if (old_instance_connected) { - crm_exit(request_shutdown(old_instance)); + rc = pcmk_pacemakerd_api_shutdown(old_instance, crm_system_name); + pcmk_dispatch_ipc(old_instance); + pcmk_free_ipc_api(old_instance); + crm_exit(pcmk_rc2exitc(rc)); } else { crm_err("Could not request shutdown of existing " "Pacemaker instance: %s", strerror(errno)); - crm_ipc_close(old_instance); - crm_ipc_destroy(old_instance); + pcmk_free_ipc_api(old_instance); crm_exit(CRM_EX_DISCONNECT); } } else if (old_instance_connected) { - crm_ipc_close(old_instance); - crm_ipc_destroy(old_instance); + pcmk_free_ipc_api(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); + pcmk_free_ipc_api(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 73e48d0b1e..02e5d4dbe8 100644 --- a/daemons/pacemakerd/pacemakerd.h +++ b/daemons/pacemakerd/pacemakerd.h @@ -1,48 +1,48 @@ /* * 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..0439986ecf 100644 --- a/daemons/pacemakerd/pcmkd_messages.c +++ b/daemons/pacemakerd/pcmkd_messages.c @@ -1,223 +1,197 @@ /* * 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 }; diff --git a/include/crm/common/ipc_pacemakerd.h b/include/crm/common/ipc_pacemakerd.h index 00e3edd359..95c1f9a8e0 100644 --- a/include/crm/common/ipc_pacemakerd.h +++ b/include/crm/common/ipc_pacemakerd.h @@ -1,71 +1,78 @@ /* * Copyright 2020 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef PCMK__IPC_PACEMAKERD__H # define PCMK__IPC_PACEMAKERD__H #ifdef __cplusplus extern "C" { #endif /** * \file * \brief IPC commands for Pacemakerd * * \ingroup core */ #include // time_t #include // pcmk_ipc_api_t enum pcmk_pacemakerd_state { pcmk_pacemakerd_state_invalid = -1, pcmk_pacemakerd_state_init = 0, pcmk_pacemakerd_state_starting_daemons, pcmk_pacemakerd_state_wait_for_ping, pcmk_pacemakerd_state_running, pcmk_pacemakerd_state_shutting_down, pcmk_pacemakerd_state_shutdown_complete, pcmk_pacemakerd_state_max = pcmk_pacemakerd_state_shutdown_complete, }; //! Possible types of pacemakerd replies enum pcmk_pacemakerd_api_reply { pcmk_pacemakerd_reply_unknown, pcmk_pacemakerd_reply_ping, + pcmk_pacemakerd_reply_shutdown, }; /*! * Pacemakerd reply passed to event callback */ typedef struct { enum pcmk_pacemakerd_api_reply reply_type; union { // pcmk_pacemakerd_reply_ping struct { const char *sys_from; enum pcmk_pacemakerd_state state; time_t last_good; int status; } ping; + // pcmk_pacemakerd_reply_shutdown + struct { + int status; + } shutdown; } data; } pcmk_pacemakerd_api_reply_t; int pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name); +int pcmk_pacemakerd_api_shutdown(pcmk_ipc_api_t *api, const char *ipc_name); + enum pcmk_pacemakerd_state pcmk_pacemakerd_api_daemon_state_text2enum(const char *state); const char *pcmk_pacemakerd_api_daemon_state_enum2text(enum pcmk_pacemakerd_state state); #ifdef __cplusplus } #endif #endif // PCMK__IPC_PACEMAKERD__H diff --git a/lib/common/ipc_pacemakerd.c b/lib/common/ipc_pacemakerd.c index 2223a7b060..caab7a8539 100644 --- a/lib/common/ipc_pacemakerd.c +++ b/lib/common/ipc_pacemakerd.c @@ -1,232 +1,251 @@ /* * Copyright 2020 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include "crmcommon_private.h" typedef struct pacemakerd_api_private_s { enum pcmk_pacemakerd_state state; char *client_uuid; } pacemakerd_api_private_t; static const char *pacemakerd_state_str[] = { XML_PING_ATTR_PACEMAKERDSTATE_INIT, XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS, XML_PING_ATTR_PACEMAKERDSTATE_WAITPING, XML_PING_ATTR_PACEMAKERDSTATE_RUNNING, XML_PING_ATTR_PACEMAKERDSTATE_SHUTTINGDOWN, XML_PING_ATTR_PACEMAKERDSTATE_SHUTDOWNCOMPLETE }; enum pcmk_pacemakerd_state pcmk_pacemakerd_api_daemon_state_text2enum(const char *state) { int i; if (state == NULL) { return pcmk_pacemakerd_state_invalid; } for (i=pcmk_pacemakerd_state_init; i <= pcmk_pacemakerd_state_max; i++) { if (pcmk__str_eq(state, pacemakerd_state_str[i], pcmk__str_none)) { return i; } } return pcmk_pacemakerd_state_invalid; } const char * pcmk_pacemakerd_api_daemon_state_enum2text( enum pcmk_pacemakerd_state state) { if ((state >= pcmk_pacemakerd_state_init) && (state <= pcmk_pacemakerd_state_max)) { return pacemakerd_state_str[state]; } return "invalid"; } // \return Standard Pacemaker return code static int new_data(pcmk_ipc_api_t *api) { struct pacemakerd_api_private_s *private = NULL; api->api_data = calloc(1, sizeof(struct pacemakerd_api_private_s)); if (api->api_data == NULL) { return errno; } private = api->api_data; private->state = pcmk_pacemakerd_state_invalid; /* other as with cib, controld, ... we are addressing pacemakerd just from the local node -> pid is unique and thus sufficient as an ID */ private->client_uuid = pcmk__getpid_s(); return pcmk_rc_ok; } static void free_data(void *data) { free(((struct pacemakerd_api_private_s *) data)->client_uuid); free(data); } // \return Standard Pacemaker return code static int post_connect(pcmk_ipc_api_t *api) { struct pacemakerd_api_private_s *private = NULL; if (api->api_data == NULL) { return EINVAL; } private = api->api_data; private->state = pcmk_pacemakerd_state_invalid; return pcmk_rc_ok; } static void post_disconnect(pcmk_ipc_api_t *api) { struct pacemakerd_api_private_s *private = NULL; if (api->api_data == NULL) { return; } private = api->api_data; private->state = pcmk_pacemakerd_state_invalid; return; } static bool reply_expected(pcmk_ipc_api_t *api, xmlNode *request) { const char *command = crm_element_value(request, F_CRM_TASK); if (command == NULL) { return false; } // We only need to handle commands that functions in this file can send - return !strcmp(command, CRM_OP_PING); + return pcmk__str_any_of(command, CRM_OP_PING, CRM_OP_QUIT, NULL); } static void dispatch(pcmk_ipc_api_t *api, xmlNode *reply) { crm_exit_t status = CRM_EX_OK; xmlNode *msg_data = NULL; pcmk_pacemakerd_api_reply_t reply_data = { pcmk_pacemakerd_reply_unknown }; const char *value = NULL; long long value_ll = 0; + if (pcmk__str_eq((const char *) reply->name, "ack", pcmk__str_casei)) { + return; + } + value = crm_element_value(reply, F_CRM_MSG_TYPE); if ((value == NULL) || (strcmp(value, XML_ATTR_RESPONSE))) { crm_debug("Unrecognizable pacemakerd message: invalid message type '%s'", crm_str(value)); status = CRM_EX_PROTOCOL; goto done; } if (crm_element_value(reply, XML_ATTR_REFERENCE) == NULL) { crm_debug("Unrecognizable pacemakerd message: no reference"); status = CRM_EX_PROTOCOL; goto done; } value = crm_element_value(reply, F_CRM_TASK); - if ((value == NULL) || strcmp(value, CRM_OP_PING)) { - crm_debug("Unrecognizable pacemakerd message: '%s'", crm_str(value)); - status = CRM_EX_PROTOCOL; - goto done; - } // Parse useful info from reply - msg_data = get_message_xml(reply, F_CRM_DATA); crm_element_value_ll(msg_data, XML_ATTR_TSTAMP, &value_ll); - reply_data.reply_type = pcmk_pacemakerd_reply_ping; - reply_data.data.ping.state = - pcmk_pacemakerd_api_daemon_state_text2enum( - crm_element_value(msg_data, XML_PING_ATTR_PACEMAKERDSTATE)); - reply_data.data.ping.status = - pcmk__str_eq(crm_element_value(msg_data, XML_PING_ATTR_STATUS), "ok", - pcmk__str_casei)?pcmk_rc_ok:pcmk_rc_error; - reply_data.data.ping.last_good = (time_t) value_ll; - reply_data.data.ping.sys_from = crm_element_value(msg_data, - XML_PING_ATTR_SYSFROM); + if (pcmk__str_eq(value, CRM_OP_PING, pcmk__str_none)) { + reply_data.reply_type = pcmk_pacemakerd_reply_ping; + reply_data.data.ping.state = + pcmk_pacemakerd_api_daemon_state_text2enum( + crm_element_value(msg_data, XML_PING_ATTR_PACEMAKERDSTATE)); + reply_data.data.ping.status = + pcmk__str_eq(crm_element_value(msg_data, XML_PING_ATTR_STATUS), "ok", + pcmk__str_casei)?pcmk_rc_ok:pcmk_rc_error; + reply_data.data.ping.last_good = (time_t) value_ll; + reply_data.data.ping.sys_from = crm_element_value(msg_data, + XML_PING_ATTR_SYSFROM); + } else if (pcmk__str_eq(value, CRM_OP_QUIT, pcmk__str_none)) { + reply_data.reply_type = pcmk_pacemakerd_reply_shutdown; + reply_data.data.shutdown.status = atoi(crm_element_value(msg_data, XML_LRM_ATTR_OPSTATUS)); + } else { + crm_debug("Unrecognizable pacemakerd message: '%s'", crm_str(value)); + status = CRM_EX_PROTOCOL; + goto done; + } done: pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data); } pcmk__ipc_methods_t * pcmk__pacemakerd_api_methods() { pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t)); if (cmds != NULL) { cmds->new_data = new_data; cmds->free_data = free_data; cmds->post_connect = post_connect; cmds->reply_expected = reply_expected; cmds->dispatch = dispatch; cmds->post_disconnect = post_disconnect; } return cmds; } -int -pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name) +static int +do_pacemakerd_api_call(pcmk_ipc_api_t *api, const char *ipc_name, const char *task) { pacemakerd_api_private_t *private; xmlNode *cmd; int rc; CRM_CHECK(api != NULL, return -EINVAL); private = api->api_data; CRM_ASSERT(private != NULL); - cmd = create_request(CRM_OP_PING, NULL, NULL, CRM_SYSTEM_MCP, + cmd = create_request(task, NULL, NULL, CRM_SYSTEM_MCP, ipc_name?ipc_name:((crm_system_name? crm_system_name : "client")), private->client_uuid); if (cmd) { rc = pcmk__send_ipc_request(api, cmd); if (rc != pcmk_rc_ok) { crm_debug("Couldn't ping pacemakerd: %s rc=%d", pcmk_rc_str(rc), rc); rc = ECOMM; } free_xml(cmd); } else { rc = ENOMSG; } return rc; } + +int +pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name) +{ + return do_pacemakerd_api_call(api, ipc_name, CRM_OP_PING); +} + +int +pcmk_pacemakerd_api_shutdown(pcmk_ipc_api_t *api, const char *ipc_name) +{ + return do_pacemakerd_api_call(api, ipc_name, CRM_OP_QUIT); +}