root/lib/common/ipc_attrd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_pairs_data
  2. reply_expected
  3. dispatch
  4. pcmk__attrd_api_methods
  5. create_attrd_op
  6. connect_and_send_attrd_request
  7. pcmk__attrd_api_clear_failures
  8. pcmk__attrd_api_delete
  9. pcmk__attrd_api_purge
  10. pcmk__attrd_api_query
  11. pcmk__attrd_api_refresh
  12. add_op_attr
  13. populate_update_op
  14. pcmk__attrd_api_update
  15. pcmk__attrd_api_update_list

   1 /*
   2  * Copyright 2011-2025 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdio.h>
  13 
  14 #include <libxml/xmlstring.h>               // xmlChar
  15 
  16 #include <crm/crm.h>
  17 #include <crm/common/attrs_internal.h>
  18 #include <crm/common/ipc.h>
  19 #include <crm/common/ipc_attrd_internal.h>
  20 #include <crm/common/xml.h>
  21 #include "crmcommon_private.h"
  22 
  23 static void
  24 set_pairs_data(pcmk__attrd_api_reply_t *data, xmlNode *msg_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  25 {
  26     const char *name = NULL;
  27     pcmk__attrd_query_pair_t *pair;
  28 
  29     name = crm_element_value(msg_data, PCMK__XA_ATTR_NAME);
  30 
  31     for (xmlNode *node = pcmk__xe_first_child(msg_data, PCMK_XE_NODE, NULL,
  32                                               NULL);
  33          node != NULL; node = pcmk__xe_next(node, PCMK_XE_NODE)) {
  34 
  35         pair = pcmk__assert_alloc(1, sizeof(pcmk__attrd_query_pair_t));
  36 
  37         pair->node = crm_element_value(node, PCMK__XA_ATTR_HOST);
  38         pair->name = name;
  39         pair->value = crm_element_value(node, PCMK__XA_ATTR_VALUE);
  40         data->data.pairs = g_list_prepend(data->data.pairs, pair);
  41     }
  42 }
  43 
  44 static bool
  45 reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
  46 {
  47     const char *command = crm_element_value(request, PCMK_XA_TASK);
  48 
  49     return pcmk__str_any_of(command,
  50                             PCMK__ATTRD_CMD_CLEAR_FAILURE,
  51                             PCMK__ATTRD_CMD_QUERY,
  52                             PCMK__ATTRD_CMD_REFRESH,
  53                             PCMK__ATTRD_CMD_UPDATE,
  54                             PCMK__ATTRD_CMD_UPDATE_BOTH,
  55                             PCMK__ATTRD_CMD_UPDATE_DELAY,
  56                             NULL);
  57 }
  58 
  59 static bool
  60 dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
     /* [previous][next][first][last][top][bottom][index][help] */
  61 {
  62     const char *value = NULL;
  63     crm_exit_t status = CRM_EX_OK;
  64 
  65     pcmk__attrd_api_reply_t reply_data = {
  66         pcmk__attrd_reply_unknown
  67     };
  68 
  69     if (pcmk__xe_is(reply, PCMK__XE_ACK)) {
  70         return false;
  71     }
  72 
  73     /* Do some basic validation of the reply */
  74     value = crm_element_value(reply, PCMK__XA_T);
  75     if (pcmk__str_empty(value)
  76         || !pcmk__str_eq(value, PCMK__VALUE_ATTRD, pcmk__str_none)) {
  77         crm_info("Unrecognizable message from attribute manager: "
  78                  "message type '%s' not '" PCMK__VALUE_ATTRD "'",
  79                  pcmk__s(value, ""));
  80         status = CRM_EX_PROTOCOL;
  81         goto done;
  82     }
  83 
  84     value = crm_element_value(reply, PCMK__XA_SUBT);
  85 
  86     /* Only the query command gets a reply for now. NULL counts as query for
  87      * backward compatibility with attribute managers <2.1.3 that didn't set it.
  88      */
  89     if (pcmk__str_eq(value, PCMK__ATTRD_CMD_QUERY, pcmk__str_null_matches)) {
  90         if (!xmlHasProp(reply, (const xmlChar *) PCMK__XA_ATTR_NAME)) {
  91             status = ENXIO; // Most likely, the attribute doesn't exist
  92             goto done;
  93         }
  94         reply_data.reply_type = pcmk__attrd_reply_query;
  95         set_pairs_data(&reply_data, reply);
  96 
  97     } else {
  98         crm_info("Unrecognizable message from attribute manager: "
  99                  "message subtype '%s' unknown", pcmk__s(value, ""));
 100         status = CRM_EX_PROTOCOL;
 101         goto done;
 102     }
 103 
 104 done:
 105     pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
 106 
 107     /* Free any reply data that was allocated */
 108     if (reply_data.data.pairs) {
 109         g_list_free_full(reply_data.data.pairs, free);
 110     }
 111 
 112     return false;
 113 }
 114 
 115 pcmk__ipc_methods_t *
 116 pcmk__attrd_api_methods(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 117 {
 118     pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
 119 
 120     if (cmds != NULL) {
 121         cmds->new_data = NULL;
 122         cmds->free_data = NULL;
 123         cmds->post_connect = NULL;
 124         cmds->reply_expected = reply_expected;
 125         cmds->dispatch = dispatch;
 126     }
 127     return cmds;
 128 }
 129 
 130 /*!
 131  * \internal
 132  * \brief Create a generic attribute manager operation
 133  *
 134  * \param[in] user_name  If not NULL, ACL user to set for operation
 135  *
 136  * \return XML of attribute manager operation
 137  */
 138 static xmlNode *
 139 create_attrd_op(const char *user_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 140 {
 141     xmlNode *attrd_op = pcmk__xe_create(NULL, __func__);
 142 
 143     crm_xml_add(attrd_op, PCMK__XA_T, PCMK__VALUE_ATTRD);
 144     crm_xml_add(attrd_op, PCMK__XA_SRC, pcmk__s(crm_system_name, "unknown"));
 145     crm_xml_add(attrd_op, PCMK__XA_ATTR_USER, user_name);
 146 
 147     return attrd_op;
 148 }
 149 
 150 static int
 151 connect_and_send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 152 {
 153     static const int max_retries = 5;
 154     int remaining_attempts = max_retries;
 155     int rc = pcmk_rc_ok;
 156     bool created_api = false;
 157     enum pcmk_ipc_dispatch dispatch = pcmk_ipc_dispatch_sync;
 158 
 159     if (api == NULL) {
 160         rc = pcmk_new_ipc_api(&api, pcmk_ipc_attrd);
 161         if (rc != pcmk_rc_ok) {
 162             return rc;
 163         }
 164         created_api = true;
 165     } else {
 166         dispatch = api->dispatch_type;
 167     }
 168 
 169     // If attrd is killed and is being restarted we will temporarily get
 170     // ECONNREFUSED on connect if it is already dead or ENOTCONN if it died
 171     // after we connected to it. We should wait a bit and retry in those cases.
 172     do {
 173         if (rc == ENOTCONN || rc == ECONNREFUSED) {
 174             sleep(max_retries - remaining_attempts);
 175         }
 176         rc = pcmk__connect_ipc(api, dispatch, remaining_attempts);
 177         if (rc == pcmk_rc_ok) {
 178             rc = pcmk__send_ipc_request(api, request);
 179         }
 180         remaining_attempts--;
 181     } while ((rc == ENOTCONN || rc == ECONNREFUSED) && remaining_attempts >= 0);
 182 
 183     if (created_api) {
 184         pcmk_free_ipc_api(api);
 185     }
 186     return rc;
 187 }
 188 
 189 int
 190 pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 191                                const char *resource, const char *operation,
 192                                const char *interval_spec, const char *user_name,
 193                                uint32_t options)
 194 {
 195     int rc = pcmk_rc_ok;
 196     xmlNode *request = create_attrd_op(user_name);
 197     const char *interval_desc = NULL;
 198     const char *op_desc = NULL;
 199     const char *target = pcmk__node_attr_target(node);
 200 
 201     if (target != NULL) {
 202         node = target;
 203     }
 204 
 205     if (operation) {
 206         interval_desc = pcmk__s(interval_spec, "nonrecurring");
 207         op_desc = operation;
 208     } else {
 209         interval_desc = "all";
 210         op_desc = "operations";
 211     }
 212     crm_debug("Asking %s to clear failure of %s %s for %s on %s",
 213               pcmk_ipc_name(api, true), interval_desc, op_desc,
 214               pcmk__s(resource, "all resources"), pcmk__s(node, "all nodes"));
 215 
 216     crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_CLEAR_FAILURE);
 217     crm_xml_add(request, PCMK__XA_ATTR_HOST, node);
 218     crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource);
 219     crm_xml_add(request, PCMK__XA_ATTR_CLEAR_OPERATION, operation);
 220     crm_xml_add(request, PCMK__XA_ATTR_CLEAR_INTERVAL, interval_spec);
 221     crm_xml_add_int(request, PCMK__XA_ATTR_IS_REMOTE,
 222                     pcmk_is_set(options, pcmk__node_attr_remote));
 223 
 224     rc = connect_and_send_attrd_request(api, request);
 225 
 226     pcmk__xml_free(request);
 227     return rc;
 228 }
 229 
 230 int
 231 pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 232                        uint32_t options)
 233 {
 234     const char *target = NULL;
 235 
 236     if (name == NULL) {
 237         return EINVAL;
 238     }
 239 
 240     target = pcmk__node_attr_target(node);
 241 
 242     if (target != NULL) {
 243         node = target;
 244     }
 245 
 246     /* Make sure the right update option is set. */
 247     options &= ~pcmk__node_attr_delay;
 248     options |= pcmk__node_attr_value;
 249 
 250     return pcmk__attrd_api_update(api, node, name, NULL, NULL, NULL, NULL, options);
 251 }
 252 
 253 int
 254 pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node, bool reap)
     /* [previous][next][first][last][top][bottom][index][help] */
 255 {
 256     int rc = pcmk_rc_ok;
 257     xmlNode *request = NULL;
 258     const char *target = pcmk__node_attr_target(node);
 259 
 260     if (target != NULL) {
 261         node = target;
 262     }
 263 
 264     crm_debug("Asking %s to purge transient attributes%s for %s",
 265               pcmk_ipc_name(api, true),
 266               (reap? " and node cache entries" : ""),
 267               pcmk__s(node, "local node"));
 268 
 269     request = create_attrd_op(NULL);
 270 
 271     crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE);
 272     pcmk__xe_set_bool_attr(request, PCMK__XA_REAP, reap);
 273     crm_xml_add(request, PCMK__XA_ATTR_HOST, node);
 274 
 275     rc = connect_and_send_attrd_request(api, request);
 276 
 277     pcmk__xml_free(request);
 278     return rc;
 279 }
 280 
 281 int
 282 pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 283                       uint32_t options)
 284 {
 285     int rc = pcmk_rc_ok;
 286     xmlNode *request = NULL;
 287     const char *target = NULL;
 288 
 289     if (name == NULL) {
 290         return EINVAL;
 291     }
 292 
 293     if (pcmk_is_set(options, pcmk__node_attr_query_all)) {
 294         node = NULL;
 295     } else {
 296         target = pcmk__node_attr_target(node);
 297 
 298         if (target != NULL) {
 299             node = target;
 300         } else if (node == NULL) {
 301             node = "localhost";
 302         }
 303     }
 304 
 305     crm_debug("Querying %s for value of '%s'%s%s",
 306               pcmk_ipc_name(api, true), name,
 307               ((node == NULL)? "" : " on "), pcmk__s(node, ""));
 308 
 309     request = create_attrd_op(NULL);
 310 
 311     crm_xml_add(request, PCMK__XA_ATTR_NAME, name);
 312     crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_QUERY);
 313     crm_xml_add(request, PCMK__XA_ATTR_HOST, node);
 314 
 315     rc = connect_and_send_attrd_request(api, request);
 316     pcmk__xml_free(request);
 317     return rc;
 318 }
 319 
 320 int
 321 pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 322 {
 323     int rc = pcmk_rc_ok;
 324     xmlNode *request = NULL;
 325     const char *target = pcmk__node_attr_target(node);
 326 
 327     if (target != NULL) {
 328         node = target;
 329     }
 330 
 331     crm_debug("Asking %s to write all transient attributes for %s to CIB",
 332               pcmk_ipc_name(api, true), pcmk__s(node, "local node"));
 333 
 334     request = create_attrd_op(NULL);
 335 
 336     crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_REFRESH);
 337     crm_xml_add(request, PCMK__XA_ATTR_HOST, node);
 338 
 339     rc = connect_and_send_attrd_request(api, request);
 340 
 341     pcmk__xml_free(request);
 342     return rc;
 343 }
 344 
 345 static void
 346 add_op_attr(xmlNode *op, uint32_t options)
     /* [previous][next][first][last][top][bottom][index][help] */
 347 {
 348     if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) {
 349         crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE_BOTH);
 350     } else if (pcmk_is_set(options, pcmk__node_attr_value)) {
 351         crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 352     } else if (pcmk_is_set(options, pcmk__node_attr_delay)) {
 353         crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE_DELAY);
 354     }
 355 }
 356 
 357 static void
 358 populate_update_op(xmlNode *op, const char *node, const char *name, const char *value,
     /* [previous][next][first][last][top][bottom][index][help] */
 359                    const char *dampen, const char *set, uint32_t options)
 360 {
 361     if (pcmk_is_set(options, pcmk__node_attr_pattern)) {
 362         crm_xml_add(op, PCMK__XA_ATTR_REGEX, name);
 363     } else {
 364         crm_xml_add(op, PCMK__XA_ATTR_NAME, name);
 365     }
 366 
 367     if (pcmk_is_set(options, pcmk__node_attr_utilization)) {
 368         crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, PCMK_XE_UTILIZATION);
 369     } else {
 370         crm_xml_add(op, PCMK__XA_ATTR_SET_TYPE, PCMK_XE_INSTANCE_ATTRIBUTES);
 371     }
 372 
 373     add_op_attr(op, options);
 374 
 375     crm_xml_add(op, PCMK__XA_ATTR_VALUE, value);
 376     crm_xml_add(op, PCMK__XA_ATTR_DAMPENING, dampen);
 377     crm_xml_add(op, PCMK__XA_ATTR_HOST, node);
 378     crm_xml_add(op, PCMK__XA_ATTR_SET, set);
 379     crm_xml_add_int(op, PCMK__XA_ATTR_IS_REMOTE,
 380                     pcmk_is_set(options, pcmk__node_attr_remote));
 381     crm_xml_add_int(op, PCMK__XA_ATTR_IS_PRIVATE,
 382                     pcmk_is_set(options, pcmk__node_attr_private));
 383 
 384     if (pcmk_is_set(options, pcmk__node_attr_sync_local)) {
 385         crm_xml_add(op, PCMK__XA_ATTR_SYNC_POINT, PCMK__VALUE_LOCAL);
 386     } else if (pcmk_is_set(options, pcmk__node_attr_sync_cluster)) {
 387         crm_xml_add(op, PCMK__XA_ATTR_SYNC_POINT, PCMK__VALUE_CLUSTER);
 388     }
 389 }
 390 
 391 int
 392 pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 393                        const char *value, const char *dampen, const char *set,
 394                        const char *user_name, uint32_t options)
 395 {
 396     int rc = pcmk_rc_ok;
 397     xmlNode *request = NULL;
 398     const char *target = NULL;
 399 
 400     if (name == NULL) {
 401         return EINVAL;
 402     }
 403 
 404     target = pcmk__node_attr_target(node);
 405 
 406     if (target != NULL) {
 407         node = target;
 408     }
 409 
 410     crm_debug("Asking %s to update '%s' to '%s' for %s",
 411               pcmk_ipc_name(api, true), name, pcmk__s(value, "(null)"),
 412               pcmk__s(node, "local node"));
 413 
 414     request = create_attrd_op(user_name);
 415     populate_update_op(request, node, name, value, dampen, set, options);
 416 
 417     rc = connect_and_send_attrd_request(api, request);
 418 
 419     pcmk__xml_free(request);
 420     return rc;
 421 }
 422 
 423 int
 424 pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen,
     /* [previous][next][first][last][top][bottom][index][help] */
 425                             const char *set, const char *user_name,
 426                             uint32_t options)
 427 {
 428     int rc = pcmk_rc_ok;
 429     xmlNode *request = NULL;
 430 
 431     if (attrs == NULL) {
 432         return EINVAL;
 433     }
 434 
 435     /* There are two different ways of handling a list of attributes:
 436      *
 437      * (1) For messages originating from some command line tool, we have to send
 438      *     them one at a time.  In this loop, we just call pcmk__attrd_api_update
 439      *     for each, letting it deal with creating the API object if it doesn't
 440      *     already exist.
 441      *
 442      *     The reason we can't use a single message in this case is that we can't
 443      *     trust that the server supports it.  Remote nodes could be involved
 444      *     here, and there's no guarantee that a newer client running on a remote
 445      *     node is talking to (or proxied through) a cluster node with a newer
 446      *     attrd.  We also can't just try sending a single message and then falling
 447      *     back on multiple.  There's no handshake with the attrd server to
 448      *     determine its version.  And then we would need to do that fallback in the
 449      *     dispatch function for this to work for all connection types (mainloop in
 450      *     particular), and at that point we won't know what the original message
 451      *     was in order to break it apart and resend as individual messages.
 452      *
 453      * (2) For messages between daemons, we can be assured that the local attrd
 454      *     will support the new message and that it can send to the other attrds
 455      *     as one request or split up according to the minimum supported version.
 456      */
 457     for (GList *iter = attrs; iter != NULL; iter = iter->next) {
 458         pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data;
 459 
 460         if (pcmk__is_daemon) {
 461             const char *target = NULL;
 462             xmlNode *child = NULL;
 463 
 464             /* First time through this loop - create the basic request. */
 465             if (request == NULL) {
 466                 request = create_attrd_op(user_name);
 467                 add_op_attr(request, options);
 468             }
 469 
 470             /* Add a child node for this operation.  We add the task to the top
 471              * level XML node so attrd_ipc_dispatch doesn't need changes.  And
 472              * then we also add the task to each child node in populate_update_op
 473              * so attrd_client_update knows what form of update is taking place.
 474              */
 475             child = pcmk__xe_create(request, PCMK_XE_OP);
 476             target = pcmk__node_attr_target(pair->node);
 477 
 478             if (target != NULL) {
 479                 pair->node = target;
 480             }
 481 
 482             populate_update_op(child, pair->node, pair->name, pair->value, dampen,
 483                                set, options);
 484         } else {
 485             rc = pcmk__attrd_api_update(api, pair->node, pair->name, pair->value,
 486                                         dampen, set, user_name, options);
 487         }
 488     }
 489 
 490     /* If we were doing multiple attributes at once, we still need to send the
 491      * request.  Do that now, creating and destroying the API object if needed.
 492      */
 493     if (pcmk__is_daemon) {
 494         rc = connect_and_send_attrd_request(api, request);
 495         pcmk__xml_free(request);
 496     }
 497 
 498     return rc;
 499 }

/* [previous][next][first][last][top][bottom][index][help] */