diff --git a/tools/Makefile.am b/tools/Makefile.am index 70a3ebed4d..a278fa352f 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,168 +1,169 @@ # # Copyright 2004-2019 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 $(top_srcdir)/mk/common.mk if BUILD_SYSTEMD systemdsystemunit_DATA = crm_mon.service endif noinst_HEADERS = crm_mon.h crm_resource.h pcmkdir = $(datadir)/$(PACKAGE) pcmk_DATA = report.common report.collector sbin_SCRIPTS = crm_report crm_standby crm_master crm_failcount if BUILD_CIBSECRETS sbin_SCRIPTS += cibsecret endif noinst_SCRIPTS = pcmk_simtimes EXTRA_DIST = crm_diff.8.inc \ crm_error.8.inc \ crm_mon.sysconfig \ crm_mon.8.inc \ crm_node.8.inc \ crm_resource.8.inc \ crm_rule.8.inc \ crm_simulate.8.inc \ + crmadmin.8.inc \ fix-manpages \ stonith_admin.8.inc sbin_PROGRAMS = attrd_updater \ cibadmin \ crmadmin \ crm_simulate \ crm_attribute \ crm_diff \ crm_error \ crm_mon \ crm_node \ crm_resource \ crm_rule \ crm_shadow \ crm_verify \ crm_ticket \ iso8601 \ stonith_admin if BUILD_SERVICELOG sbin_PROGRAMS += notifyServicelogEvent endif if BUILD_OPENIPMI_SERVICELOG sbin_PROGRAMS += ipmiservicelogd endif ## SOURCES # A few tools are just thin wrappers around crm_attribute. # This makes their help get updated when crm_attribute changes # (see mk/common.mk). MAN8DEPS = crm_attribute crmadmin_SOURCES = crmadmin.c crmadmin_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_error_SOURCES = crm_error.c crm_error_LDADD = $(top_builddir)/lib/common/libcrmcommon.la cibadmin_SOURCES = cibadmin.c cibadmin_LDADD = $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_shadow_SOURCES = crm_shadow.c crm_shadow_LDADD = $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_node_SOURCES = crm_node.c crm_node_LDADD = $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_simulate_SOURCES = crm_simulate.c crm_simulate_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_diff_SOURCES = crm_diff.c crm_diff_LDADD = $(top_builddir)/lib/common/libcrmcommon.la crm_mon_SOURCES = crm_mon.c crm_mon_curses.c crm_mon_print.c crm_mon_runtime.c crm_mon_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/fencing/libstonithd.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la \ $(CURSESLIBS) crm_verify_SOURCES = crm_verify.c crm_verify_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_attribute_SOURCES = crm_attribute.c crm_attribute_LDADD = $(top_builddir)/lib/cluster/libcrmcluster.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_resource_SOURCES = crm_resource.c \ crm_resource_ban.c \ crm_resource_print.c \ crm_resource_runtime.c crm_resource_LDADD = $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/fencing/libstonithd.la \ $(top_builddir)/lib/lrmd/liblrmd.la \ $(top_builddir)/lib/services/libcrmservice.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_rule_SOURCES = crm_rule.c crm_rule_LDADD = $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/common/libcrmcommon.la iso8601_SOURCES = iso8601.c iso8601_LDADD = $(top_builddir)/lib/common/libcrmcommon.la attrd_updater_SOURCES = attrd_updater.c attrd_updater_LDADD = $(top_builddir)/lib/common/libcrmcommon.la crm_ticket_SOURCES = crm_ticket.c crm_ticket_LDADD = $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la stonith_admin_SOURCES = stonith_admin.c stonith_admin_LDADD = $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/fencing/libstonithd.la \ $(top_builddir)/lib/common/libcrmcommon.la if BUILD_SERVICELOG notifyServicelogEvent_SOURCES = notifyServicelogEvent.c notifyServicelogEvent_CFLAGS = $(SERVICELOG_CFLAGS) notifyServicelogEvent_LDADD = $(top_builddir)/lib/common/libcrmcommon.la $(SERVICELOG_LIBS) endif if BUILD_OPENIPMI_SERVICELOG ipmiservicelogd_SOURCES = ipmiservicelogd.c ipmiservicelogd_CFLAGS = $(OPENIPMI_SERVICELOG_CFLAGS) $(SERVICELOG_CFLAGS) ipmiservicelogd_LDFLAGS = $(top_builddir)/lib/common/libcrmcommon.la $(OPENIPMI_SERVICELOG_LIBS) $(SERVICELOG_LIBS) endif CLEANFILES = $(man8_MANS) diff --git a/tools/crmadmin.8.inc b/tools/crmadmin.8.inc new file mode 100644 index 0000000000..ab5efbe0a9 --- /dev/null +++ b/tools/crmadmin.8.inc @@ -0,0 +1,5 @@ +[synopsis] +crmadmin [options] [node] + +/the Pacemaker controller/ +.SH OPTIONS diff --git a/tools/crmadmin.c b/tools/crmadmin.c index 2ebdd14588..5f3d9d5e75 100644 --- a/tools/crmadmin.c +++ b/tools/crmadmin.c @@ -1,614 +1,618 @@ /* * 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 +#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); void do_find_node_list(xmlNode *xml_node); 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 gboolean BE_SILENT = FALSE; static char *dest_node = NULL; static crm_exit_t exit_code = CRM_EX_OK; -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 - }, - { - "quiet", no_argument, NULL, 'q', - "\tDisplay only the essential query information", pcmk__option_default - }, - { - "verbose", no_argument, NULL, 'V', - "\tIncrease debug output", pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\nCommands:", pcmk__option_default - }, - /* daemon options */ - { - "status", required_argument, NULL, 'S', - "Display the status of the specified node.", pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\n\tResult is state of node's internal finite state machine, which " - "can be useful for debugging\n", - pcmk__option_default - }, - { - "pacemakerd", no_argument, NULL, 'P', - "Display the status of local pacemakerd.", pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\n\tResult is the state of the sub-daemons watched by pacemakerd.\n", - pcmk__option_default - }, - { - "dc_lookup", no_argument, NULL, 'D', - "Display the uname of the node co-ordinating the cluster.", - pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\n\tThis is an internal detail rarely useful to administrators " - "except when deciding on which node to examine the logs.\n", - pcmk__option_default - }, - { - "nodes", no_argument, NULL, 'N', - "\tDisplay the uname of all member nodes", pcmk__option_default + +struct { + gboolean quiet; + 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 }, - { - "election", no_argument, NULL, 'E', - "(Advanced) Start an election for the cluster co-ordinator", - pcmk__option_default + { "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 }, - { - "kill", required_argument, NULL, 'K', - "(Advanced) Stop controller (not rest of cluster stack) on " - "specified node", pcmk__option_default + { "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 }, - { - "health", no_argument, NULL, 'H', - NULL, pcmk__option_hidden + { "nodes", 'N', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the uname of all member nodes", + NULL }, - { - "-spacer-", no_argument, NULL, '-', - "\nAdditional Options:", pcmk__option_default + { "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 }, - { - XML_ATTR_TIMEOUT, required_argument, NULL, 't', - "Time (in milliseconds) to wait before declaring the operation failed", - pcmk__option_default + { "kill", 'K', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, command_cb, + "(Advanced) Stop controller (not rest of cluster stack) on specified node", + NULL }, - { - "bash-export", no_argument, NULL, 'B', - "Display nodes as shell commands of the form 'export uname=uuid' " - "(valid with -N/--nodes)", - pcmk__option_default + { "health", 'H', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.health, + NULL, + NULL }, - { - "ipc-name", required_argument, NULL, 'i', - "Name to use for ipc instead of 'crmadmin' (with -P/--pacemakerd).", - pcmk__option_default + + { 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 }, - { - "-spacer-", no_argument, NULL, '-', - "\nNotes:", pcmk__option_default + { "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)", }, - { - "-spacer-", no_argument, NULL, '-', - "\nThe -K and -E commands do not work and may be removed in a future " - "version.", - pcmk__option_default + { "ipc-name", 'i', 0, G_OPTION_ARG_STRING, &ipc_name, + "Name to use for ipc instead of 'crmadmin' (with -P/--pacemakerd).", + NULL }, - { 0, 0, 0, 0 } + + { 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; +} + 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) { pcmk_controld_api_reply_t *reply = event_data; switch (event_type) { case pcmk_ipc_event_disconnect: if (exit_code == CRM_EX_DISCONNECT) { // Unexpected fprintf(stderr, "error: Lost connection to controller\n"); } 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) { fprintf(stderr, "error: Bad reply from controller: %s", crm_exit_str(status)); exit_code = status; goto done; } if (reply->reply_type != pcmk_controld_reply_ping) { fprintf(stderr, "error: Unknown reply type %d from controller\n", reply->reply_type); goto done; } // Parse desired information from reply switch (command) { case cmd_health: printf("Status of %s@%s: %s (%s)\n", reply->data.ping.sys_from, reply->host_from, reply->data.ping.fsa_state, reply->data.ping.result); if (BE_SILENT && (reply->data.ping.fsa_state != NULL)) { fprintf(stderr, "%s\n", reply->data.ping.fsa_state); } exit_code = CRM_EX_OK; break; case cmd_whois_dc: printf("Designated Controller is: %s\n", reply->host_from); if (BE_SILENT && (reply->host_from != NULL)) { fprintf(stderr, "%s\n", reply->host_from); } exit_code = CRM_EX_OK; break; default: // Not really possible here 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) { // Unexpected fprintf(stderr, "error: Lost connection to pacemakerd\n"); } 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) { fprintf(stderr, "error: Bad reply from pacemakerd: %s", crm_exit_str(status)); exit_code = status; goto done; } if (reply->reply_type != pcmk_pacemakerd_reply_ping) { fprintf(stderr, "error: Unknown reply type %d from pacemakerd\n", reply->reply_type); goto done; } // 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); printf("Status of %s: '%s' %s %s\n", 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)?"last updated":"", (reply->data.ping.status == pcmk_rc_ok)?pinged_buf:""); if (BE_SILENT && (reply->data.ping.state != pcmk_pacemakerd_state_invalid)) { fprintf(stderr, "%s\n", (reply->data.ping.status == pcmk_rc_ok)? pcmk_pacemakerd_api_daemon_state_enum2text( reply->data.ping.state): "query failed"); } exit_code = CRM_EX_OK; free(pinged_buf); } break; default: // Not really possible here exit_code = CRM_EX_SOFTWARE; break; } done: pcmk_disconnect_ipc(pacemakerd_api); quit_main_loop(exit_code); } // \return Standard Pacemaker return 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) { do_find_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) { + GOptionContext *context = NULL; + + const char *description = "Report bugs to users@clusterlabs.org"; + + GOptionEntry extra_prog_entries[] = { + { "quiet", 'q', 0, G_OPTION_ARG_NONE, &options.quiet, + "Display only the essential query information", + NULL }, + + { NULL } + }; + + context = pcmk__build_arg_context(args, NULL, NULL, 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 option_index = 0; int argerr = 0; - int flag; int rc; pcmk_ipc_api_t *controld_api = NULL; pcmk_ipc_api_t *pacemakerd_api = NULL; - bool need_controld_api = true; - bool need_pacemakerd_api = false; + + pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); + + GError *error = NULL; + GOptionContext *context = NULL; + gchar **processed_args = NULL; + + context = build_arg_context(args); crm_log_cli_init("crmadmin"); - pcmk__set_cli_options(NULL, " [options]", long_options, - "query and manage the Pacemaker controller"); - if (argc < 2) { - pcmk__cli_help('?', CRM_EX_USAGE); + + 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); + rc = CRM_EX_USAGE; + goto done; } - while (1) { - flag = pcmk__next_cli_option(argc, argv, &option_index, NULL); - if (flag == -1) - break; + for (int i = 0; i < args->verbosity; i++) { + BE_VERBOSE = TRUE; + crm_bump_log_level(argc, argv); + } - switch (flag) { - case 'V': - BE_VERBOSE = TRUE; - crm_bump_log_level(argc, argv); - break; - case 't': - message_timeout_ms = (guint) atoi(optarg); - if (message_timeout_ms < 1) { - message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS; - } - break; - case 'i': - ipc_name = strdup(optarg); - break; - case '$': - case '?': - pcmk__cli_help(flag, CRM_EX_OK); - break; - case 'D': - command = cmd_whois_dc; - break; - case 'B': - BASH_EXPORT = TRUE; - break; - case 'K': - command = cmd_shutdown; - crm_trace("Option %c => %s", flag, optarg); - if (dest_node != NULL) { - free(dest_node); - } - dest_node = strdup(optarg); - break; - case 'q': - BE_SILENT = TRUE; - break; - case 'P': - command = cmd_pacemakerd_health; - need_pacemakerd_api = true; - need_controld_api = false; - break; - case 'S': - command = cmd_health; - crm_trace("Option %c => %s", flag, optarg); - if (dest_node != NULL) { - free(dest_node); - } - dest_node = strdup(optarg); - break; - case 'E': - command = cmd_elect_dc; - break; - case 'N': - command = cmd_list_nodes; - need_controld_api = false; - break; - case 'H': - fprintf(stderr, "Cluster-wide health option not supported\n"); - ++argerr; - break; - default: - printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); - ++argerr; - break; + if (args->version) { + /* FIXME: When crmadmin is converted to use formatted output, this can go. */ + pcmk__cli_help('v', CRM_EX_USAGE); + } + + if (options.timeout) { + message_timeout_ms = (guint) options.timeout; + if (message_timeout_ms < 1) { + message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS; } } - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); + if (options.quiet) { + BE_SILENT = TRUE; + } + + if (options.health) { + fprintf(stderr, "Cluster-wide health option not supported\n"); + ++argerr; } if (optind > argc) { ++argerr; } if (command == cmd_none) { fprintf(stderr, "error: Must specify a command option\n\n"); ++argerr; } if (argerr) { - pcmk__cli_help('?', CRM_EX_USAGE); + char *help = g_option_context_get_help(context, TRUE, NULL); + + fprintf(stderr, "%s", help); + g_free(help); + rc = 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) { fprintf(stderr, "error: Could not connect to controller: %s\n", 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) { fprintf(stderr, "error: Could not connect to controller: %s\n", pcmk_rc_str(rc)); exit_code = pcmk_rc2exitc(rc); 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) { fprintf(stderr, "error: Could not connect to pacemakerd: %s\n", 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) { fprintf(stderr, "error: Could not connect to pacemakerd: %s\n", pcmk_rc_str(rc)); exit_code = pcmk_rc2exitc(rc); goto done; } } if (do_work(controld_api?controld_api:pacemakerd_api)) { // 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); } 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); 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; break; case cmd_none: // not actually possible here break; } if (rc != pcmk_rc_ok) { fprintf(stderr, "error: Command failed: %s", pcmk_rc_str(rc)); exit_code = pcmk_rc2exitc(rc); } return need_reply; } gboolean admin_message_timeout(gpointer data) { fprintf(stderr, "error: No reply received from controller before timeout (%dms)\n", message_timeout_ms); message_timer_id = 0; quit_main_loop(CRM_EX_TIMEOUT); return FALSE; // Tells glib to remove source } void do_find_node_list(xmlNode * xml_node) { int found = 0; xmlNode *node = NULL; xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node); for (node = first_named_child(nodes, XML_CIB_TAG_NODE); node != NULL; node = crm_next_same_xml(node)) { if (BASH_EXPORT) { printf("export %s=%s\n", crm_element_value(node, XML_ATTR_UNAME), crm_element_value(node, XML_ATTR_ID)); } else { const char *node_type = crm_element_value(node, XML_ATTR_TYPE); if (node_type == NULL) { node_type = "member"; } printf("%s node: %s (%s)\n", node_type, crm_element_value(node, XML_ATTR_UNAME), crm_element_value(node, XML_ATTR_ID)); } found++; } // @TODO List Pacemaker Remote nodes that don't have a entry if (found == 0) { printf("No nodes configured\n"); } }