diff --git a/daemons/controld/controld_attrd.c b/daemons/controld/controld_attrd.c index eff8070818..3474aac078 100644 --- a/daemons/controld/controld_attrd.c +++ b/daemons/controld/controld_attrd.c @@ -1,158 +1,158 @@ /* - * Copyright 2006-2024 the Pacemaker project contributors + * Copyright 2006-2025 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 static pcmk_ipc_api_t *attrd_api = NULL; void controld_close_attrd_ipc(void) { if (attrd_api != NULL) { crm_trace("Closing connection to " PCMK__SERVER_ATTRD); pcmk_disconnect_ipc(attrd_api); pcmk_free_ipc_api(attrd_api); attrd_api = NULL; } } static inline const char * node_type(bool is_remote) { return is_remote? "Pacemaker Remote" : "cluster"; } static inline const char * when(void) { return pcmk_is_set(controld_globals.fsa_input_register, R_SHUTDOWN)? " at shutdown" : ""; } static void handle_attr_error(void) { if (AM_I_DC) { /* We are unable to provide accurate information to the * scheduler, so allow another node to take over DC. * @TODO Should we do this unconditionally on any failure? */ crmd_exit(CRM_EX_FATAL); } else if (pcmk_is_set(controld_globals.fsa_input_register, R_SHUTDOWN)) { // Fast-track shutdown since unable to request via attribute register_fsa_input(C_FSA_INTERNAL, I_FAIL, NULL); } } void update_attrd(const char *host, const char *name, const char *value, const char *user_name, gboolean is_remote_node) { int rc = pcmk_rc_ok; if (attrd_api == NULL) { rc = pcmk_new_ipc_api(&attrd_api, pcmk_ipc_attrd); } if (rc == pcmk_rc_ok) { uint32_t attrd_opts = pcmk__node_attr_value; if (is_remote_node) { pcmk__set_node_attr_flags(attrd_opts, pcmk__node_attr_remote); } rc = pcmk__attrd_api_update(attrd_api, host, name, value, NULL, NULL, user_name, attrd_opts); } if (rc != pcmk_rc_ok) { do_crm_log(AM_I_DC? LOG_CRIT : LOG_ERR, "Could not update attribute %s=%s for %s node %s%s: %s " QB_XS " rc=%d", name, value, node_type(is_remote_node), host, when(), pcmk_rc_str(rc), rc); handle_attr_error(); } } void update_attrd_list(GList *attrs, uint32_t opts) { int rc = pcmk_rc_ok; if (attrd_api == NULL) { rc = pcmk_new_ipc_api(&attrd_api, pcmk_ipc_attrd); } if (rc == pcmk_rc_ok) { rc = pcmk__attrd_api_update_list(attrd_api, attrs, NULL, NULL, NULL, opts | pcmk__node_attr_value); } if (rc != pcmk_rc_ok) { do_crm_log(AM_I_DC? LOG_CRIT : LOG_ERR, "Could not update multiple node attributes: %s " QB_XS " rc=%d", pcmk_rc_str(rc), rc); handle_attr_error(); } } void update_attrd_remote_node_removed(const char *host, const char *user_name) { int rc = pcmk_rc_ok; if (attrd_api == NULL) { rc = pcmk_new_ipc_api(&attrd_api, pcmk_ipc_attrd); } if (rc == pcmk_rc_ok) { crm_trace("Asking attribute manager to purge Pacemaker Remote node %s", host); - rc = pcmk__attrd_api_purge(attrd_api, host, true); + rc = pcmk__attrd_api_purge(attrd_api, host); } if (rc != pcmk_rc_ok) { crm_err("Could not purge Pacemaker Remote node %s " "in attribute manager%s: %s " QB_XS " rc=%d", host, when(), pcmk_rc_str(rc), rc); } } void update_attrd_clear_failures(const char *host, const char *rsc, const char *op, const char *interval_spec, gboolean is_remote_node) { int rc = pcmk_rc_ok; if (attrd_api == NULL) { rc = pcmk_new_ipc_api(&attrd_api, pcmk_ipc_attrd); } if (rc == pcmk_rc_ok) { uint32_t attrd_opts = pcmk__node_attr_none; if (is_remote_node) { pcmk__set_node_attr_flags(attrd_opts, pcmk__node_attr_remote); } rc = pcmk__attrd_api_clear_failures(attrd_api, host, rsc, op, interval_spec, NULL, attrd_opts); } if (rc != pcmk_rc_ok) { const char *interval_desc = "all"; if (op != NULL) { interval_desc = pcmk__s(interval_spec, "nonrecurring"); } crm_err("Could not clear failure of %s %s for %s on %s node %s%s: %s " QB_XS " rc=%d", interval_desc, pcmk__s(op, "operations"), pcmk__s(rsc, "all resources"), node_type(is_remote_node), host, when(), pcmk_rc_str(rc), rc); } } diff --git a/include/crm/common/ipc_attrd_internal.h b/include/crm/common/ipc_attrd_internal.h index 15450a6679..f708580e49 100644 --- a/include/crm/common/ipc_attrd_internal.h +++ b/include/crm/common/ipc_attrd_internal.h @@ -1,199 +1,198 @@ /* * Copyright 2022-2024 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__CRM_COMMON_IPC_ATTRD_INTERNAL__H #define PCMK__CRM_COMMON_IPC_ATTRD_INTERNAL__H #include // GList #include // pcmk_ipc_api_t #ifdef __cplusplus extern "C" { #endif //! Possible types of attribute manager replies enum pcmk__attrd_api_reply { pcmk__attrd_reply_unknown, pcmk__attrd_reply_query, }; // Information passed with pcmk__attrd_reply_query typedef struct { const char *node; const char *name; const char *value; } pcmk__attrd_query_pair_t; /*! * Attribute manager reply passed to event callback * * \note The pointers in the reply are only guaranteed to be meaningful for the * execution of the callback; if the values are needed for later, the * callback should copy them. */ typedef struct { enum pcmk__attrd_api_reply reply_type; union { // pcmk__attrd_reply_query GList *pairs; } data; } pcmk__attrd_api_reply_t; /*! * \internal * \brief Send a request to the attribute manager to clear resource failure * * \param[in,out] api Attribute manager IPC object * \param[in] node Affect only this node (or NULL for all nodes) * \param[in] resource Name of resource to clear (or NULL for all) * \param[in] operation Name of operation to clear (or NULL for all) * \param[in] interval_spec If operation is not NULL, its interval * \param[in] user_name ACL user to pass to the attribute manager * \param[in] options Bitmask of pcmk__node_attr_opts * * \note If \p api is NULL, a new temporary connection will be created * just for this operation and destroyed afterwards. If \p api is * not NULL but is not yet connected to the attribute manager, the object * will be connected for this operation and left connected afterwards. * This allows for reusing an IPC connection. * * \return Standard Pacemaker return code */ int pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node, const char *resource, const char *operation, const char *interval_spec, const char *user_name, uint32_t options); /*! * \internal * * \brief Delete a previously set attribute by setting its value to NULL * * \param[in,out] api Connection to the attribute manager (or NULL to use * a temporary new connection) * \param[in] node Delete attribute for this node (or NULL for local) * \param[in] name Attribute name * \param[in] options Bitmask of pcmk__node_attr_opts * * \return Standard Pacemaker return code */ int pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options); /*! * \internal * \brief Request removal of a node's transient attributes * - * \param[in,out] api Attribute manager IPC object - * \param[in] node Node whose attributes should be purged - * \param[in] reap If true, also request removal from node caches + * \param[in,out] api Attribute manager IPC object + * \param[in] node Node whose attributes should be purged * * \note If \p api is NULL, a new temporary connection will be created * just for this operation and destroyed afterwards. If \p api is * not NULL but is not yet connected to the attribute manager, the object * will be connected for this operation and left connected afterwards. * This allows for reusing an IPC connection. * * \return Standard Pacemaker return code */ -int pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node, bool reap); +int pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node); /*! * \internal * \brief Get the value of an attribute from the attribute manager * * \param[in,out] api Connection to the attribute manager * \param[in] node Look up the attribute for this node * (or NULL for the local node) * \param[in] name Attribute name * \param[in] options Bitmask of pcmk__node_attr_opts * * \note Passing pcmk__node_attr_query_all will cause the function to query * the value of \p name on all nodes, regardless of the value of \p node. * * \return Standard Pacemaker return code */ int pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options); /*! * \internal * \brief Tell the attribute manager to update the CIB with current values * * \param[in,out] api Attribute manager IPC object * \param[in] node Affect only this node (or NULL for all nodes) * * \note If \p api is NULL, a new temporary connection will be created * just for this operation and destroyed afterwards. If \p api is * not NULL but is not yet connected to the attribute manager, the object * will be connected for this operation and left connected afterwards. * This allows for reusing an IPC connection. * * \return Standard Pacemaker return code */ int pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node); /*! * \internal * \brief Update an attribute's value, time to wait, or both * * \param[in,out] api Attribute manager IPC object * \param[in] node Affect only this node (or NULL for current node) * \param[in] name Attribute name * \param[in] value The attribute's new value, or NULL to unset * \param[in] dampen The new time to wait value, or NULL to unset * \param[in] set ID of attribute set to use (or NULL for first) * \param[in] user_name ACL user to pass to the attribute manager * \param[in] options Bitmask of pcmk__node_attr_opts * * \note If \p api is NULL, a new temporary connection will be created * just for this operation and destroyed afterwards. If \p api is * not NULL but is not yet connected to the attribute manager, the object * will be connected for this operation and left connected afterwards. * This allows for reusing an IPC connection. * * \return Standard Pacemaker return code */ int pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name, const char *value, const char *dampen, const char *set, const char *user_name, uint32_t options); /*! * \internal * \brief Like pcmk__attrd_api_update, but for multiple attributes at once * * \param[in,out] api Attribute manager IPC object * \param[in,out] attrs A list of pcmk__attr_query_pair_t structs * \param[in] dampen The new time to wait value, or NULL to unset * \param[in] set ID of attribute set to use (or NULL for first) * \param[in] user_name ACL user to pass to the attribute manager * \param[in] options Bitmask of pcmk__node_attr_opts * * \note If \p api is NULL, a new temporary connection will be created * just for this operation and destroyed afterwards. If \p api is * not NULL but is not yet connected to the attribute manager, the object * will be connected for this operation and left connected afterwards. * This allows for reusing an IPC connection. * * \note Not all attrd versions support setting multiple attributes at once. * For those servers that do not, this function will fall back to just * sending a separate IPC request for each attribute. * * \return Standard Pacemaker return code */ int pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen, const char *set, const char *user_name, uint32_t options); #ifdef __cplusplus } #endif #endif // PCMK__CRM_COMMON_IPC_ATTRD_INTERNAL__H diff --git a/lib/common/ipc_attrd.c b/lib/common/ipc_attrd.c index 1d87c29d61..7e0b02d400 100644 --- a/lib/common/ipc_attrd.c +++ b/lib/common/ipc_attrd.c @@ -1,499 +1,498 @@ /* * Copyright 2011-2025 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. */ #include #include #include // xmlChar #include #include #include #include #include #include "crmcommon_private.h" static void set_pairs_data(pcmk__attrd_api_reply_t *data, xmlNode *msg_data) { const char *name = NULL; pcmk__attrd_query_pair_t *pair; name = pcmk__xe_get(msg_data, PCMK__XA_ATTR_NAME); for (xmlNode *node = pcmk__xe_first_child(msg_data, PCMK_XE_NODE, NULL, NULL); node != NULL; node = pcmk__xe_next(node, PCMK_XE_NODE)) { pair = pcmk__assert_alloc(1, sizeof(pcmk__attrd_query_pair_t)); pair->node = pcmk__xe_get(node, PCMK__XA_ATTR_HOST); pair->name = name; pair->value = pcmk__xe_get(node, PCMK__XA_ATTR_VALUE); data->data.pairs = g_list_prepend(data->data.pairs, pair); } } static bool reply_expected(pcmk_ipc_api_t *api, const xmlNode *request) { const char *command = pcmk__xe_get(request, PCMK_XA_TASK); return pcmk__str_any_of(command, PCMK__ATTRD_CMD_CLEAR_FAILURE, PCMK__ATTRD_CMD_QUERY, PCMK__ATTRD_CMD_REFRESH, PCMK__ATTRD_CMD_UPDATE, PCMK__ATTRD_CMD_UPDATE_BOTH, PCMK__ATTRD_CMD_UPDATE_DELAY, NULL); } static bool dispatch(pcmk_ipc_api_t *api, xmlNode *reply) { const char *value = NULL; crm_exit_t status = CRM_EX_OK; pcmk__attrd_api_reply_t reply_data = { pcmk__attrd_reply_unknown }; if (pcmk__xe_is(reply, PCMK__XE_ACK)) { return false; } /* Do some basic validation of the reply */ value = pcmk__xe_get(reply, PCMK__XA_T); if (pcmk__str_empty(value) || !pcmk__str_eq(value, PCMK__VALUE_ATTRD, pcmk__str_none)) { crm_info("Unrecognizable message from attribute manager: " "message type '%s' not '" PCMK__VALUE_ATTRD "'", pcmk__s(value, "")); status = CRM_EX_PROTOCOL; goto done; } value = pcmk__xe_get(reply, PCMK__XA_SUBT); /* Only the query command gets a reply for now. NULL counts as query for * backward compatibility with attribute managers <2.1.3 that didn't set it. */ if (pcmk__str_eq(value, PCMK__ATTRD_CMD_QUERY, pcmk__str_null_matches)) { if (!xmlHasProp(reply, (const xmlChar *) PCMK__XA_ATTR_NAME)) { status = ENXIO; // Most likely, the attribute doesn't exist goto done; } reply_data.reply_type = pcmk__attrd_reply_query; set_pairs_data(&reply_data, reply); } else { crm_info("Unrecognizable message from attribute manager: " "message subtype '%s' unknown", pcmk__s(value, "")); status = CRM_EX_PROTOCOL; goto done; } done: pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data); /* Free any reply data that was allocated */ if (reply_data.data.pairs) { g_list_free_full(reply_data.data.pairs, free); } return false; } pcmk__ipc_methods_t * pcmk__attrd_api_methods(void) { pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t)); if (cmds != NULL) { cmds->new_data = NULL; cmds->free_data = NULL; cmds->post_connect = NULL; cmds->reply_expected = reply_expected; cmds->dispatch = dispatch; } return cmds; } /*! * \internal * \brief Create a generic attribute manager operation * * \param[in] user_name If not NULL, ACL user to set for operation * * \return XML of attribute manager operation */ static xmlNode * create_attrd_op(const char *user_name) { xmlNode *attrd_op = pcmk__xe_create(NULL, __func__); pcmk__xe_set(attrd_op, PCMK__XA_T, PCMK__VALUE_ATTRD); pcmk__xe_set(attrd_op, PCMK__XA_SRC, pcmk__s(crm_system_name, "unknown")); pcmk__xe_set(attrd_op, PCMK__XA_ATTR_USER, user_name); return attrd_op; } static int connect_and_send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request) { static const int max_retries = 5; int remaining_attempts = max_retries; int rc = pcmk_rc_ok; bool created_api = false; enum pcmk_ipc_dispatch dispatch = pcmk_ipc_dispatch_sync; if (api == NULL) { rc = pcmk_new_ipc_api(&api, pcmk_ipc_attrd); if (rc != pcmk_rc_ok) { return rc; } created_api = true; } else { dispatch = api->dispatch_type; } // If attrd is killed and is being restarted we will temporarily get // ECONNREFUSED on connect if it is already dead or ENOTCONN if it died // after we connected to it. We should wait a bit and retry in those cases. do { if (rc == ENOTCONN || rc == ECONNREFUSED) { sleep(max_retries - remaining_attempts); } rc = pcmk__connect_ipc(api, dispatch, remaining_attempts); if (rc == pcmk_rc_ok) { rc = pcmk__send_ipc_request(api, request); } remaining_attempts--; } while ((rc == ENOTCONN || rc == ECONNREFUSED) && remaining_attempts >= 0); if (created_api) { pcmk_free_ipc_api(api); } return rc; } int pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node, const char *resource, const char *operation, const char *interval_spec, const char *user_name, uint32_t options) { int rc = pcmk_rc_ok; xmlNode *request = create_attrd_op(user_name); const char *interval_desc = NULL; const char *op_desc = NULL; const char *target = pcmk__node_attr_target(node); if (target != NULL) { node = target; } if (operation) { interval_desc = pcmk__s(interval_spec, "nonrecurring"); op_desc = operation; } else { interval_desc = "all"; op_desc = "operations"; } crm_debug("Asking %s to clear failure of %s %s for %s on %s", pcmk_ipc_name(api, true), interval_desc, op_desc, pcmk__s(resource, "all resources"), pcmk__s(node, "all nodes")); pcmk__xe_set(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_CLEAR_FAILURE); pcmk__xe_set(request, PCMK__XA_ATTR_HOST, node); pcmk__xe_set(request, PCMK__XA_ATTR_RESOURCE, resource); pcmk__xe_set(request, PCMK__XA_ATTR_CLEAR_OPERATION, operation); pcmk__xe_set(request, PCMK__XA_ATTR_CLEAR_INTERVAL, interval_spec); pcmk__xe_set_int(request, PCMK__XA_ATTR_IS_REMOTE, pcmk_is_set(options, pcmk__node_attr_remote)); rc = connect_and_send_attrd_request(api, request); pcmk__xml_free(request); return rc; } int pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options) { const char *target = NULL; if (name == NULL) { return EINVAL; } target = pcmk__node_attr_target(node); if (target != NULL) { node = target; } /* Make sure the right update option is set. */ options &= ~pcmk__node_attr_delay; options |= pcmk__node_attr_value; return pcmk__attrd_api_update(api, node, name, NULL, NULL, NULL, NULL, options); } int -pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node, bool reap) +pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node) { int rc = pcmk_rc_ok; xmlNode *request = NULL; const char *target = pcmk__node_attr_target(node); if (target != NULL) { node = target; } - crm_debug("Asking %s to purge transient attributes%s for %s", - pcmk_ipc_name(api, true), - (reap? " and node cache entries" : ""), - pcmk__s(node, "local node")); + crm_debug("Asking %s to purge transient attributes and node cache entries " + "for %s", + pcmk_ipc_name(api, true), pcmk__s(node, "local node")); request = create_attrd_op(NULL); pcmk__xe_set(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE); - pcmk__xe_set_bool_attr(request, PCMK__XA_REAP, reap); + pcmk__xe_set_bool_attr(request, PCMK__XA_REAP, true); pcmk__xe_set(request, PCMK__XA_ATTR_HOST, node); rc = connect_and_send_attrd_request(api, request); pcmk__xml_free(request); return rc; } int pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options) { int rc = pcmk_rc_ok; xmlNode *request = NULL; const char *target = NULL; if (name == NULL) { return EINVAL; } if (pcmk_is_set(options, pcmk__node_attr_query_all)) { node = NULL; } else { target = pcmk__node_attr_target(node); if (target != NULL) { node = target; } else if (node == NULL) { node = "localhost"; } } crm_debug("Querying %s for value of '%s'%s%s", pcmk_ipc_name(api, true), name, ((node == NULL)? "" : " on "), pcmk__s(node, "")); request = create_attrd_op(NULL); pcmk__xe_set(request, PCMK__XA_ATTR_NAME, name); pcmk__xe_set(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_QUERY); pcmk__xe_set(request, PCMK__XA_ATTR_HOST, node); rc = connect_and_send_attrd_request(api, request); pcmk__xml_free(request); return rc; } int pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node) { int rc = pcmk_rc_ok; xmlNode *request = NULL; const char *target = pcmk__node_attr_target(node); if (target != NULL) { node = target; } crm_debug("Asking %s to write all transient attributes for %s to CIB", pcmk_ipc_name(api, true), pcmk__s(node, "local node")); request = create_attrd_op(NULL); pcmk__xe_set(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_REFRESH); pcmk__xe_set(request, PCMK__XA_ATTR_HOST, node); rc = connect_and_send_attrd_request(api, request); pcmk__xml_free(request); return rc; } static void add_op_attr(xmlNode *op, uint32_t options) { if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) { pcmk__xe_set(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE_BOTH); } else if (pcmk_is_set(options, pcmk__node_attr_value)) { pcmk__xe_set(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE); } else if (pcmk_is_set(options, pcmk__node_attr_delay)) { pcmk__xe_set(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE_DELAY); } } static void populate_update_op(xmlNode *op, const char *node, const char *name, const char *value, const char *dampen, const char *set, uint32_t options) { if (pcmk_is_set(options, pcmk__node_attr_pattern)) { pcmk__xe_set(op, PCMK__XA_ATTR_REGEX, name); } else { pcmk__xe_set(op, PCMK__XA_ATTR_NAME, name); } if (pcmk_is_set(options, pcmk__node_attr_utilization)) { pcmk__xe_set(op, PCMK__XA_ATTR_SET_TYPE, PCMK_XE_UTILIZATION); } else { pcmk__xe_set(op, PCMK__XA_ATTR_SET_TYPE, PCMK_XE_INSTANCE_ATTRIBUTES); } add_op_attr(op, options); pcmk__xe_set(op, PCMK__XA_ATTR_VALUE, value); pcmk__xe_set(op, PCMK__XA_ATTR_DAMPENING, dampen); pcmk__xe_set(op, PCMK__XA_ATTR_HOST, node); pcmk__xe_set(op, PCMK__XA_ATTR_SET, set); pcmk__xe_set_int(op, PCMK__XA_ATTR_IS_REMOTE, pcmk_is_set(options, pcmk__node_attr_remote)); pcmk__xe_set_int(op, PCMK__XA_ATTR_IS_PRIVATE, pcmk_is_set(options, pcmk__node_attr_private)); if (pcmk_is_set(options, pcmk__node_attr_sync_local)) { pcmk__xe_set(op, PCMK__XA_ATTR_SYNC_POINT, PCMK__VALUE_LOCAL); } else if (pcmk_is_set(options, pcmk__node_attr_sync_cluster)) { pcmk__xe_set(op, PCMK__XA_ATTR_SYNC_POINT, PCMK__VALUE_CLUSTER); } } int pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name, const char *value, const char *dampen, const char *set, const char *user_name, uint32_t options) { int rc = pcmk_rc_ok; xmlNode *request = NULL; const char *target = NULL; if (name == NULL) { return EINVAL; } target = pcmk__node_attr_target(node); if (target != NULL) { node = target; } crm_debug("Asking %s to update '%s' to '%s' for %s", pcmk_ipc_name(api, true), name, pcmk__s(value, "(null)"), pcmk__s(node, "local node")); request = create_attrd_op(user_name); populate_update_op(request, node, name, value, dampen, set, options); rc = connect_and_send_attrd_request(api, request); pcmk__xml_free(request); return rc; } int pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen, const char *set, const char *user_name, uint32_t options) { int rc = pcmk_rc_ok; xmlNode *request = NULL; if (attrs == NULL) { return EINVAL; } /* There are two different ways of handling a list of attributes: * * (1) For messages originating from some command line tool, we have to send * them one at a time. In this loop, we just call pcmk__attrd_api_update * for each, letting it deal with creating the API object if it doesn't * already exist. * * The reason we can't use a single message in this case is that we can't * trust that the server supports it. Remote nodes could be involved * here, and there's no guarantee that a newer client running on a remote * node is talking to (or proxied through) a cluster node with a newer * attrd. We also can't just try sending a single message and then falling * back on multiple. There's no handshake with the attrd server to * determine its version. And then we would need to do that fallback in the * dispatch function for this to work for all connection types (mainloop in * particular), and at that point we won't know what the original message * was in order to break it apart and resend as individual messages. * * (2) For messages between daemons, we can be assured that the local attrd * will support the new message and that it can send to the other attrds * as one request or split up according to the minimum supported version. */ for (GList *iter = attrs; iter != NULL; iter = iter->next) { pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data; if (pcmk__is_daemon) { const char *target = NULL; xmlNode *child = NULL; /* First time through this loop - create the basic request. */ if (request == NULL) { request = create_attrd_op(user_name); add_op_attr(request, options); } /* Add a child node for this operation. We add the task to the top * level XML node so attrd_ipc_dispatch doesn't need changes. And * then we also add the task to each child node in populate_update_op * so attrd_client_update knows what form of update is taking place. */ child = pcmk__xe_create(request, PCMK_XE_OP); target = pcmk__node_attr_target(pair->node); if (target != NULL) { pair->node = target; } populate_update_op(child, pair->node, pair->name, pair->value, dampen, set, options); } else { rc = pcmk__attrd_api_update(api, pair->node, pair->name, pair->value, dampen, set, user_name, options); } } /* If we were doing multiple attributes at once, we still need to send the * request. Do that now, creating and destroying the API object if needed. */ if (pcmk__is_daemon) { rc = connect_and_send_attrd_request(api, request); pcmk__xml_free(request); } return rc; }