diff --git a/tools/Makefile.am b/tools/Makefile.am index 8ce42d7d89..2c6de7f125 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,170 +1,171 @@ # # Copyright 2004-2021 the Pacemaker project contributors # # The version control history for this file may have further details. # # This source code is licensed under the GNU General Public License version 2 # or later (GPLv2+) WITHOUT ANY WARRANTY. # include $(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 \ +EXTRA_DIST = 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_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/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_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/crm_attribute.8.inc b/tools/crm_attribute.8.inc new file mode 100644 index 0000000000..2be825315c --- /dev/null +++ b/tools/crm_attribute.8.inc @@ -0,0 +1,5 @@ +[synopsis] +crm_attribute -n [options] + +/and node attributes/ +.SH OPTIONS diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c index 1e9cae971d..2cc8d26650 100644 --- a/tools/crm_attribute.c +++ b/tools/crm_attribute.c @@ -1,550 +1,525 @@ /* * Copyright 2004-2021 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include +#define SUMMARY "crm_attribute - query and update Pacemaker cluster options and node attributes" + crm_exit_t exit_code = CRM_EX_OK; +uint64_t cib_opts = cib_sync_call; struct { char command; - char *attr_id; - char *attr_name; - char *attr_pattern; + gchar *attr_default; + gchar *attr_id; + gchar *attr_name; + gchar *attr_pattern; + char *attr_value; char *dest_node; - char *set_name; - const char *attr_default; - const char *attr_value; - const char *dest_uname; - const char *set_type; - const char *type; + gchar *dest_uname; + gboolean inhibit; + gchar *set_name; + char *set_type; + gchar *type; + gboolean promotion_score; } options = { - .command = 'G' + .command = 'G', + .promotion_score = FALSE }; gboolean BE_QUIET = FALSE; -static pcmk__cli_option_t long_options[] = { - // long option, argument type, storage, short option, description, flags - { - "help", no_argument, NULL, '?', - "\tThis text", pcmk__option_default - }, - { - "version", no_argument, NULL, '$', - "\tVersion information", pcmk__option_default - }, - { - "verbose", no_argument, NULL, 'V', - "\tIncrease debug output", pcmk__option_default - }, - { - "quiet", no_argument, NULL, 'q', - "\tPrint only the value on stdout\n", pcmk__option_default - }, +#define INDENT " " - { - "-spacer-", no_argument, NULL, '-', - "\nOptions for selecting attribute:", pcmk__option_default - }, - { - "name", required_argument, NULL, 'n', - "Operate on attribute or option with this name", - pcmk__option_default - }, - { - "pattern", required_argument, NULL, 'P', - "Operate on all attributes matching this pattern " - "(with -v/-D and -l reboot)", - pcmk__option_default - }, - { - "promotion", optional_argument, NULL, 'p', - "Operate on node attribute used as promotion score for specified " - "resource, or resource given in OCF_RESOURCE_INSTANCE environment " - "variable if none is specified; this also defaults -l/--lifetime " - "to reboot (normally invoked from an OCF resource agent)", - pcmk__option_default - }, - { - "set-name", required_argument, NULL, 's', - "(Advanced) Operate on instance of specified attribute that is " - "within set with this XML ID", - pcmk__option_default - }, - { - "id", required_argument, NULL, 'i', - "\t(Advanced) Operate on instance of specified attribute with this " - "XML ID", - pcmk__option_default - }, +static gboolean +delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { + options.command = 'D'; - { - "-spacer-", no_argument, NULL, '-', - "\nCommands:", pcmk__option_default - }, - { - "query", no_argument, NULL, 'G', - "\tQuery the current value of the attribute/option", - pcmk__option_default - }, - { - "update", required_argument, NULL, 'v', - "Update the value of the attribute/option", pcmk__option_default - }, - { - "delete", no_argument, NULL, 'D', - "\tDelete the attribute/option", pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\nAdditional Options:", pcmk__option_default - }, - { - "node", required_argument, NULL, 'N', - "Set a node attribute for named node (instead of a cluster option). " - "See also: -l", - pcmk__option_default - }, - { - "type", required_argument, NULL, 't', - "Which part of the configuration to update/delete/query the option in", - pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\t\t\tValid values: crm_config, rsc_defaults, op_defaults, tickets", - pcmk__option_default - }, - { - "lifetime", required_argument, NULL, 'l', - "Lifetime of the node attribute", pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\t\t\tValid values: reboot, forever", pcmk__option_default - }, - { - "utilization", no_argument, NULL, 'z', - "Set an utilization attribute for the node.", pcmk__option_default - }, - { - "default", required_argument, NULL, 'd', - "(Advanced) Default value to display if none is found in configuration", - pcmk__option_default - }, - { - "inhibit-policy-engine", no_argument, NULL, '!', - NULL, pcmk__option_hidden - }, + if (options.attr_value) { + free(options.attr_value); + } + + options.attr_value = NULL; + return TRUE; +} + +static gboolean +promotion_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { + char *score_name = NULL; + + options.promotion_score = TRUE; + + if (options.attr_name) { + g_free(options.attr_name); + } + + score_name = pcmk_promotion_score_name(optarg); + if (score_name != NULL) { + options.attr_name = g_strdup(score_name); + free(score_name); + } else { + options.attr_name = NULL; + } + + return TRUE; +} + +static gboolean +update_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { + options.command = 'v'; + + if (options.attr_value) { + free(options.attr_value); + } + + options.attr_value = strdup(optarg); + return TRUE; +} + +static gboolean +utilization_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { + if (options.type) { + g_free(options.type); + } + + options.type = g_strdup(XML_CIB_TAG_NODES); + + if (options.set_type) { + free(options.set_type); + } + + options.set_type = strdup(XML_TAG_UTILIZATION); + return TRUE; +} + +static gboolean +value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { + options.command = 'G'; + + if (options.attr_value) { + free(options.attr_value); + } - /* legacy */ - { - "quiet", no_argument, NULL, 'Q', - NULL, pcmk__option_hidden + options.attr_value = NULL; + return TRUE; +} + +static GOptionEntry selecting_entries[] = { + { "id", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id, + "(Advanced) Operate on instance of specified attribute with this\n" + INDENT "XML ID", + "XML_ID" }, - { - "node-uname", required_argument, NULL, 'U', - NULL, pcmk__option_hidden + + { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name, + "Operate on attribute or option with this name", + "NAME" }, - { - "get-value", no_argument, NULL, 'G', - NULL, pcmk__option_hidden + + { "pattern", 'P', 0, G_OPTION_ARG_STRING, &options.attr_pattern, + "Operate on all attributes matching this pattern\n" + INDENT "(with -v/-D and -l reboot)", + "PATTERN" }, - { - "delete-attr", no_argument, NULL, 'D', - NULL, pcmk__option_hidden + + { "promotion", 'p', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, promotion_cb, + "Operate on node attribute used as promotion score for specified\n" + INDENT "resource, or resource given in OCF_RESOURCE_INSTANCE environment\n" + INDENT "variable if none is specified; this also defaults -l/--lifetime\n" + INDENT "to reboot (normally invoked from an OCF resource agent)", + "RESOURCE" }, - { - "attr-value", required_argument, NULL, 'v', - NULL, pcmk__option_hidden + + { "set-name", 's', 0, G_OPTION_ARG_STRING, &options.set_name, + "(Advanced) Operate on instance of specified attribute that is\n" + INDENT "within set with this XML ID", + "NAME" }, - { - "attr-name", required_argument, NULL, 'n', - NULL, pcmk__option_hidden + + { NULL } +}; + +static GOptionEntry command_entries[] = { + { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, delete_cb, + "Delete the attribute/option", + NULL }, - { - "attr-id", required_argument, NULL, 'i', - NULL, pcmk__option_hidden + + { "query", 'G', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb, + "Query the current value of the attribute/option", + NULL }, - { - "-spacer-", no_argument, NULL, '-', - "\nExamples:", pcmk__option_paragraph + { "update", 'v', 0, G_OPTION_ARG_CALLBACK, update_cb, + "Update the value of the attribute/option", + "VALUE" }, - { - "-spacer-", no_argument, NULL, '-', - "Add new node attribute called 'location' with the value of 'office' " - "for host 'myhost':", - pcmk__option_paragraph + + { NULL } +}; + +static GOptionEntry addl_entries[] = { + { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default, + "(Advanced) Default value to display if none is found in configuration", + "VALUE" }, - { - "-spacer-", no_argument, NULL, '-', - " crm_attribute --node myhost --name location --update office", - pcmk__option_example + + { "lifetime", 'l', 0, G_OPTION_ARG_STRING, &options.type, + "Lifetime of the node attribute.\n" + INDENT "Valid values: reboot, forever", + "LIFETIME" }, - { - "-spacer-", no_argument, NULL, '-', - "Query the value of the 'location' node attribute for host 'myhost':", - pcmk__option_paragraph + + { "node", 'N', 0, G_OPTION_ARG_STRING, &options.dest_uname, + "Set a node attribute for named node (instead of a cluster option).\n" + INDENT "See also: -l", + "NODE" }, - { - "-spacer-", no_argument, NULL, '-', - " crm_attribute --node myhost --name location --query", - pcmk__option_example + + { "type", 't', 0, G_OPTION_ARG_STRING, &options.type, + "Which part of the configuration to update/delete/query the option in.\n" + INDENT "Valid values: crm_config, rsc_defaults, op_defaults, tickets", + "SECTION" }, - { - "-spacer-", no_argument, NULL, '-', - "Change the value of the 'location' node attribute for host 'myhost':", - pcmk__option_paragraph + + { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, utilization_cb, + "Set an utilization attribute for the node.", + NULL }, - { - "-spacer-", no_argument, NULL, '-', - " crm_attribute --node myhost --name location --update backoffice", - pcmk__option_example + + { "inhibit-policy-engine", '!', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.inhibit, + NULL, NULL }, - { - "-spacer-", no_argument, NULL, '-', - "Delete the 'location' node attribute for host 'myhost':", - pcmk__option_paragraph + + { NULL } +}; + +static GOptionEntry deprecated_entries[] = { + { "attr-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_id, + NULL, NULL }, - { - "-spacer-", no_argument, NULL, '-', - " crm_attribute --node myhost --name location --delete", - pcmk__option_example + + { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_name, + NULL, NULL }, - { - "-spacer-", no_argument, NULL, '-', - "Query the value of the cluster-delay cluster option:", - pcmk__option_paragraph + + { "attr-value", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, update_cb, + NULL, NULL }, - { - "-spacer-", no_argument, NULL, '-', - " crm_attribute --type crm_config --name cluster-delay --query", - pcmk__option_example + + { "delete-attr", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, delete_cb, + NULL, NULL }, - { - "-spacer-", no_argument, NULL, '-', - "Query value of the \"cluster-delay\" cluster option and print only " - "the value:", - pcmk__option_paragraph + + { "get-value", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, value_cb, + NULL, NULL }, - { - "-spacer-", no_argument, NULL, '-', - " crm_attribute --type crm_config --name cluster-delay --query --quiet", - pcmk__option_example + + { "node-uname", 'U', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.dest_uname, + NULL, NULL }, - { 0, 0, 0, 0 } + + { NULL } }; +static GOptionContext * +build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) { + GOptionContext *context = NULL; + + GOptionEntry extra_prog_entries[] = { + { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet), + "Print only the value on stdout", + NULL }, + + { "quiet", 'Q', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &(args->quiet), + NULL, NULL + }, + + { NULL } + }; + + const char *description = "Examples:\n\n" + "Add new node attribute called 'location' with the value of 'office' for host 'myhost':\n\n" + "\tcrm_attribute --node myhost --name location --update office\n\n" + "Query the value of the 'location' node attribute for host 'myhost':\n\n" + "\tcrm_attribute --node myhost --name location --query\n\n" + "Change the value of the 'location' node attribute for host 'myhost':\n\n" + "\tcrm_attribute --node myhost --name location --update backoffice\n\n" + "Delete the 'location' node attribute for host 'myhost':\n\n" + "\tcrm_attribute --node myhost --name location --delete\n\n" + "Query the value of the 'cluster-delay' cluster option:\n\n" + "\tcrm_attribute --type crm_config --name cluster-delay --query\n\n" + "Query value of the 'cluster-delay' cluster option and print only the value:\n\n" + "\tcrm_attribute --type crm_config --name cluster-delay --query --quiet\n\n"; + + context = pcmk__build_arg_context(args, NULL, group, NULL); + pcmk__add_main_args(context, extra_prog_entries); + g_option_context_set_description(context, description); + + pcmk__add_arg_group(context, "selections", "Selecting attributes:", + "Show selecting options", selecting_entries); + pcmk__add_arg_group(context, "command", "Commands:", + "Show command options", command_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) { cib_t *the_cib = NULL; - int rc = pcmk_ok; - - int cib_opts = cib_sync_call; - int argerr = 0; - int flag; - - int option_index = 0; int is_remote_node = 0; - bool try_attrd = true; - bool promotion_score = false; int attrd_opts = pcmk__node_attr_none; - pcmk__cli_init_logging("crm_attribute", 0); - pcmk__set_cli_options(NULL, "-n [options]", - long_options, - "query and update Pacemaker cluster options " - "and node attributes"); + int rc = pcmk_ok; + GError *error = NULL; + + GOptionGroup *output_group = NULL; + pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); + gchar **processed_args = pcmk__cmdline_preproc(argv, "DGNPdilnpstv"); + GOptionContext *context = build_arg_context(args, &output_group); - if (argc < 2) { - pcmk__cli_help('?', CRM_EX_USAGE); + if (!g_option_context_parse_strv(context, &processed_args, &error)) { + exit_code = CRM_EX_USAGE; + goto done; } - 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 'G': - options.command = flag; - options.attr_value = optarg; - break; - case 'D': - case 'v': - options.command = flag; - options.attr_value = optarg; - crm_log_args(argc, argv); - break; - case 'q': - case 'Q': - BE_QUIET = TRUE; - break; - case 'U': - case 'N': - options.dest_uname = strdup(optarg); - break; - case 's': - options.set_name = strdup(optarg); - break; - case 'l': - case 't': - options.type = optarg; - break; - case 'z': - options.type = XML_CIB_TAG_NODES; - options.set_type = XML_TAG_UTILIZATION; - break; - case 'n': - options.attr_name = strdup(optarg); - break; - case 'p': - promotion_score = true; - options.attr_name = pcmk_promotion_score_name(optarg); - if (options.attr_name == NULL) { - fprintf(stderr, "-p/--promotion must be called from an " - " OCF resource agent or with a resource ID " - " specified\n\n"); - ++argerr; - } - break; - case 'P': - options.attr_pattern = strdup(optarg); - break; - case 'i': - options.attr_id = strdup(optarg); - break; - case 'd': - options.attr_default = optarg; - break; - case '!': - crm_warn("Inhibiting notifications for this update"); - cib__set_call_options(cib_opts, crm_system_name, - cib_inhibit_notify); - break; - default: - printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); - ++argerr; - break; - } + pcmk__cli_init_logging("crm_attribute", 0); + + if (args->version) { + g_strfreev(processed_args); + pcmk__free_arg_context(context); + /* FIXME: When crm_attribute is converted to use formatted output, this can go. */ + pcmk__cli_help('v', CRM_EX_USAGE); } - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); + if (options.promotion_score && options.attr_name == NULL) { + fprintf(stderr, "-p/--promotion must be called from an " + " OCF resource agent or with a resource ID " + " specified\n\n"); + exit_code = CRM_EX_USAGE; + goto done; } - if (optind > argc) { - ++argerr; + if (options.inhibit) { + crm_warn("Inhibiting notifications for this update"); + cib__set_call_options(cib_opts, crm_system_name, cib_inhibit_notify); } - if (argerr) { - pcmk__cli_help('?', CRM_EX_USAGE); + if (args->quiet) { + BE_QUIET = TRUE; } the_cib = cib_new(); rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command); if (rc != pcmk_ok) { fprintf(stderr, "Could not connect to the CIB: %s\n", pcmk_strerror(rc)); exit_code = crm_errno2exit(rc); goto done; } // Use default CIB location if not given if (options.type == NULL) { - if (promotion_score) { + if (options.promotion_score) { // Updating a promotion score node attribute - options.type = "reboot"; + options.type = g_strdup(XML_CIB_TAG_STATUS); } else if (options.dest_uname != NULL) { // Updating some other node attribute - options.type = "forever"; + options.type = g_strdup(XML_CIB_TAG_NODES); } else { // Updating cluster options - options.type = XML_CIB_TAG_CRMCONFIG; + options.type = g_strdup(XML_CIB_TAG_CRMCONFIG); } - } - - if (pcmk__str_eq(options.type, "reboot", pcmk__str_casei)) { - options.type = XML_CIB_TAG_STATUS; + } else if (pcmk__str_eq(options.type, "reboot", pcmk__str_casei)) { + options.type = g_strdup(XML_CIB_TAG_STATUS); } else if (pcmk__str_eq(options.type, "forever", pcmk__str_casei)) { - options.type = XML_CIB_TAG_NODES; + options.type = g_strdup(XML_CIB_TAG_NODES); } // Use default node if not given (except for cluster options and tickets) if (!pcmk__strcase_any_of(options.type, XML_CIB_TAG_CRMCONFIG, XML_CIB_TAG_TICKETS, NULL)) { /* If we are being called from a resource agent via the cluster, * the correct local node name will be passed as an environment * variable. Otherwise, we have to ask the cluster. */ - options.dest_uname = pcmk__node_attr_target(options.dest_uname); + const char *target = pcmk__node_attr_target(options.dest_uname); + + if (target != NULL) { + g_free(options.dest_uname); + options.dest_uname = g_strdup(target); + } + if (options.dest_uname == NULL) { - options.dest_uname = get_local_node_name(); + options.dest_uname = g_strdup(get_local_node_name()); } rc = query_node_uuid(the_cib, options.dest_uname, &options.dest_node, &is_remote_node); if (pcmk_ok != rc) { fprintf(stderr, "Could not map name=%s to a UUID\n", options.dest_uname); exit_code = crm_errno2exit(rc); goto done; } } if ((options.command == 'D') && (options.attr_name == NULL) && (options.attr_pattern == NULL)) { fprintf(stderr, "Error: must specify attribute name or pattern to delete\n"); exit_code = CRM_EX_USAGE; goto done; } if (options.attr_pattern) { if (((options.command != 'v') && (options.command != 'D')) || !pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei)) { fprintf(stderr, "Error: pattern can only be used with till-reboot update or delete\n"); exit_code = CRM_EX_USAGE; goto done; } options.command = 'u'; - free(options.attr_name); + g_free(options.attr_name); options.attr_name = options.attr_pattern; } // Only go through attribute manager for transient attributes try_attrd = pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei); // Don't try to contact attribute manager if we're using a file as CIB if (getenv("CIB_file") || getenv("CIB_shadow")) { try_attrd = FALSE; } if (is_remote_node) { attrd_opts = pcmk__node_attr_remote; } if (((options.command == 'v') || (options.command == 'D') || (options.command == 'u')) && try_attrd && (pcmk__node_attr_request(NULL, options.command, options.dest_uname, options.attr_name, options.attr_value, options.type, options.set_name, NULL, NULL, attrd_opts) == pcmk_rc_ok)) { crm_info("Update %s=%s sent via pacemaker-attrd", options.attr_name, ((options.command == 'D')? "" : options.attr_value)); } else if (options.command == 'D') { rc = delete_attr_delegate(the_cib, cib_opts, options.type, options.dest_node, options.set_type, options.set_name, options.attr_id, options.attr_name, options.attr_value, TRUE, NULL); if (rc == -ENXIO) { /* Nothing to delete... * which means it's not there... * which is what the admin wanted */ rc = pcmk_ok; } } else if (options.command == 'v') { CRM_LOG_ASSERT(options.type != NULL); CRM_LOG_ASSERT(options.attr_name != NULL); CRM_LOG_ASSERT(options.attr_value != NULL); rc = update_attr_delegate(the_cib, cib_opts, options.type, options.dest_node, options.set_type, options.set_name, options.attr_id, options.attr_name, options.attr_value, TRUE, NULL, is_remote_node ? "remote" : NULL); } else { /* query */ char *read_value = NULL; rc = read_attr_delegate(the_cib, options.type, options.dest_node, options.set_type, options.set_name, options.attr_id, options.attr_name, &read_value, TRUE, NULL); if (rc == -ENXIO && options.attr_default) { read_value = strdup(options.attr_default); rc = pcmk_ok; } crm_info("Read %s=%s %s%s", options.attr_name, crm_str(read_value), options.set_name ? "in " : "", options.set_name ? options.set_name : ""); if (rc == -ENOTUNIQ) { // Multiple matches (already displayed) are not error for queries rc = pcmk_ok; } else if (BE_QUIET == FALSE) { fprintf(stdout, "%s%s %s%s %s%s value=%s\n", options.type ? "scope=" : "", options.type ? options.type : "", options.attr_id ? "id=" : "", options.attr_id ? options.attr_id : "", options.attr_name ? "name=" : "", options.attr_name ? options.attr_name : "", read_value ? read_value : "(null)"); } else if (read_value != NULL) { fprintf(stdout, "%s\n", read_value); } free(read_value); } if (rc == -ENOTUNIQ) { printf("Please choose from one of the matches above and supply the 'id' with --attr-id\n"); exit_code = crm_errno2exit(rc); } else if (rc != pcmk_ok) { fprintf(stderr, "Error performing operation: %s\n", pcmk_strerror(rc)); exit_code = crm_errno2exit(rc); } done: - free(options.attr_id); - free(options.attr_name); + g_strfreev(processed_args); + pcmk__free_arg_context(context); + + free(options.attr_default); + g_free(options.attr_id); + g_free(options.attr_name); free(options.attr_value); free(options.dest_node); - free(options.set_name); + g_free(options.dest_uname); + g_free(options.set_name); + free(options.set_type); + g_free(options.type); if (the_cib) { the_cib->cmds->signoff(the_cib); cib_delete(the_cib); } + pcmk__output_and_clear_error(error, NULL); return crm_exit(exit_code); }