diff --git a/tools/crmadmin.c b/tools/crmadmin.c index e61dbf47f3..ec902df606 100644 --- a/tools/crmadmin.c +++ b/tools/crmadmin.c @@ -1,772 +1,843 @@ /* * Copyright 2004-2020 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 // atoi() #include // gboolean, GMainLoop, etc. #include // xmlNode #include #include #include #include #include #include #include #include #include #include #define SUMMARY "query and manage the Pacemaker controller" #define DEFAULT_MESSAGE_TIMEOUT_MS 30000 static guint message_timer_id = 0; static guint message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS; static GMainLoop *mainloop = NULL; bool need_controld_api = true; -bool need_pacemakerd_api = false; bool do_work(pcmk_ipc_api_t *api); static char *ipc_name = NULL; gboolean admin_message_timeout(gpointer data); static enum { cmd_none, cmd_shutdown, cmd_health, cmd_elect_dc, cmd_whois_dc, cmd_list_nodes, cmd_pacemakerd_health, } command = cmd_none; static gboolean BE_VERBOSE = FALSE; static gboolean BASH_EXPORT = FALSE; static char *dest_node = NULL; static crm_exit_t exit_code = CRM_EX_OK; pcmk__output_t *out = NULL; struct { gboolean health; gint timeout; } options; gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); static GOptionEntry command_options[] = { { "status", 'S', 0, G_OPTION_ARG_CALLBACK, command_cb, "Display the status of the specified node." "\n Result is state of node's internal finite state" "\n machine, which can be useful for debugging", NULL }, { "pacemakerd", 'P', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Display the status of local pacemakerd." "\n Result is the state of the sub-daemons watched" "\n by pacemakerd.", NULL }, { "dc_lookup", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Display the uname of the node co-ordinating the cluster." "\n This is an internal detail rarely useful to" "\n administrators except when deciding on which" "\n node to examine the logs.", NULL }, { "nodes", 'N', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "Display the uname of all member nodes", NULL }, { "election", 'E', G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, "(Advanced) Start an election for the cluster co-ordinator", NULL }, { "kill", 'K', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, command_cb, "(Advanced) Stop controller (not rest of cluster stack) on specified node", NULL }, { "health", 'H', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.health, NULL, NULL }, { NULL } }; static GOptionEntry additional_options[] = { { "timeout", 't', 0, G_OPTION_ARG_INT, &options.timeout, "Time (in milliseconds) to wait before declaring the" "\n operation failed", NULL }, { "bash-export", 'B', 0, G_OPTION_ARG_NONE, &BASH_EXPORT, "Display nodes as shell commands of the form 'export uname=uuid'" "\n (valid with -N/--nodes)", }, { "ipc-name", 'i', 0, G_OPTION_ARG_STRING, &ipc_name, "Name to use for ipc instead of 'crmadmin' (with -P/--pacemakerd).", NULL }, { NULL } }; gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { if (!strcmp(option_name, "--status") || !strcmp(option_name, "-S")) { command = cmd_health; crm_trace("Option %c => %s", 'S', optarg); } if (!strcmp(option_name, "--pacemakerd") || !strcmp(option_name, "-P")) { command = cmd_pacemakerd_health; - need_pacemakerd_api = true; - need_controld_api = false; } if (!strcmp(option_name, "--dc_lookup") || !strcmp(option_name, "-D")) { command = cmd_whois_dc; } if (!strcmp(option_name, "--nodes") || !strcmp(option_name, "-N")) { command = cmd_list_nodes; - need_controld_api = false; } if (!strcmp(option_name, "--election") || !strcmp(option_name, "-E")) { command = cmd_elect_dc; } if (!strcmp(option_name, "--kill") || !strcmp(option_name, "-K")) { command = cmd_shutdown; crm_trace("Option %c => %s", 'K', optarg); } if (optarg) { if (dest_node != NULL) { free(dest_node); } dest_node = strdup(optarg); } return TRUE; } PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *", "const char *") static int health_text(pcmk__output_t *out, va_list args) { const char *sys_from = va_arg(args, const char *); const char *host_from = va_arg(args, const char *); const char *fsa_state = va_arg(args, const char *); const char *result = va_arg(args, const char *); if (!out->is_quiet(out)) { out->info(out, "Status of %s@%s: %s (%s)", crm_str(sys_from), crm_str(host_from), crm_str(fsa_state), crm_str(result)); } else if (fsa_state != NULL) { out->info(out, "%s", fsa_state); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *", "const char *") static int health_xml(pcmk__output_t *out, va_list args) { const char *sys_from = va_arg(args, const char *); const char *host_from = va_arg(args, const char *); const char *fsa_state = va_arg(args, const char *); const char *result = va_arg(args, const char *); xmlNodePtr node = pcmk__output_create_xml_node(out, crm_str(sys_from)); xmlSetProp(node, (pcmkXmlStr) "node_name", (pcmkXmlStr) crm_str(host_from)); xmlSetProp(node, (pcmkXmlStr) "state", (pcmkXmlStr) crm_str(fsa_state)); xmlSetProp(node, (pcmkXmlStr) "result", (pcmkXmlStr) crm_str(result)); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *", "const char *", "const char *") static int pacemakerd_health_text(pcmk__output_t *out, va_list args) { const char *sys_from = va_arg(args, const char *); const char *state = va_arg(args, const char *); const char *last_updated = va_arg(args, const char *); if (!out->is_quiet(out)) { out->info(out, "Status of %s: '%s' %s %s", crm_str(sys_from), crm_str(state), (!pcmk__str_empty(last_updated))? "last updated":"", crm_str(last_updated)); } else { out->info(out, "%s", crm_str(state)); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *", "const char *", "const char *") static int pacemakerd_health_xml(pcmk__output_t *out, va_list args) { const char *sys_from = va_arg(args, const char *); const char *state = va_arg(args, const char *); const char *last_updated = va_arg(args, const char *); xmlNodePtr node = pcmk__output_create_xml_node(out, crm_str(sys_from)); xmlSetProp(node, (pcmkXmlStr) "state", (pcmkXmlStr) crm_str(state)); xmlSetProp(node, (pcmkXmlStr) "last_updated", (pcmkXmlStr) crm_str(last_updated)); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("dc", "const char *") static int dc_text(pcmk__output_t *out, va_list args) { const char *dc = va_arg(args, const char *); if (!out->is_quiet(out)) { out->info(out, "Designated Controller is: %s", crm_str(dc)); } else if (dc != NULL) { out->info(out, "%s", dc); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("dc", "const char *") static int dc_xml(pcmk__output_t *out, va_list args) { const char *dc = va_arg(args, const char *); xmlNodePtr node = pcmk__output_create_xml_node(out, "dc"); xmlSetProp(node, (pcmkXmlStr) "node_name", (pcmkXmlStr) crm_str(dc)); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("crmadmin-node-list", "struct xmlNode *") static int crmadmin_node_list(pcmk__output_t *out, va_list args) { xmlNode *xml_node = va_arg(args, xmlNode *); int found = 0; xmlNode *node = NULL; xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node); out->begin_list(out, NULL, NULL, "nodes"); for (node = first_named_child(nodes, XML_CIB_TAG_NODE); node != NULL; node = crm_next_same_xml(node)) { const char *node_type = BASH_EXPORT ? NULL : crm_element_value(node, XML_ATTR_TYPE); out->message(out, "crmadmin-node", node_type, crm_str(crm_element_value(node, XML_ATTR_UNAME)), crm_str(crm_element_value(node, XML_ATTR_ID))); found++; } // @TODO List Pacemaker Remote nodes that don't have a entry out->end_list(out); if (found == 0) { out->info(out, "No nodes configured"); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *", "const char *") static int crmadmin_node_text(pcmk__output_t *out, va_list args) { const char *type = va_arg(args, const char *); const char *name = va_arg(args, const char *); const char *id = va_arg(args, const char *); if (BASH_EXPORT) { out->info(out, "export %s=%s", crm_str(name), crm_str(id)); } else { out->info(out, "%s node: %s (%s)", type ? type : "member", crm_str(name), crm_str(id)); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *", "const char *") static int crmadmin_node_xml(pcmk__output_t *out, va_list args) { const char *type = va_arg(args, const char *); const char *name = va_arg(args, const char *); const char *id = va_arg(args, const char *); xmlNodePtr node = pcmk__output_create_xml_node(out, "node"); xmlSetProp(node, (pcmkXmlStr) "type", (pcmkXmlStr) (type ? type : "member")); xmlSetProp(node, (pcmkXmlStr) "name", (pcmkXmlStr) crm_str(name)); xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) crm_str(id)); return pcmk_rc_ok; } static pcmk__message_entry_t fmt_functions[] = { {"health", "default", health_text }, {"health", "xml", health_xml }, {"pacemakerd-health", "default", pacemakerd_health_text }, {"pacemakerd-health", "xml", pacemakerd_health_xml }, {"dc", "default", dc_text }, {"dc", "xml", dc_xml }, {"crmadmin-node-list", "default", crmadmin_node_list }, {"crmadmin-node", "default", crmadmin_node_text }, {"crmadmin-node", "xml", crmadmin_node_xml }, { NULL, NULL, NULL } }; static pcmk__supported_format_t formats[] = { PCMK__SUPPORTED_FORMAT_TEXT, PCMK__SUPPORTED_FORMAT_XML, { NULL, NULL, NULL } }; +static void +start_main_loop() +{ + exit_code = CRM_EX_DISCONNECT; // For unexpected disconnects + mainloop = g_main_loop_new(NULL, FALSE); + message_timer_id = g_timeout_add(message_timeout_ms, + admin_message_timeout, NULL); + g_main_loop_run(mainloop); +} + static void quit_main_loop(crm_exit_t ec) { exit_code = ec; if (mainloop != NULL) { GMainLoop *mloop = mainloop; mainloop = NULL; // Don't re-enter this block pcmk_quit_main_loop(mloop, 10); g_main_loop_unref(mloop); } } static void -controller_event_cb(pcmk_ipc_api_t *controld_api, - enum pcmk_ipc_event event_type, crm_exit_t status, - void *event_data, void *user_data) +event_done(pcmk_ipc_api_t *api) +{ + pcmk_disconnect_ipc(api); + quit_main_loop(exit_code); +} + +static pcmk_controld_api_reply_t * +controld_event_reply(pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data) { pcmk_controld_api_reply_t *reply = event_data; switch (event_type) { case pcmk_ipc_event_disconnect: if (exit_code == CRM_EX_DISCONNECT) { // Unexpected out->err(out, "error: Lost connection to controller"); } - goto done; - break; + event_done(controld_api); + return NULL; case pcmk_ipc_event_reply: break; default: - return; + return NULL; } if (message_timer_id != 0) { g_source_remove(message_timer_id); message_timer_id = 0; } if (status != CRM_EX_OK) { out->err(out, "error: Bad reply from controller: %s", crm_exit_str(status)); exit_code = status; - goto done; + event_done(controld_api); + return NULL; } if (reply->reply_type != pcmk_controld_reply_ping) { out->err(out, "error: Unknown reply type %d from controller", reply->reply_type); - goto done; + event_done(controld_api); + return NULL; } - // Parse desired information from reply - switch (command) { - case cmd_health: - out->message(out, "health", - reply->data.ping.sys_from, - reply->host_from, - reply->data.ping.fsa_state, - reply->data.ping.result); - exit_code = CRM_EX_OK; - break; + return reply; +} - case cmd_whois_dc: - out->message(out, "dc", reply->host_from); - exit_code = CRM_EX_OK; - break; +static void +controller_status_event_cb(pcmk_ipc_api_t *controld_api, + enum pcmk_ipc_event event_type, crm_exit_t status, + void *event_data, void *user_data) +{ + pcmk_controld_api_reply_t *reply = controld_event_reply(controld_api, + event_type, status, event_data); - default: // Not really possible here - exit_code = CRM_EX_SOFTWARE; - break; + if (reply != NULL) { + out->message(out, "health", + reply->data.ping.sys_from, + reply->host_from, + reply->data.ping.fsa_state, + reply->data.ping.result); + exit_code = CRM_EX_OK; } -done: - pcmk_disconnect_ipc(controld_api); - quit_main_loop(exit_code); + event_done(controld_api); +} + +static void +designated_controller_event_cb(pcmk_ipc_api_t *controld_api, + enum pcmk_ipc_event event_type, crm_exit_t status, + void *event_data, void *user_data) +{ + pcmk_controld_api_reply_t *reply = controld_event_reply(controld_api, + event_type, status, event_data); + + if (reply != NULL) { + out->message(out, "dc", reply->host_from); + exit_code = CRM_EX_OK; + } + + event_done(controld_api); } 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; + crm_time_t *crm_when = crm_time_new(NULL); + char *pinged_buf = NULL; + switch (event_type) { case pcmk_ipc_event_disconnect: if (exit_code == CRM_EX_DISCONNECT) { // Unexpected out->err(out, "error: Lost connection to pacemakerd"); } - goto done; - break; + event_done(pacemakerd_api); + return; case pcmk_ipc_event_reply: break; default: return; } if (message_timer_id != 0) { g_source_remove(message_timer_id); message_timer_id = 0; } if (status != CRM_EX_OK) { out->err(out, "error: Bad reply from pacemakerd: %s", crm_exit_str(status)); - exit_code = status; - goto done; + event_done(pacemakerd_api); + return; } if (reply->reply_type != pcmk_pacemakerd_reply_ping) { out->err(out, "error: Unknown reply type %d from pacemakerd", reply->reply_type); - goto done; + event_done(pacemakerd_api); + return; } // Parse desired information from reply - switch (command) { - case cmd_pacemakerd_health: - { - crm_time_t *crm_when = crm_time_new(NULL); - char *pinged_buf = NULL; - - crm_time_set_timet(crm_when, &reply->data.ping.last_good); - pinged_buf = crm_time_as_string(crm_when, - crm_time_log_date | crm_time_log_timeofday | - crm_time_log_with_timezone); - - out->message(out, "pacemakerd-health", - reply->data.ping.sys_from, - (reply->data.ping.status == pcmk_rc_ok)? - pcmk_pacemakerd_api_daemon_state_enum2text( - reply->data.ping.state):"query failed", - (reply->data.ping.status == pcmk_rc_ok)?pinged_buf:""); - exit_code = CRM_EX_OK; - free(pinged_buf); - } - break; + crm_time_set_timet(crm_when, &reply->data.ping.last_good); + pinged_buf = crm_time_as_string(crm_when, + crm_time_log_date | crm_time_log_timeofday | + crm_time_log_with_timezone); + + out->message(out, "pacemakerd-health", + reply->data.ping.sys_from, + (reply->data.ping.status == pcmk_rc_ok)? + pcmk_pacemakerd_api_daemon_state_enum2text( + reply->data.ping.state):"query failed", + (reply->data.ping.status == pcmk_rc_ok)?pinged_buf:""); + exit_code = CRM_EX_OK; + free(pinged_buf); + + event_done(pacemakerd_api); +} - default: // Not really possible here - exit_code = CRM_EX_SOFTWARE; - break; +static pcmk_ipc_api_t * +ipc_connect(enum pcmk_ipc_server server, pcmk_ipc_callback_t cb) +{ + int rc; + pcmk_ipc_api_t *api = NULL; + + rc = pcmk_new_ipc_api(&api, server); + if (api == NULL) { + out->err(out, "error: Could not connect to %s: %s", + (server == pcmk_ipc_controld) ? "controller" : "pacemakerd", + pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); + return NULL; + } + pcmk_register_ipc_callback(api, cb, NULL); + rc = pcmk_connect_ipc(api, pcmk_ipc_dispatch_main); + if (rc != pcmk_rc_ok) { + out->err(out, "error: Could not connect to %s: %s", + (server == pcmk_ipc_controld) ? "controller" : "pacemakerd", + pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); + return NULL; } -done: - pcmk_disconnect_ipc(pacemakerd_api); - quit_main_loop(exit_code); + return api; +} + +static void +pcmk__controller_status() +{ + pcmk_ipc_api_t *controld_api = ipc_connect(pcmk_ipc_controld, controller_status_event_cb); + + if (controld_api != NULL) { + int rc = pcmk_controld_api_ping(controld_api, dest_node); + if (rc != pcmk_rc_ok) { + out->err(out, "error: Command failed: %s", pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); + } + + start_main_loop(); + + pcmk_free_ipc_api(controld_api); + } +} + +static void +pcmk__designated_controller() +{ + pcmk_ipc_api_t *controld_api = ipc_connect(pcmk_ipc_controld, designated_controller_event_cb); + + if (controld_api != NULL) { + int rc = pcmk_controld_api_ping(controld_api, dest_node); + if (rc != pcmk_rc_ok) { + out->err(out, "error: Command failed: %s", pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); + } + + start_main_loop(); + + pcmk_free_ipc_api(controld_api); + } +} + +static void +pcmk__pacemakerd_status() +{ + pcmk_ipc_api_t *pacemakerd_api = ipc_connect(pcmk_ipc_pacemakerd, pacemakerd_event_cb); + + if (pacemakerd_api != NULL) { + int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name); + if (rc != pcmk_rc_ok) { + out->err(out, "error: Command failed: %s", pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); + } + + start_main_loop(); + + pcmk_free_ipc_api(pacemakerd_api); + } } // \return Standard Pacemaker return code static int -list_nodes() +pcmk__list_nodes() { cib_t *the_cib = cib_new(); xmlNode *output = NULL; int rc; if (the_cib == NULL) { return ENOMEM; } rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command); if (rc != pcmk_ok) { return pcmk_legacy2rc(rc); } rc = the_cib->cmds->query(the_cib, NULL, &output, cib_scope_local | cib_sync_call); if (rc == pcmk_ok) { out->message(out, "crmadmin-node-list", output); free_xml(output); } the_cib->cmds->signoff(the_cib); return pcmk_legacy2rc(rc); } static GOptionContext * build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) { GOptionContext *context = NULL; const char *description = "Report bugs to users@clusterlabs.org"; GOptionEntry extra_prog_entries[] = { { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet), "Display only the essential query information", NULL }, { NULL } }; context = pcmk__build_arg_context(args, "text (default), xml", group, NULL); g_option_context_set_description(context, description); /* Add the -q option, which cannot be part of the globally supported options * because some tools use that flag for something else. */ pcmk__add_main_args(context, extra_prog_entries); pcmk__add_arg_group(context, "command", "Commands:", "Show command options", command_options); pcmk__add_arg_group(context, "additional", "Additional Options:", "Show additional options", additional_options); return context; } int main(int argc, char **argv) { int argerr = 0; int rc; pcmk_ipc_api_t *controld_api = NULL; - pcmk_ipc_api_t *pacemakerd_api = NULL; pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); GError *error = NULL; GOptionContext *context = NULL; GOptionGroup *output_group = NULL; gchar **processed_args = NULL; context = build_arg_context(args, &output_group); pcmk__register_formats(output_group, formats); crm_log_cli_init("crmadmin"); processed_args = pcmk__cmdline_preproc(argv, "itBDEHKNPS"); if (!g_option_context_parse_strv(context, &processed_args, &error)) { fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message); exit_code = CRM_EX_USAGE; goto done; } for (int i = 0; i < args->verbosity; i++) { BE_VERBOSE = TRUE; crm_bump_log_level(argc, argv); } rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv); if (rc != pcmk_rc_ok) { fprintf(stderr, "Error creating output format %s: %s\n", args->output_ty, pcmk_rc_str(rc)); exit_code = CRM_EX_ERROR; goto done; } out->quiet = args->quiet; pcmk__register_messages(out, fmt_functions); if (!pcmk__force_args(context, &error, "%s --xml-simple-list --xml-substitute", g_get_prgname())) { goto done; } if (args->version) { out->version(out, false); goto done; } if (options.timeout) { message_timeout_ms = (guint) options.timeout; if (message_timeout_ms < 1) { message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS; } } if (options.health) { out->err(out, "Cluster-wide health option not supported"); ++argerr; } if (optind > argc) { ++argerr; } if (command == cmd_none) { out->err(out, "error: Must specify a command option"); ++argerr; } if (argerr) { char *help = g_option_context_get_help(context, TRUE, NULL); out->err(out, "%s", help); g_free(help); exit_code = CRM_EX_USAGE; goto done; } - // Connect to the controller if needed - if (need_controld_api) { - rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld); - if (controld_api == NULL) { - out->err(out, "error: Could not connect to controller: %s", - pcmk_rc_str(rc)); - exit_code = pcmk_rc2exitc(rc); - goto done; - } - pcmk_register_ipc_callback(controld_api, controller_event_cb, NULL); - rc = pcmk_connect_ipc(controld_api, pcmk_ipc_dispatch_main); - if (rc != pcmk_rc_ok) { - out->err(out, "error: Could not connect to controller: %s", - pcmk_rc_str(rc)); - exit_code = pcmk_rc2exitc(rc); + switch (command) { + case cmd_health: + pcmk__controller_status(); goto done; - } - } - - // Connect to pacemakerd if needed - if (need_pacemakerd_api) { - rc = pcmk_new_ipc_api(&pacemakerd_api, pcmk_ipc_pacemakerd); - if (pacemakerd_api == NULL) { - out->err(out, "error: Could not connect to pacemakerd: %s", - pcmk_rc_str(rc)); - exit_code = pcmk_rc2exitc(rc); + case cmd_pacemakerd_health: + pcmk__pacemakerd_status(); goto done; - } - pcmk_register_ipc_callback(pacemakerd_api, pacemakerd_event_cb, NULL); - rc = pcmk_connect_ipc(pacemakerd_api, pcmk_ipc_dispatch_main); - if (rc != pcmk_rc_ok) { - out->err(out, "error: Could not connect to pacemakerd: %s", - pcmk_rc_str(rc)); - exit_code = pcmk_rc2exitc(rc); + case cmd_list_nodes: + rc = pcmk__list_nodes(); + // might need movink + if (rc != pcmk_rc_ok) { + out->err(out, "error: Command failed: %s", pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); + } + break; + case cmd_whois_dc: + pcmk__designated_controller(); goto done; - } + default: + rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld); + if (controld_api == NULL) { + out->err(out, "error: Could not connect to controller: %s", + pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); + goto done; + } + rc = pcmk_connect_ipc(controld_api, pcmk_ipc_dispatch_main); + if (rc != pcmk_rc_ok) { + out->err(out, "error: Could not connect to controller: %s", + pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); + goto done; + } + break; } - if (do_work(controld_api?controld_api:pacemakerd_api)) { + if (do_work(controld_api?controld_api:NULL)) { // A reply is needed from controller, so run main loop to get it - exit_code = CRM_EX_DISCONNECT; // For unexpected disconnects - mainloop = g_main_loop_new(NULL, FALSE); - message_timer_id = g_timeout_add(message_timeout_ms, - admin_message_timeout, NULL); - g_main_loop_run(mainloop); + start_main_loop(); } done: if (controld_api != NULL) { pcmk_ipc_api_t *capi = controld_api; controld_api = NULL; // Ensure we can't free this twice pcmk_free_ipc_api(capi); } - if (pacemakerd_api != NULL) { - pcmk_ipc_api_t *capi = pacemakerd_api; - pacemakerd_api = NULL; // Ensure we can't free this twice - pcmk_free_ipc_api(capi); - } - if (mainloop != NULL) { g_main_loop_unref(mainloop); mainloop = NULL; } g_strfreev(processed_args); g_clear_error(&error); pcmk__free_arg_context(context); if (out != NULL) { out->finish(out, exit_code, true, NULL); pcmk__output_free(out); } return crm_exit(exit_code); } // \return True if reply from controller is needed bool do_work(pcmk_ipc_api_t *api) { bool need_reply = false; int rc = pcmk_rc_ok; switch (command) { case cmd_shutdown: rc = pcmk_controld_api_shutdown(api, dest_node); break; - case cmd_health: // dest_node != NULL - case cmd_whois_dc: // dest_node == NULL - rc = pcmk_controld_api_ping(api, dest_node); - need_reply = true; - break; - case cmd_elect_dc: rc = pcmk_controld_api_start_election(api); break; - case cmd_list_nodes: - rc = list_nodes(); - break; - - case cmd_pacemakerd_health: - rc = pcmk_pacemakerd_api_ping(api, ipc_name); - need_reply = true; + case cmd_none: // not actually possible here break; - case cmd_none: // not actually possible here + default: break; } if (rc != pcmk_rc_ok) { out->err(out, "error: Command failed: %s", pcmk_rc_str(rc)); exit_code = pcmk_rc2exitc(rc); } return need_reply; } gboolean admin_message_timeout(gpointer data) { out->err(out, "error: No reply received from controller before timeout (%dms)", message_timeout_ms); message_timer_id = 0; quit_main_loop(CRM_EX_TIMEOUT); return FALSE; // Tells glib to remove source }