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

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