diff --git a/include/pacemaker-internal.h b/include/pacemaker-internal.h index 5a14ebffb3..48f0162e1e 100644 --- a/include/pacemaker-internal.h +++ b/include/pacemaker-internal.h @@ -1,26 +1,25 @@ /* * Copyright 2019-2022 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 PACEMAKER_INTERNAL__H # define PACEMAKER_INTERNAL__H # include -# include # include # include # include # include # include # include # include # include # include # include #endif diff --git a/include/pcmki/Makefile.am b/include/pcmki/Makefile.am index 3ba6aa8a0b..e1db94198a 100644 --- a/include/pcmki/Makefile.am +++ b/include/pcmki/Makefile.am @@ -1,25 +1,24 @@ # # Copyright 2019-2022 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. # MAINTAINERCLEANFILES = Makefile.in noinst_HEADERS = pcmki_acl.h \ - pcmki_error.h \ pcmki_cluster_queries.h \ pcmki_fence.h \ pcmki_output.h \ pcmki_resource.h \ pcmki_sched_allocate.h \ pcmki_sched_utils.h \ pcmki_scheduler.h \ pcmki_simulate.h \ pcmki_status.h \ pcmki_transition.h .PHONY: $(ARCHIVE_VERSION) diff --git a/include/pcmki/pcmki_error.h b/include/pcmki/pcmki_error.h deleted file mode 100644 index cc66086710..0000000000 --- a/include/pcmki/pcmki_error.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2007-2019 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__PCMKI_PCMKI_ERROR__H -# define PCMK__PCMKI_PCMKI_ERROR__H - -#define CMD_ERR(fmt, args...) do { \ - crm_warn(fmt, ##args); \ - fprintf(stderr, fmt "\n", ##args); \ - } while(0) - -#endif diff --git a/tools/Makefile.am b/tools/Makefile.am index f4709e2fe5..b40d5345e7 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,176 +1,177 @@ # # Copyright 2004-2022 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 include $(top_srcdir)/mk/man.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 = attrd_updater.8.inc \ crm_attribute.8.inc \ crm_diff.8.inc \ crm_error.8.inc \ crm_mon.8.inc \ crm_node.8.inc \ crm_resource.8.inc \ crm_rule.8.inc \ crm_simulate.8.inc \ + crm_ticket.8.inc \ crm_verify.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 \ $(top_builddir)/lib/pacemaker/libpacemaker.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/pacemaker/libpacemaker.la \ $(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_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/pacemaker/libpacemaker.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/pacemaker/libpacemaker.la \ $(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/crm_ticket.8.inc b/tools/crm_ticket.8.inc new file mode 100644 index 0000000000..5ae5124ff9 --- /dev/null +++ b/tools/crm_ticket.8.inc @@ -0,0 +1,5 @@ +[synopsis] +crm_ticket | [options] + +/and deleted./ +.SH OPTIONS diff --git a/tools/crm_ticket.c b/tools/crm_ticket.c index 5b8d9fb024..8abfc59562 100644 --- a/tools/crm_ticket.c +++ b/tools/crm_ticket.c @@ -1,1077 +1,1021 @@ /* * Copyright 2012-2022 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include -gboolean do_force = FALSE; -gboolean BE_QUIET = FALSE; -const char *ticket_id = NULL; -const char *get_attr_name = NULL; -const char *attr_name = NULL; -const char *attr_value = NULL; -const char *attr_id = NULL; -const char *set_name = NULL; -const char *attr_default = NULL; -const char *xml_file = NULL; -char ticket_cmd = 'S'; +GError *error = NULL; + +#define SUMMARY "Perform tasks related to cluster tickets\n\n" \ + "Allows ticket attributes to be queried, modified and deleted." + +struct { + gchar *attr_default; + gchar *attr_id; + char *attr_name; + char *attr_value; + gboolean force; + char *get_attr_name; + gboolean quiet; + gchar *set_name; + char ticket_cmd; + gchar *ticket_id; + gchar *xml_file; +} options = { + .ticket_cmd = 'S' +}; + +GList *attr_delete; +GHashTable *attr_set; +bool modified = false; int cib_options = cib_sync_call; -static pe_ticket_t * -find_ticket(const char *ticket_id, pe_working_set_t * data_set) -{ - pe_ticket_t *ticket = NULL; +#define INDENT " " - ticket = g_hash_table_lookup(data_set->tickets, ticket_id); +static gboolean +attr_value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + pcmk__str_update(&options.attr_value, optarg); - return ticket; + if (!options.attr_name || !options.attr_value) { + return TRUE; + } + + g_hash_table_insert(attr_set, strdup(options.attr_name), strdup(options.attr_value)); + pcmk__str_update(&options.attr_name, NULL); + pcmk__str_update(&options.attr_value, NULL); + + modified = true; + + return TRUE; +} + +static gboolean +command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + if (pcmk__str_any_of(option_name, "--info", "-l", NULL)) { + options.ticket_cmd = 'l'; + } else if (pcmk__str_any_of(option_name, "--details", "-L", NULL)) { + options.ticket_cmd = 'L'; + } else if (pcmk__str_any_of(option_name, "--raw", "-w", NULL)) { + options.ticket_cmd = 'w'; + } else if (pcmk__str_any_of(option_name, "--query-xml", "-q", NULL)) { + options.ticket_cmd = 'q'; + } else if (pcmk__str_any_of(option_name, "--constraints", "-c", NULL)) { + options.ticket_cmd = 'c'; + } else if (pcmk__str_any_of(option_name, "--cleanup", "-C", NULL)) { + options.ticket_cmd = 'C'; + } + + return TRUE; +} + +static gboolean +delete_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + attr_delete = g_list_append(attr_delete, strdup(optarg)); + modified = true; + return TRUE; +} + +static gboolean +get_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + pcmk__str_update(&options.get_attr_name, optarg); + options.ticket_cmd = 'G'; + return TRUE; +} + +static gboolean +grant_standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + if (pcmk__str_any_of(option_name, "--grant", "-g", NULL)) { + g_hash_table_insert(attr_set, strdup("granted"), strdup("true")); + modified = true; + } else if (pcmk__str_any_of(option_name, "--revoke", "-r", NULL)) { + g_hash_table_insert(attr_set, strdup("granted"), strdup("false")); + modified = true; + } else if (pcmk__str_any_of(option_name, "--standby", "-s", NULL)) { + g_hash_table_insert(attr_set, strdup("standby"), strdup("true")); + modified = true; + } else if (pcmk__str_any_of(option_name, "--activate", "-a", NULL)) { + g_hash_table_insert(attr_set, strdup("standby"), strdup("false")); + modified = true; + } + + return TRUE; +} + +static gboolean +set_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + pcmk__str_update(&options.attr_name, optarg); + + if (!options.attr_name || !options.attr_value) { + return TRUE; + } + + g_hash_table_insert(attr_set, strdup(options.attr_name), strdup(options.attr_value)); + pcmk__str_update(&options.attr_name, NULL); + pcmk__str_update(&options.attr_value, NULL); + + modified = true; + + return TRUE; +} + +static GOptionEntry query_entries[] = { + { "info", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the information of ticket(s)", + NULL }, + + { "details", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the details of ticket(s)", + NULL }, + + { "raw", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the IDs of ticket(s)", + NULL }, + + { "query-xml", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Query the XML of ticket(s)", + NULL }, + + { "constraints", 'c', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the rsc_ticket constraints that apply to ticket(s)", + NULL }, + + { NULL } +}; + +static GOptionEntry command_entries[] = { + { "grant", 'g', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb, + "Grant a ticket to this cluster site", + NULL }, + + { "revoke", 'r', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb, + "Revoke a ticket from this cluster site", + NULL }, + + { "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb, + "Tell this cluster site this ticket is standby", + NULL }, + + { "activate", 'a', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb, + "Tell this cluster site this ticket is active", + NULL }, + + { NULL } +}; + +static GOptionEntry advanced_entries[] = { + { "get-attr", 'G', 0, G_OPTION_ARG_CALLBACK, get_attr_cb, + "Display the named attribute for a ticket", + "ATTRIBUTE" }, + + { "set-attr", 'S', 0, G_OPTION_ARG_CALLBACK, set_attr_cb, + "Set the named attribute for a ticket", + "ATTRIBUTE" }, + + { "delete-attr", 'D', 0, G_OPTION_ARG_CALLBACK, delete_attr_cb, + "Delete the named attribute for a ticket", + "ATTRIBUTE" }, + + { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Delete all state of a ticket at this cluster site", + NULL }, + + { NULL} +}; + +static GOptionEntry addl_entries[] = { + { "attr-value", 'v', 0, G_OPTION_ARG_CALLBACK, attr_value_cb, + "Attribute value to use with -S", + "VALUE" }, + + { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default, + "(Advanced) Default attribute value to display if none is found\n" + INDENT "(for use with -G)", + "VALUE" }, + + { "force", 'f', 0, G_OPTION_ARG_NONE, &options.force, + "(Advanced) Force the action to be performed", + NULL }, + + { "ticket", 't', 0, G_OPTION_ARG_STRING, &options.ticket_id, + "Ticket ID", + "ID" }, + + { "xml-file", 'x', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.xml_file, + NULL, + NULL }, + + { NULL } +}; + +static GOptionEntry deprecated_entries[] = { + { "set-name", 'n', 0, G_OPTION_ARG_STRING, &options.set_name, + "(Advanced) ID of the instance_attributes object to change", + "ID" }, + + { "nvpair", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id, + "(Advanced) ID of the nvpair object to change/delete", + "ID" }, + + { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &options.quiet, + "Print only the value on stdout", + NULL }, + + { NULL } +}; + +static pe_ticket_t * +find_ticket(gchar *ticket_id, pe_working_set_t * data_set) +{ + return g_hash_table_lookup(data_set->tickets, ticket_id); } static void print_date(time_t time) { int lpc = 0; char date_str[26]; asctime_r(localtime(&time), date_str); for (; lpc < 26; lpc++) { if (date_str[lpc] == '\n') { date_str[lpc] = 0; } } fprintf(stdout, "'%s'", date_str); } -static int -print_ticket(pe_ticket_t * ticket, gboolean raw, gboolean details) +static void +print_ticket(pe_ticket_t * ticket, bool raw, bool details) { if (raw) { fprintf(stdout, "%s\n", ticket->id); - return pcmk_ok; + return; } fprintf(stdout, "%s\t%s %s", ticket->id, ticket->granted ? "granted" : "revoked", ticket->standby ? "[standby]" : " "); if (details && g_hash_table_size(ticket->state) > 0) { GHashTableIter iter; const char *name = NULL; const char *value = NULL; int lpc = 0; fprintf(stdout, " ("); g_hash_table_iter_init(&iter, ticket->state); while (g_hash_table_iter_next(&iter, (void **)&name, (void **)&value)) { if (lpc > 0) { fprintf(stdout, ", "); } fprintf(stdout, "%s=", name); if (pcmk__str_any_of(name, "last-granted", "expires", NULL)) { long long time_ll; pcmk__scan_ll(value, &time_ll, 0); print_date((time_t) time_ll); } else { fprintf(stdout, "%s", value); } lpc++; } fprintf(stdout, ")\n"); } else { if (ticket->last_granted > -1) { fprintf(stdout, " last-granted="); print_date(ticket->last_granted); } fprintf(stdout, "\n"); } - return pcmk_ok; + return; } -static int -print_ticket_list(pe_working_set_t * data_set, gboolean raw, gboolean details) +static void +print_ticket_list(pe_working_set_t * data_set, bool raw, bool details) { GHashTableIter iter; pe_ticket_t *ticket = NULL; g_hash_table_iter_init(&iter, data_set->tickets); while (g_hash_table_iter_next(&iter, NULL, (void **)&ticket)) { print_ticket(ticket, raw, details); } - - return pcmk_ok; } #define XPATH_MAX 1024 static int -find_ticket_state(cib_t * the_cib, const char *ticket_id, xmlNode ** ticket_state_xml) +find_ticket_state(cib_t * the_cib, gchar *ticket_id, xmlNode ** ticket_state_xml) { int offset = 0; - int rc = pcmk_ok; + int rc = pcmk_rc_ok; xmlNode *xml_search = NULL; char *xpath_string = NULL; CRM_ASSERT(ticket_state_xml != NULL); *ticket_state_xml = NULL; xpath_string = calloc(1, XPATH_MAX); offset += snprintf(xpath_string + offset, XPATH_MAX - offset, "%s", "/cib/status/tickets"); if (ticket_id) { offset += snprintf(xpath_string + offset, XPATH_MAX - offset, "/%s[@id=\"%s\"]", XML_CIB_TAG_TICKET_STATE, ticket_id); } CRM_LOG_ASSERT(offset > 0); rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search, cib_sync_call | cib_scope_local | cib_xpath); + rc = pcmk_legacy2rc(rc); - if (rc != pcmk_ok) { + if (rc != pcmk_rc_ok) { goto done; } crm_log_xml_debug(xml_search, "Match"); if (xml_has_children(xml_search)) { if (ticket_id) { fprintf(stdout, "Multiple ticket_states match ticket_id=%s\n", ticket_id); } *ticket_state_xml = xml_search; } else { *ticket_state_xml = xml_search; } done: free(xpath_string); return rc; } static int -find_ticket_constraints(cib_t * the_cib, const char *ticket_id, xmlNode ** ticket_cons_xml) +find_ticket_constraints(cib_t * the_cib, gchar *ticket_id, xmlNode ** ticket_cons_xml) { int offset = 0; - int rc = pcmk_ok; + int rc = pcmk_rc_ok; xmlNode *xml_search = NULL; char *xpath_string = NULL; const char *xpath_base = NULL; CRM_ASSERT(ticket_cons_xml != NULL); *ticket_cons_xml = NULL; xpath_base = pcmk_cib_xpath_for(XML_CIB_TAG_CONSTRAINTS); if (xpath_base == NULL) { crm_err(XML_CIB_TAG_CONSTRAINTS " CIB element not known (bug?)"); return -ENOMSG; } xpath_string = calloc(1, XPATH_MAX); offset += snprintf(xpath_string + offset, XPATH_MAX - offset, "%s/%s", xpath_base, XML_CONS_TAG_RSC_TICKET); if (ticket_id) { offset += snprintf(xpath_string + offset, XPATH_MAX - offset, "[@ticket=\"%s\"]", ticket_id); } CRM_LOG_ASSERT(offset > 0); rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search, cib_sync_call | cib_scope_local | cib_xpath); + rc = pcmk_legacy2rc(rc); - if (rc != pcmk_ok) { + if (rc != pcmk_rc_ok) { goto done; } crm_log_xml_debug(xml_search, "Match"); *ticket_cons_xml = xml_search; done: free(xpath_string); return rc; } static int -dump_ticket_xml(cib_t * the_cib, const char *ticket_id) +dump_ticket_xml(cib_t * the_cib, gchar *ticket_id) { - int rc = pcmk_ok; + int rc = pcmk_rc_ok; xmlNode *state_xml = NULL; rc = find_ticket_state(the_cib, ticket_id, &state_xml); if (state_xml == NULL) { return rc; } fprintf(stdout, "State XML:\n"); if (state_xml) { char *state_xml_str = NULL; state_xml_str = dump_xml_formatted(state_xml); fprintf(stdout, "\n%s\n", crm_str(state_xml_str)); free_xml(state_xml); free(state_xml_str); } - return pcmk_ok; + return rc; } static int -dump_constraints(cib_t * the_cib, const char *ticket_id) +dump_constraints(cib_t * the_cib, gchar *ticket_id) { - int rc = pcmk_ok; + int rc = pcmk_rc_ok; xmlNode *cons_xml = NULL; char *cons_xml_str = NULL; rc = find_ticket_constraints(the_cib, ticket_id, &cons_xml); if (cons_xml == NULL) { return rc; } cons_xml_str = dump_xml_formatted(cons_xml); fprintf(stdout, "Constraints XML:\n\n%s\n", crm_str(cons_xml_str)); free_xml(cons_xml); free(cons_xml_str); - return pcmk_ok; + return rc; } static int -get_ticket_state_attr(const char *ticket_id, const char *attr_name, const char **attr_value, +get_ticket_state_attr(gchar *ticket_id, const char *attr_name, const char **attr_value, pe_working_set_t * data_set) { pe_ticket_t *ticket = NULL; CRM_ASSERT(attr_value != NULL); *attr_value = NULL; ticket = g_hash_table_lookup(data_set->tickets, ticket_id); if (ticket == NULL) { - return -ENXIO; + return ENXIO; } *attr_value = g_hash_table_lookup(ticket->state, attr_name); if (*attr_value == NULL) { - return -ENXIO; + return ENXIO; } - return pcmk_ok; + return pcmk_rc_ok; } -static gboolean -ticket_warning(const char *ticket_id, const char *action) +static void +ticket_warning(gchar *ticket_id, const char *action) { - gboolean rc = FALSE; int offset = 0; static int text_max = 1024; char *warning = NULL; const char *word = NULL; warning = calloc(1, text_max); if (pcmk__str_eq(action, "grant", pcmk__str_casei)) { offset += snprintf(warning + offset, text_max - offset, "This command cannot help you verify whether '%s' has been already granted elsewhere.\n", ticket_id); word = "to"; } else { offset += snprintf(warning + offset, text_max - offset, "Revoking '%s' can trigger the specified 'loss-policy'(s) relating to '%s'.\n\n", ticket_id, ticket_id); offset += snprintf(warning + offset, text_max - offset, "You can check that with:\ncrm_ticket --ticket %s --constraints\n\n", ticket_id); offset += snprintf(warning + offset, text_max - offset, "Otherwise before revoking '%s', you may want to make '%s' standby with:\ncrm_ticket --ticket %s --standby\n\n", ticket_id, ticket_id, ticket_id); word = "from"; } offset += snprintf(warning + offset, text_max - offset, "If you really want to %s '%s' %s this site now, and you know what you are doing,\n", action, ticket_id, word); offset += snprintf(warning + offset, text_max - offset, "please specify --force."); CRM_LOG_ASSERT(offset > 0); fprintf(stdout, "%s\n", warning); free(warning); - return rc; } -static gboolean -allow_modification(const char *ticket_id, GList *attr_delete, - GHashTable *attr_set) +static bool +allow_modification(gchar *ticket_id) { const char *value = NULL; GList *list_iter = NULL; - if (do_force) { - return TRUE; + if (options.force) { + return true; } if (g_hash_table_lookup_extended(attr_set, "granted", NULL, (gpointer *) & value)) { if (crm_is_true(value)) { ticket_warning(ticket_id, "grant"); - return FALSE; + return false; } else { ticket_warning(ticket_id, "revoke"); - return FALSE; + return false; } } for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) { const char *key = (const char *)list_iter->data; if (pcmk__str_eq(key, "granted", pcmk__str_casei)) { ticket_warning(ticket_id, "revoke"); - return FALSE; + return false; } } - return TRUE; + return true; } static int -modify_ticket_state(const char * ticket_id, GList *attr_delete, GHashTable * attr_set, - cib_t * cib, pe_working_set_t * data_set) +modify_ticket_state(gchar * ticket_id, cib_t * cib, pe_working_set_t * data_set) { - int rc = pcmk_ok; + int rc = pcmk_rc_ok; xmlNode *xml_top = NULL; xmlNode *ticket_state_xml = NULL; - gboolean found = FALSE; + bool found = false; GList *list_iter = NULL; GHashTableIter hash_iter; char *key = NULL; char *value = NULL; pe_ticket_t *ticket = NULL; rc = find_ticket_state(cib, ticket_id, &ticket_state_xml); - if (rc == pcmk_ok) { + if (rc == pcmk_rc_ok) { crm_debug("Found a match state for ticket: id=%s", ticket_id); xml_top = ticket_state_xml; - found = TRUE; + found = true; - } else if (rc != -ENXIO) { + } else if (rc != ENXIO) { return rc; } else if (g_hash_table_size(attr_set) == 0){ - return pcmk_ok; + return pcmk_rc_ok; } else { xmlNode *xml_obj = NULL; xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS); xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS); ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE); crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id); } for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) { const char *key = (const char *)list_iter->data; xml_remove_prop(ticket_state_xml, key); } ticket = find_ticket(ticket_id, data_set); g_hash_table_iter_init(&hash_iter, attr_set); while (g_hash_table_iter_next(&hash_iter, (gpointer *) & key, (gpointer *) & value)) { crm_xml_add(ticket_state_xml, key, value); if (pcmk__str_eq(key, "granted", pcmk__str_casei) && (ticket == NULL || ticket->granted == FALSE) && crm_is_true(value)) { char *now = pcmk__ttoa(time(NULL)); crm_xml_add(ticket_state_xml, "last-granted", now); free(now); } } if (found && (attr_delete != NULL)) { crm_log_xml_debug(xml_top, "Replace"); rc = cib->cmds->replace(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options); + rc = pcmk_legacy2rc(rc); } else { crm_log_xml_debug(xml_top, "Update"); rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, cib_options); + rc = pcmk_legacy2rc(rc); } free_xml(xml_top); return rc; } static int -delete_ticket_state(const char *ticket_id, cib_t * cib) +delete_ticket_state(gchar *ticket_id, cib_t * cib) { xmlNode *ticket_state_xml = NULL; - int rc = pcmk_ok; + int rc = pcmk_rc_ok; rc = find_ticket_state(cib, ticket_id, &ticket_state_xml); - if (rc == -ENXIO) { - return pcmk_ok; + if (rc == ENXIO) { + return pcmk_rc_ok; - } else if (rc != pcmk_ok) { + } else if (rc != pcmk_rc_ok) { return rc; } crm_log_xml_debug(ticket_state_xml, "Delete"); rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options); + rc = pcmk_legacy2rc(rc); - if (rc == pcmk_ok) { + if (rc == pcmk_rc_ok) { fprintf(stdout, "Cleaned up %s\n", ticket_id); } free_xml(ticket_state_xml); return rc; } -static pcmk__cli_option_t long_options[] = { - // long option, argument type, storage, short option, description, flags - { - "help", no_argument, NULL, '?', - "\t\tThis text", pcmk__option_default - }, - { - "version", no_argument, NULL, '$', - "\t\tVersion information", pcmk__option_default - }, - { - "verbose", no_argument, NULL, 'V', - "\t\tIncrease debug output", pcmk__option_default - }, - { - "quiet", no_argument, NULL, 'Q', - "\t\tPrint only the value on stdout\n", pcmk__option_default - }, - { - "ticket", required_argument, NULL, 't', - "\tTicket ID", pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\nQueries:", pcmk__option_default - }, - { - "info", no_argument, NULL, 'l', - "\t\tDisplay the information of ticket(s)", pcmk__option_default - }, - { - "details", no_argument, NULL, 'L', - "\t\tDisplay the details of ticket(s)", pcmk__option_default - }, - { - "raw", no_argument, NULL, 'w', - "\t\tDisplay the IDs of ticket(s)", pcmk__option_default - }, - { - "query-xml", no_argument, NULL, 'q', - "\tQuery the XML of ticket(s)", pcmk__option_default - }, - { - "constraints", no_argument, NULL, 'c', - "\tDisplay the rsc_ticket constraints that apply to ticket(s)", - pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\nCommands:", pcmk__option_default - }, - { - "grant", no_argument, NULL, 'g', - "\t\tGrant a ticket to this cluster site", pcmk__option_default - }, - { - "revoke", no_argument, NULL, 'r', - "\t\tRevoke a ticket from this cluster site", pcmk__option_default - }, - { - "standby", no_argument, NULL, 's', - "\t\tTell this cluster site this ticket is standby", - pcmk__option_default - }, - { - "activate", no_argument, NULL, 'a', - "\tTell this cluster site this ticket is active", pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\nAdvanced Commands:", pcmk__option_default - }, - { - "get-attr", required_argument, NULL, 'G', - "\tDisplay the named attribute for a ticket", pcmk__option_default - }, - { - "set-attr", required_argument, NULL, 'S', - "\tSet the named attribute for a ticket", pcmk__option_default - }, - { - "delete-attr", required_argument, NULL, 'D', - "\tDelete the named attribute for a ticket", pcmk__option_default - }, - { - "cleanup", no_argument, NULL, 'C', - "\t\tDelete all state of a ticket at this cluster site", - pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\nAdditional Options:", pcmk__option_default - }, - { - "attr-value", required_argument, NULL, 'v', - "\tAttribute value to use with -S", pcmk__option_default - }, - { - "default", required_argument, NULL, 'd', - "\t(Advanced) Default attribute value to display if none is found " - "(for use with -G)", - pcmk__option_default - }, - { - "force", no_argument, NULL, 'f', - "\t\t(Advanced) Force the action to be performed", pcmk__option_default - }, - { - "xml-file", required_argument, NULL, 'x', - NULL, pcmk__option_hidden - }, - - /* legacy options */ - { - "set-name", required_argument, NULL, 'n', - "\t(Advanced) ID of the instance_attributes object to change", - pcmk__option_default - }, - { - "nvpair", required_argument, NULL, 'i', - "\t(Advanced) ID of the nvpair object to change/delete", - pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\nExamples:", pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - "Display the info of tickets:", pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --info", pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Display the detailed info of tickets:", pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --details", pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Display the XML of 'ticketA':", pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --ticket ticketA --query-xml", pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Display the rsc_ticket constraints that apply to 'ticketA':", - pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --ticket ticketA --constraints", pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Grant 'ticketA' to this cluster site:", pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --ticket ticketA --grant", pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Revoke 'ticketA' from this cluster site:", pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --ticket ticketA --revoke", pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Make 'ticketA' standby (the cluster site will treat a granted " - "'ticketA' as 'standby', and the dependent resources will be " - "stopped or demoted gracefully without triggering loss-policies):", - pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --ticket ticketA --standby", pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Activate 'ticketA' from being standby:", pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --ticket ticketA --activate", pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Get the value of the 'granted' attribute for 'ticketA':", - pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --ticket ticketA --get-attr granted", pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Set the value of the 'standby' attribute for 'ticketA':", - pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --ticket ticketA --set-attr standby --attr-value true", - pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Delete the 'granted' attribute for 'ticketA':", pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --ticket ticketA --delete-attr granted", - pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Erase the operation history of 'ticketA' at this cluster site:", - pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - "The cluster site will 'forget' the existing ticket state.", - pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_ticket --ticket ticketA --cleanup", pcmk__option_example - }, - { 0, 0, 0, 0 } -}; +static GOptionContext * +build_arg_context(pcmk__common_args_t *args) { + GOptionContext *context = NULL; + + const char *description = "Examples:\n\n" + "Display the info of tickets:\n\n" + "\tcrm_ticket --info\n\n" + "Display the detailed info of tickets:\n\n" + "\tcrm_ticket --details\n\n" + "Display the XML of 'ticketA':\n\n" + "\tcrm_ticket --ticket ticketA --query-xml\n\n" + "Display the rsc_ticket constraints that apply to 'ticketA':\n\n" + "\tcrm_ticket --ticket ticketA --constraints\n\n" + "Grant 'ticketA' to this cluster site:\n\n" + "\tcrm_ticket --ticket ticketA --grant\n\n" + "Revoke 'ticketA' from this cluster site:\n\n" + "\tcrm_ticket --ticket ticketA --revoke\n\n" + "Make 'ticketA' standby (the cluster site will treat a granted\n" + "'ticketA' as 'standby', and the dependent resources will be\n" + "stopped or demoted gracefully without triggering loss-policies):\n\n" + "\tcrm_ticket --ticket ticketA --standby\n\n" + "Activate 'ticketA' from being standby:\n\n" + "\tcrm_ticket --ticket ticketA --activate\n\n" + "Get the value of the 'granted' attribute for 'ticketA':\n\n" + "\tcrm_ticket --ticket ticketA --get-attr granted\n\n" + "Set the value of the 'standby' attribute for 'ticketA':\n\n" + "\tcrm_ticket --ticket ticketA --set-attr standby --attr-value true\n\n" + "Delete the 'granted' attribute for 'ticketA':\n\n" + "\tcrm_ticket --ticket ticketA --delete-attr granted\n\n" + "Erase the operation history of 'ticketA' at this cluster site,\n" + "causing the cluster site to 'forget' the existing ticket state:\n\n" + "\tcrm_ticket --ticket ticketA --cleanup\n\n"; + + context = pcmk__build_arg_context(args, NULL, NULL, NULL); + g_option_context_set_description(context, description); + + pcmk__add_arg_group(context, "queries", "Queries:", + "Show queries", query_entries); + pcmk__add_arg_group(context, "commands", "Commands:", + "Show command options", command_entries); + pcmk__add_arg_group(context, "advanced", "Advanced Options:", + "Show advanced options", advanced_entries); + pcmk__add_arg_group(context, "additional", "Additional Options:", + "Show additional options", addl_entries); + pcmk__add_arg_group(context, "deprecated", "Deprecated Options:", + "Show deprecated options", deprecated_entries); + + return context; +} int main(int argc, char **argv) { pe_working_set_t *data_set = NULL; xmlNode *cib_xml_copy = NULL; cib_t *cib_conn = NULL; crm_exit_t exit_code = CRM_EX_OK; - int rc = pcmk_ok; - - int option_index = 0; - int argerr = 0; - int flag; - guint modified = 0; + int rc = pcmk_rc_ok; - GList *attr_delete = NULL; - GHashTable *attr_set = pcmk__strkey_table(free, free); + pcmk__common_args_t *args = NULL; + GOptionContext *context = NULL; + gchar **processed_args = NULL; - crm_log_init(NULL, LOG_CRIT, FALSE, FALSE, argc, argv, FALSE); - pcmk__set_cli_options(NULL, "| [options]", long_options, - "perform tasks related to cluster tickets\n\n" - "Allows ticket attributes to be queried, modified " - "and deleted.\n"); - - if (argc < 2) { - pcmk__cli_help('?', CRM_EX_USAGE); - } + attr_set = pcmk__strkey_table(free, free); + attr_delete = NULL; - 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 '$': - case '?': - pcmk__cli_help(flag, CRM_EX_OK); - break; - case 'Q': - BE_QUIET = TRUE; - break; - case 't': - ticket_id = optarg; - break; - case 'l': - case 'L': - case 'w': - case 'q': - case 'c': - ticket_cmd = flag; - break; - case 'g': - g_hash_table_insert(attr_set, strdup("granted"), strdup("true")); - modified++; - break; - case 'r': - g_hash_table_insert(attr_set, strdup("granted"), strdup("false")); - modified++; - break; - case 's': - g_hash_table_insert(attr_set, strdup("standby"), strdup("true")); - modified++; - break; - case 'a': - g_hash_table_insert(attr_set, strdup("standby"), strdup("false")); - modified++; - break; - case 'G': - get_attr_name = optarg; - ticket_cmd = flag; - break; - case 'S': - attr_name = optarg; - if (attr_name && attr_value) { - g_hash_table_insert(attr_set, strdup(attr_name), strdup(attr_value)); - attr_name = NULL; - attr_value = NULL; - modified++; - } - break; - case 'D': - attr_delete = g_list_append(attr_delete, optarg); - modified++; - break; - case 'C': - ticket_cmd = flag; - break; - case 'v': - attr_value = optarg; - if (attr_name && attr_value) { - g_hash_table_insert(attr_set, strdup(attr_name), strdup(attr_value)); - attr_name = NULL; - attr_value = NULL; - modified++; - } - break; - case 'd': - attr_default = optarg; - break; - case 'f': - do_force = TRUE; - break; - case 'x': - xml_file = optarg; - break; - case 'n': - set_name = optarg; - break; - case 'i': - attr_id = optarg; - break; - - default: - CMD_ERR("Argument code 0%o (%c) is not (?yet?) supported", flag, flag); - ++argerr; - break; - } - } + args = pcmk__new_common_args(SUMMARY); + context = build_arg_context(args); + processed_args = pcmk__cmdline_preproc(argv, "dintvxCDGS"); - if (optind < argc && argv[optind] != NULL) { - CMD_ERR("non-option ARGV-elements:"); - while (optind < argc && argv[optind] != NULL) { - CMD_ERR("%s", argv[optind++]); - ++argerr; - } + if (!g_option_context_parse_strv(context, &processed_args, &error)) { + exit_code = CRM_EX_USAGE; + goto done; } - if (optind > argc) { - ++argerr; - } + pcmk__cli_init_logging("crm_ticket", args->verbosity); - if (argerr) { - pcmk__cli_help('?', CRM_EX_USAGE); + if (args->version) { + g_strfreev(processed_args); + pcmk__free_arg_context(context); + /* FIXME: When crm_ticket is converted to use formatted output, this can go. */ + pcmk__cli_help('v', CRM_EX_OK); } data_set = pe_new_working_set(); if (data_set == NULL) { crm_perror(LOG_CRIT, "Could not allocate working set"); exit_code = CRM_EX_OSERR; goto done; } pe__set_working_set_flags(data_set, pe_flag_no_counts|pe_flag_no_compat); cib_conn = cib_new(); if (cib_conn == NULL) { - CMD_ERR("Could not connect to the CIB manager"); exit_code = CRM_EX_DISCONNECT; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not connect to the CIB manager"); goto done; } rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); - if (rc != pcmk_ok) { - CMD_ERR("Could not connect to CIB: %s", pcmk_strerror(rc)); - exit_code = crm_errno2exit(rc); + rc = pcmk_legacy2rc(rc); + + if (rc != pcmk_rc_ok) { + exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not connect to the CIB: %s", + pcmk_rc_str(rc)); goto done; } - if (xml_file != NULL) { - cib_xml_copy = filename2xml(xml_file); + if (options.xml_file != NULL) { + cib_xml_copy = filename2xml(options.xml_file); } else { rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml_copy, cib_scope_local | cib_sync_call); - if (rc != pcmk_ok) { - CMD_ERR("Could not get local CIB: %s", pcmk_strerror(rc)); - exit_code = crm_errno2exit(rc); + rc = pcmk_legacy2rc(rc); + + if (rc != pcmk_rc_ok) { + exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not get local CIB: %s", + pcmk_rc_str(rc)); goto done; } } - if (cli_config_update(&cib_xml_copy, NULL, FALSE) == FALSE) { - CMD_ERR("Could not update local CIB to latest schema version"); + if (!cli_config_update(&cib_xml_copy, NULL, FALSE)) { exit_code = CRM_EX_CONFIG; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not update local CIB to latest schema version"); goto done; } data_set->input = cib_xml_copy; data_set->now = crm_time_new(NULL); cluster_status(data_set); /* For recording the tickets that are referenced in rsc_ticket constraints * but have never been granted yet. */ pcmk__unpack_constraints(data_set); - if (ticket_cmd == 'l' || ticket_cmd == 'L' || ticket_cmd == 'w') { - gboolean raw = FALSE; - gboolean details = FALSE; + if (options.ticket_cmd == 'l' || options.ticket_cmd == 'L' || options.ticket_cmd == 'w') { + bool raw = false; + bool details = false; - if (ticket_cmd == 'L') { - details = TRUE; - } else if (ticket_cmd == 'w') { - raw = TRUE; + if (options.ticket_cmd == 'L') { + details = true; + } else if (options.ticket_cmd == 'w') { + raw = true; } - if (ticket_id) { - pe_ticket_t *ticket = find_ticket(ticket_id, data_set); + if (options.ticket_id) { + pe_ticket_t *ticket = find_ticket(options.ticket_id, data_set); if (ticket == NULL) { - CMD_ERR("No such ticket '%s'", ticket_id); exit_code = CRM_EX_NOSUCH; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "No such ticket '%s'", options.ticket_id); goto done; } - rc = print_ticket(ticket, raw, details); + print_ticket(ticket, raw, details); } else { - rc = print_ticket_list(data_set, raw, details); + print_ticket_list(data_set, raw, details); } - if (rc != pcmk_ok) { - CMD_ERR("Could not print ticket: %s", pcmk_strerror(rc)); - } - exit_code = crm_errno2exit(rc); - } else if (ticket_cmd == 'q') { - rc = dump_ticket_xml(cib_conn, ticket_id); - if (rc != pcmk_ok) { - CMD_ERR("Could not query ticket XML: %s", pcmk_strerror(rc)); + } else if (options.ticket_cmd == 'q') { + rc = dump_ticket_xml(cib_conn, options.ticket_id); + exit_code = pcmk_rc2exitc(rc); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not query ticket XML: %s", pcmk_rc_str(rc)); } - exit_code = crm_errno2exit(rc); - } else if (ticket_cmd == 'c') { - rc = dump_constraints(cib_conn, ticket_id); - if (rc != pcmk_ok) { - CMD_ERR("Could not show ticket constraints: %s", pcmk_strerror(rc)); + } else if (options.ticket_cmd == 'c') { + rc = dump_constraints(cib_conn, options.ticket_id); + exit_code = pcmk_rc2exitc(rc); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not show ticket constraints: %s", pcmk_rc_str(rc)); } - exit_code = crm_errno2exit(rc); - } else if (ticket_cmd == 'G') { + } else if (options.ticket_cmd == 'G') { const char *value = NULL; - if (ticket_id == NULL) { - CMD_ERR("Must supply ticket ID with -t"); + if (options.ticket_id == NULL) { exit_code = CRM_EX_NOSUCH; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply ticket ID with -t"); goto done; } - rc = get_ticket_state_attr(ticket_id, get_attr_name, &value, data_set); - if (rc == pcmk_ok) { + rc = get_ticket_state_attr(options.ticket_id, options.get_attr_name, &value, data_set); + if (rc == pcmk_rc_ok) { fprintf(stdout, "%s\n", value); - } else if (rc == -ENXIO && attr_default) { - fprintf(stdout, "%s\n", attr_default); - rc = pcmk_ok; + } else if (rc == ENXIO && options.attr_default) { + fprintf(stdout, "%s\n", options.attr_default); + rc = pcmk_rc_ok; } - exit_code = crm_errno2exit(rc); + exit_code = pcmk_rc2exitc(rc); - } else if (ticket_cmd == 'C') { - if (ticket_id == NULL) { - CMD_ERR("Must supply ticket ID with -t"); + } else if (options.ticket_cmd == 'C') { + if (options.ticket_id == NULL) { exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply ticket ID with -t"); goto done; } - if (do_force == FALSE) { + if (options.force == FALSE) { pe_ticket_t *ticket = NULL; - ticket = find_ticket(ticket_id, data_set); + ticket = find_ticket(options.ticket_id, data_set); if (ticket == NULL) { - CMD_ERR("No such ticket '%s'", ticket_id); exit_code = CRM_EX_NOSUCH; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "No such ticket '%s'", options.ticket_id); goto done; } if (ticket->granted) { - ticket_warning(ticket_id, "revoke"); + ticket_warning(options.ticket_id, "revoke"); exit_code = CRM_EX_INSUFFICIENT_PRIV; goto done; } } - rc = delete_ticket_state(ticket_id, cib_conn); - if (rc != pcmk_ok) { - CMD_ERR("Could not clean up ticket: %s", pcmk_strerror(rc)); + rc = delete_ticket_state(options.ticket_id, cib_conn); + exit_code = pcmk_rc2exitc(rc); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not clean up ticket: %s", pcmk_rc_str(rc)); } - exit_code = crm_errno2exit(rc); } else if (modified) { - if (ticket_id == NULL) { - CMD_ERR("Must supply ticket ID with -t"); + if (options.ticket_id == NULL) { exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply ticket ID with -t"); goto done; } - if (attr_value - && (pcmk__str_empty(attr_name))) { - CMD_ERR("Must supply attribute name with -S for -v %s", attr_value); + if (options.attr_value + && (pcmk__str_empty(options.attr_name))) { exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply attribute name with -S for -v %s", options.attr_value); goto done; } - if (attr_name - && (pcmk__str_empty(attr_value))) { - CMD_ERR("Must supply attribute value with -v for -S %s", attr_name); + if (options.attr_name + && (pcmk__str_empty(options.attr_value))) { exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply attribute value with -v for -S %s", options.attr_value); goto done; } - if (allow_modification(ticket_id, attr_delete, attr_set) == FALSE) { - CMD_ERR("Ticket modification not allowed"); + if (!allow_modification(options.ticket_id)) { exit_code = CRM_EX_INSUFFICIENT_PRIV; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Ticket modification not allowed"); goto done; } - rc = modify_ticket_state(ticket_id, attr_delete, attr_set, cib_conn, data_set); - if (rc != pcmk_ok) { - CMD_ERR("Could not modify ticket: %s", pcmk_strerror(rc)); + rc = modify_ticket_state(options.ticket_id, cib_conn, data_set); + exit_code = pcmk_rc2exitc(rc); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not modify ticket: %s", pcmk_rc_str(rc)); } - exit_code = crm_errno2exit(rc); - } else if (ticket_cmd == 'S') { + } else if (options.ticket_cmd == 'S') { /* Correct usage was handled in the "if (modified)" block above, so * this is just for reporting usage errors */ - if (pcmk__str_empty(attr_name)) { + if (pcmk__str_empty(options.attr_name)) { // We only get here if ticket_cmd was left as default - CMD_ERR("Must supply a command"); exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Must supply a command"); goto done; } - if (ticket_id == NULL) { - CMD_ERR("Must supply ticket ID with -t"); + if (options.ticket_id == NULL) { exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply ticket ID with -t"); goto done; } - if (pcmk__str_empty(attr_value)) { - CMD_ERR("Must supply value with -v for -S %s", attr_name); + if (pcmk__str_empty(options.attr_value)) { exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply value with -v for -S %s", options.attr_name); goto done; } } else { - CMD_ERR("Unknown command: %c", ticket_cmd); exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Unknown command: %c", options.ticket_cmd); } done: if (attr_set) { g_hash_table_destroy(attr_set); } attr_set = NULL; if (attr_delete) { - g_list_free(attr_delete); + g_list_free_full(attr_delete, free); } attr_delete = NULL; pe_free_working_set(data_set); data_set = NULL; cib__clean_up_connection(&cib_conn); - if (rc == -pcmk_err_no_quorum) { - CMD_ERR("Use --force to ignore quorum"); + if (rc == pcmk_rc_no_quorum) { + g_set_error(&error, PCMK__RC_ERROR, rc, "Use --force to ignore quorum"); } + g_strfreev(processed_args); + pcmk__free_arg_context(context); + g_free(options.attr_default); + g_free(options.attr_id); + free(options.attr_name); + free(options.attr_value); + free(options.get_attr_name); + g_free(options.set_name); + g_free(options.ticket_id); + g_free(options.xml_file); + + pcmk__output_and_clear_error(error, NULL); + crm_exit(exit_code); }