diff --git a/tools/Makefile.am b/tools/Makefile.am index 2e09e8527b..8882028801 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,173 +1,174 @@ # # 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 = crm_attribute.8.inc \ +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_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/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/attrd_updater.8.inc b/tools/attrd_updater.8.inc new file mode 100644 index 0000000000..256969a999 --- /dev/null +++ b/tools/attrd_updater.8.inc @@ -0,0 +1,5 @@ +[synopsis] +attrd_updater -n [options] + +/node attributes/ +.SH OPTIONS diff --git a/tools/attrd_updater.c b/tools/attrd_updater.c index 8c942e4d57..6587edf3a7 100644 --- a/tools/attrd_updater.c +++ b/tools/attrd_updater.c @@ -1,484 +1,441 @@ /* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #define SUMMARY "query and update Pacemaker node attributes" +GError *error = NULL; + struct { char command; char *attr_dampen; char *attr_name; char *attr_node; char *attr_section; char *attr_set; char *attr_value; int attr_options; gboolean query_all; } options = { .attr_options = pcmk__node_attr_none, - .command = 'Q' + .command = 'Q', }; -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\n", pcmk__option_default - }, - { - "name", required_argument, NULL, 'n', - "The attribute's name", pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\nCommands:", pcmk__option_default - }, - { - "update", required_argument, NULL, 'U', - "Update attribute's value in pacemaker-attrd. If this causes the value " - "to change, it will also be updated in the cluster configuration.", - pcmk__option_default - }, - { - "update-both", required_argument, NULL, 'B', - "Update attribute's value and time to wait (dampening) in " - "pacemaker-attrd. If this causes the value or dampening to change, " - "the attribute will also be written to the cluster configuration, " - "so be aware that repeatedly changing the dampening reduces its " - "effectiveness.", - pcmk__option_default - }, - { - "update-delay", no_argument, NULL, 'Y', - "Update attribute's dampening in pacemaker-attrd (requires " - "-d/--delay). If this causes the dampening to change, the " - "attribute will also be written to the cluster configuration, so " - "be aware that repeatedly changing the dampening reduces its " - "effectiveness.", - pcmk__option_default - }, - { - "query", no_argument, NULL, 'Q', - "\tQuery the attribute's value from pacemaker-attrd", - pcmk__option_default - }, - { - "delete", no_argument, NULL, 'D', - "\tDelete attribute from pacemaker-attrd. If a value was previously " - "set, it will also be removed from the cluster configuration", - pcmk__option_default - }, - { - "refresh", no_argument, NULL, 'R', - "\t(Advanced) Force the pacemaker-attrd daemon to resend all current " - "values to the CIB", - pcmk__option_default - }, - - { - "-spacer-", no_argument, NULL, '-', - "\nAdditional options:", pcmk__option_default - }, - { - "delay", required_argument, NULL, 'd', - "The time to wait (dampening) in seconds for further changes " - "before writing", - pcmk__option_default - }, - { - "set", required_argument, NULL, 's', - "(Advanced) The attribute set in which to place the value", - pcmk__option_default - }, - { - "node", required_argument, NULL, 'N', - "Set the attribute for the named node (instead of the local one)", - pcmk__option_default - }, - { - "all", no_argument, NULL, 'A', - "Show values of the attribute for all nodes (query only)", - pcmk__option_default - }, - - // @TODO Implement --lifetime - { - "lifetime", required_argument, NULL, 'l', - "(Not yet implemented) Lifetime of the node attribute (silently " - "ignored by cluster)", - pcmk__option_default - }, - { - "private", no_argument, NULL, 'p', - "\tIf this creates a new attribute, never write the attribute to CIB", - pcmk__option_default - }, - - /* Legacy options */ - { - "quiet", no_argument, NULL, 'q', - NULL, pcmk__option_hidden - }, - { - "update", required_argument, NULL, 'v', - NULL, pcmk__option_hidden - }, - { - "section", required_argument, NULL, 'S', - NULL, pcmk__option_hidden - }, - { 0, 0, 0, 0 } +static gboolean +command_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + pcmk__str_update(&options.attr_value, optarg); + + if (pcmk__str_any_of(option_name, "--update-both", "-B", NULL)) { + options.command = 'B'; + } else if (pcmk__str_any_of(option_name, "--delete", "-D", NULL)) { + options.command = 'D'; + } else if (pcmk__str_any_of(option_name, "--query", "-Q", NULL)) { + options.command = 'Q'; + } else if (pcmk__str_any_of(option_name, "--refresh", "-R", NULL)) { + options.command = 'R'; + } else if (pcmk__str_any_of(option_name, "--update", "-U", NULL)) { + options.command = 'U'; + } + + return TRUE; +} + +static gboolean +private_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_private); + return TRUE; +} + +#define INDENT " " + +static GOptionEntry required_entries[] = { + { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name, + "The attribute's name", + "NAME" }, + + { NULL } +}; + +static GOptionEntry command_entries[] = { + { "update", 'U', 0, G_OPTION_ARG_CALLBACK, command_cb, + "Update attribute's value in pacemaker-attrd. If this causes the value\n" + INDENT "to change, it will also be updated in the cluster configuration.", + "VALUE" }, + + { "update-both", 'B', 0, G_OPTION_ARG_CALLBACK, command_cb, + "Update attribute's value and time to wait (dampening) in\n" + INDENT "pacemaker-attrd. If this causes the value or dampening to change,\n" + INDENT "the attribute will also be written to the cluster configuration,\n" + INDENT "so be aware that repeatedly changing the dampening reduces its\n" + INDENT "effectiveness.", + "VALUE" }, + + { "update-delay", 'Y', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Update attribute's dampening in pacemaker-attrd (requires\n" + INDENT "-d/--delay). If this causes the dampening to change, the\n" + INDENT "attribute will also be written to the cluster configuration, so\n" + INDENT "be aware that repeatedly changing the dampening reduces its\n" + INDENT "effectiveness.", + NULL }, + + { "query", 'Q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Query the attribute's value from pacemaker-attrd", + NULL }, + + { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Delete attribute from pacemaker-attrd. If a value was previously\n" + INDENT "set, it will also be removed from the cluster configuration", + NULL }, + + { "refresh", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "(Advanced) Force the pacemaker-attrd daemon to resend all current\n" + INDENT "values to the CIB", + NULL }, + + { NULL } +}; + +static GOptionEntry addl_entries[] = { + { "delay", 'd', 0, G_OPTION_ARG_STRING, &options.attr_dampen, + "The time to wait (dampening) in seconds for further changes\n" + INDENT "before writing", + "SECONDS" }, + + { "set", 's', 0, G_OPTION_ARG_STRING, &options.attr_set, + "(Advanced) The attribute set in which to place the value", + "SET" }, + + { "node", 'N', 0, G_OPTION_ARG_STRING, &options.attr_node, + "Set the attribute for the named node (instead of the local one)", + "NODE" }, + + { "all", 'A', 0, G_OPTION_ARG_NONE, &options.query_all, + "Show values of the attribute for all nodes (query only)", + NULL }, + + { "lifetime", 'l', 0, G_OPTION_ARG_STRING, &options.attr_section, + "(Not yet implemented) Lifetime of the node attribute (silently\n" + INDENT "ignored by cluster)", + "SECTION" }, + + { "private", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, private_cb, + "If this creates a new attribute, never write the attribute to CIB", + NULL }, + + { NULL } +}; + +static GOptionEntry deprecated_entries[] = { + { NULL, 'v', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, command_cb, + NULL, + NULL }, + + { "section", 'S', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_section, + NULL, + NULL }, + + { NULL } }; static int do_query(const char *attr_name, const char *attr_node, gboolean query_all); static int do_update(char command, const char *attr_node, const char *attr_name, const char *attr_value, const char *attr_section, const char *attr_set, const char *attr_dampen, int attr_options); +static GOptionContext * +build_arg_context(pcmk__common_args_t *args) { + GOptionContext *context = NULL; + + context = pcmk__build_arg_context(args, NULL, NULL, NULL); + + pcmk__add_arg_group(context, "required", "Required Arguments:", + "Show required arguments", required_entries); + pcmk__add_arg_group(context, "command", "Command:", + "Show command options (mutually exclusive)", 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; +} + // Free memory at exit to make analyzers happy #define cleanup_memory() \ + g_strfreev(processed_args); \ + pcmk__free_arg_context(context); \ free(options.attr_dampen); \ free(options.attr_name); \ free(options.attr_node); \ free(options.attr_section); \ free(options.attr_set); \ free(options.attr_value); int main(int argc, char **argv) { - int index = 0; - int argerr = 0; - int flag; crm_exit_t exit_code = CRM_EX_OK; - pcmk__cli_init_logging("attrd_updater", 0); - pcmk__set_cli_options(NULL, "-n [options]", - long_options, - "query and update Pacemaker node attributes"); + pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); + GOptionContext *context = build_arg_context(args); + gchar **processed_args = pcmk__cmdline_preproc(argv, "dlnsvBNUS"); - if (argc < 2) { - pcmk__cli_help('?', CRM_EX_USAGE); + if (!g_option_context_parse_strv(context, &processed_args, &error)) { + exit_code = CRM_EX_USAGE; + cleanup_memory(); + crm_exit(exit_code); } - while (1) { - flag = pcmk__next_cli_option(argc, argv, &index, NULL); - if (flag == -1) - break; - - switch (flag) { - case 'V': - crm_bump_log_level(argc, argv); - break; - case '?': - case '$': - cleanup_memory(); - pcmk__cli_help(flag, CRM_EX_OK); - break; - case 'n': - pcmk__str_update(&options.attr_name, optarg); - break; - case 's': - pcmk__str_update(&options.attr_set, optarg); - break; - case 'd': - pcmk__str_update(&options.attr_dampen, optarg); - break; - case 'l': - case 'S': - pcmk__str_update(&options.attr_section, optarg); - break; - case 'N': - pcmk__str_update(&options.attr_node, optarg); - break; - case 'A': - options.query_all = TRUE; - break; - case 'p': - pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_private); - break; - case 'q': - break; - case 'Y': - options.command = flag; - crm_log_args(argc, argv); /* Too much? */ - break; - case 'Q': - case 'B': - case 'R': - case 'D': - case 'U': - case 'v': - if (options.attr_value != NULL) { - free(options.attr_value); - } - - options.command = flag; - options.attr_value = strdup(optarg); - crm_log_args(argc, argv); /* Too much? */ - break; - default: - ++argerr; - break; - } - } + pcmk__cli_init_logging("attrd_updater", args->verbosity); - if (optind > argc) { - ++argerr; - } + if (args->version) { + cleanup_memory(); - if (options.command != 'R' && options.attr_name == NULL) { - ++argerr; + /* FIXME: When attrd_updater is converted to use formatted output, this can go. */ + pcmk__cli_help('v', CRM_EX_OK); } - if (argerr) { + if (options.command != 'R' && options.attr_name == NULL) { + exit_code = CRM_EX_USAGE; cleanup_memory(); - pcmk__cli_help('?', CRM_EX_USAGE); + crm_exit(exit_code); } if (options.command == 'Q') { exit_code = crm_errno2exit(do_query(options.attr_name, options.attr_node, options.query_all)); } else { /* @TODO We don't know whether the specified node is a Pacemaker Remote * node or not, so we can't set pcmk__node_attr_remote when appropriate. * However, it's not a big problem, because pacemaker-attrd will learn * and remember a node's "remoteness". */ const char *target = pcmk__node_attr_target(options.attr_node); exit_code = pcmk_rc2exitc(do_update(options.command, target == NULL ? options.attr_node : target, options.attr_name, options.attr_value, options.attr_section, options.attr_set, options.attr_dampen, options.attr_options)); } cleanup_memory(); crm_exit(exit_code); } /*! * \internal * \brief Submit a query request to pacemaker-attrd and wait for reply * * \param[in] name Name of attribute to query * \param[in] host Query applies to this host only (or all hosts if NULL) * \param[out] reply On success, will be set to new XML tree with reply * * \return pcmk_ok on success, -errno on error * \note On success, caller is responsible for freeing result via free_xml(*reply) */ static int send_attrd_query(const char *name, const char *host, xmlNode **reply) { int rc; crm_ipc_t *ipc; xmlNode *query; /* Build the query XML */ query = create_xml_node(NULL, __func__); if (query == NULL) { return -ENOMEM; } crm_xml_add(query, F_TYPE, T_ATTRD); crm_xml_add(query, F_ORIG, crm_system_name); crm_xml_add(query, PCMK__XA_ATTR_NODE_NAME, host); crm_xml_add(query, PCMK__XA_TASK, PCMK__ATTRD_CMD_QUERY); crm_xml_add(query, PCMK__XA_ATTR_NAME, name); /* Connect to pacemaker-attrd, send query XML and get reply */ crm_debug("Sending query for value of %s on %s", name, (host? host : "all nodes")); ipc = crm_ipc_new(T_ATTRD, 0); if (crm_ipc_connect(ipc) == FALSE) { crm_perror(LOG_ERR, "Connection to cluster attribute manager failed"); rc = -ENOTCONN; } else { rc = crm_ipc_send(ipc, query, crm_ipc_client_response, 0, reply); if (rc > 0) { rc = pcmk_ok; } crm_ipc_close(ipc); } crm_ipc_destroy(ipc); free_xml(query); return(rc); } /*! * \brief Validate pacemaker-attrd's XML reply to an query * * param[in] reply Root of reply XML tree to validate * param[in] attr_name Name of attribute that was queried * * \return pcmk_ok on success, * -errno on error (-ENXIO = requested attribute does not exist) */ static int validate_attrd_reply(xmlNode *reply, const char *attr_name) { const char *reply_attr; if (reply == NULL) { fprintf(stderr, "Could not query value of %s: reply did not contain valid XML\n", attr_name); return -pcmk_err_schema_validation; } crm_log_xml_trace(reply, "Reply"); reply_attr = crm_element_value(reply, PCMK__XA_ATTR_NAME); if (reply_attr == NULL) { fprintf(stderr, "Could not query value of %s: attribute does not exist\n", attr_name); return -ENXIO; } if (!pcmk__str_eq(crm_element_value(reply, F_TYPE), T_ATTRD, pcmk__str_casei) || (crm_element_value(reply, PCMK__XA_ATTR_VERSION) == NULL) || strcmp(reply_attr, attr_name)) { fprintf(stderr, "Could not query value of %s: reply did not contain expected identification\n", attr_name); return -pcmk_err_schema_validation; } return pcmk_ok; } /*! * \brief Print the attribute values in a pacemaker-attrd XML query reply * * \param[in] reply Root of XML tree with query reply * \param[in] attr_name Name of attribute that was queried * * \return TRUE if any values were printed */ static gboolean print_attrd_values(xmlNode *reply, const char *attr_name) { xmlNode *child; const char *reply_host, *reply_value; gboolean have_values = FALSE; /* Iterate through reply's XML tags (a node tag for each host-value pair) */ for (child = pcmk__xml_first_child(reply); child != NULL; child = pcmk__xml_next(child)) { if (!pcmk__str_eq((const char *)child->name, XML_CIB_TAG_NODE, pcmk__str_casei)) { crm_warn("Ignoring unexpected %s tag in query reply", child->name); } else { reply_host = crm_element_value(child, PCMK__XA_ATTR_NODE_NAME); reply_value = crm_element_value(child, PCMK__XA_ATTR_VALUE); if (reply_host == NULL) { crm_warn("Ignoring %s tag without %s attribute in query reply", XML_CIB_TAG_NODE, PCMK__XA_ATTR_NODE_NAME); } else { printf("name=\"%s\" host=\"%s\" value=\"%s\"\n", attr_name, reply_host, (reply_value? reply_value : "")); have_values = TRUE; } } } return have_values; } /*! * \brief Submit a query to pacemaker-attrd and print reply * * \param[in] attr_name Name of attribute to be affected by request * \param[in] attr_node Name of host to query for (or NULL for localhost) * \param[in] query_all If TRUE, ignore attr_node and query all nodes instead * * \return pcmk_ok on success, -errno on error */ static int do_query(const char *attr_name, const char *attr_node, gboolean query_all) { xmlNode *reply = NULL; int rc; /* Decide which node(s) to query */ if (query_all == TRUE) { attr_node = NULL; } else { const char *target = pcmk__node_attr_target(attr_node); if (target != NULL) { attr_node = target; } } /* Build and send pacemaker-attrd request, and get XML reply */ rc = send_attrd_query(attr_name, attr_node, &reply); if (rc != pcmk_ok) { fprintf(stderr, "Could not query value of %s: %s (%d)\n", attr_name, pcmk_strerror(rc), rc); return rc; } /* Validate the XML reply */ rc = validate_attrd_reply(reply, attr_name); if (rc != pcmk_ok) { if (reply != NULL) { free_xml(reply); } return rc; } /* Print the values from the reply */ if (print_attrd_values(reply, attr_name) == FALSE) { fprintf(stderr, "Could not query value of %s: reply had attribute name but no host values\n", attr_name); free_xml(reply); return -pcmk_err_schema_validation; } return pcmk_ok; } static int do_update(char command, const char *attr_node, const char *attr_name, const char *attr_value, const char *attr_section, const char *attr_set, const char *attr_dampen, int attr_options) { int rc = pcmk__node_attr_request(NULL, command, attr_node, attr_name, attr_value, attr_section, attr_set, attr_dampen, NULL, attr_options); if (rc != pcmk_rc_ok) { fprintf(stderr, "Could not update %s=%s: %s (%d)\n", attr_name, attr_value, pcmk_rc_str(rc), rc); } return rc; }