| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <crm_internal.h> |
| |
| #include <stdio.h> |
| #include <stdbool.h> |
| #include <stdlib.h> // atoi() |
| |
| #include <glib.h> // gboolean, GMainLoop, etc. |
| #include <libxml/tree.h> // xmlNode |
| |
| #include <crm/crm.h> |
| #include <crm/cib.h> |
| #include <crm/msg_xml.h> |
| #include <crm/common/cmdline_internal.h> |
| #include <crm/common/output_internal.h> |
| #include <crm/common/xml.h> |
| #include <crm/common/iso8601.h> |
| #include <crm/common/ipc_controld.h> |
| #include <crm/common/ipc_pacemakerd.h> |
| #include <crm/common/mainloop.h> |
| |
| #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++; |
| } |
| |
| |
| 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 |
| quit_main_loop(crm_exit_t ec) |
| { |
| exit_code = ec; |
| if (mainloop != NULL) { |
| GMainLoop *mloop = mainloop; |
| |
| mainloop = NULL; |
| 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) |
| { |
| pcmk_controld_api_reply_t *reply = event_data; |
| |
| switch (event_type) { |
| case pcmk_ipc_event_disconnect: |
| if (exit_code == CRM_EX_DISCONNECT) { |
| out->err(out, "error: Lost connection to controller"); |
| } |
| goto done; |
| break; |
| |
| 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 controller: %s", |
| crm_exit_str(status)); |
| exit_code = status; |
| goto done; |
| } |
| |
| if (reply->reply_type != pcmk_controld_reply_ping) { |
| out->err(out, "error: Unknown reply type %d from controller", |
| reply->reply_type); |
| goto done; |
| } |
| |
| |
| 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; |
| |
| case cmd_whois_dc: |
| out->message(out, "dc", reply->host_from); |
| exit_code = CRM_EX_OK; |
| break; |
| |
| default: |
| exit_code = CRM_EX_SOFTWARE; |
| break; |
| } |
| |
| done: |
| pcmk_disconnect_ipc(controld_api); |
| quit_main_loop(exit_code); |
| } |
| |
| 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_disconnect: |
| if (exit_code == CRM_EX_DISCONNECT) { |
| out->err(out, "error: Lost connection to pacemakerd"); |
| } |
| goto done; |
| break; |
| |
| 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; |
| } |
| |
| if (reply->reply_type != pcmk_pacemakerd_reply_ping) { |
| out->err(out, "error: Unknown reply type %d from pacemakerd", |
| reply->reply_type); |
| goto done; |
| } |
| |
| |
| 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; |
| |
| default: |
| exit_code = CRM_EX_SOFTWARE; |
| break; |
| } |
| |
| done: |
| pcmk_disconnect_ipc(pacemakerd_api); |
| quit_main_loop(exit_code); |
| } |
| |
| |
| static int |
| 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); |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| 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); |
| goto done; |
| } |
| } |
| |
| |
| 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); |
| 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); |
| goto done; |
| } |
| } |
| |
| if (do_work(controld_api?controld_api:pacemakerd_api)) { |
| |
| exit_code = CRM_EX_DISCONNECT; |
| 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); |
| } |
| |
| done: |
| |
| if (controld_api != NULL) { |
| pcmk_ipc_api_t *capi = controld_api; |
| controld_api = NULL; |
| pcmk_free_ipc_api(capi); |
| } |
| |
| if (pacemakerd_api != NULL) { |
| pcmk_ipc_api_t *capi = pacemakerd_api; |
| pacemakerd_api = NULL; |
| 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); |
| |
| } |
| |
| |
| 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: |
| case cmd_whois_dc: |
| 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; |
| break; |
| |
| case cmd_none: |
| 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; |
| } |