root/daemons/based/based_callbacks.c

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

DEFINITIONS

This source file includes following definitions.
  1. cib_legacy_mode
  2. cib_ipc_accept
  3. cib_ipc_dispatch_rw
  4. cib_ipc_dispatch_ro
  5. cib_ipc_closed
  6. cib_ipc_destroy
  7. create_cib_reply
  8. do_local_notify
  9. cib_common_callback_worker
  10. cib_common_callback
  11. cib_digester_cb
  12. process_ping_reply
  13. local_notify_destroy_callback
  14. check_local_notify
  15. queue_local_notify
  16. parse_local_options_v1
  17. parse_local_options_v2
  18. parse_local_options
  19. parse_peer_options_v1
  20. parse_peer_options_v2
  21. parse_peer_options
  22. forward_request
  23. send_peer_reply
  24. cib_process_request
  25. prepare_input
  26. contains_config_change
  27. cib_process_command
  28. cib_peer_callback
  29. cib_force_exit
  30. disconnect_remote_client
  31. initiate_exit
  32. cib_shutdown
  33. terminate_cib

   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 <sys/param.h>
  13 #include <stdio.h>
  14 #include <sys/types.h>
  15 #include <unistd.h>
  16 
  17 #include <stdlib.h>
  18 #include <stdint.h>     // uint32_t, uint64_t, UINT64_C()
  19 #include <errno.h>
  20 #include <fcntl.h>
  21 #include <inttypes.h>   // PRIu64
  22 
  23 #include <glib.h>
  24 #include <libxml/tree.h>
  25 
  26 #include <crm/crm.h>
  27 #include <crm/cib.h>
  28 #include <crm/cluster/internal.h>
  29 
  30 #include <crm/common/xml.h>
  31 #include <crm/common/remote_internal.h>
  32 
  33 #include <pacemaker-based.h>
  34 
  35 #define EXIT_ESCALATION_MS 10000
  36 
  37 static unsigned long cib_local_bcast_num = 0;
  38 
  39 typedef struct cib_local_notify_s {
  40     xmlNode *notify_src;
  41     char *client_id;
  42     gboolean from_peer;
  43     gboolean sync_reply;
  44 } cib_local_notify_t;
  45 
  46 int next_client_id = 0;
  47 
  48 gboolean legacy_mode = FALSE;
  49 
  50 qb_ipcs_service_t *ipcs_ro = NULL;
  51 qb_ipcs_service_t *ipcs_rw = NULL;
  52 qb_ipcs_service_t *ipcs_shm = NULL;
  53 
  54 static int cib_process_command(xmlNode *request,
  55                                const cib__operation_t *operation,
  56                                cib__op_fn_t op_function, xmlNode **reply,
  57                                xmlNode **cib_diff, bool privileged);
  58 
  59 static gboolean cib_common_callback(qb_ipcs_connection_t *c, void *data,
  60                                     size_t size, gboolean privileged);
  61 
  62 gboolean
  63 cib_legacy_mode(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  64 {
  65     return legacy_mode;
  66 }
  67 
  68 static int32_t
  69 cib_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
  70 {
  71     if (cib_shutdown_flag) {
  72         crm_info("Ignoring new IPC client [%d] during shutdown",
  73                  pcmk__client_pid(c));
  74         return -ECONNREFUSED;
  75     }
  76 
  77     if (pcmk__new_client(c, uid, gid) == NULL) {
  78         return -ENOMEM;
  79     }
  80     return 0;
  81 }
  82 
  83 static int32_t
  84 cib_ipc_dispatch_rw(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
  85 {
  86     pcmk__client_t *client = pcmk__find_client(c);
  87 
  88     crm_trace("%p message from %s", c, client->id);
  89     return cib_common_callback(c, data, size, TRUE);
  90 }
  91 
  92 static int32_t
  93 cib_ipc_dispatch_ro(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
  94 {
  95     pcmk__client_t *client = pcmk__find_client(c);
  96 
  97     crm_trace("%p message from %s", c, client->id);
  98     return cib_common_callback(c, data, size, FALSE);
  99 }
 100 
 101 /* Error code means? */
 102 static int32_t
 103 cib_ipc_closed(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 104 {
 105     pcmk__client_t *client = pcmk__find_client(c);
 106 
 107     if (client == NULL) {
 108         return 0;
 109     }
 110     crm_trace("Connection %p", c);
 111     pcmk__free_client(client);
 112     return 0;
 113 }
 114 
 115 static void
 116 cib_ipc_destroy(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 117 {
 118     crm_trace("Connection %p", c);
 119     cib_ipc_closed(c);
 120     if (cib_shutdown_flag) {
 121         cib_shutdown(0);
 122     }
 123 }
 124 
 125 struct qb_ipcs_service_handlers ipc_ro_callbacks = {
 126     .connection_accept = cib_ipc_accept,
 127     .connection_created = NULL,
 128     .msg_process = cib_ipc_dispatch_ro,
 129     .connection_closed = cib_ipc_closed,
 130     .connection_destroyed = cib_ipc_destroy
 131 };
 132 
 133 struct qb_ipcs_service_handlers ipc_rw_callbacks = {
 134     .connection_accept = cib_ipc_accept,
 135     .connection_created = NULL,
 136     .msg_process = cib_ipc_dispatch_rw,
 137     .connection_closed = cib_ipc_closed,
 138     .connection_destroyed = cib_ipc_destroy
 139 };
 140 
 141 /*!
 142  * \internal
 143  * \brief Create reply XML for a CIB request
 144  *
 145  * \param[in] op            CIB operation type
 146  * \param[in] call_id       CIB call ID
 147  * \param[in] client_id     CIB client ID
 148  * \param[in] call_options  Group of <tt>enum cib_call_options</tt> flags
 149  * \param[in] rc            Request return code
 150  * \param[in] call_data     Request output data
 151  *
 152  * \return Reply XML
 153  *
 154  * \note The caller is responsible for freeing the return value using
 155  *       \p free_xml().
 156  */
 157 static xmlNode *
 158 create_cib_reply(const char *op, const char *call_id, const char *client_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 159                  int call_options, int rc, xmlNode *call_data)
 160 {
 161     xmlNode *reply = pcmk__xe_create(NULL, PCMK__XE_CIB_REPLY);
 162 
 163     crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_CIB);
 164     crm_xml_add(reply, PCMK__XA_CIB_OP, op);
 165     crm_xml_add(reply, PCMK__XA_CIB_CALLID, call_id);
 166     crm_xml_add(reply, PCMK__XA_CIB_CLIENTID, client_id);
 167     crm_xml_add_int(reply, PCMK__XA_CIB_CALLOPT, call_options);
 168     crm_xml_add_int(reply, PCMK__XA_CIB_RC, rc);
 169 
 170     if (call_data != NULL) {
 171         xmlNode *wrapper = pcmk__xe_create(reply, PCMK__XE_CIB_CALLDATA);
 172 
 173         crm_trace("Attaching reply output");
 174         pcmk__xml_copy(wrapper, call_data);
 175     }
 176 
 177     crm_log_xml_explicit(reply, "cib:reply");
 178     return reply;
 179 }
 180 
 181 static void
 182 do_local_notify(const xmlNode *notify_src, const char *client_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 183                 bool sync_reply, bool from_peer)
 184 {
 185     int msg_id = 0;
 186     int rc = pcmk_rc_ok;
 187     pcmk__client_t *client_obj = NULL;
 188     uint32_t flags = crm_ipc_server_event;
 189 
 190     CRM_CHECK((notify_src != NULL) && (client_id != NULL), return);
 191 
 192     crm_element_value_int(notify_src, PCMK__XA_CIB_CALLID, &msg_id);
 193 
 194     client_obj = pcmk__find_client_by_id(client_id);
 195     if (client_obj == NULL) {
 196         crm_debug("Could not notify client %s%s %s of call %d result: "
 197                   "client no longer exists", client_id,
 198                   (from_peer? " (originator of delegated request)" : ""),
 199                   (sync_reply? "synchronously" : "asynchronously"), msg_id);
 200         return;
 201     }
 202 
 203     if (sync_reply) {
 204         flags = crm_ipc_flags_none;
 205         if (client_obj->ipcs != NULL) {
 206             msg_id = client_obj->request_id;
 207             client_obj->request_id = 0;
 208         }
 209     }
 210 
 211     switch (PCMK__CLIENT_TYPE(client_obj)) {
 212         case pcmk__client_ipc:
 213             rc = pcmk__ipc_send_xml(client_obj, msg_id, notify_src, flags);
 214             break;
 215 #ifdef HAVE_GNUTLS_GNUTLS_H
 216         case pcmk__client_tls:
 217 #endif
 218         case pcmk__client_tcp:
 219             rc = pcmk__remote_send_xml(client_obj->remote, notify_src);
 220             break;
 221         default:
 222             rc = EPROTONOSUPPORT;
 223             break;
 224     }
 225     if (rc == pcmk_rc_ok) {
 226         crm_trace("Notified %s client %s%s %s of call %d result",
 227                   pcmk__client_type_str(PCMK__CLIENT_TYPE(client_obj)),
 228                   pcmk__client_name(client_obj),
 229                   (from_peer? " (originator of delegated request)" : ""),
 230                   (sync_reply? "synchronously" : "asynchronously"), msg_id);
 231     } else {
 232         crm_warn("Could not notify %s client %s%s %s of call %d result: %s",
 233                  pcmk__client_type_str(PCMK__CLIENT_TYPE(client_obj)),
 234                  pcmk__client_name(client_obj),
 235                  (from_peer? " (originator of delegated request)" : ""),
 236                  (sync_reply? "synchronously" : "asynchronously"), msg_id,
 237                  pcmk_rc_str(rc));
 238     }
 239 }
 240 
 241 void
 242 cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request,
     /* [previous][next][first][last][top][bottom][index][help] */
 243                            pcmk__client_t *cib_client, gboolean privileged)
 244 {
 245     const char *op = crm_element_value(op_request, PCMK__XA_CIB_OP);
 246     int call_options = cib_none;
 247 
 248     crm_element_value_int(op_request, PCMK__XA_CIB_CALLOPT, &call_options);
 249 
 250     /* Requests with cib_transaction set should not be sent to based directly
 251      * (outside of a commit-transaction request)
 252      */
 253     if (pcmk_is_set(call_options, cib_transaction)) {
 254         return;
 255     }
 256 
 257     if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) {
 258         if (flags & crm_ipc_client_response) {
 259             xmlNode *ack = pcmk__xe_create(NULL, __func__);
 260 
 261             crm_xml_add(ack, PCMK__XA_CIB_OP, CRM_OP_REGISTER);
 262             crm_xml_add(ack, PCMK__XA_CIB_CLIENTID, cib_client->id);
 263             pcmk__ipc_send_xml(cib_client, id, ack, flags);
 264             cib_client->request_id = 0;
 265             free_xml(ack);
 266         }
 267         return;
 268 
 269     } else if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) {
 270         /* Update the notify filters for this client */
 271         int on_off = 0;
 272         crm_exit_t status = CRM_EX_OK;
 273         uint64_t bit = UINT64_C(0);
 274         const char *type = crm_element_value(op_request,
 275                                              PCMK__XA_CIB_NOTIFY_TYPE);
 276 
 277         crm_element_value_int(op_request, PCMK__XA_CIB_NOTIFY_ACTIVATE,
 278                               &on_off);
 279 
 280         crm_debug("Setting %s callbacks %s for client %s",
 281                   type, (on_off? "on" : "off"), pcmk__client_name(cib_client));
 282 
 283         if (pcmk__str_eq(type, PCMK__VALUE_CIB_POST_NOTIFY, pcmk__str_none)) {
 284             bit = cib_notify_post;
 285 
 286         } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_PRE_NOTIFY,
 287                                 pcmk__str_none)) {
 288             bit = cib_notify_pre;
 289 
 290         } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_UPDATE_CONFIRMATION,
 291                                 pcmk__str_none)) {
 292             bit = cib_notify_confirm;
 293 
 294         } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_DIFF_NOTIFY,
 295                                 pcmk__str_none)) {
 296             bit = cib_notify_diff;
 297 
 298         } else {
 299             status = CRM_EX_INVALID_PARAM;
 300         }
 301 
 302         if (bit != 0) {
 303             if (on_off) {
 304                 pcmk__set_client_flags(cib_client, bit);
 305             } else {
 306                 pcmk__clear_client_flags(cib_client, bit);
 307             }
 308         }
 309 
 310         pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_ACK, NULL, status);
 311         return;
 312     }
 313 
 314     cib_process_request(op_request, privileged, cib_client);
 315 }
 316 
 317 int32_t
 318 cib_common_callback(qb_ipcs_connection_t * c, void *data, size_t size, gboolean privileged)
     /* [previous][next][first][last][top][bottom][index][help] */
 319 {
 320     uint32_t id = 0;
 321     uint32_t flags = 0;
 322     int call_options = 0;
 323     pcmk__client_t *cib_client = pcmk__find_client(c);
 324     xmlNode *op_request = pcmk__client_data2xml(cib_client, data, &id, &flags);
 325 
 326     if (op_request) {
 327         crm_element_value_int(op_request, PCMK__XA_CIB_CALLOPT, &call_options);
 328     }
 329 
 330     if (op_request == NULL) {
 331         crm_trace("Invalid message from %p", c);
 332         pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_NACK, NULL,
 333                            CRM_EX_PROTOCOL);
 334         return 0;
 335 
 336     } else if(cib_client == NULL) {
 337         crm_trace("Invalid client %p", c);
 338         return 0;
 339     }
 340 
 341     if (pcmk_is_set(call_options, cib_sync_call)) {
 342         CRM_LOG_ASSERT(flags & crm_ipc_client_response);
 343         CRM_LOG_ASSERT(cib_client->request_id == 0);    /* This means the client has two synchronous events in-flight */
 344         cib_client->request_id = id;    /* Reply only to the last one */
 345     }
 346 
 347     if (cib_client->name == NULL) {
 348         const char *value = crm_element_value(op_request,
 349                                               PCMK__XA_CIB_CLIENTNAME);
 350 
 351         if (value == NULL) {
 352             cib_client->name = pcmk__itoa(cib_client->pid);
 353         } else {
 354             cib_client->name = pcmk__str_copy(value);
 355             if (crm_is_daemon_name(value)) {
 356                 pcmk__set_client_flags(cib_client, cib_is_daemon);
 357             }
 358         }
 359     }
 360 
 361     /* Allow cluster daemons more leeway before being evicted */
 362     if (pcmk_is_set(cib_client->flags, cib_is_daemon)) {
 363         const char *qmax = cib_config_lookup(PCMK_OPT_CLUSTER_IPC_LIMIT);
 364 
 365         if (pcmk__set_client_queue_max(cib_client, qmax)) {
 366             crm_trace("IPC threshold for client %s[%u] is now %u",
 367                       pcmk__client_name(cib_client), cib_client->pid,
 368                       cib_client->queue_max);
 369         }
 370     }
 371 
 372     crm_xml_add(op_request, PCMK__XA_CIB_CLIENTID, cib_client->id);
 373     crm_xml_add(op_request, PCMK__XA_CIB_CLIENTNAME, cib_client->name);
 374 
 375     CRM_LOG_ASSERT(cib_client->user != NULL);
 376     pcmk__update_acl_user(op_request, PCMK__XA_CIB_USER, cib_client->user);
 377 
 378     cib_common_callback_worker(id, flags, op_request, cib_client, privileged);
 379     free_xml(op_request);
 380 
 381     return 0;
 382 }
 383 
 384 static uint64_t ping_seq = 0;
 385 static char *ping_digest = NULL;
 386 static bool ping_modified_since = FALSE;
 387 
 388 static gboolean
 389 cib_digester_cb(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 390 {
 391     if (based_is_primary) {
 392         char buffer[32];
 393         xmlNode *ping = pcmk__xe_create(NULL, PCMK__XE_PING);
 394 
 395         ping_seq++;
 396         free(ping_digest);
 397         ping_digest = NULL;
 398         ping_modified_since = FALSE;
 399         snprintf(buffer, 32, "%" PRIu64, ping_seq);
 400         crm_trace("Requesting peer digests (%s)", buffer);
 401 
 402         crm_xml_add(ping, PCMK__XA_T, PCMK__VALUE_CIB);
 403         crm_xml_add(ping, PCMK__XA_CIB_OP, CRM_OP_PING);
 404         crm_xml_add(ping, PCMK__XA_CIB_PING_ID, buffer);
 405 
 406         crm_xml_add(ping, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
 407         pcmk__cluster_send_message(NULL, crm_msg_cib, ping);
 408 
 409         free_xml(ping);
 410     }
 411     return FALSE;
 412 }
 413 
 414 static void
 415 process_ping_reply(xmlNode *reply) 
     /* [previous][next][first][last][top][bottom][index][help] */
 416 {
 417     uint64_t seq = 0;
 418     const char *host = crm_element_value(reply, PCMK__XA_SRC);
 419 
 420     xmlNode *wrapper = pcmk__xe_first_child(reply, PCMK__XE_CIB_CALLDATA, NULL,
 421                                             NULL);
 422     xmlNode *pong = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 423 
 424     const char *seq_s = crm_element_value(pong, PCMK__XA_CIB_PING_ID);
 425     const char *digest = crm_element_value(pong, PCMK__XA_DIGEST);
 426 
 427     if (seq_s == NULL) {
 428         crm_debug("Ignoring ping reply with no " PCMK__XA_CIB_PING_ID);
 429         return;
 430 
 431     } else {
 432         long long seq_ll;
 433 
 434         if (pcmk__scan_ll(seq_s, &seq_ll, 0LL) != pcmk_rc_ok) {
 435             return;
 436         }
 437         seq = (uint64_t) seq_ll;
 438     }
 439 
 440     if(digest == NULL) {
 441         crm_trace("Ignoring ping reply %s from %s with no digest", seq_s, host);
 442 
 443     } else if(seq != ping_seq) {
 444         crm_trace("Ignoring out of sequence ping reply %s from %s", seq_s, host);
 445 
 446     } else if(ping_modified_since) {
 447         crm_trace("Ignoring ping reply %s from %s: cib updated since", seq_s, host);
 448 
 449     } else {
 450         const char *version = crm_element_value(pong, PCMK_XA_CRM_FEATURE_SET);
 451 
 452         if(ping_digest == NULL) {
 453             crm_trace("Calculating new digest");
 454             ping_digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, version);
 455         }
 456 
 457         crm_trace("Processing ping reply %s from %s (%s)", seq_s, host, digest);
 458         if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) {
 459             xmlNode *wrapper = pcmk__xe_first_child(pong, PCMK__XE_CIB_CALLDATA,
 460                                                     NULL, NULL);
 461             xmlNode *remote_cib = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 462 
 463             const char *admin_epoch_s = NULL;
 464             const char *epoch_s = NULL;
 465             const char *num_updates_s = NULL;
 466 
 467             if (remote_cib != NULL) {
 468                 admin_epoch_s = crm_element_value(remote_cib,
 469                                                   PCMK_XA_ADMIN_EPOCH);
 470                 epoch_s = crm_element_value(remote_cib, PCMK_XA_EPOCH);
 471                 num_updates_s = crm_element_value(remote_cib,
 472                                                   PCMK_XA_NUM_UPDATES);
 473             }
 474 
 475             crm_notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s %p",
 476                        crm_element_value(the_cib, PCMK_XA_ADMIN_EPOCH),
 477                        crm_element_value(the_cib, PCMK_XA_EPOCH),
 478                        crm_element_value(the_cib, PCMK_XA_NUM_UPDATES),
 479                        ping_digest, host,
 480                        pcmk__s(admin_epoch_s, "_"),
 481                        pcmk__s(epoch_s, "_"),
 482                        pcmk__s(num_updates_s, "_"),
 483                        digest, remote_cib);
 484 
 485             if(remote_cib && remote_cib->children) {
 486                 // Additional debug
 487                 xml_calculate_changes(the_cib, remote_cib);
 488                 pcmk__log_xml_changes(LOG_INFO, remote_cib);
 489                 crm_trace("End of differences");
 490             }
 491 
 492             free_xml(remote_cib);
 493             sync_our_cib(reply, FALSE);
 494         }
 495     }
 496 }
 497 
 498 static void
 499 local_notify_destroy_callback(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 500 {
 501     cib_local_notify_t *notify = data;
 502 
 503     free_xml(notify->notify_src);
 504     free(notify->client_id);
 505     free(notify);
 506 }
 507 
 508 static void
 509 check_local_notify(int bcast_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 510 {
 511     const cib_local_notify_t *notify = NULL;
 512 
 513     if (!local_notify_queue) {
 514         return;
 515     }
 516 
 517     notify = pcmk__intkey_table_lookup(local_notify_queue, bcast_id);
 518 
 519     if (notify) {
 520         do_local_notify(notify->notify_src, notify->client_id, notify->sync_reply,
 521                         notify->from_peer);
 522         pcmk__intkey_table_remove(local_notify_queue, bcast_id);
 523     }
 524 }
 525 
 526 static void
 527 queue_local_notify(xmlNode * notify_src, const char *client_id, gboolean sync_reply,
     /* [previous][next][first][last][top][bottom][index][help] */
 528                    gboolean from_peer)
 529 {
 530     cib_local_notify_t *notify = pcmk__assert_alloc(1,
 531                                                     sizeof(cib_local_notify_t));
 532 
 533     notify->notify_src = notify_src;
 534     notify->client_id = pcmk__str_copy(client_id);
 535     notify->sync_reply = sync_reply;
 536     notify->from_peer = from_peer;
 537 
 538     if (!local_notify_queue) {
 539         local_notify_queue = pcmk__intkey_table(local_notify_destroy_callback);
 540     }
 541     pcmk__intkey_table_insert(local_notify_queue, cib_local_bcast_num, notify);
 542     // cppcheck doesn't know notify will get freed when hash table is destroyed
 543     // cppcheck-suppress memleak
 544 }
 545 
 546 static void
 547 parse_local_options_v1(const pcmk__client_t *cib_client,
     /* [previous][next][first][last][top][bottom][index][help] */
 548                        const cib__operation_t *operation, int call_options,
 549                        const char *host, const char *op, gboolean *local_notify,
 550                        gboolean *needs_reply, gboolean *process,
 551                        gboolean *needs_forward)
 552 {
 553     if (pcmk_is_set(operation->flags, cib__op_attr_modifies)
 554         && !pcmk_is_set(call_options, cib_inhibit_bcast)) {
 555         /* we need to send an update anyway */
 556         *needs_reply = TRUE;
 557     } else {
 558         *needs_reply = FALSE;
 559     }
 560 
 561     if (host == NULL && (call_options & cib_scope_local)) {
 562         crm_trace("Processing locally scoped %s op from client %s",
 563                   op, pcmk__client_name(cib_client));
 564         *local_notify = TRUE;
 565 
 566     } else if ((host == NULL) && based_is_primary) {
 567         crm_trace("Processing %s op locally from client %s as primary",
 568                   op, pcmk__client_name(cib_client));
 569         *local_notify = TRUE;
 570 
 571     } else if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) {
 572         crm_trace("Processing locally addressed %s op from client %s",
 573                   op, pcmk__client_name(cib_client));
 574         *local_notify = TRUE;
 575 
 576     } else if (stand_alone) {
 577         *needs_forward = FALSE;
 578         *local_notify = TRUE;
 579         *process = TRUE;
 580 
 581     } else {
 582         crm_trace("%s op from %s needs to be forwarded to client %s",
 583                   op, pcmk__client_name(cib_client),
 584                   pcmk__s(host, "the primary instance"));
 585         *needs_forward = TRUE;
 586         *process = FALSE;
 587     }
 588 }
 589 
 590 static void
 591 parse_local_options_v2(const pcmk__client_t *cib_client,
     /* [previous][next][first][last][top][bottom][index][help] */
 592                        const cib__operation_t *operation, int call_options,
 593                        const char *host, const char *op, gboolean *local_notify,
 594                        gboolean *needs_reply, gboolean *process,
 595                        gboolean *needs_forward)
 596 {
 597     // Process locally and notify local client
 598     *process = TRUE;
 599     *needs_reply = FALSE;
 600     *local_notify = TRUE;
 601     *needs_forward = FALSE;
 602 
 603     if (pcmk_is_set(operation->flags, cib__op_attr_local)) {
 604         /* Always process locally if cib__op_attr_local is set.
 605          *
 606          * @COMPAT: Currently host is ignored. At a compatibility break, throw
 607          * an error (from cib_process_request() or earlier) if host is not NULL or
 608          * OUR_NODENAME.
 609          */
 610         crm_trace("Processing always-local %s op from client %s",
 611                   op, pcmk__client_name(cib_client));
 612 
 613         if (!pcmk__str_eq(host, OUR_NODENAME,
 614                           pcmk__str_casei|pcmk__str_null_matches)) {
 615 
 616             crm_warn("Operation '%s' is always local but its target host is "
 617                      "set to '%s'",
 618                      op, host);
 619         }
 620         return;
 621     }
 622 
 623     if (pcmk_is_set(operation->flags, cib__op_attr_modifies)
 624         || !pcmk__str_eq(host, OUR_NODENAME,
 625                          pcmk__str_casei|pcmk__str_null_matches)) {
 626 
 627         // Forward modifying and non-local requests via cluster
 628         *process = FALSE;
 629         *needs_reply = FALSE;
 630         *local_notify = FALSE;
 631         *needs_forward = TRUE;
 632 
 633         crm_trace("%s op from %s needs to be forwarded to %s",
 634                   op, pcmk__client_name(cib_client),
 635                   pcmk__s(host, "all nodes"));
 636         return;
 637     }
 638 
 639     if (stand_alone) {
 640         crm_trace("Processing %s op from client %s (stand-alone)",
 641                   op, pcmk__client_name(cib_client));
 642 
 643     } else {
 644         crm_trace("Processing %saddressed %s op from client %s",
 645                   ((host != NULL)? "locally " : "un"),
 646                   op, pcmk__client_name(cib_client));
 647     }
 648 }
 649 
 650 static void
 651 parse_local_options(const pcmk__client_t *cib_client,
     /* [previous][next][first][last][top][bottom][index][help] */
 652                     const cib__operation_t *operation, int call_options,
 653                     const char *host, const char *op, gboolean *local_notify,
 654                     gboolean *needs_reply, gboolean *process,
 655                     gboolean *needs_forward)
 656 {
 657     if(cib_legacy_mode()) {
 658         parse_local_options_v1(cib_client, operation, call_options, host,
 659                                op, local_notify, needs_reply, process,
 660                                needs_forward);
 661     } else {
 662         parse_local_options_v2(cib_client, operation, call_options, host,
 663                                op, local_notify, needs_reply, process,
 664                                needs_forward);
 665     }
 666 }
 667 
 668 static gboolean
 669 parse_peer_options_v1(const cib__operation_t *operation, xmlNode *request,
     /* [previous][next][first][last][top][bottom][index][help] */
 670                       gboolean *local_notify, gboolean *needs_reply,
 671                       gboolean *process)
 672 {
 673     const char *op = NULL;
 674     const char *host = NULL;
 675     const char *delegated = NULL;
 676     const char *originator = crm_element_value(request, PCMK__XA_SRC);
 677     const char *reply_to = crm_element_value(request, PCMK__XA_CIB_ISREPLYTO);
 678 
 679     gboolean is_reply = pcmk__str_eq(reply_to, OUR_NODENAME, pcmk__str_casei);
 680 
 681     if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) {
 682         *needs_reply = FALSE;
 683         if (is_reply) {
 684             *local_notify = TRUE;
 685             crm_trace("Processing global/peer update from %s"
 686                       " that originated from us", originator);
 687         } else {
 688             crm_trace("Processing global/peer update from %s", originator);
 689         }
 690         return TRUE;
 691     }
 692 
 693     op = crm_element_value(request, PCMK__XA_CIB_OP);
 694     crm_trace("Processing legacy %s request sent by %s", op, originator);
 695 
 696     if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) {
 697         /* Always process these */
 698         *local_notify = FALSE;
 699         if (reply_to == NULL || is_reply) {
 700             *process = TRUE;
 701         }
 702         if (is_reply) {
 703             *needs_reply = FALSE;
 704         }
 705         return *process;
 706     }
 707 
 708     if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) {
 709         process_ping_reply(request);
 710         return FALSE;
 711     }
 712 
 713     if (is_reply) {
 714         crm_trace("Forward reply sent from %s to local clients", originator);
 715         *process = FALSE;
 716         *needs_reply = FALSE;
 717         *local_notify = TRUE;
 718         return TRUE;
 719     }
 720 
 721     host = crm_element_value(request, PCMK__XA_CIB_HOST);
 722     if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) {
 723         crm_trace("Processing %s request sent to us from %s", op, originator);
 724         return TRUE;
 725 
 726     } else if(is_reply == FALSE && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) {
 727         crm_trace("Processing %s request sent to %s by %s", op, host?host:"everyone", originator);
 728         *needs_reply = TRUE;
 729         return TRUE;
 730 
 731     } else if ((host == NULL) && based_is_primary) {
 732         crm_trace("Processing %s request sent to primary instance from %s",
 733                   op, originator);
 734         return TRUE;
 735     }
 736 
 737     delegated = crm_element_value(request, PCMK__XA_CIB_DELEGATED_FROM);
 738     if (delegated != NULL) {
 739         crm_trace("Ignoring message for primary instance");
 740 
 741     } else if (host != NULL) {
 742         /* this is for a specific instance and we're not it */
 743         crm_trace("Ignoring msg for instance on %s", host);
 744 
 745     } else if ((reply_to == NULL) && !based_is_primary) {
 746         // This is for the primary instance, and we're not it
 747         crm_trace("Ignoring reply for primary instance");
 748 
 749     } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) {
 750         if (reply_to != NULL) {
 751             crm_debug("Processing %s from %s", op, originator);
 752             *needs_reply = FALSE;
 753 
 754         } else {
 755             crm_debug("Processing %s reply from %s", op, originator);
 756         }
 757         return TRUE;
 758 
 759     } else {
 760         crm_err("Nothing for us to do?");
 761         crm_log_xml_err(request, "Peer[inbound]");
 762     }
 763 
 764     return FALSE;
 765 }
 766 
 767 static gboolean
 768 parse_peer_options_v2(const cib__operation_t *operation, xmlNode *request,
     /* [previous][next][first][last][top][bottom][index][help] */
 769                       gboolean *local_notify, gboolean *needs_reply,
 770                       gboolean *process)
 771 {
 772     const char *host = NULL;
 773     const char *delegated = crm_element_value(request,
 774                                               PCMK__XA_CIB_DELEGATED_FROM);
 775     const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
 776     const char *originator = crm_element_value(request, PCMK__XA_SRC);
 777     const char *reply_to = crm_element_value(request, PCMK__XA_CIB_ISREPLYTO);
 778 
 779     gboolean is_reply = pcmk__str_eq(reply_to, OUR_NODENAME, pcmk__str_casei);
 780 
 781     if (originator == NULL) { // Shouldn't be possible
 782         originator = "peer";
 783     }
 784 
 785     if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) {
 786         // sync_our_cib() sets PCMK__XA_CIB_ISREPLYTO
 787         if (reply_to) {
 788             delegated = reply_to;
 789         }
 790         goto skip_is_reply;
 791 
 792     } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC_TO_ALL,
 793                             pcmk__str_none)) {
 794         // Nothing to do
 795 
 796     } else if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) {
 797         process_ping_reply(request);
 798         return FALSE;
 799 
 800     } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) {
 801         /* Only the DC (node with the oldest software) should process
 802          * this operation if PCMK__XA_CIB_SCHEMA_MAX is unset.
 803          *
 804          * If the DC is happy it will then send out another
 805          * PCMK__CIB_REQUEST_UPGRADE which will tell all nodes to do the actual
 806          * upgrade.
 807          *
 808          * Except this time PCMK__XA_CIB_SCHEMA_MAX will be set which puts a
 809          * limit on how far newer nodes will go
 810          */
 811         const char *max = crm_element_value(request, PCMK__XA_CIB_SCHEMA_MAX);
 812         const char *upgrade_rc = crm_element_value(request,
 813                                                    PCMK__XA_CIB_UPGRADE_RC);
 814 
 815         crm_trace("Parsing upgrade %s for %s with max=%s and upgrade_rc=%s",
 816                   (is_reply? "reply" : "request"),
 817                   (based_is_primary? "primary" : "secondary"),
 818                   pcmk__s(max, "none"), pcmk__s(upgrade_rc, "none"));
 819 
 820         if (upgrade_rc != NULL) {
 821             // Our upgrade request was rejected by DC, notify clients of result
 822             crm_xml_add(request, PCMK__XA_CIB_RC, upgrade_rc);
 823 
 824         } else if ((max == NULL) && based_is_primary) {
 825             /* We are the DC, check if this upgrade is allowed */
 826             goto skip_is_reply;
 827 
 828         } else if(max) {
 829             /* Ok, go ahead and upgrade to 'max' */
 830             goto skip_is_reply;
 831 
 832         } else {
 833             // Ignore broadcast client requests when we're not primary
 834             return FALSE;
 835         }
 836 
 837     } else if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) {
 838         crm_info("Detected legacy %s global update from %s", op, originator);
 839         send_sync_request(NULL);
 840         legacy_mode = TRUE;
 841         return FALSE;
 842 
 843     } else if (is_reply
 844                && pcmk_is_set(operation->flags, cib__op_attr_modifies)) {
 845         crm_trace("Ignoring legacy %s reply sent from %s to local clients", op, originator);
 846         return FALSE;
 847 
 848     } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) {
 849         *local_notify = FALSE;
 850         if (reply_to == NULL) {
 851             *process = TRUE;
 852         } else { // Not possible?
 853             crm_debug("Ignoring shutdown request from %s because reply_to=%s",
 854                       originator, reply_to);
 855         }
 856         return *process;
 857     }
 858 
 859     if (is_reply) {
 860         crm_trace("Will notify local clients for %s reply from %s",
 861                   op, originator);
 862         *process = FALSE;
 863         *needs_reply = FALSE;
 864         *local_notify = TRUE;
 865         return TRUE;
 866     }
 867 
 868   skip_is_reply:
 869     *process = TRUE;
 870     *needs_reply = FALSE;
 871 
 872     *local_notify = pcmk__str_eq(delegated, OUR_NODENAME, pcmk__str_casei);
 873 
 874     host = crm_element_value(request, PCMK__XA_CIB_HOST);
 875     if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) {
 876         crm_trace("Processing %s request sent to us from %s", op, originator);
 877         *needs_reply = TRUE;
 878         return TRUE;
 879 
 880     } else if (host != NULL) {
 881         crm_trace("Ignoring %s request intended for CIB manager on %s",
 882                   op, host);
 883         return FALSE;
 884 
 885     } else if(is_reply == FALSE && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) {
 886         *needs_reply = TRUE;
 887     }
 888 
 889     crm_trace("Processing %s request broadcast by %s call %s on %s "
 890               "(local clients will%s be notified)", op,
 891               pcmk__s(crm_element_value(request, PCMK__XA_CIB_CLIENTNAME),
 892                       "client"),
 893               pcmk__s(crm_element_value(request, PCMK__XA_CIB_CALLID),
 894                       "without ID"),
 895               originator, (*local_notify? "" : "not"));
 896     return TRUE;
 897 }
 898 
 899 static gboolean
 900 parse_peer_options(const cib__operation_t *operation, xmlNode *request,
     /* [previous][next][first][last][top][bottom][index][help] */
 901                    gboolean *local_notify, gboolean *needs_reply,
 902                    gboolean *process)
 903 {
 904     /* TODO: What happens when an update comes in after node A
 905      * requests the CIB from node B, but before it gets the reply (and
 906      * sends out the replace operation)
 907      */
 908     if(cib_legacy_mode()) {
 909         return parse_peer_options_v1(operation, request, local_notify,
 910                                      needs_reply, process);
 911     } else {
 912         return parse_peer_options_v2(operation, request, local_notify,
 913                                      needs_reply, process);
 914     }
 915 }
 916 
 917 /*!
 918  * \internal
 919  * \brief Forward a CIB request to the appropriate target host(s)
 920  *
 921  * \param[in] request  CIB request to forward
 922  */
 923 static void
 924 forward_request(xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 925 {
 926     const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
 927     const char *section = crm_element_value(request, PCMK__XA_CIB_SECTION);
 928     const char *host = crm_element_value(request, PCMK__XA_CIB_HOST);
 929     const char *originator = crm_element_value(request, PCMK__XA_SRC);
 930     const char *client_name = crm_element_value(request,
 931                                                 PCMK__XA_CIB_CLIENTNAME);
 932     const char *call_id = crm_element_value(request, PCMK__XA_CIB_CALLID);
 933     crm_node_t *peer = NULL;
 934 
 935     int log_level = LOG_INFO;
 936 
 937     if (pcmk__str_eq(op, PCMK__CIB_REQUEST_NOOP, pcmk__str_none)) {
 938         log_level = LOG_DEBUG;
 939     }
 940 
 941     do_crm_log(log_level,
 942                "Forwarding %s operation for section %s to %s (origin=%s/%s/%s)",
 943                pcmk__s(op, "invalid"),
 944                pcmk__s(section, "all"),
 945                pcmk__s(host, (cib_legacy_mode()? "primary" : "all")),
 946                pcmk__s(originator, "local"),
 947                pcmk__s(client_name, "unspecified"),
 948                pcmk__s(call_id, "unspecified"));
 949 
 950     crm_xml_add(request, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME);
 951 
 952     if (host != NULL) {
 953         peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member);
 954     }
 955     pcmk__cluster_send_message(peer, crm_msg_cib, request);
 956 
 957     // Return the request to its original state
 958     pcmk__xe_remove_attr(request, PCMK__XA_CIB_DELEGATED_FROM);
 959 }
 960 
 961 static gboolean
 962 send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gboolean broadcast)
     /* [previous][next][first][last][top][bottom][index][help] */
 963 {
 964     CRM_ASSERT(msg != NULL);
 965 
 966     if (broadcast) {
 967         /* @COMPAT: Legacy code
 968          *
 969          * This successful call modified the CIB, and the change needs to be
 970          * broadcast (sent via cluster to all nodes).
 971          */
 972         int diff_add_updates = 0;
 973         int diff_add_epoch = 0;
 974         int diff_add_admin_epoch = 0;
 975 
 976         int diff_del_updates = 0;
 977         int diff_del_epoch = 0;
 978         int diff_del_admin_epoch = 0;
 979 
 980         const char *digest = NULL;
 981         int format = 1;
 982 
 983         xmlNode *wrapper = NULL;
 984 
 985         CRM_LOG_ASSERT(result_diff != NULL);
 986         digest = crm_element_value(result_diff, PCMK__XA_DIGEST);
 987         crm_element_value_int(result_diff, PCMK_XA_FORMAT, &format);
 988 
 989         cib_diff_version_details(result_diff,
 990                                  &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
 991                                  &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
 992 
 993         crm_trace("Sending update diff %d.%d.%d -> %d.%d.%d %s",
 994                   diff_del_admin_epoch, diff_del_epoch, diff_del_updates,
 995                   diff_add_admin_epoch, diff_add_epoch, diff_add_updates, digest);
 996 
 997         crm_xml_add(msg, PCMK__XA_CIB_ISREPLYTO, originator);
 998         pcmk__xe_set_bool_attr(msg, PCMK__XA_CIB_UPDATE, true);
 999         crm_xml_add(msg, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_APPLY_PATCH);
1000         crm_xml_add(msg, PCMK__XA_CIB_USER, CRM_DAEMON_USER);
1001 
1002         if (format == 1) {
1003             CRM_ASSERT(digest != NULL);
1004         }
1005 
1006         wrapper = pcmk__xe_create(msg, PCMK__XE_CIB_UPDATE_DIFF);
1007         pcmk__xml_copy(wrapper, result_diff);
1008 
1009         crm_log_xml_explicit(msg, "copy");
1010         return pcmk__cluster_send_message(NULL, crm_msg_cib, msg);
1011 
1012     } else if (originator != NULL) {
1013         /* send reply via HA to originating node */
1014         const crm_node_t *node =
1015             pcmk__get_node(0, originator, NULL,
1016                            pcmk__node_search_cluster_member);
1017 
1018         crm_trace("Sending request result to %s only", originator);
1019         crm_xml_add(msg, PCMK__XA_CIB_ISREPLYTO, originator);
1020         return pcmk__cluster_send_message(node, crm_msg_cib, msg);
1021     }
1022 
1023     return FALSE;
1024 }
1025 
1026 /*!
1027  * \internal
1028  * \brief Handle an IPC or CPG message containing a request
1029  *
1030  * \param[in,out] request        Request XML
1031  * \param[in] privileged         Whether privileged commands may be run
1032  *                               (see cib_server_ops[] definition)
1033  * \param[in] cib_client         IPC client that sent request (or NULL if CPG)
1034  *
1035  * \return Legacy Pacemaker return code
1036  */
1037 int
1038 cib_process_request(xmlNode *request, gboolean privileged,
     /* [previous][next][first][last][top][bottom][index][help] */
1039                     const pcmk__client_t *cib_client)
1040 {
1041     // @TODO: Break into multiple smaller functions
1042     int call_options = 0;
1043 
1044     gboolean process = TRUE;        // Whether to process request locally now
1045     gboolean is_update = TRUE;      // Whether request would modify CIB
1046     gboolean needs_reply = TRUE;    // Whether to build a reply
1047     gboolean local_notify = FALSE;  // Whether to notify (local) requester
1048     gboolean needs_forward = FALSE; // Whether to forward request somewhere else
1049 
1050     xmlNode *op_reply = NULL;
1051     xmlNode *result_diff = NULL;
1052 
1053     int rc = pcmk_ok;
1054     const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
1055     const char *originator = crm_element_value(request, PCMK__XA_SRC);
1056     const char *host = crm_element_value(request, PCMK__XA_CIB_HOST);
1057     const char *target = NULL;
1058     const char *call_id = crm_element_value(request, PCMK__XA_CIB_CALLID);
1059     const char *client_id = crm_element_value(request, PCMK__XA_CIB_CLIENTID);
1060     const char *client_name = crm_element_value(request,
1061                                                 PCMK__XA_CIB_CLIENTNAME);
1062     const char *reply_to = crm_element_value(request, PCMK__XA_CIB_ISREPLYTO);
1063 
1064     const cib__operation_t *operation = NULL;
1065     cib__op_fn_t op_function = NULL;
1066 
1067     crm_element_value_int(request, PCMK__XA_CIB_CALLOPT, &call_options);
1068 
1069     if ((host != NULL) && (*host == '\0')) {
1070         host = NULL;
1071     }
1072 
1073     // @TODO: Improve trace messages. Target is accurate only for legacy mode.
1074     if (host) {
1075         target = host;
1076 
1077     } else if (call_options & cib_scope_local) {
1078         target = "local host";
1079 
1080     } else {
1081         target = "primary";
1082     }
1083 
1084     if (cib_client == NULL) {
1085         crm_trace("Processing peer %s operation from %s/%s on %s intended for %s (reply=%s)",
1086                   op, client_name, call_id, originator, target, reply_to);
1087     } else {
1088         crm_xml_add(request, PCMK__XA_SRC, OUR_NODENAME);
1089         crm_trace("Processing local %s operation from %s/%s intended for %s", op, client_name, call_id, target);
1090     }
1091 
1092     rc = cib__get_operation(op, &operation);
1093     rc = pcmk_rc2legacy(rc);
1094     if (rc != pcmk_ok) {
1095         /* TODO: construct error reply? */
1096         crm_err("Pre-processing of command failed: %s", pcmk_strerror(rc));
1097         return rc;
1098     }
1099 
1100     op_function = based_get_op_function(operation);
1101     if (op_function == NULL) {
1102         crm_err("Operation %s not supported by CIB manager", op);
1103         return -EOPNOTSUPP;
1104     }
1105 
1106     if (cib_client != NULL) {
1107         parse_local_options(cib_client, operation, call_options, host, op,
1108                             &local_notify, &needs_reply, &process,
1109                             &needs_forward);
1110 
1111     } else if (!parse_peer_options(operation, request, &local_notify,
1112                                    &needs_reply, &process)) {
1113         return rc;
1114     }
1115 
1116     if (pcmk_is_set(call_options, cib_transaction)) {
1117         /* All requests in a transaction are processed locally against a working
1118          * CIB copy, and we don't notify for individual requests because the
1119          * entire transaction is atomic.
1120          *
1121          * We still call the option parser functions above, for the sake of log
1122          * messages and checking whether we're the target for peer requests.
1123          */
1124         process = TRUE;
1125         needs_reply = FALSE;
1126         local_notify = FALSE;
1127         needs_forward = FALSE;
1128     }
1129 
1130     is_update = pcmk_is_set(operation->flags, cib__op_attr_modifies);
1131 
1132     if (pcmk_is_set(call_options, cib_discard_reply)) {
1133         /* If the request will modify the CIB, and we are in legacy mode, we
1134          * need to build a reply so we can broadcast a diff, even if the
1135          * requester doesn't want one.
1136          */
1137         needs_reply = is_update && cib_legacy_mode();
1138         local_notify = FALSE;
1139         crm_trace("Client is not interested in the reply");
1140     }
1141 
1142     if (needs_forward) {
1143         forward_request(request);
1144         return rc;
1145     }
1146 
1147     if (cib_status != pcmk_ok) {
1148         rc = cib_status;
1149         crm_err("Operation ignored, cluster configuration is invalid."
1150                 " Please repair and restart: %s", pcmk_strerror(cib_status));
1151 
1152         op_reply = create_cib_reply(op, call_id, client_id, call_options, rc,
1153                                     the_cib);
1154 
1155     } else if (process) {
1156         time_t finished = 0;
1157         time_t now = time(NULL);
1158         int level = LOG_INFO;
1159         const char *section = crm_element_value(request, PCMK__XA_CIB_SECTION);
1160         const char *admin_epoch_s = NULL;
1161         const char *epoch_s = NULL;
1162         const char *num_updates_s = NULL;
1163 
1164         rc = cib_process_command(request, operation, op_function, &op_reply,
1165                                  &result_diff, privileged);
1166 
1167         if (!is_update) {
1168             level = LOG_TRACE;
1169 
1170         } else if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) {
1171             switch (rc) {
1172                 case pcmk_ok:
1173                     level = LOG_INFO;
1174                     break;
1175                 case -pcmk_err_old_data:
1176                 case -pcmk_err_diff_resync:
1177                 case -pcmk_err_diff_failed:
1178                     level = LOG_TRACE;
1179                     break;
1180                 default:
1181                     level = LOG_ERR;
1182             }
1183 
1184         } else if (rc != pcmk_ok) {
1185             level = LOG_WARNING;
1186         }
1187 
1188         if (the_cib != NULL) {
1189             admin_epoch_s = crm_element_value(the_cib, PCMK_XA_ADMIN_EPOCH);
1190             epoch_s = crm_element_value(the_cib, PCMK_XA_EPOCH);
1191             num_updates_s = crm_element_value(the_cib, PCMK_XA_NUM_UPDATES);
1192         }
1193 
1194         do_crm_log(level,
1195                    "Completed %s operation for section %s: %s (rc=%d, origin=%s/%s/%s, version=%s.%s.%s)",
1196                    op, section ? section : "'all'", pcmk_strerror(rc), rc,
1197                    originator ? originator : "local", client_name, call_id,
1198                    pcmk__s(admin_epoch_s, "0"),
1199                    pcmk__s(epoch_s, "0"),
1200                    pcmk__s(num_updates_s, "0"));
1201 
1202         finished = time(NULL);
1203         if ((finished - now) > 3) {
1204             crm_trace("%s operation took %lds to complete", op, (long)(finished - now));
1205             crm_write_blackbox(0, NULL);
1206         }
1207 
1208         if (op_reply == NULL && (needs_reply || local_notify)) {
1209             crm_err("Unexpected NULL reply to message");
1210             crm_log_xml_err(request, "null reply");
1211             needs_reply = FALSE;
1212             local_notify = FALSE;
1213         }
1214     }
1215 
1216     if (is_update && !cib_legacy_mode()) {
1217         crm_trace("Completed pre-sync update from %s/%s/%s%s",
1218                   originator ? originator : "local", client_name, call_id,
1219                   local_notify?" with local notification":"");
1220 
1221     } else if (!needs_reply || stand_alone) {
1222         // This was a non-originating secondary update
1223         crm_trace("Completed update as secondary");
1224 
1225     } else if (cib_legacy_mode() &&
1226                rc == pcmk_ok && result_diff != NULL && !(call_options & cib_inhibit_bcast)) {
1227         gboolean broadcast = FALSE;
1228 
1229         cib_local_bcast_num++;
1230         crm_xml_add_int(request, PCMK__XA_CIB_LOCAL_NOTIFY_ID,
1231                         cib_local_bcast_num);
1232         broadcast = send_peer_reply(request, result_diff, originator, TRUE);
1233 
1234         if (broadcast && client_id && local_notify && op_reply) {
1235 
1236             /* If we have been asked to sync the reply,
1237              * and a bcast msg has gone out, we queue the local notify
1238              * until we know the bcast message has been received */
1239             local_notify = FALSE;
1240             crm_trace("Queuing local %ssync notification for %s",
1241                       (call_options & cib_sync_call) ? "" : "a-", client_id);
1242 
1243             queue_local_notify(op_reply, client_id,
1244                                pcmk_is_set(call_options, cib_sync_call),
1245                                (cib_client == NULL));
1246             op_reply = NULL;    /* the reply is queued, so don't free here */
1247         }
1248 
1249     } else if ((cib_client == NULL)
1250                && !pcmk_is_set(call_options, cib_discard_reply)) {
1251 
1252         if (is_update == FALSE || result_diff == NULL) {
1253             crm_trace("Request not broadcast: R/O call");
1254 
1255         } else if (call_options & cib_inhibit_bcast) {
1256             crm_trace("Request not broadcast: inhibited");
1257 
1258         } else if (rc != pcmk_ok) {
1259             crm_trace("Request not broadcast: call failed: %s", pcmk_strerror(rc));
1260 
1261         } else {
1262             crm_trace("Directing reply to %s", originator);
1263         }
1264 
1265         send_peer_reply(op_reply, result_diff, originator, FALSE);
1266     }
1267 
1268     if (local_notify && client_id) {
1269         crm_trace("Performing local %ssync notification for %s",
1270                   (pcmk_is_set(call_options, cib_sync_call)? "" : "a"),
1271                   client_id);
1272         if (process == FALSE) {
1273             do_local_notify(request, client_id,
1274                             pcmk_is_set(call_options, cib_sync_call),
1275                             (cib_client == NULL));
1276         } else {
1277             do_local_notify(op_reply, client_id,
1278                             pcmk_is_set(call_options, cib_sync_call),
1279                             (cib_client == NULL));
1280         }
1281     }
1282 
1283     free_xml(op_reply);
1284     free_xml(result_diff);
1285 
1286     return rc;
1287 }
1288 
1289 /*!
1290  * \internal
1291  * \brief Get a CIB operation's input from the request XML
1292  *
1293  * \param[in]  request  CIB request XML
1294  * \param[in]  type     CIB operation type
1295  * \param[out] section  Where to store CIB section name
1296  *
1297  * \return Input XML for CIB operation
1298  *
1299  * \note If not \c NULL, the return value is a non-const pointer to part of
1300  *       \p request. The caller should not free it directly.
1301  */
1302 static xmlNode *
1303 prepare_input(const xmlNode *request, enum cib__op_type type,
     /* [previous][next][first][last][top][bottom][index][help] */
1304               const char **section)
1305 {
1306     xmlNode *wrapper = NULL;
1307     xmlNode *input = NULL;
1308 
1309     *section = NULL;
1310 
1311     switch (type) {
1312         case cib__op_apply_patch:
1313             {
1314                 const char *wrapper_name = PCMK__XE_CIB_CALLDATA;
1315 
1316                 if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) {
1317                     wrapper_name = PCMK__XE_CIB_UPDATE_DIFF;
1318                 }
1319                 wrapper = pcmk__xe_first_child(request, wrapper_name, NULL,
1320                                                NULL);
1321                 input = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
1322             }
1323             break;
1324 
1325         default:
1326             wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, NULL,
1327                                            NULL);
1328             input = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
1329             *section = crm_element_value(request, PCMK__XA_CIB_SECTION);
1330             break;
1331     }
1332 
1333     // Grab the specified section
1334     if ((*section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) {
1335         input = pcmk_find_cib_element(input, *section);
1336     }
1337 
1338     return input;
1339 }
1340 
1341 // v1 and v2 patch formats
1342 #define XPATH_CONFIG_CHANGE         \
1343     "//" PCMK_XE_CRM_CONFIG " | "   \
1344     "//" PCMK_XE_CHANGE             \
1345     "[contains(@" PCMK_XA_PATH ",'/" PCMK_XE_CRM_CONFIG "/')]"
1346 
1347 static bool
1348 contains_config_change(xmlNode *diff)
     /* [previous][next][first][last][top][bottom][index][help] */
1349 {
1350     bool changed = false;
1351 
1352     if (diff) {
1353         xmlXPathObject *xpathObj = xpath_search(diff, XPATH_CONFIG_CHANGE);
1354 
1355         if (numXpathResults(xpathObj) > 0) {
1356             changed = true;
1357         }
1358         freeXpathObject(xpathObj);
1359     }
1360     return changed;
1361 }
1362 
1363 static int
1364 cib_process_command(xmlNode *request, const cib__operation_t *operation,
     /* [previous][next][first][last][top][bottom][index][help] */
1365                     cib__op_fn_t op_function, xmlNode **reply,
1366                     xmlNode **cib_diff, bool privileged)
1367 {
1368     xmlNode *input = NULL;
1369     xmlNode *output = NULL;
1370     xmlNode *result_cib = NULL;
1371 
1372     int call_options = 0;
1373 
1374     const char *op = NULL;
1375     const char *section = NULL;
1376     const char *call_id = crm_element_value(request, PCMK__XA_CIB_CALLID);
1377     const char *client_id = crm_element_value(request, PCMK__XA_CIB_CLIENTID);
1378     const char *client_name = crm_element_value(request,
1379                                                 PCMK__XA_CIB_CLIENTNAME);
1380     const char *originator = crm_element_value(request, PCMK__XA_SRC);
1381 
1382     int rc = pcmk_ok;
1383 
1384     bool config_changed = false;
1385     bool manage_counters = true;
1386 
1387     static mainloop_timer_t *digest_timer = NULL;
1388 
1389     CRM_ASSERT(cib_status == pcmk_ok);
1390 
1391     if(digest_timer == NULL) {
1392         digest_timer = mainloop_timer_add("digester", 5000, FALSE, cib_digester_cb, NULL);
1393     }
1394 
1395     *reply = NULL;
1396     *cib_diff = NULL;
1397 
1398     /* Start processing the request... */
1399     op = crm_element_value(request, PCMK__XA_CIB_OP);
1400     crm_element_value_int(request, PCMK__XA_CIB_CALLOPT, &call_options);
1401 
1402     if (!privileged && pcmk_is_set(operation->flags, cib__op_attr_privileged)) {
1403         rc = -EACCES;
1404         crm_trace("Failed due to lack of privileges: %s", pcmk_strerror(rc));
1405         goto done;
1406     }
1407 
1408     input = prepare_input(request, operation->type, &section);
1409 
1410     if (!pcmk_is_set(operation->flags, cib__op_attr_modifies)) {
1411         rc = cib_perform_op(NULL, op, call_options, op_function, true, section,
1412                             request, input, false, &config_changed, &the_cib,
1413                             &result_cib, NULL, &output);
1414 
1415         CRM_CHECK(result_cib == NULL, free_xml(result_cib));
1416         goto done;
1417     }
1418 
1419     /* @COMPAT: Handle a valid write action (legacy)
1420      *
1421      * @TODO: Re-evaluate whether this is all truly legacy. The cib_force_diff
1422      * portion is. However, PCMK__XA_CIB_UPDATE may be set by a sync operation
1423      * even in non-legacy mode, and manage_counters tells xml_create_patchset()
1424      * whether to update version/epoch info.
1425      */
1426     if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) {
1427         manage_counters = false;
1428         cib__set_call_options(call_options, "call", cib_force_diff);
1429         crm_trace("Global update detected");
1430 
1431         CRM_LOG_ASSERT(pcmk__str_any_of(op,
1432                                         PCMK__CIB_REQUEST_APPLY_PATCH,
1433                                         PCMK__CIB_REQUEST_REPLACE,
1434                                         NULL));
1435     }
1436 
1437     ping_modified_since = TRUE;
1438     if (pcmk_is_set(call_options, cib_inhibit_bcast)) {
1439         crm_trace("Skipping update: inhibit broadcast");
1440         manage_counters = false;
1441     }
1442 
1443     // result_cib must not be modified after cib_perform_op() returns
1444     rc = cib_perform_op(NULL, op, call_options, op_function, false, section,
1445                         request, input, manage_counters, &config_changed,
1446                         &the_cib, &result_cib, cib_diff, &output);
1447 
1448     // @COMPAT: Legacy code
1449     if (!manage_counters) {
1450         int format = 1;
1451 
1452         // If the diff is NULL at this point, it's because nothing changed
1453         if (*cib_diff != NULL) {
1454             crm_element_value_int(*cib_diff, PCMK_XA_FORMAT, &format);
1455         }
1456 
1457         if (format == 1) {
1458             config_changed = cib__config_changed_v1(NULL, NULL, cib_diff);
1459         }
1460     }
1461 
1462     /* Always write to disk for successful ops with the flag set. This also
1463      * negates the need to detect ordering changes.
1464      */
1465     if ((rc == pcmk_ok)
1466         && pcmk_is_set(operation->flags, cib__op_attr_writes_through)) {
1467 
1468         config_changed = true;
1469     }
1470 
1471     if ((rc == pcmk_ok)
1472         && !pcmk_any_flags_set(call_options, cib_dryrun|cib_transaction)) {
1473 
1474         if (result_cib != the_cib) {
1475             if (pcmk_is_set(operation->flags, cib__op_attr_writes_through)) {
1476                 config_changed = true;
1477             }
1478 
1479             crm_trace("Activating %s->%s%s",
1480                       crm_element_value(the_cib, PCMK_XA_NUM_UPDATES),
1481                       crm_element_value(result_cib, PCMK_XA_NUM_UPDATES),
1482                       (config_changed? " changed" : ""));
1483 
1484             rc = activateCibXml(result_cib, config_changed, op);
1485             if (rc != pcmk_ok) {
1486                 crm_err("Failed to activate new CIB: %s", pcmk_strerror(rc));
1487             }
1488         }
1489 
1490         if ((rc == pcmk_ok) && contains_config_change(*cib_diff)) {
1491             cib_read_config(config_hash, result_cib);
1492         }
1493 
1494         /* @COMPAT Nodes older than feature set 3.19.0 don't support
1495          * transactions. In a mixed-version cluster with nodes <3.19.0, we must
1496          * sync the updated CIB, so that the older nodes receive the changes.
1497          * Any node that has already applied the transaction will ignore the
1498          * synced CIB.
1499          *
1500          * To ensure the updated CIB is synced from only one node, we sync it
1501          * from the originator.
1502          */
1503         if ((operation->type == cib__op_commit_transact)
1504             && pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei)
1505             && compare_version(crm_element_value(the_cib,
1506                                                  PCMK_XA_CRM_FEATURE_SET),
1507                                "3.19.0") < 0) {
1508 
1509             sync_our_cib(request, TRUE);
1510         }
1511 
1512         mainloop_timer_stop(digest_timer);
1513         mainloop_timer_start(digest_timer);
1514 
1515     } else if (rc == -pcmk_err_schema_validation) {
1516         CRM_ASSERT(result_cib != the_cib);
1517 
1518         if (output != NULL) {
1519             crm_log_xml_info(output, "cib:output");
1520             free_xml(output);
1521         }
1522 
1523         output = result_cib;
1524 
1525     } else {
1526         crm_trace("Not activating %d %d %s", rc,
1527                   pcmk_is_set(call_options, cib_dryrun),
1528                   crm_element_value(result_cib, PCMK_XA_NUM_UPDATES));
1529 
1530         if (result_cib != the_cib) {
1531             free_xml(result_cib);
1532         }
1533     }
1534 
1535     if (!pcmk_any_flags_set(call_options,
1536                             cib_dryrun|cib_inhibit_notify|cib_transaction)) {
1537         crm_trace("Sending notifications %d",
1538                   pcmk_is_set(call_options, cib_dryrun));
1539         cib_diff_notify(op, rc, call_id, client_id, client_name, originator,
1540                         input, *cib_diff);
1541     }
1542 
1543     pcmk__log_xml_patchset(LOG_TRACE, *cib_diff);
1544 
1545   done:
1546     if (!pcmk_is_set(call_options, cib_discard_reply) || cib_legacy_mode()) {
1547         *reply = create_cib_reply(op, call_id, client_id, call_options, rc,
1548                                   output);
1549     }
1550 
1551     if (output != the_cib) {
1552         free_xml(output);
1553     }
1554     crm_trace("done");
1555     return rc;
1556 }
1557 
1558 void
1559 cib_peer_callback(xmlNode * msg, void *private_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1560 {
1561     const char *reason = NULL;
1562     const char *originator = crm_element_value(msg, PCMK__XA_SRC);
1563 
1564     if (cib_legacy_mode()
1565         && pcmk__str_eq(originator, OUR_NODENAME,
1566                         pcmk__str_casei|pcmk__str_null_matches)) {
1567         /* message is from ourselves */
1568         int bcast_id = 0;
1569 
1570         if (crm_element_value_int(msg, PCMK__XA_CIB_LOCAL_NOTIFY_ID,
1571                                   &bcast_id) == 0) {
1572             check_local_notify(bcast_id);
1573         }
1574         return;
1575 
1576     } else if (crm_peer_cache == NULL) {
1577         reason = "membership not established";
1578         goto bail;
1579     }
1580 
1581     if (crm_element_value(msg, PCMK__XA_CIB_CLIENTNAME) == NULL) {
1582         crm_xml_add(msg, PCMK__XA_CIB_CLIENTNAME, originator);
1583     }
1584 
1585     /* crm_log_xml_trace(msg, "Peer[inbound]"); */
1586     cib_process_request(msg, TRUE, NULL);
1587     return;
1588 
1589   bail:
1590     if (reason) {
1591         const char *op = crm_element_value(msg, PCMK__XA_CIB_OP);
1592 
1593         crm_warn("Discarding %s message from %s: %s", op, originator, reason);
1594     }
1595 }
1596 
1597 static gboolean
1598 cib_force_exit(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
1599 {
1600     crm_notice("Forcing exit!");
1601     terminate_cib(__func__, CRM_EX_ERROR);
1602     return FALSE;
1603 }
1604 
1605 static void
1606 disconnect_remote_client(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1607 {
1608     pcmk__client_t *a_client = value;
1609 
1610     crm_err("Can't disconnect client %s: Not implemented",
1611             pcmk__client_name(a_client));
1612 }
1613 
1614 static void
1615 initiate_exit(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1616 {
1617     int active = 0;
1618     xmlNode *leaving = NULL;
1619 
1620     active = pcmk__cluster_num_active_nodes();
1621     if (active < 2) { // This is the last active node
1622         terminate_cib(__func__, 0);
1623         return;
1624     }
1625 
1626     crm_info("Sending shutdown request to %d peers", active);
1627 
1628     leaving = pcmk__xe_create(NULL, PCMK__XE_EXIT_NOTIFICATION);
1629     crm_xml_add(leaving, PCMK__XA_T, PCMK__VALUE_CIB);
1630     crm_xml_add(leaving, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SHUTDOWN);
1631 
1632     pcmk__cluster_send_message(NULL, crm_msg_cib, leaving);
1633     free_xml(leaving);
1634 
1635     g_timeout_add(EXIT_ESCALATION_MS, cib_force_exit, NULL);
1636 }
1637 
1638 void
1639 cib_shutdown(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
1640 {
1641     struct qb_ipcs_stats srv_stats;
1642 
1643     if (cib_shutdown_flag == FALSE) {
1644         int disconnects = 0;
1645         qb_ipcs_connection_t *c = NULL;
1646 
1647         cib_shutdown_flag = TRUE;
1648 
1649         c = qb_ipcs_connection_first_get(ipcs_rw);
1650         while (c != NULL) {
1651             qb_ipcs_connection_t *last = c;
1652 
1653             c = qb_ipcs_connection_next_get(ipcs_rw, last);
1654 
1655             crm_debug("Disconnecting r/w client %p...", last);
1656             qb_ipcs_disconnect(last);
1657             qb_ipcs_connection_unref(last);
1658             disconnects++;
1659         }
1660 
1661         c = qb_ipcs_connection_first_get(ipcs_ro);
1662         while (c != NULL) {
1663             qb_ipcs_connection_t *last = c;
1664 
1665             c = qb_ipcs_connection_next_get(ipcs_ro, last);
1666 
1667             crm_debug("Disconnecting r/o client %p...", last);
1668             qb_ipcs_disconnect(last);
1669             qb_ipcs_connection_unref(last);
1670             disconnects++;
1671         }
1672 
1673         c = qb_ipcs_connection_first_get(ipcs_shm);
1674         while (c != NULL) {
1675             qb_ipcs_connection_t *last = c;
1676 
1677             c = qb_ipcs_connection_next_get(ipcs_shm, last);
1678 
1679             crm_debug("Disconnecting non-blocking r/w client %p...", last);
1680             qb_ipcs_disconnect(last);
1681             qb_ipcs_connection_unref(last);
1682             disconnects++;
1683         }
1684 
1685         disconnects += pcmk__ipc_client_count();
1686 
1687         crm_debug("Disconnecting %d remote clients", pcmk__ipc_client_count());
1688         pcmk__foreach_ipc_client(disconnect_remote_client, NULL);
1689         crm_info("Disconnected %d clients", disconnects);
1690     }
1691 
1692     qb_ipcs_stats_get(ipcs_rw, &srv_stats, QB_FALSE);
1693 
1694     if (pcmk__ipc_client_count() == 0) {
1695         crm_info("All clients disconnected (%d)", srv_stats.active_connections);
1696         initiate_exit();
1697 
1698     } else {
1699         crm_info("Waiting on %d clients to disconnect (%d)",
1700                  pcmk__ipc_client_count(), srv_stats.active_connections);
1701     }
1702 }
1703 
1704 extern int remote_fd;
1705 extern int remote_tls_fd;
1706 
1707 /*!
1708  * \internal
1709  * \brief Close remote sockets, free the global CIB and quit
1710  *
1711  * \param[in] caller           Name of calling function (for log message)
1712  * \param[in] fast             If -1, skip disconnect; if positive, exit that
1713  */
1714 void
1715 terminate_cib(const char *caller, int fast)
     /* [previous][next][first][last][top][bottom][index][help] */
1716 {
1717     crm_info("%s: Exiting%s...", caller,
1718              (fast > 0)? " fast" : mainloop ? " from mainloop" : "");
1719 
1720     if (remote_fd > 0) {
1721         close(remote_fd);
1722         remote_fd = 0;
1723     }
1724     if (remote_tls_fd > 0) {
1725         close(remote_tls_fd);
1726         remote_tls_fd = 0;
1727     }
1728 
1729     uninitializeCib();
1730 
1731     if (fast > 0) {
1732         /* Quit fast on error */
1733         pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm);
1734         crm_exit(fast);
1735 
1736     } else if ((mainloop != NULL) && g_main_loop_is_running(mainloop)) {
1737         /* Quit via returning from the main loop. If fast == -1, we skip the
1738          * disconnect here, and it will be done when the main loop returns
1739          * (this allows the peer status callback to avoid messing with the
1740          * peer caches).
1741          */
1742         if (fast == 0) {
1743             pcmk_cluster_disconnect(crm_cluster);
1744         }
1745         g_main_loop_quit(mainloop);
1746 
1747     } else {
1748         /* Quit via clean exit. Even the peer status callback can disconnect
1749          * here, because we're not returning control to the caller. */
1750         pcmk_cluster_disconnect(crm_cluster);
1751         pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm);
1752         crm_exit(CRM_EX_OK);
1753     }
1754 }

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