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. cib_common_callback_worker
  8. cib_common_callback
  9. cib_digester_cb
  10. process_ping_reply
  11. do_local_notify
  12. local_notify_destroy_callback
  13. check_local_notify
  14. queue_local_notify
  15. parse_local_options_v1
  16. parse_local_options_v2
  17. parse_local_options
  18. parse_peer_options_v1
  19. parse_peer_options_v2
  20. parse_peer_options
  21. forward_request
  22. send_peer_reply
  23. cib_process_request
  24. calculate_section_digest
  25. contains_config_change
  26. cib_process_command
  27. cib_peer_callback
  28. cib_force_exit
  29. disconnect_remote_client
  30. initiate_exit
  31. cib_shutdown
  32. terminate_cib

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

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