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->priv->node_name;
  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             pcmk__node_status_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 != NULL) && (node->name != NULL)) {
 161                 // Use cached name if available
 162                 host = node->name;
 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->priv->node_name,
 240                           attrd_cluster->priv->node_id);
 241     }
 242 }
 243 
 244 /* Convert a single IPC message with a regex into one with multiple children, one
 245  * for each regex match.
 246  */
 247 static int
 248 expand_regexes(xmlNode *xml, const char *attr, const char *value, const char *regex)
     /* [previous][next][first][last][top][bottom][index][help] */
 249 {
 250     if (attr == NULL && regex) {
 251         bool matched = false;
 252         GHashTableIter aIter;
 253         regex_t r_patt;
 254 
 255         crm_debug("Setting %s to %s", regex, value);
 256         if (regcomp(&r_patt, regex, REG_EXTENDED|REG_NOSUB)) {
 257             return EINVAL;
 258         }
 259 
 260         g_hash_table_iter_init(&aIter, attributes);
 261         while (g_hash_table_iter_next(&aIter, (gpointer *) & attr, NULL)) {
 262             int status = regexec(&r_patt, attr, 0, NULL, 0);
 263 
 264             if (status == 0) {
 265                 xmlNode *child = pcmk__xe_create(xml, PCMK_XE_OP);
 266 
 267                 crm_trace("Matched %s with %s", attr, regex);
 268                 matched = true;
 269 
 270                 /* Copy all the non-conflicting attributes from the parent over,
 271                  * but remove the regex and replace it with the name.
 272                  */
 273                 pcmk__xe_copy_attrs(child, xml, pcmk__xaf_no_overwrite);
 274                 pcmk__xe_remove_attr(child, PCMK__XA_ATTR_REGEX);
 275                 crm_xml_add(child, PCMK__XA_ATTR_NAME, attr);
 276             }
 277         }
 278 
 279         regfree(&r_patt);
 280 
 281         /* Return a code if we never matched anything.  This should not be treated
 282          * as an error.  It indicates there was a regex, and it was a valid regex,
 283          * but simply did not match anything and the caller should not continue
 284          * doing any regex-related processing.
 285          */
 286         if (!matched) {
 287             return pcmk_rc_op_unsatisfied;
 288         }
 289 
 290     } else if (attr == NULL) {
 291         return pcmk_rc_bad_nvpair;
 292     }
 293 
 294     return pcmk_rc_ok;
 295 }
 296 
 297 static int
 298 handle_regexes(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 299 {
 300     xmlNode *xml = request->xml;
 301     int rc = pcmk_rc_ok;
 302 
 303     const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 304     const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 305     const char *regex = crm_element_value(xml, PCMK__XA_ATTR_REGEX);
 306 
 307     rc = expand_regexes(xml, attr, value, regex);
 308 
 309     if (rc == EINVAL) {
 310         pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
 311                             "Bad regex '%s' for update from client %s", regex,
 312                             pcmk__client_name(request->ipc_client));
 313 
 314     } else if (rc == pcmk_rc_bad_nvpair) {
 315         crm_err("Update request did not specify attribute or regular expression");
 316         pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
 317                             "Client %s update request did not specify attribute or regular expression",
 318                             pcmk__client_name(request->ipc_client));
 319     }
 320 
 321     return rc;
 322 }
 323 
 324 static int
 325 handle_value_expansion(const char **value, xmlNode *xml, const char *op,
     /* [previous][next][first][last][top][bottom][index][help] */
 326                        const char *attr)
 327 {
 328     attribute_t *a = g_hash_table_lookup(attributes, attr);
 329 
 330     if (a == NULL && pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_DELAY, pcmk__str_none)) {
 331         return EINVAL;
 332     }
 333 
 334     if (*value && attrd_value_needs_expansion(*value)) {
 335         int int_value;
 336         attribute_value_t *v = NULL;
 337 
 338         if (a) {
 339             const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
 340             v = g_hash_table_lookup(a->values, host);
 341         }
 342 
 343         int_value = attrd_expand_value(*value, (v? v->current : NULL));
 344 
 345         crm_info("Expanded %s=%s to %d", attr, *value, int_value);
 346         crm_xml_add_int(xml, PCMK__XA_ATTR_VALUE, int_value);
 347 
 348         /* Replacing the value frees the previous memory, so re-query it */
 349         *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 350     }
 351 
 352     return pcmk_rc_ok;
 353 }
 354 
 355 static void
 356 send_update_msg_to_cluster(pcmk__request_t *request, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 357 {
 358     if (pcmk__str_eq(attrd_request_sync_point(xml), PCMK__VALUE_CLUSTER, pcmk__str_none)) {
 359         /* The client is waiting on the cluster-wide sync point.  In this case,
 360          * the response ACK is not sent until this attrd broadcasts the update
 361          * and receives its own confirmation back from all peers.
 362          */
 363         attrd_expect_confirmations(request, attrd_cluster_sync_point_update);
 364         attrd_send_message(NULL, xml, true); /* ends up at attrd_peer_message() */
 365 
 366     } else {
 367         /* The client is either waiting on the local sync point or was not
 368          * waiting on any sync point at all.  For the local sync point, the
 369          * response ACK is sent in attrd_peer_update.  For clients not
 370          * waiting on any sync point, the response ACK is sent in
 371          * handle_update_request immediately before this function was called.
 372          */
 373         attrd_send_message(NULL, xml, false); /* ends up at attrd_peer_message() */
 374     }
 375 }
 376 
 377 static int
 378 send_child_update(xmlNode *child, void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
 379 {
 380     pcmk__request_t *request = (pcmk__request_t *) data;
 381 
 382     /* Calling pcmk__set_result is handled by one of these calls to
 383      * attrd_client_update, so no need to do it again here.
 384      */
 385     request->xml = child;
 386     attrd_client_update(request);
 387     return pcmk_rc_ok;
 388 }
 389 
 390 xmlNode *
 391 attrd_client_update(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 392 {
 393     xmlNode *xml = NULL;
 394     const char *attr, *value, *regex;
 395 
 396     CRM_CHECK((request != NULL) && (request->xml != NULL), return NULL);
 397 
 398     xml = request->xml;
 399 
 400     /* If the message has children, that means it is a message from a newer
 401      * client that supports sending multiple operations at a time.  There are
 402      * two ways we can handle that.
 403      */
 404     if (xml->children != NULL) {
 405         if (ATTRD_SUPPORTS_MULTI_MESSAGE(minimum_protocol_version)) {
 406             /* First, if all peers support a certain protocol version, we can
 407              * just broadcast the big message and they'll handle it.  However,
 408              * we also need to apply all the transformations in this function
 409              * to the children since they don't happen anywhere else.
 410              */
 411             for (xmlNode *child = pcmk__xe_first_child(xml, PCMK_XE_OP, NULL,
 412                                                        NULL);
 413                  child != NULL; child = pcmk__xe_next(child, PCMK_XE_OP)) {
 414 
 415                 attr = crm_element_value(child, PCMK__XA_ATTR_NAME);
 416                 value = crm_element_value(child, PCMK__XA_ATTR_VALUE);
 417 
 418                 handle_missing_host(child);
 419 
 420                 if (handle_value_expansion(&value, child, request->op, attr) == EINVAL) {
 421                     pcmk__format_result(&request->result, CRM_EX_NOSUCH, PCMK_EXEC_ERROR,
 422                                         "Attribute %s does not exist", attr);
 423                     return NULL;
 424                 }
 425             }
 426 
 427             send_update_msg_to_cluster(request, xml);
 428             pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 429 
 430         } else {
 431             /* Save the original xml node pointer so it can be restored after iterating
 432              * over all the children.
 433              */
 434             xmlNode *orig_xml = request->xml;
 435 
 436             /* Second, if they do not support that protocol version, split it
 437              * up into individual messages and call attrd_client_update on
 438              * each one.
 439              */
 440             pcmk__xe_foreach_child(xml, PCMK_XE_OP, send_child_update, request);
 441             request->xml = orig_xml;
 442         }
 443 
 444         return NULL;
 445     }
 446 
 447     attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 448     value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 449     regex = crm_element_value(xml, PCMK__XA_ATTR_REGEX);
 450 
 451     if (handle_regexes(request) != pcmk_rc_ok) {
 452         /* Error handling was already dealt with in handle_regexes, so just return. */
 453         return NULL;
 454     } else if (regex) {
 455         /* Recursively call attrd_client_update on the new message with regexes
 456          * expanded.  If supported by the attribute daemon, this means that all
 457          * matches can also be handled atomically.
 458          */
 459         return attrd_client_update(request);
 460     }
 461 
 462     handle_missing_host(xml);
 463 
 464     if (handle_value_expansion(&value, xml, request->op, attr) == EINVAL) {
 465         pcmk__format_result(&request->result, CRM_EX_NOSUCH, PCMK_EXEC_ERROR,
 466                             "Attribute %s does not exist", attr);
 467         return NULL;
 468     }
 469 
 470     crm_debug("Broadcasting %s[%s]=%s%s",
 471               attr, crm_element_value(xml, PCMK__XA_ATTR_HOST),
 472               value, (attrd_election_won()? " (writer)" : ""));
 473 
 474     send_update_msg_to_cluster(request, xml);
 475     pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 476     return NULL;
 477 }
 478 
 479 /*!
 480  * \internal
 481  * \brief Accept a new client IPC connection
 482  *
 483  * \param[in,out] c    New connection
 484  * \param[in]     uid  Client user id
 485  * \param[in]     gid  Client group id
 486  *
 487  * \return pcmk_ok on success, -errno otherwise
 488  */
 489 static int32_t
 490 attrd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 491 {
 492     crm_trace("New client connection %p", c);
 493     if (attrd_shutting_down(false)) {
 494         crm_info("Ignoring new connection from pid %d during shutdown",
 495                  pcmk__client_pid(c));
 496         return -ECONNREFUSED;
 497     }
 498 
 499     if (pcmk__new_client(c, uid, gid) == NULL) {
 500         return -ENOMEM;
 501     }
 502     return pcmk_ok;
 503 }
 504 
 505 /*!
 506  * \internal
 507  * \brief Destroy a client IPC connection
 508  *
 509  * \param[in] c  Connection to destroy
 510  *
 511  * \return FALSE (i.e. do not re-run this callback)
 512  */
 513 static int32_t
 514 attrd_ipc_closed(qb_ipcs_connection_t *c)
     /* [previous][next][first][last][top][bottom][index][help] */
 515 {
 516     pcmk__client_t *client = pcmk__find_client(c);
 517 
 518     if (client == NULL) {
 519         crm_trace("Ignoring request to clean up unknown connection %p", c);
 520     } else {
 521         crm_trace("Cleaning up closed client connection %p", c);
 522 
 523         /* Remove the client from the sync point waitlist if it's present. */
 524         attrd_remove_client_from_waitlist(client);
 525 
 526         /* And no longer wait for confirmations from any peers. */
 527         attrd_do_not_wait_for_client(client);
 528 
 529         pcmk__free_client(client);
 530     }
 531 
 532     return FALSE;
 533 }
 534 
 535 /*!
 536  * \internal
 537  * \brief Destroy a client IPC connection
 538  *
 539  * \param[in,out] c  Connection to destroy
 540  *
 541  * \note We handle a destroyed connection the same as a closed one,
 542  *       but we need a separate handler because the return type is different.
 543  */
 544 static void
 545 attrd_ipc_destroy(qb_ipcs_connection_t *c)
     /* [previous][next][first][last][top][bottom][index][help] */
 546 {
 547     crm_trace("Destroying client connection %p", c);
 548     attrd_ipc_closed(c);
 549 }
 550 
 551 static int32_t
 552 attrd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 553 {
 554     uint32_t id = 0;
 555     uint32_t flags = 0;
 556     pcmk__client_t *client = pcmk__find_client(c);
 557     xmlNode *xml = NULL;
 558 
 559     // Sanity-check, and parse XML from IPC data
 560     CRM_CHECK((c != NULL) && (client != NULL), return 0);
 561     if (data == NULL) {
 562         crm_debug("No IPC data from PID %d", pcmk__client_pid(c));
 563         return 0;
 564     }
 565 
 566     xml = pcmk__client_data2xml(client, data, &id, &flags);
 567 
 568     if (xml == NULL) {
 569         crm_debug("Unrecognizable IPC data from PID %d", pcmk__client_pid(c));
 570         pcmk__ipc_send_ack(client, id, flags, PCMK__XE_ACK, NULL,
 571                            CRM_EX_PROTOCOL);
 572         return 0;
 573 
 574     } else {
 575         pcmk__request_t request = {
 576             .ipc_client     = client,
 577             .ipc_id         = id,
 578             .ipc_flags      = flags,
 579             .peer           = NULL,
 580             .xml            = xml,
 581             .call_options   = 0,
 582             .result         = PCMK__UNKNOWN_RESULT,
 583         };
 584 
 585         pcmk__assert(client->user != NULL);
 586         pcmk__update_acl_user(xml, PCMK__XA_ATTR_USER, client->user);
 587 
 588         request.op = crm_element_value_copy(request.xml, PCMK_XA_TASK);
 589         CRM_CHECK(request.op != NULL, return 0);
 590 
 591         attrd_handle_request(&request);
 592         pcmk__reset_request(&request);
 593     }
 594 
 595     pcmk__xml_free(xml);
 596     return 0;
 597 }
 598 
 599 static struct qb_ipcs_service_handlers ipc_callbacks = {
 600     .connection_accept = attrd_ipc_accept,
 601     .connection_created = NULL,
 602     .msg_process = attrd_ipc_dispatch,
 603     .connection_closed = attrd_ipc_closed,
 604     .connection_destroyed = attrd_ipc_destroy
 605 };
 606 
 607 void
 608 attrd_ipc_fini(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 609 {
 610     if (ipcs != NULL) {
 611         pcmk__drop_all_clients(ipcs);
 612         qb_ipcs_destroy(ipcs);
 613         ipcs = NULL;
 614     }
 615 
 616     attrd_unregister_handlers();
 617     pcmk__client_cleanup();
 618 }
 619 
 620 /*!
 621  * \internal
 622  * \brief Set up attrd IPC communication
 623  */
 624 void
 625 attrd_init_ipc(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 626 {
 627     pcmk__serve_attrd_ipc(&ipcs, &ipc_callbacks);
 628 }

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