root/daemons/attrd/attrd_ipc.c

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

DEFINITIONS

This source file includes following definitions.
  1. build_query_reply
  2. attrd_client_clear_failure
  3. attrd_client_peer_remove
  4. attrd_client_query
  5. attrd_client_refresh
  6. handle_missing_host
  7. expand_regexes
  8. handle_regexes
  9. handle_value_expansion
  10. send_update_msg_to_cluster
  11. send_child_update
  12. attrd_client_update
  13. attrd_ipc_accept
  14. attrd_ipc_closed
  15. attrd_ipc_destroy
  16. attrd_ipc_dispatch
  17. attrd_ipc_fini
  18. attrd_init_ipc

   1 /*
   2  * Copyright 2004-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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <errno.h>
  13 #include <stdint.h>
  14 #include <stdlib.h>
  15 #include <inttypes.h>   // PRIu32
  16 #include <sys/types.h>
  17 
  18 #include <crm/cluster.h>
  19 #include <crm/cluster/internal.h>
  20 #include <crm/common/acl_internal.h>
  21 #include <crm/common/ipc_internal.h>
  22 #include <crm/common/logging.h>
  23 #include <crm/common/results.h>
  24 #include <crm/common/strings_internal.h>
  25 #include <crm/common/util.h>
  26 #include <crm/common/xml.h>
  27 
  28 #include "pacemaker-attrd.h"
  29 
  30 static qb_ipcs_service_t *ipcs = NULL;
  31 
  32 /*!
  33  * \internal
  34  * \brief Build the XML reply to a client query
  35  *
  36  * \param[in] attr Name of requested attribute
  37  * \param[in] host Name of requested host (or NULL for all hosts)
  38  *
  39  * \return New XML reply
  40  * \note Caller is responsible for freeing the resulting XML
  41  */
  42 static xmlNode *build_query_reply(const char *attr, const char *host)
     /* [previous][next][first][last][top][bottom][index][help] */
  43 {
  44     xmlNode *reply = pcmk__xe_create(NULL, __func__);
  45     attribute_t *a;
  46 
  47     crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_ATTRD);
  48     crm_xml_add(reply, PCMK__XA_SUBT, PCMK__ATTRD_CMD_QUERY);
  49     crm_xml_add(reply, PCMK__XA_ATTR_VERSION, ATTRD_PROTOCOL_VERSION);
  50 
  51     /* If desired attribute exists, add its value(s) to the reply */
  52     a = g_hash_table_lookup(attributes, attr);
  53     if (a) {
  54         attribute_value_t *v;
  55         xmlNode *host_value;
  56 
  57         crm_xml_add(reply, PCMK__XA_ATTR_NAME, attr);
  58 
  59         /* Allow caller to use "localhost" to refer to local node */
  60         if (pcmk__str_eq(host, "localhost", pcmk__str_casei)) {
  61             host = attrd_cluster->priv->node_name;
  62             crm_trace("Mapped localhost to %s", host);
  63         }
  64 
  65         /* If a specific node was requested, add its value */
  66         if (host) {
  67             v = g_hash_table_lookup(a->values, host);
  68             host_value = pcmk__xe_create(reply, PCMK_XE_NODE);
  69             crm_xml_add(host_value, PCMK__XA_ATTR_HOST, host);
  70             crm_xml_add(host_value, PCMK__XA_ATTR_VALUE,
  71                         (v? v->current : NULL));
  72 
  73         /* Otherwise, add all nodes' values */
  74         } else {
  75             GHashTableIter iter;
  76 
  77             g_hash_table_iter_init(&iter, a->values);
  78             while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) {
  79                 host_value = pcmk__xe_create(reply, PCMK_XE_NODE);
  80                 crm_xml_add(host_value, PCMK__XA_ATTR_HOST, v->nodename);
  81                 crm_xml_add(host_value, PCMK__XA_ATTR_VALUE, v->current);
  82             }
  83         }
  84     }
  85     return reply;
  86 }
  87 
  88 xmlNode *
  89 attrd_client_clear_failure(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
  90 {
  91     xmlNode *xml = request->xml;
  92     const char *rsc, *op, *interval_spec;
  93 
  94     if (minimum_protocol_version >= 2) {
  95         /* Propagate to all peers (including ourselves).
  96          * This ends up at attrd_peer_message().
  97          */
  98         attrd_send_message(NULL, xml, false);
  99         pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 100         return NULL;
 101     }
 102 
 103     rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
 104     op = crm_element_value(xml, PCMK__XA_ATTR_CLEAR_OPERATION);
 105     interval_spec = crm_element_value(xml, PCMK__XA_ATTR_CLEAR_INTERVAL);
 106 
 107     /* Map this to an update */
 108     crm_xml_add(xml, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 109 
 110     /* Add regular expression matching desired attributes */
 111 
 112     if (rsc) {
 113         char *pattern;
 114 
 115         if (op == NULL) {
 116             pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc);
 117 
 118         } else {
 119             guint interval_ms = 0U;
 120 
 121             pcmk_parse_interval_spec(interval_spec, &interval_ms);
 122             pattern = crm_strdup_printf(ATTRD_RE_CLEAR_OP,
 123                                         rsc, op, interval_ms);
 124         }
 125 
 126         crm_xml_add(xml, PCMK__XA_ATTR_REGEX, pattern);
 127         free(pattern);
 128 
 129     } else {
 130         crm_xml_add(xml, PCMK__XA_ATTR_REGEX, ATTRD_RE_CLEAR_ALL);
 131     }
 132 
 133     /* Make sure attribute and value are not set, so we delete via regex */
 134     pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_NAME);
 135     pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_VALUE);
 136 
 137     return attrd_client_update(request);
 138 }
 139 
 140 xmlNode *
 141 attrd_client_peer_remove(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 142 {
 143     xmlNode *xml = request->xml;
 144 
 145     // Host and ID are not used in combination, rather host has precedence
 146     const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
 147     char *host_alloc = NULL;
 148 
 149     attrd_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags);
 150 
 151     if (host == NULL) {
 152         int nodeid = 0;
 153 
 154         crm_element_value_int(xml, PCMK__XA_ATTR_HOST_ID, &nodeid);
 155         if (nodeid > 0) {
 156             pcmk__node_status_t *node = NULL;
 157             char *host_alloc = NULL;
 158 
 159             node = pcmk__search_node_caches(nodeid, NULL, NULL,
 160                                             pcmk__node_search_cluster_member);
 161             if ((node != NULL) && (node->name != NULL)) {
 162                 // Use cached name if available
 163                 host = node->name;
 164             } else {
 165                 // Otherwise ask cluster layer
 166                 host_alloc = pcmk__cluster_node_name(nodeid);
 167                 host = host_alloc;
 168             }
 169             crm_xml_add(xml, PCMK__XA_ATTR_HOST, host);
 170         }
 171     }
 172 
 173     if (host) {
 174         crm_info("Client %s is requesting all values for %s be removed",
 175                  pcmk__client_name(request->ipc_client), host);
 176         attrd_send_message(NULL, xml, false); /* ends up at attrd_peer_message() */
 177         free(host_alloc);
 178     } else {
 179         crm_info("Ignoring request by client %s to remove all peer values without specifying peer",
 180                  pcmk__client_name(request->ipc_client));
 181     }
 182 
 183     pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 184     return NULL;
 185 }
 186 
 187 xmlNode *
 188 attrd_client_query(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 189 {
 190     xmlNode *query = request->xml;
 191     xmlNode *reply = NULL;
 192     const char *attr = NULL;
 193 
 194     crm_debug("Query arrived from %s", pcmk__client_name(request->ipc_client));
 195 
 196     /* Request must specify attribute name to query */
 197     attr = crm_element_value(query, PCMK__XA_ATTR_NAME);
 198     if (attr == NULL) {
 199         pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
 200                             "Ignoring malformed query from %s (no attribute name given)",
 201                             pcmk__client_name(request->ipc_client));
 202         return NULL;
 203     }
 204 
 205     /* Build the XML reply */
 206     reply = build_query_reply(attr,
 207                               crm_element_value(query, PCMK__XA_ATTR_HOST));
 208     if (reply == NULL) {
 209         pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
 210                             "Could not respond to query from %s: could not create XML reply",
 211                             pcmk__client_name(request->ipc_client));
 212         return NULL;
 213     } else {
 214         pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 215     }
 216 
 217     request->ipc_client->request_id = 0;
 218     return reply;
 219 }
 220 
 221 xmlNode *
 222 attrd_client_refresh(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 223 {
 224     crm_info("Updating all attributes");
 225 
 226     attrd_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags);
 227     attrd_write_attributes(attrd_write_all|attrd_write_no_delay);
 228 
 229     pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 230     return NULL;
 231 }
 232 
 233 static void
 234 handle_missing_host(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 235 {
 236     if (crm_element_value(xml, PCMK__XA_ATTR_HOST) == NULL) {
 237         crm_trace("Inferring local node %s with XML ID %s",
 238                   attrd_cluster->priv->node_name,
 239                   attrd_cluster->priv->node_xml_id);
 240         crm_xml_add(xml, PCMK__XA_ATTR_HOST, attrd_cluster->priv->node_name);
 241         crm_xml_add(xml, PCMK__XA_ATTR_HOST_ID,
 242                     attrd_cluster->priv->node_xml_id);
 243     }
 244 }
 245 
 246 /* Convert a single IPC message with a regex into one with multiple children, one
 247  * for each regex match.
 248  */
 249 static int
 250 expand_regexes(xmlNode *xml, const char *attr, const char *value, const char *regex)
     /* [previous][next][first][last][top][bottom][index][help] */
 251 {
 252     if (attr == NULL && regex) {
 253         bool matched = false;
 254         GHashTableIter aIter;
 255         regex_t r_patt;
 256 
 257         crm_debug("Setting %s to %s", regex, value);
 258         if (regcomp(&r_patt, regex, REG_EXTENDED|REG_NOSUB)) {
 259             return EINVAL;
 260         }
 261 
 262         g_hash_table_iter_init(&aIter, attributes);
 263         while (g_hash_table_iter_next(&aIter, (gpointer *) & attr, NULL)) {
 264             int status = regexec(&r_patt, attr, 0, NULL, 0);
 265 
 266             if (status == 0) {
 267                 xmlNode *child = pcmk__xe_create(xml, PCMK_XE_OP);
 268 
 269                 crm_trace("Matched %s with %s", attr, regex);
 270                 matched = true;
 271 
 272                 /* Copy all the non-conflicting attributes from the parent over,
 273                  * but remove the regex and replace it with the name.
 274                  */
 275                 pcmk__xe_copy_attrs(child, xml, pcmk__xaf_no_overwrite);
 276                 pcmk__xe_remove_attr(child, PCMK__XA_ATTR_REGEX);
 277                 crm_xml_add(child, PCMK__XA_ATTR_NAME, attr);
 278             }
 279         }
 280 
 281         regfree(&r_patt);
 282 
 283         /* Return a code if we never matched anything.  This should not be treated
 284          * as an error.  It indicates there was a regex, and it was a valid regex,
 285          * but simply did not match anything and the caller should not continue
 286          * doing any regex-related processing.
 287          */
 288         if (!matched) {
 289             return pcmk_rc_op_unsatisfied;
 290         }
 291 
 292     } else if (attr == NULL) {
 293         return pcmk_rc_bad_nvpair;
 294     }
 295 
 296     return pcmk_rc_ok;
 297 }
 298 
 299 static int
 300 handle_regexes(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 301 {
 302     xmlNode *xml = request->xml;
 303     int rc = pcmk_rc_ok;
 304 
 305     const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 306     const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 307     const char *regex = crm_element_value(xml, PCMK__XA_ATTR_REGEX);
 308 
 309     rc = expand_regexes(xml, attr, value, regex);
 310 
 311     if (rc == EINVAL) {
 312         pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
 313                             "Bad regex '%s' for update from client %s", regex,
 314                             pcmk__client_name(request->ipc_client));
 315 
 316     } else if (rc == pcmk_rc_bad_nvpair) {
 317         crm_err("Update request did not specify attribute or regular expression");
 318         pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
 319                             "Client %s update request did not specify attribute or regular expression",
 320                             pcmk__client_name(request->ipc_client));
 321     }
 322 
 323     return rc;
 324 }
 325 
 326 static int
 327 handle_value_expansion(const char **value, xmlNode *xml, const char *op,
     /* [previous][next][first][last][top][bottom][index][help] */
 328                        const char *attr)
 329 {
 330     attribute_t *a = g_hash_table_lookup(attributes, attr);
 331 
 332     if (a == NULL && pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_DELAY, pcmk__str_none)) {
 333         return EINVAL;
 334     }
 335 
 336     if (*value && attrd_value_needs_expansion(*value)) {
 337         int int_value;
 338         attribute_value_t *v = NULL;
 339 
 340         if (a) {
 341             const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
 342             v = g_hash_table_lookup(a->values, host);
 343         }
 344 
 345         int_value = attrd_expand_value(*value, (v? v->current : NULL));
 346 
 347         crm_info("Expanded %s=%s to %d", attr, *value, int_value);
 348         crm_xml_add_int(xml, PCMK__XA_ATTR_VALUE, int_value);
 349 
 350         /* Replacing the value frees the previous memory, so re-query it */
 351         *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 352     }
 353 
 354     return pcmk_rc_ok;
 355 }
 356 
 357 static void
 358 send_update_msg_to_cluster(pcmk__request_t *request, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 359 {
 360     if (pcmk__str_eq(attrd_request_sync_point(xml), PCMK__VALUE_CLUSTER, pcmk__str_none)) {
 361         /* The client is waiting on the cluster-wide sync point.  In this case,
 362          * the response ACK is not sent until this attrd broadcasts the update
 363          * and receives its own confirmation back from all peers.
 364          */
 365         attrd_expect_confirmations(request, attrd_cluster_sync_point_update);
 366         attrd_send_message(NULL, xml, true); /* ends up at attrd_peer_message() */
 367 
 368     } else {
 369         /* The client is either waiting on the local sync point or was not
 370          * waiting on any sync point at all.  For the local sync point, the
 371          * response ACK is sent in attrd_peer_update.  For clients not
 372          * waiting on any sync point, the response ACK is sent in
 373          * handle_update_request immediately before this function was called.
 374          */
 375         attrd_send_message(NULL, xml, false); /* ends up at attrd_peer_message() */
 376     }
 377 }
 378 
 379 static int
 380 send_child_update(xmlNode *child, void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
 381 {
 382     pcmk__request_t *request = (pcmk__request_t *) data;
 383 
 384     /* Calling pcmk__set_result is handled by one of these calls to
 385      * attrd_client_update, so no need to do it again here.
 386      */
 387     request->xml = child;
 388     attrd_client_update(request);
 389     return pcmk_rc_ok;
 390 }
 391 
 392 xmlNode *
 393 attrd_client_update(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 394 {
 395     xmlNode *xml = NULL;
 396     const char *attr, *value, *regex;
 397 
 398     CRM_CHECK((request != NULL) && (request->xml != NULL), return NULL);
 399 
 400     xml = request->xml;
 401 
 402     /* If the message has children, that means it is a message from a newer
 403      * client that supports sending multiple operations at a time.  There are
 404      * two ways we can handle that.
 405      */
 406     if (xml->children != NULL) {
 407         if (ATTRD_SUPPORTS_MULTI_MESSAGE(minimum_protocol_version)) {
 408             /* First, if all peers support a certain protocol version, we can
 409              * just broadcast the big message and they'll handle it.  However,
 410              * we also need to apply all the transformations in this function
 411              * to the children since they don't happen anywhere else.
 412              */
 413             for (xmlNode *child = pcmk__xe_first_child(xml, PCMK_XE_OP, NULL,
 414                                                        NULL);
 415                  child != NULL; child = pcmk__xe_next(child, PCMK_XE_OP)) {
 416 
 417                 attr = crm_element_value(child, PCMK__XA_ATTR_NAME);
 418                 value = crm_element_value(child, PCMK__XA_ATTR_VALUE);
 419 
 420                 handle_missing_host(child);
 421 
 422                 if (handle_value_expansion(&value, child, request->op, attr) == EINVAL) {
 423                     pcmk__format_result(&request->result, CRM_EX_NOSUCH, PCMK_EXEC_ERROR,
 424                                         "Attribute %s does not exist", attr);
 425                     return NULL;
 426                 }
 427             }
 428 
 429             send_update_msg_to_cluster(request, xml);
 430             pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 431 
 432         } else {
 433             /* Save the original xml node pointer so it can be restored after iterating
 434              * over all the children.
 435              */
 436             xmlNode *orig_xml = request->xml;
 437 
 438             /* Second, if they do not support that protocol version, split it
 439              * up into individual messages and call attrd_client_update on
 440              * each one.
 441              */
 442             pcmk__xe_foreach_child(xml, PCMK_XE_OP, send_child_update, request);
 443             request->xml = orig_xml;
 444         }
 445 
 446         return NULL;
 447     }
 448 
 449     attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 450     value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 451     regex = crm_element_value(xml, PCMK__XA_ATTR_REGEX);
 452 
 453     if (handle_regexes(request) != pcmk_rc_ok) {
 454         /* Error handling was already dealt with in handle_regexes, so just return. */
 455         return NULL;
 456     } else if (regex) {
 457         /* Recursively call attrd_client_update on the new message with regexes
 458          * expanded.  If supported by the attribute daemon, this means that all
 459          * matches can also be handled atomically.
 460          */
 461         return attrd_client_update(request);
 462     }
 463 
 464     handle_missing_host(xml);
 465 
 466     if (handle_value_expansion(&value, xml, request->op, attr) == EINVAL) {
 467         pcmk__format_result(&request->result, CRM_EX_NOSUCH, PCMK_EXEC_ERROR,
 468                             "Attribute %s does not exist", attr);
 469         return NULL;
 470     }
 471 
 472     crm_debug("Broadcasting %s[%s]=%s%s",
 473               attr, crm_element_value(xml, PCMK__XA_ATTR_HOST),
 474               value, (attrd_election_won()? " (writer)" : ""));
 475 
 476     send_update_msg_to_cluster(request, xml);
 477     pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 478     return NULL;
 479 }
 480 
 481 /*!
 482  * \internal
 483  * \brief Accept a new client IPC connection
 484  *
 485  * \param[in,out] c    New connection
 486  * \param[in]     uid  Client user id
 487  * \param[in]     gid  Client group id
 488  *
 489  * \return pcmk_ok on success, -errno otherwise
 490  */
 491 static int32_t
 492 attrd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 493 {
 494     crm_trace("New client connection %p", c);
 495     if (attrd_shutting_down(false)) {
 496         crm_info("Ignoring new connection from pid %d during shutdown",
 497                  pcmk__client_pid(c));
 498         return -ECONNREFUSED;
 499     }
 500 
 501     if (pcmk__new_client(c, uid, gid) == NULL) {
 502         return -ENOMEM;
 503     }
 504     return pcmk_ok;
 505 }
 506 
 507 /*!
 508  * \internal
 509  * \brief Destroy a client IPC connection
 510  *
 511  * \param[in] c  Connection to destroy
 512  *
 513  * \return FALSE (i.e. do not re-run this callback)
 514  */
 515 static int32_t
 516 attrd_ipc_closed(qb_ipcs_connection_t *c)
     /* [previous][next][first][last][top][bottom][index][help] */
 517 {
 518     pcmk__client_t *client = pcmk__find_client(c);
 519 
 520     if (client == NULL) {
 521         crm_trace("Ignoring request to clean up unknown connection %p", c);
 522     } else {
 523         crm_trace("Cleaning up closed client connection %p", c);
 524 
 525         /* Remove the client from the sync point waitlist if it's present. */
 526         attrd_remove_client_from_waitlist(client);
 527 
 528         /* And no longer wait for confirmations from any peers. */
 529         attrd_do_not_wait_for_client(client);
 530 
 531         pcmk__free_client(client);
 532     }
 533 
 534     return FALSE;
 535 }
 536 
 537 /*!
 538  * \internal
 539  * \brief Destroy a client IPC connection
 540  *
 541  * \param[in,out] c  Connection to destroy
 542  *
 543  * \note We handle a destroyed connection the same as a closed one,
 544  *       but we need a separate handler because the return type is different.
 545  */
 546 static void
 547 attrd_ipc_destroy(qb_ipcs_connection_t *c)
     /* [previous][next][first][last][top][bottom][index][help] */
 548 {
 549     crm_trace("Destroying client connection %p", c);
 550     attrd_ipc_closed(c);
 551 }
 552 
 553 static int32_t
 554 attrd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 555 {
 556     int rc = pcmk_rc_ok;
 557     uint32_t id = 0;
 558     uint32_t flags = 0;
 559     pcmk__client_t *client = pcmk__find_client(c);
 560     xmlNode *xml = NULL;
 561 
 562     // Sanity-check, and parse XML from IPC data
 563     CRM_CHECK((c != NULL) && (client != NULL), return 0);
 564     if (data == NULL) {
 565         crm_debug("No IPC data from PID %d", pcmk__client_pid(c));
 566         return 0;
 567     }
 568 
 569     rc = pcmk__ipc_msg_append(&client->buffer, data);
 570 
 571     if (rc == pcmk_rc_ipc_more) {
 572         /* We haven't read the complete message yet, so just return. */
 573         return 0;
 574 
 575     } else if (rc == pcmk_rc_ok) {
 576         /* We've read the complete message and there's already a header on
 577          * the front.  Pass it off for processing.
 578          */
 579         xml = pcmk__client_data2xml(client, &id, &flags);
 580         g_byte_array_free(client->buffer, TRUE);
 581         client->buffer = NULL;
 582 
 583     } else {
 584         /* Some sort of error occurred reassembling the message.  All we can
 585          * do is clean up, log an error and return.
 586          */
 587         crm_err("Error when reading IPC message: %s", pcmk_rc_str(rc));
 588 
 589         if (client->buffer != NULL) {
 590             g_byte_array_free(client->buffer, TRUE);
 591             client->buffer = NULL;
 592         }
 593 
 594         return 0;
 595     }
 596 
 597     if (xml == NULL) {
 598         crm_debug("Unrecognizable IPC data from PID %d", pcmk__client_pid(c));
 599         pcmk__ipc_send_ack(client, id, flags, PCMK__XE_ACK, NULL,
 600                            CRM_EX_PROTOCOL);
 601         return 0;
 602 
 603     } else {
 604         pcmk__request_t request = {
 605             .ipc_client     = client,
 606             .ipc_id         = id,
 607             .ipc_flags      = flags,
 608             .peer           = NULL,
 609             .xml            = xml,
 610             .call_options   = 0,
 611             .result         = PCMK__UNKNOWN_RESULT,
 612         };
 613 
 614         pcmk__assert(client->user != NULL);
 615         pcmk__update_acl_user(xml, PCMK__XA_ATTR_USER, client->user);
 616 
 617         request.op = crm_element_value_copy(request.xml, PCMK_XA_TASK);
 618         CRM_CHECK(request.op != NULL, return 0);
 619 
 620         attrd_handle_request(&request);
 621         pcmk__reset_request(&request);
 622     }
 623 
 624     pcmk__xml_free(xml);
 625     return 0;
 626 }
 627 
 628 static struct qb_ipcs_service_handlers ipc_callbacks = {
 629     .connection_accept = attrd_ipc_accept,
 630     .connection_created = NULL,
 631     .msg_process = attrd_ipc_dispatch,
 632     .connection_closed = attrd_ipc_closed,
 633     .connection_destroyed = attrd_ipc_destroy
 634 };
 635 
 636 void
 637 attrd_ipc_fini(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 638 {
 639     if (ipcs != NULL) {
 640         pcmk__drop_all_clients(ipcs);
 641         qb_ipcs_destroy(ipcs);
 642         ipcs = NULL;
 643     }
 644 
 645     attrd_unregister_handlers();
 646     pcmk__client_cleanup();
 647 }
 648 
 649 /*!
 650  * \internal
 651  * \brief Set up attrd IPC communication
 652  */
 653 void
 654 attrd_init_ipc(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 655 {
 656     pcmk__serve_attrd_ipc(&ipcs, &ipc_callbacks);
 657 }

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