root/cib/callbacks.c

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

DEFINITIONS

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

   1 /*
   2  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) any later version.
   8  *
   9  * This software is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU General Public
  15  * License along with this library; if not, write to the Free Software
  16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17  */
  18 
  19 #include <crm_internal.h>
  20 
  21 #include <sys/param.h>
  22 #include <stdio.h>
  23 #include <sys/types.h>
  24 #include <unistd.h>
  25 
  26 #include <stdlib.h>
  27 #include <errno.h>
  28 #include <fcntl.h>
  29 
  30 #include <crm/crm.h>
  31 #include <crm/cib.h>
  32 #include <crm/msg_xml.h>
  33 #include <crm/cluster/internal.h>
  34 
  35 #include <crm/common/xml.h>
  36 
  37 #include <cibio.h>
  38 #include <callbacks.h>
  39 #include <cibmessages.h>
  40 #include <notify.h>
  41 #include "common.h"
  42 
  43 static unsigned long cib_local_bcast_num = 0;
  44 
  45 typedef struct cib_local_notify_s {
  46     xmlNode *notify_src;
  47     char *client_id;
  48     gboolean from_peer;
  49     gboolean sync_reply;
  50 } cib_local_notify_t;
  51 
  52 int next_client_id = 0;
  53 
  54 #if SUPPORT_PLUGIN
  55 gboolean legacy_mode = TRUE;
  56 #else
  57 gboolean legacy_mode = FALSE;
  58 #endif
  59 
  60 qb_ipcs_service_t *ipcs_ro = NULL;
  61 qb_ipcs_service_t *ipcs_rw = NULL;
  62 qb_ipcs_service_t *ipcs_shm = NULL;
  63 
  64 gint cib_GCompareFunc(gconstpointer a, gconstpointer b);
  65 gboolean can_write(int flags);
  66 void send_cib_replace(const xmlNode * sync_request, const char *host);
  67 void cib_process_request(xmlNode * request, gboolean privileged, gboolean force_synchronous,
  68                          gboolean from_peer, crm_client_t * cib_client);
  69 
  70 
  71 int cib_process_command(xmlNode * request, xmlNode ** reply,
  72                         xmlNode ** cib_diff, gboolean privileged);
  73 
  74 gboolean cib_common_callback(qb_ipcs_connection_t * c, void *data, size_t size,
  75                              gboolean privileged);
  76 
  77 #if !SUPPORT_PLUGIN
  78 static gboolean cib_read_legacy_mode(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  79 {
  80     static gboolean init = TRUE;
  81     static gboolean legacy = FALSE;
  82 
  83     if(init) {
  84         init = FALSE;
  85         legacy = daemon_option_enabled("cib", "legacy");
  86         if(legacy) {
  87             crm_notice("Enabled legacy mode");
  88         }
  89     }
  90 
  91     return legacy;
  92 }
  93 #endif
  94 
  95 gboolean cib_legacy_mode(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97 #if SUPPORT_PLUGIN
  98     return TRUE;
  99 #else
 100     if(cib_read_legacy_mode()) {
 101         return TRUE;
 102     }
 103     return legacy_mode;
 104 #endif
 105 }
 106 
 107 
 108 static int32_t
 109 cib_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 110 {
 111     crm_trace("Connection %p", c);
 112     if (cib_shutdown_flag) {
 113         crm_info("Ignoring new client [%d] during shutdown", crm_ipcs_client_pid(c));
 114         return -EPERM;
 115     }
 116 
 117     if (crm_client_new(c, uid, gid) == NULL) {
 118         return -EIO;
 119     }
 120     return 0;
 121 }
 122 
 123 static void
 124 cib_ipc_created(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 125 {
 126     crm_trace("Connection %p", c);
 127 }
 128 
 129 static int32_t
 130 cib_ipc_dispatch_rw(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 131 {
 132     crm_client_t *client = crm_client_get(c);
 133 
 134     crm_trace("%p message from %s", c, client->id);
 135     return cib_common_callback(c, data, size, TRUE);
 136 }
 137 
 138 static int32_t
 139 cib_ipc_dispatch_ro(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 140 {
 141     crm_client_t *client = crm_client_get(c);
 142 
 143     crm_trace("%p message from %s", c, client->id);
 144     return cib_common_callback(c, data, size, FALSE);
 145 }
 146 
 147 /* Error code means? */
 148 static int32_t
 149 cib_ipc_closed(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 150 {
 151     crm_client_t *client = crm_client_get(c);
 152 
 153     if (client == NULL) {
 154         return 0;
 155     }
 156     crm_trace("Connection %p", c);
 157     crm_client_destroy(client);
 158     return 0;
 159 }
 160 
 161 static void
 162 cib_ipc_destroy(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 163 {
 164     crm_trace("Connection %p", c);
 165     cib_ipc_closed(c);
 166     if (cib_shutdown_flag) {
 167         cib_shutdown(0);
 168     }
 169 }
 170 
 171 struct qb_ipcs_service_handlers ipc_ro_callbacks = {
 172     .connection_accept = cib_ipc_accept,
 173     .connection_created = cib_ipc_created,
 174     .msg_process = cib_ipc_dispatch_ro,
 175     .connection_closed = cib_ipc_closed,
 176     .connection_destroyed = cib_ipc_destroy
 177 };
 178 
 179 struct qb_ipcs_service_handlers ipc_rw_callbacks = {
 180     .connection_accept = cib_ipc_accept,
 181     .connection_created = cib_ipc_created,
 182     .msg_process = cib_ipc_dispatch_rw,
 183     .connection_closed = cib_ipc_closed,
 184     .connection_destroyed = cib_ipc_destroy
 185 };
 186 
 187 void
 188 cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request,
     /* [previous][next][first][last][top][bottom][index][help] */
 189                            crm_client_t * cib_client, gboolean privileged)
 190 {
 191     const char *op = crm_element_value(op_request, F_CIB_OPERATION);
 192 
 193     if (crm_str_eq(op, CRM_OP_REGISTER, TRUE)) {
 194         if (flags & crm_ipc_client_response) {
 195             xmlNode *ack = create_xml_node(NULL, __FUNCTION__);
 196 
 197             crm_xml_add(ack, F_CIB_OPERATION, CRM_OP_REGISTER);
 198             crm_xml_add(ack, F_CIB_CLIENTID, cib_client->id);
 199             crm_ipcs_send(cib_client, id, ack, flags);
 200             cib_client->request_id = 0;
 201             free_xml(ack);
 202         }
 203         return;
 204 
 205     } else if (crm_str_eq(op, T_CIB_NOTIFY, TRUE)) {
 206         /* Update the notify filters for this client */
 207         int on_off = 0;
 208         long long bit = 0;
 209         const char *type = crm_element_value(op_request, F_CIB_NOTIFY_TYPE);
 210 
 211         crm_element_value_int(op_request, F_CIB_NOTIFY_ACTIVATE, &on_off);
 212 
 213         crm_debug("Setting %s callbacks for %s (%s): %s",
 214                   type, cib_client->name, cib_client->id, on_off ? "on" : "off");
 215 
 216         if (safe_str_eq(type, T_CIB_POST_NOTIFY)) {
 217             bit = cib_notify_post;
 218 
 219         } else if (safe_str_eq(type, T_CIB_PRE_NOTIFY)) {
 220             bit = cib_notify_pre;
 221 
 222         } else if (safe_str_eq(type, T_CIB_UPDATE_CONFIRM)) {
 223             bit = cib_notify_confirm;
 224 
 225         } else if (safe_str_eq(type, T_CIB_DIFF_NOTIFY)) {
 226             bit = cib_notify_diff;
 227 
 228         } else if (safe_str_eq(type, T_CIB_REPLACE_NOTIFY)) {
 229             bit = cib_notify_replace;
 230         }
 231 
 232         if (on_off) {
 233             set_bit(cib_client->options, bit);
 234         } else {
 235             clear_bit(cib_client->options, bit);
 236         }
 237 
 238         if (flags & crm_ipc_client_response) {
 239             /* TODO - include rc */
 240             crm_ipcs_send_ack(cib_client, id, flags, "ack", __FUNCTION__, __LINE__);
 241         }
 242         return;
 243     }
 244 
 245     cib_process_request(op_request, FALSE, privileged, FALSE, cib_client);
 246 }
 247 
 248 int32_t
 249 cib_common_callback(qb_ipcs_connection_t * c, void *data, size_t size, gboolean privileged)
     /* [previous][next][first][last][top][bottom][index][help] */
 250 {
 251     uint32_t id = 0;
 252     uint32_t flags = 0;
 253     int call_options = 0;
 254     crm_client_t *cib_client = crm_client_get(c);
 255     xmlNode *op_request = crm_ipcs_recv(cib_client, data, size, &id, &flags);
 256 
 257     if (op_request) {
 258         crm_element_value_int(op_request, F_CIB_CALLOPTS, &call_options);
 259     }
 260 
 261     if (op_request == NULL) {
 262         crm_trace("Invalid message from %p", c);
 263         crm_ipcs_send_ack(cib_client, id, flags, "nack", __FUNCTION__, __LINE__);
 264         return 0;
 265 
 266     } else if(cib_client == NULL) {
 267         crm_trace("Invalid client %p", c);
 268         return 0;
 269     }
 270 
 271     if (is_set(call_options, cib_sync_call)) {
 272         CRM_LOG_ASSERT(flags & crm_ipc_client_response);
 273         CRM_LOG_ASSERT(cib_client->request_id == 0);    /* This means the client has two synchronous events in-flight */
 274         cib_client->request_id = id;    /* Reply only to the last one */
 275     }
 276 
 277     if (cib_client->name == NULL) {
 278         const char *value = crm_element_value(op_request, F_CIB_CLIENTNAME);
 279 
 280         if (value == NULL) {
 281             cib_client->name = crm_itoa(cib_client->pid);
 282         } else {
 283             cib_client->name = strdup(value);
 284             if (crm_is_daemon_name(value)) {
 285                 set_bit(cib_client->options, cib_is_daemon);
 286             }
 287         }
 288     }
 289 
 290     /* Allow cluster daemons more leeway before being evicted */
 291     if (is_set(cib_client->options, cib_is_daemon)) {
 292         const char *qmax = cib_config_lookup("cluster-ipc-limit");
 293 
 294         if (crm_set_client_queue_max(cib_client, qmax)) {
 295             crm_trace("IPC threshold for %s[%u] is now %u",
 296                       cib_client->name, cib_client->pid, cib_client->queue_max);
 297         }
 298     }
 299 
 300     crm_xml_add(op_request, F_CIB_CLIENTID, cib_client->id);
 301     crm_xml_add(op_request, F_CIB_CLIENTNAME, cib_client->name);
 302 
 303 #if ENABLE_ACL
 304     CRM_LOG_ASSERT(cib_client->user != NULL);
 305     crm_acl_get_set_user(op_request, F_CIB_USER, cib_client->user);
 306 #endif
 307 
 308     crm_log_xml_trace(op_request, "Client[inbound]");
 309 
 310     cib_common_callback_worker(id, flags, op_request, cib_client, privileged);
 311     free_xml(op_request);
 312 
 313     return 0;
 314 }
 315 
 316 static uint64_t ping_seq = 0;
 317 static char *ping_digest = NULL;
 318 static bool ping_modified_since = FALSE;
 319 int sync_our_cib(xmlNode * request, gboolean all);
 320 
 321 static gboolean
 322 cib_digester_cb(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 323 {
 324     if (cib_is_master) {
 325         char buffer[32];
 326         xmlNode *ping = create_xml_node(NULL, "ping");
 327 
 328         ping_seq++;
 329         free(ping_digest);
 330         ping_digest = NULL;
 331         ping_modified_since = FALSE;
 332         snprintf(buffer, 32, U64T, ping_seq);
 333         crm_trace("Requesting peer digests (%s)", buffer);
 334 
 335         crm_xml_add(ping, F_TYPE, "cib");
 336         crm_xml_add(ping, F_CIB_OPERATION, CRM_OP_PING);
 337         crm_xml_add(ping, F_CIB_PING_ID, buffer);
 338 
 339         crm_xml_add(ping, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
 340         send_cluster_message(NULL, crm_msg_cib, ping, TRUE);
 341 
 342         free_xml(ping);
 343     }
 344     return FALSE;
 345 }
 346 
 347 static void
 348 process_ping_reply(xmlNode *reply) 
     /* [previous][next][first][last][top][bottom][index][help] */
 349 {
 350     uint64_t seq = 0;
 351     const char *host = crm_element_value(reply, F_ORIG);
 352 
 353     xmlNode *pong = get_message_xml(reply, F_CIB_CALLDATA);
 354     const char *seq_s = crm_element_value(pong, F_CIB_PING_ID);
 355     const char *digest = crm_element_value(pong, XML_ATTR_DIGEST);
 356 
 357     if (seq_s) {
 358         seq = crm_int_helper(seq_s, NULL);
 359     }
 360 
 361     if(digest == NULL) {
 362         crm_trace("Ignoring ping reply %s from %s with no digest", seq_s, host);
 363 
 364     } else if(seq != ping_seq) {
 365         crm_trace("Ignoring out of sequence ping reply %s from %s", seq_s, host);
 366 
 367     } else if(ping_modified_since) {
 368         crm_trace("Ignoring ping reply %s from %s: cib updated since", seq_s, host);
 369 
 370     } else {
 371         const char *version = crm_element_value(pong, XML_ATTR_CRM_VERSION);
 372 
 373         if(ping_digest == NULL) {
 374             crm_trace("Calculating new digest");
 375             ping_digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, version);
 376         }
 377 
 378         crm_trace("Processing ping reply %s from %s (%s)", seq_s, host, digest);
 379         if(safe_str_eq(ping_digest, digest) == FALSE) {
 380             xmlNode *remote_cib = get_message_xml(pong, F_CIB_CALLDATA);
 381 
 382             crm_notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s %p",
 383                        crm_element_value(the_cib, XML_ATTR_GENERATION_ADMIN),
 384                        crm_element_value(the_cib, XML_ATTR_GENERATION),
 385                        crm_element_value(the_cib, XML_ATTR_NUMUPDATES),
 386                        ping_digest, host,
 387                        remote_cib?crm_element_value(remote_cib, XML_ATTR_GENERATION_ADMIN):"_",
 388                        remote_cib?crm_element_value(remote_cib, XML_ATTR_GENERATION):"_",
 389                        remote_cib?crm_element_value(remote_cib, XML_ATTR_NUMUPDATES):"_",
 390                        digest, remote_cib);
 391 
 392             if(remote_cib && remote_cib->children) {
 393                 /* Additional debug */
 394                 xml_calculate_changes(the_cib, remote_cib);
 395                 xml_log_changes(LOG_INFO, __FUNCTION__, remote_cib);
 396                 crm_trace("End of differences");
 397             }
 398 
 399             free_xml(remote_cib);
 400             sync_our_cib(reply, FALSE);
 401         }
 402     }
 403 }
 404 
 405 static void
 406 do_local_notify(xmlNode * notify_src, const char *client_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 407                 gboolean sync_reply, gboolean from_peer)
 408 {
 409     /* send callback to originating child */
 410     crm_client_t *client_obj = NULL;
 411     int local_rc = pcmk_ok;
 412     int call_id = 0;
 413 
 414     crm_element_value_int(notify_src, F_CIB_CALLID, &call_id);
 415 
 416     if (client_id != NULL) {
 417         client_obj = crm_client_get_by_id(client_id);
 418     }
 419 
 420     if (client_obj == NULL) {
 421         local_rc = -ECONNRESET;
 422         crm_trace("No client to sent response %d to, F_CIB_CLIENTID not set.", call_id);
 423 
 424     } else {
 425         int rid = 0;
 426 
 427         if (sync_reply) {
 428             if (client_obj->ipcs) {
 429                 CRM_LOG_ASSERT(client_obj->request_id);
 430 
 431                 rid = client_obj->request_id;
 432                 client_obj->request_id = 0;
 433 
 434                 crm_trace("Sending response %d to %s %s",
 435                           rid, client_obj->name,
 436                           from_peer ? "(originator of delegated request)" : "");
 437             } else {
 438                 crm_trace("Sending response [call %d] to %s %s",
 439                           call_id, client_obj->name, from_peer ? "(originator of delegated request)" : "");
 440             }
 441 
 442         } else {
 443             crm_trace("Sending event %d to %s %s",
 444                       call_id, client_obj->name, from_peer ? "(originator of delegated request)" : "");
 445         }
 446 
 447         switch (client_obj->kind) {
 448             case CRM_CLIENT_IPC:
 449                 if (crm_ipcs_send(client_obj, rid, notify_src, sync_reply?crm_ipc_flags_none:crm_ipc_server_event) < 0) {
 450                     local_rc = -ENOMSG;
 451                 }
 452                 break;
 453 #ifdef HAVE_GNUTLS_GNUTLS_H
 454             case CRM_CLIENT_TLS:
 455 #endif
 456             case CRM_CLIENT_TCP:
 457                 crm_remote_send(client_obj->remote, notify_src);
 458                 break;
 459             default:
 460                 crm_err("Unknown transport %d for %s", client_obj->kind, client_obj->name);
 461         }
 462     }
 463 
 464     if (local_rc != pcmk_ok && client_obj != NULL) {
 465         crm_warn("%sSync reply to %s failed: %s",
 466                  sync_reply ? "" : "A-",
 467                  client_obj ? client_obj->name : "<unknown>", pcmk_strerror(local_rc));
 468     }
 469 }
 470 
 471 static void
 472 local_notify_destroy_callback(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 473 {
 474     cib_local_notify_t *notify = data;
 475 
 476     free_xml(notify->notify_src);
 477     free(notify->client_id);
 478     free(notify);
 479 }
 480 
 481 static void
 482 check_local_notify(int bcast_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 483 {
 484     cib_local_notify_t *notify = NULL;
 485 
 486     if (!local_notify_queue) {
 487         return;
 488     }
 489 
 490     notify = g_hash_table_lookup(local_notify_queue, GINT_TO_POINTER(bcast_id));
 491 
 492     if (notify) {
 493         do_local_notify(notify->notify_src, notify->client_id, notify->sync_reply,
 494                         notify->from_peer);
 495         g_hash_table_remove(local_notify_queue, GINT_TO_POINTER(bcast_id));
 496     }
 497 }
 498 
 499 static void
 500 queue_local_notify(xmlNode * notify_src, const char *client_id, gboolean sync_reply,
     /* [previous][next][first][last][top][bottom][index][help] */
 501                    gboolean from_peer)
 502 {
 503     cib_local_notify_t *notify = calloc(1, sizeof(cib_local_notify_t));
 504 
 505     notify->notify_src = notify_src;
 506     notify->client_id = strdup(client_id);
 507     notify->sync_reply = sync_reply;
 508     notify->from_peer = from_peer;
 509 
 510     if (!local_notify_queue) {
 511         local_notify_queue = g_hash_table_new_full(g_direct_hash,
 512                                                    g_direct_equal, NULL,
 513                                                    local_notify_destroy_callback);
 514     }
 515 
 516     g_hash_table_insert(local_notify_queue, GINT_TO_POINTER(cib_local_bcast_num), notify);
 517 }
 518 
 519 static void
 520 parse_local_options_v1(crm_client_t * cib_client, int call_type, int call_options, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 521                     const char *op, gboolean * local_notify, gboolean * needs_reply,
 522                     gboolean * process, gboolean * needs_forward)
 523 {
 524     if (cib_op_modifies(call_type)
 525         && !(call_options & cib_inhibit_bcast)) {
 526         /* we need to send an update anyway */
 527         *needs_reply = TRUE;
 528     } else {
 529         *needs_reply = FALSE;
 530     }
 531 
 532     if (host == NULL && (call_options & cib_scope_local)) {
 533         crm_trace("Processing locally scoped %s op from %s", op, cib_client->name);
 534         *local_notify = TRUE;
 535 
 536     } else if (host == NULL && cib_is_master) {
 537         crm_trace("Processing master %s op locally from %s", op, cib_client->name);
 538         *local_notify = TRUE;
 539 
 540     } else if (safe_str_eq(host, cib_our_uname)) {
 541         crm_trace("Processing locally addressed %s op from %s", op, cib_client->name);
 542         *local_notify = TRUE;
 543 
 544     } else if (stand_alone) {
 545         *needs_forward = FALSE;
 546         *local_notify = TRUE;
 547         *process = TRUE;
 548 
 549     } else {
 550         crm_trace("%s op from %s needs to be forwarded to %s",
 551                   op, cib_client->name, host ? host : "the master instance");
 552         *needs_forward = TRUE;
 553         *process = FALSE;
 554     }
 555 }
 556 
 557 static void
 558 parse_local_options_v2(crm_client_t * cib_client, int call_type, int call_options, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 559                     const char *op, gboolean * local_notify, gboolean * needs_reply,
 560                     gboolean * process, gboolean * needs_forward)
 561 {
 562     if (cib_op_modifies(call_type)) {
 563         if(safe_str_eq(op, CIB_OP_MASTER) || safe_str_eq(op, CIB_OP_SLAVE)) {
 564             /* Always handle these locally */
 565             *process = TRUE;
 566             *needs_reply = FALSE;
 567             *local_notify = TRUE;
 568             *needs_forward = FALSE;
 569             return;
 570 
 571         } else {
 572             /* Redirect all other updates via CPG */
 573             *needs_reply = TRUE;
 574             *needs_forward = TRUE;
 575             *process = FALSE;
 576             crm_trace("%s op from %s needs to be forwarded to %s",
 577                       op, cib_client->name, host ? host : "the master instance");
 578             return;
 579         }
 580     }
 581 
 582 
 583     *process = TRUE;
 584     *needs_reply = FALSE;
 585     *local_notify = TRUE;
 586     *needs_forward = FALSE;
 587 
 588     if (stand_alone) {
 589         crm_trace("Processing %s op from %s (stand-alone)", op, cib_client->name);
 590 
 591     } else if (host == NULL) {
 592         crm_trace("Processing unaddressed %s op from %s", op, cib_client->name);
 593 
 594     } else if (safe_str_eq(host, cib_our_uname)) {
 595         crm_trace("Processing locally addressed %s op from %s", op, cib_client->name);
 596 
 597     } else {
 598         crm_trace("%s op from %s needs to be forwarded to %s", op, cib_client->name, host);
 599         *needs_forward = TRUE;
 600         *process = FALSE;
 601     }
 602 }
 603 
 604 static void
 605 parse_local_options(crm_client_t * cib_client, int call_type, int call_options, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 606                     const char *op, gboolean * local_notify, gboolean * needs_reply,
 607                     gboolean * process, gboolean * needs_forward)
 608 {
 609     if(cib_legacy_mode()) {
 610         parse_local_options_v1(cib_client, call_type, call_options, host,
 611                                op, local_notify, needs_reply, process, needs_forward);
 612     } else {
 613         parse_local_options_v2(cib_client, call_type, call_options, host,
 614                                op, local_notify, needs_reply, process, needs_forward);
 615     }
 616 }
 617 
 618 static gboolean
 619 parse_peer_options_v1(int call_type, xmlNode * request,
     /* [previous][next][first][last][top][bottom][index][help] */
 620                    gboolean * local_notify, gboolean * needs_reply, gboolean * process,
 621                    gboolean * needs_forward)
 622 {
 623     const char *op = NULL;
 624     const char *host = NULL;
 625     const char *delegated = NULL;
 626     const char *originator = crm_element_value(request, F_ORIG);
 627     const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
 628     const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE);
 629 
 630     gboolean is_reply = safe_str_eq(reply_to, cib_our_uname);
 631 
 632     if (crm_is_true(update)) {
 633         *needs_reply = FALSE;
 634         if (is_reply) {
 635             *local_notify = TRUE;
 636             crm_trace("Processing global/peer update from %s"
 637                       " that originated from us", originator);
 638         } else {
 639             crm_trace("Processing global/peer update from %s", originator);
 640         }
 641         return TRUE;
 642     }
 643 
 644     crm_trace("Processing %s request sent by %s", op, originator);
 645     op = crm_element_value(request, F_CIB_OPERATION);
 646     if (safe_str_eq(op, "cib_shutdown_req")) {
 647         /* Always process these */
 648         *local_notify = FALSE;
 649         if (reply_to == NULL || is_reply) {
 650             *process = TRUE;
 651         }
 652         if (is_reply) {
 653             *needs_reply = FALSE;
 654         }
 655         return *process;
 656     }
 657 
 658     if (is_reply && safe_str_eq(op, CRM_OP_PING)) {
 659         process_ping_reply(request);
 660         return FALSE;
 661     }
 662 
 663     if (is_reply) {
 664         crm_trace("Forward reply sent from %s to local clients", originator);
 665         *process = FALSE;
 666         *needs_reply = FALSE;
 667         *local_notify = TRUE;
 668         return TRUE;
 669     }
 670 
 671     host = crm_element_value(request, F_CIB_HOST);
 672     if (host != NULL && safe_str_eq(host, cib_our_uname)) {
 673         crm_trace("Processing %s request sent to us from %s", op, originator);
 674         return TRUE;
 675 
 676     } else if(is_reply == FALSE && safe_str_eq(op, CRM_OP_PING)) {
 677         crm_trace("Processing %s request sent to %s by %s", op, host?host:"everyone", originator);
 678         *needs_reply = TRUE;
 679         return TRUE;
 680 
 681     } else if (host == NULL && cib_is_master == TRUE) {
 682         crm_trace("Processing %s request sent to master instance from %s", op, originator);
 683         return TRUE;
 684     }
 685 
 686     delegated = crm_element_value(request, F_CIB_DELEGATED);
 687     if (delegated != NULL) {
 688         crm_trace("Ignoring msg for master instance");
 689 
 690     } else if (host != NULL) {
 691         /* this is for a specific instance and we're not it */
 692         crm_trace("Ignoring msg for instance on %s", crm_str(host));
 693 
 694     } else if (reply_to == NULL && cib_is_master == FALSE) {
 695         /* this is for the master instance and we're not it */
 696         crm_trace("Ignoring reply to %s", crm_str(reply_to));
 697 
 698     } else if (safe_str_eq(op, "cib_shutdown_req")) {
 699         if (reply_to != NULL) {
 700             crm_debug("Processing %s from %s", op, host);
 701             *needs_reply = FALSE;
 702 
 703         } else {
 704             crm_debug("Processing %s reply from %s", op, host);
 705         }
 706         return TRUE;
 707 
 708     } else {
 709         crm_err("Nothing for us to do?");
 710         crm_log_xml_err(request, "Peer[inbound]");
 711     }
 712 
 713     return FALSE;
 714 }
 715 
 716 static gboolean
 717 parse_peer_options_v2(int call_type, xmlNode * request,
     /* [previous][next][first][last][top][bottom][index][help] */
 718                    gboolean * local_notify, gboolean * needs_reply, gboolean * process,
 719                    gboolean * needs_forward)
 720 {
 721     const char *host = NULL;
 722     const char *delegated = crm_element_value(request, F_CIB_DELEGATED);
 723     const char *op = crm_element_value(request, F_CIB_OPERATION);
 724     const char *originator = crm_element_value(request, F_ORIG);
 725     const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
 726     const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE);
 727 
 728     gboolean is_reply = safe_str_eq(reply_to, cib_our_uname);
 729 
 730     if(safe_str_eq(op, CIB_OP_REPLACE)) {
 731         /* sync_our_cib() sets F_CIB_ISREPLY */
 732         if (reply_to) {
 733             delegated = reply_to;
 734         }
 735         goto skip_is_reply;
 736 
 737     } else if(safe_str_eq(op, CIB_OP_SYNC)) {
 738 
 739     } else if (is_reply && safe_str_eq(op, CRM_OP_PING)) {
 740         process_ping_reply(request);
 741         return FALSE;
 742 
 743     } else if (safe_str_eq(op, CIB_OP_UPGRADE)) {
 744         /* Only the DC (node with the oldest software) should process
 745          * this operation if F_CIB_SCHEMA_MAX is unset
 746          *
 747          * If the DC is happy it will then send out another
 748          * CIB_OP_UPGRADE which will tell all nodes to do the actual
 749          * upgrade.
 750          *
 751          * Except this time F_CIB_SCHEMA_MAX will be set which puts a
 752          * limit on how far newer nodes will go
 753          */
 754         const char *max = crm_element_value(request, F_CIB_SCHEMA_MAX);
 755 
 756         crm_trace("Parsing %s operation%s for %s with max=%s",
 757                   op, is_reply?" reply":"", cib_is_master?"master":"slave", max);
 758         if(max == NULL && cib_is_master) {
 759             /* We are the DC, check if this upgrade is allowed */
 760             goto skip_is_reply;
 761 
 762         } else if(max) {
 763             /* Ok, go ahead and upgrade to 'max' */
 764             goto skip_is_reply;
 765 
 766         } else {
 767             return FALSE; /* Ignore */
 768         }
 769 
 770     } else if (crm_is_true(update)) {
 771         crm_info("Detected legacy %s global update from %s", op, originator);
 772         send_sync_request(NULL);
 773         legacy_mode = TRUE;
 774         return FALSE;
 775 
 776     } else if (is_reply && cib_op_modifies(call_type)) {
 777         crm_trace("Ignoring legacy %s reply sent from %s to local clients", op, originator);
 778         return FALSE;
 779 
 780     } else if (safe_str_eq(op, "cib_shutdown_req")) {
 781         /* Legacy handling */
 782         crm_debug("Legacy handling of %s message from %s", op, originator);
 783         *local_notify = FALSE;
 784         if (reply_to == NULL) {
 785             *process = TRUE;
 786         }
 787         return *process;
 788     }
 789 
 790     if(is_reply) {
 791         crm_trace("Handling %s reply sent from %s to local clients", op, originator);
 792         *process = FALSE;
 793         *needs_reply = FALSE;
 794         *local_notify = TRUE;
 795         return TRUE;
 796     }
 797 
 798   skip_is_reply:
 799     *process = TRUE;
 800     *needs_reply = FALSE;
 801 
 802     if(safe_str_eq(delegated, cib_our_uname)) {
 803         *local_notify = TRUE;
 804     } else {
 805         *local_notify = FALSE;
 806     }
 807 
 808     host = crm_element_value(request, F_CIB_HOST);
 809     if (host != NULL && safe_str_eq(host, cib_our_uname)) {
 810         crm_trace("Processing %s request sent to us from %s", op, originator);
 811         *needs_reply = TRUE;
 812         return TRUE;
 813 
 814     } else if (host != NULL) {
 815         /* this is for a specific instance and we're not it */
 816         crm_trace("Ignoring %s operation for instance on %s", op, crm_str(host));
 817         return FALSE;
 818 
 819     } else if(is_reply == FALSE && safe_str_eq(op, CRM_OP_PING)) {
 820         *needs_reply = TRUE;
 821     }
 822 
 823     crm_trace("Processing %s request sent to everyone by %s/%s on %s %s", op,
 824               crm_element_value(request, F_CIB_CLIENTNAME),
 825               crm_element_value(request, F_CIB_CALLID),
 826               originator, (*local_notify)?"(notify)":"");
 827     return TRUE;
 828 }
 829 
 830 static gboolean
 831 parse_peer_options(int call_type, xmlNode * request,
     /* [previous][next][first][last][top][bottom][index][help] */
 832                    gboolean * local_notify, gboolean * needs_reply, gboolean * process,
 833                    gboolean * needs_forward)
 834 {
 835     /* TODO: What happens when an update comes in after node A
 836      * requests the CIB from node B, but before it gets the reply (and
 837      * sends out the replace operation)
 838      */
 839     if(cib_legacy_mode()) {
 840         return parse_peer_options_v1(
 841             call_type, request, local_notify, needs_reply, process, needs_forward);
 842     } else {
 843         return parse_peer_options_v2(
 844             call_type, request, local_notify, needs_reply, process, needs_forward);
 845     }
 846 }
 847 
 848 static void
 849 forward_request(xmlNode * request, crm_client_t * cib_client, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 850 {
 851     const char *op = crm_element_value(request, F_CIB_OPERATION);
 852     const char *host = crm_element_value(request, F_CIB_HOST);
 853 
 854     crm_xml_add(request, F_CIB_DELEGATED, cib_our_uname);
 855 
 856     if (host != NULL) {
 857         crm_trace("Forwarding %s op to %s", op, host);
 858         send_cluster_message(crm_get_peer(0, host), crm_msg_cib, request, FALSE);
 859 
 860     } else {
 861         crm_trace("Forwarding %s op to master instance", op);
 862         send_cluster_message(NULL, crm_msg_cib, request, FALSE);
 863     }
 864 
 865     /* Return the request to its original state */
 866     xml_remove_prop(request, F_CIB_DELEGATED);
 867 
 868     if (call_options & cib_discard_reply) {
 869         crm_trace("Client not interested in reply");
 870     }
 871 }
 872 
 873 static gboolean
 874 send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gboolean broadcast)
     /* [previous][next][first][last][top][bottom][index][help] */
 875 {
 876     CRM_ASSERT(msg != NULL);
 877 
 878     if (broadcast) {
 879         /* this (successful) call modified the CIB _and_ the
 880          * change needs to be broadcast...
 881          *   send via HA to other nodes
 882          */
 883         int diff_add_updates = 0;
 884         int diff_add_epoch = 0;
 885         int diff_add_admin_epoch = 0;
 886 
 887         int diff_del_updates = 0;
 888         int diff_del_epoch = 0;
 889         int diff_del_admin_epoch = 0;
 890 
 891         const char *digest = NULL;
 892         int format = 1;
 893 
 894         CRM_LOG_ASSERT(result_diff != NULL);
 895         digest = crm_element_value(result_diff, XML_ATTR_DIGEST);
 896         crm_element_value_int(result_diff, "format", &format);
 897 
 898         cib_diff_version_details(result_diff,
 899                                  &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
 900                                  &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
 901 
 902         crm_trace("Sending update diff %d.%d.%d -> %d.%d.%d %s",
 903                   diff_del_admin_epoch, diff_del_epoch, diff_del_updates,
 904                   diff_add_admin_epoch, diff_add_epoch, diff_add_updates, digest);
 905 
 906         crm_xml_add(msg, F_CIB_ISREPLY, originator);
 907         crm_xml_add(msg, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE);
 908         crm_xml_add(msg, F_CIB_OPERATION, CIB_OP_APPLY_DIFF);
 909         crm_xml_add(msg, F_CIB_USER, CRM_DAEMON_USER);
 910 
 911         if (format == 1) {
 912             CRM_ASSERT(digest != NULL);
 913         }
 914 
 915         add_message_xml(msg, F_CIB_UPDATE_DIFF, result_diff);
 916         crm_log_xml_explicit(msg, "copy");
 917         return send_cluster_message(NULL, crm_msg_cib, msg, TRUE);
 918 
 919     } else if (originator != NULL) {
 920         /* send reply via HA to originating node */
 921         crm_trace("Sending request result to %s only", originator);
 922         crm_xml_add(msg, F_CIB_ISREPLY, originator);
 923         return send_cluster_message(crm_get_peer(0, originator), crm_msg_cib, msg, FALSE);
 924     }
 925 
 926     return FALSE;
 927 }
 928 
 929 void
 930 cib_process_request(xmlNode * request, gboolean force_synchronous, gboolean privileged,
     /* [previous][next][first][last][top][bottom][index][help] */
 931                     gboolean unused, crm_client_t * cib_client)
 932 {
 933     int call_type = 0;
 934     int call_options = 0;
 935 
 936     gboolean process = TRUE;
 937     gboolean is_update = TRUE;
 938     gboolean from_peer = TRUE;
 939     gboolean needs_reply = TRUE;
 940     gboolean local_notify = FALSE;
 941     gboolean needs_forward = FALSE;
 942     gboolean global_update = crm_is_true(crm_element_value(request, F_CIB_GLOBAL_UPDATE));
 943 
 944     xmlNode *op_reply = NULL;
 945     xmlNode *result_diff = NULL;
 946 
 947     int rc = pcmk_ok;
 948     const char *op = crm_element_value(request, F_CIB_OPERATION);
 949     const char *originator = crm_element_value(request, F_ORIG);
 950     const char *host = crm_element_value(request, F_CIB_HOST);
 951     const char *target = NULL;
 952     const char *call_id = crm_element_value(request, F_CIB_CALLID);
 953     const char *client_id = crm_element_value(request, F_CIB_CLIENTID);
 954     const char *client_name = crm_element_value(request, F_CIB_CLIENTNAME);
 955     const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
 956 
 957     if (cib_client) {
 958         from_peer = FALSE;
 959     }
 960 
 961     cib_num_ops++;
 962     if (cib_num_ops == 0) {
 963         cib_num_fail = 0;
 964         cib_num_local = 0;
 965         cib_num_updates = 0;
 966         crm_info("Stats wrapped around");
 967     }
 968 
 969     crm_element_value_int(request, F_CIB_CALLOPTS, &call_options);
 970     if (force_synchronous) {
 971         call_options |= cib_sync_call;
 972     }
 973 
 974     if (host != NULL && strlen(host) == 0) {
 975         host = NULL;
 976     }
 977 
 978     if (host) {
 979         target = host;
 980 
 981     } else if (call_options & cib_scope_local) {
 982         target = "local host";
 983 
 984     } else {
 985         target = "master";
 986     }
 987 
 988     if (from_peer) {
 989         crm_trace("Processing peer %s operation from %s/%s on %s intended for %s (reply=%s)",
 990                   op, client_name, call_id, originator, target, reply_to);
 991     } else {
 992         crm_xml_add(request, F_ORIG, cib_our_uname);
 993         crm_trace("Processing local %s operation from %s/%s intended for %s", op, client_name, call_id, target);
 994     }
 995 
 996     rc = cib_get_operation_id(op, &call_type);
 997     if (rc != pcmk_ok) {
 998         /* TODO: construct error reply? */
 999         crm_err("Pre-processing of command failed: %s", pcmk_strerror(rc));
1000         return;
1001     }
1002 
1003     if (from_peer == FALSE) {
1004         parse_local_options(cib_client, call_type, call_options, host, op,
1005                             &local_notify, &needs_reply, &process, &needs_forward);
1006 
1007     } else if (parse_peer_options(call_type, request, &local_notify,
1008                                   &needs_reply, &process, &needs_forward) == FALSE) {
1009         return;
1010     }
1011 
1012     is_update = cib_op_modifies(call_type);
1013     if (is_update) {
1014         cib_num_updates++;
1015     }
1016 
1017     if (call_options & cib_discard_reply) {
1018         needs_reply = is_update;
1019         local_notify = FALSE;
1020     }
1021 
1022     if (needs_forward) {
1023         const char *host = crm_element_value(request, F_CIB_HOST);
1024         const char *section = crm_element_value(request, F_CIB_SECTION);
1025         int log_level = LOG_INFO;
1026 
1027         if (safe_str_eq(op, CRM_OP_NOOP)) {
1028             log_level = LOG_DEBUG;
1029         }
1030 
1031         do_crm_log(log_level,
1032                    "Forwarding %s operation for section %s to %s (origin=%s/%s/%s)",
1033                    op,
1034                    section ? section : "'all'",
1035                    host ? host : cib_legacy_mode() ? "master" : "all",
1036                    originator ? originator : "local",
1037                    client_name, call_id);
1038 
1039         forward_request(request, cib_client, call_options);
1040         return;
1041     }
1042 
1043     if (cib_status != pcmk_ok) {
1044         const char *call = crm_element_value(request, F_CIB_CALLID);
1045 
1046         rc = cib_status;
1047         crm_err("Operation ignored, cluster configuration is invalid."
1048                 " Please repair and restart: %s", pcmk_strerror(cib_status));
1049 
1050         op_reply = create_xml_node(NULL, "cib-reply");
1051         crm_xml_add(op_reply, F_TYPE, T_CIB);
1052         crm_xml_add(op_reply, F_CIB_OPERATION, op);
1053         crm_xml_add(op_reply, F_CIB_CALLID, call);
1054         crm_xml_add(op_reply, F_CIB_CLIENTID, client_id);
1055         crm_xml_add_int(op_reply, F_CIB_CALLOPTS, call_options);
1056         crm_xml_add_int(op_reply, F_CIB_RC, rc);
1057 
1058         crm_trace("Attaching reply output");
1059         add_message_xml(op_reply, F_CIB_CALLDATA, the_cib);
1060 
1061         crm_log_xml_explicit(op_reply, "cib:reply");
1062 
1063     } else if (process) {
1064         time_t finished = 0;
1065 
1066         int now = time(NULL);
1067         int level = LOG_INFO;
1068         const char *section = crm_element_value(request, F_CIB_SECTION);
1069 
1070         cib_num_local++;
1071         rc = cib_process_command(request, &op_reply, &result_diff, privileged);
1072 
1073         if (is_update == FALSE) {
1074             level = LOG_TRACE;
1075 
1076         } else if (global_update) {
1077             switch (rc) {
1078                 case pcmk_ok:
1079                     level = LOG_INFO;
1080                     break;
1081                 case -pcmk_err_old_data:
1082                 case -pcmk_err_diff_resync:
1083                 case -pcmk_err_diff_failed:
1084                     level = LOG_TRACE;
1085                     break;
1086                 default:
1087                     level = LOG_ERR;
1088             }
1089 
1090         } else if (rc != pcmk_ok && is_update) {
1091             cib_num_fail++;
1092             level = LOG_WARNING;
1093         }
1094 
1095         do_crm_log(level,
1096                    "Completed %s operation for section %s: %s (rc=%d, origin=%s/%s/%s, version=%s.%s.%s)",
1097                    op, section ? section : "'all'", pcmk_strerror(rc), rc,
1098                    originator ? originator : "local", client_name, call_id,
1099                    the_cib ? crm_element_value(the_cib, XML_ATTR_GENERATION_ADMIN) : "0",
1100                    the_cib ? crm_element_value(the_cib, XML_ATTR_GENERATION) : "0",
1101                    the_cib ? crm_element_value(the_cib, XML_ATTR_NUMUPDATES) : "0");
1102 
1103         finished = time(NULL);
1104         if (finished - now > 3) {
1105             crm_trace("%s operation took %lds to complete", op, (long)(finished - now));
1106             crm_write_blackbox(0, NULL);
1107         }
1108 
1109         if (op_reply == NULL && (needs_reply || local_notify)) {
1110             crm_err("Unexpected NULL reply to message");
1111             crm_log_xml_err(request, "null reply");
1112             needs_reply = FALSE;
1113             local_notify = FALSE;
1114         }
1115     }
1116 
1117     /* from now on we are the server */
1118     if(is_update && cib_legacy_mode() == FALSE) {
1119         crm_trace("Completed pre-sync update from %s/%s/%s%s",
1120                   originator ? originator : "local", client_name, call_id,
1121                   local_notify?" with local notification":"");
1122 
1123     } else if (needs_reply == FALSE || stand_alone) {
1124         /* nothing more to do...
1125          * this was a non-originating slave update
1126          */
1127         crm_trace("Completed slave update");
1128 
1129     } else if (cib_legacy_mode() &&
1130                rc == pcmk_ok && result_diff != NULL && !(call_options & cib_inhibit_bcast)) {
1131         gboolean broadcast = FALSE;
1132 
1133         cib_local_bcast_num++;
1134         crm_xml_add_int(request, F_CIB_LOCAL_NOTIFY_ID, cib_local_bcast_num);
1135         broadcast = send_peer_reply(request, result_diff, originator, TRUE);
1136 
1137         if (broadcast && client_id && local_notify && op_reply) {
1138 
1139             /* If we have been asked to sync the reply,
1140              * and a bcast msg has gone out, we queue the local notify
1141              * until we know the bcast message has been received */
1142             local_notify = FALSE;
1143             crm_trace("Queuing local %ssync notification for %s",
1144                       (call_options & cib_sync_call) ? "" : "a-", client_id);
1145 
1146             queue_local_notify(op_reply, client_id, (call_options & cib_sync_call), from_peer);
1147             op_reply = NULL;    /* the reply is queued, so don't free here */
1148         }
1149 
1150     } else if (call_options & cib_discard_reply) {
1151         crm_trace("Caller isn't interested in reply");
1152 
1153     } else if (from_peer) {
1154         if (is_update == FALSE || result_diff == NULL) {
1155             crm_trace("Request not broadcast: R/O call");
1156 
1157         } else if (call_options & cib_inhibit_bcast) {
1158             crm_trace("Request not broadcast: inhibited");
1159 
1160         } else if (rc != pcmk_ok) {
1161             crm_trace("Request not broadcast: call failed: %s", pcmk_strerror(rc));
1162 
1163         } else {
1164             crm_trace("Directing reply to %s", originator);
1165         }
1166 
1167         send_peer_reply(op_reply, result_diff, originator, FALSE);
1168     }
1169 
1170     if (local_notify && client_id) {
1171         crm_trace("Performing local %ssync notification for %s",
1172                   (call_options & cib_sync_call) ? "" : "a-", client_id);
1173         if (process == FALSE) {
1174             do_local_notify(request, client_id, call_options & cib_sync_call, from_peer);
1175         } else {
1176             do_local_notify(op_reply, client_id, call_options & cib_sync_call, from_peer);
1177         }
1178     }
1179 
1180     free_xml(op_reply);
1181     free_xml(result_diff);
1182 
1183     return;
1184 }
1185 
1186 int
1187 cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gboolean privileged)
     /* [previous][next][first][last][top][bottom][index][help] */
1188 {
1189     xmlNode *input = NULL;
1190     xmlNode *output = NULL;
1191     xmlNode *result_cib = NULL;
1192     xmlNode *current_cib = NULL;
1193 
1194     int call_type = 0;
1195     int call_options = 0;
1196 
1197     const char *op = NULL;
1198     const char *section = NULL;
1199     const char *call_id = crm_element_value(request, F_CIB_CALLID);
1200 
1201     int rc = pcmk_ok;
1202     int rc2 = pcmk_ok;
1203 
1204     gboolean send_r_notify = FALSE;
1205     gboolean global_update = FALSE;
1206     gboolean config_changed = FALSE;
1207     gboolean manage_counters = TRUE;
1208 
1209     static mainloop_timer_t *digest_timer = NULL;
1210 
1211     CRM_ASSERT(cib_status == pcmk_ok);
1212 
1213     if(digest_timer == NULL) {
1214         digest_timer = mainloop_timer_add("digester", 5000, FALSE, cib_digester_cb, NULL);
1215     }
1216 
1217     *reply = NULL;
1218     *cib_diff = NULL;
1219     current_cib = the_cib;
1220 
1221     /* Start processing the request... */
1222     op = crm_element_value(request, F_CIB_OPERATION);
1223     crm_element_value_int(request, F_CIB_CALLOPTS, &call_options);
1224     rc = cib_get_operation_id(op, &call_type);
1225 
1226     if (rc == pcmk_ok && privileged == FALSE) {
1227         rc = cib_op_can_run(call_type, call_options, privileged, global_update);
1228     }
1229 
1230     rc2 = cib_op_prepare(call_type, request, &input, &section);
1231     if (rc == pcmk_ok) {
1232         rc = rc2;
1233     }
1234 
1235     if (rc != pcmk_ok) {
1236         crm_trace("Call setup failed: %s", pcmk_strerror(rc));
1237         goto done;
1238 
1239     } else if (cib_op_modifies(call_type) == FALSE) {
1240         rc = cib_perform_op(op, call_options, cib_op_func(call_type), TRUE,
1241                             section, request, input, FALSE, &config_changed,
1242                             current_cib, &result_cib, NULL, &output);
1243 
1244         CRM_CHECK(result_cib == NULL, free_xml(result_cib));
1245         goto done;
1246     }
1247 
1248     /* Handle a valid write action */
1249     global_update = crm_is_true(crm_element_value(request, F_CIB_GLOBAL_UPDATE));
1250     if (global_update) {
1251         /* legacy code */
1252         manage_counters = FALSE;
1253         call_options |= cib_force_diff;
1254         crm_trace("Global update detected");
1255 
1256         CRM_CHECK(call_type == 3 || call_type == 4, crm_err("Call type: %d", call_type);
1257                   crm_log_xml_err(request, "bad op"));
1258     }
1259 
1260     if (rc == pcmk_ok) {
1261         ping_modified_since = TRUE;
1262         if (call_options & cib_inhibit_bcast) {
1263             /* skip */
1264             crm_trace("Skipping update: inhibit broadcast");
1265             manage_counters = FALSE;
1266         }
1267 
1268         if (is_not_set(call_options, cib_dryrun) && safe_str_eq(section, XML_CIB_TAG_STATUS)) {
1269             /* Copying large CIBs accounts for a huge percentage of our CIB usage */
1270             call_options |= cib_zero_copy;
1271         } else {
1272             clear_bit(call_options, cib_zero_copy);
1273         }
1274 
1275         /* result_cib must not be modified after cib_perform_op() returns */
1276         rc = cib_perform_op(op, call_options, cib_op_func(call_type), FALSE,
1277                             section, request, input, manage_counters, &config_changed,
1278                             current_cib, &result_cib, cib_diff, &output);
1279 
1280         if (manage_counters == FALSE) {
1281             int format = 1;
1282             /* Legacy code
1283              * If the diff is NULL at this point, it's because nothing changed
1284              */
1285             if (*cib_diff) {
1286                 crm_element_value_int(*cib_diff, "format", &format);
1287             }
1288 
1289             if (format == 1) {
1290                 config_changed = cib_config_changed(NULL, NULL, cib_diff);
1291             }
1292         }
1293 
1294         /* Always write to disk for replace ops,
1295          * this also negates the need to detect ordering changes
1296          */
1297         if (crm_str_eq(CIB_OP_REPLACE, op, TRUE)) {
1298             config_changed = TRUE;
1299         }
1300     }
1301 
1302     if (rc == pcmk_ok && is_not_set(call_options, cib_dryrun)) {
1303         crm_trace("Activating %s->%s%s%s",
1304                   crm_element_value(current_cib, XML_ATTR_NUMUPDATES),
1305                   crm_element_value(result_cib, XML_ATTR_NUMUPDATES),
1306                   (is_set(call_options, cib_zero_copy)? " zero-copy" : ""),
1307                   (config_changed? " changed" : ""));
1308         if(is_not_set(call_options, cib_zero_copy)) {
1309             rc = activateCibXml(result_cib, config_changed, op);
1310             crm_trace("Activated %s (%d)",
1311                       crm_element_value(current_cib, XML_ATTR_NUMUPDATES), rc);
1312         }
1313 
1314         if (rc == pcmk_ok && cib_internal_config_changed(*cib_diff)) {
1315             cib_read_config(config_hash, result_cib);
1316         }
1317 
1318         if (crm_str_eq(CIB_OP_REPLACE, op, TRUE)) {
1319             if (section == NULL) {
1320                 send_r_notify = TRUE;
1321 
1322             } else if (safe_str_eq(section, XML_TAG_CIB)) {
1323                 send_r_notify = TRUE;
1324 
1325             } else if (safe_str_eq(section, XML_CIB_TAG_NODES)) {
1326                 send_r_notify = TRUE;
1327 
1328             } else if (safe_str_eq(section, XML_CIB_TAG_STATUS)) {
1329                 send_r_notify = TRUE;
1330             }
1331 
1332         } else if (crm_str_eq(CIB_OP_ERASE, op, TRUE)) {
1333             send_r_notify = TRUE;
1334         }
1335 
1336         mainloop_timer_stop(digest_timer);
1337         mainloop_timer_start(digest_timer);
1338 
1339     } else if (rc == -pcmk_err_schema_validation) {
1340         CRM_ASSERT(is_not_set(call_options, cib_zero_copy));
1341 
1342         if (output != NULL) {
1343             crm_log_xml_info(output, "cib:output");
1344             free_xml(output);
1345         }
1346 
1347         output = result_cib;
1348 
1349     } else {
1350         crm_trace("Not activating %d %d %s", rc, is_set(call_options, cib_dryrun), crm_element_value(result_cib, XML_ATTR_NUMUPDATES));
1351         if(is_not_set(call_options, cib_zero_copy)) {
1352             free_xml(result_cib);
1353         }
1354     }
1355 
1356     if ((call_options & (cib_inhibit_notify|cib_dryrun)) == 0) {
1357         const char *client = crm_element_value(request, F_CIB_CLIENTNAME);
1358 
1359         crm_trace("Sending notifications %d", is_set(call_options, cib_dryrun));
1360         cib_diff_notify(call_options, client, call_id, op, input, rc, *cib_diff);
1361     }
1362 
1363     if (send_r_notify) {
1364         const char *origin = crm_element_value(request, F_ORIG);
1365 
1366         cib_replace_notify(origin, the_cib, rc, *cib_diff);
1367     }
1368 
1369     xml_log_patchset(LOG_TRACE, "cib:diff", *cib_diff);
1370   done:
1371     if ((call_options & cib_discard_reply) == 0) {
1372         const char *caller = crm_element_value(request, F_CIB_CLIENTID);
1373 
1374         *reply = create_xml_node(NULL, "cib-reply");
1375         crm_xml_add(*reply, F_TYPE, T_CIB);
1376         crm_xml_add(*reply, F_CIB_OPERATION, op);
1377         crm_xml_add(*reply, F_CIB_CALLID, call_id);
1378         crm_xml_add(*reply, F_CIB_CLIENTID, caller);
1379         crm_xml_add_int(*reply, F_CIB_CALLOPTS, call_options);
1380         crm_xml_add_int(*reply, F_CIB_RC, rc);
1381 
1382         if (output != NULL) {
1383             crm_trace("Attaching reply output");
1384             add_message_xml(*reply, F_CIB_CALLDATA, output);
1385         }
1386 
1387         crm_log_xml_explicit(*reply, "cib:reply");
1388     }
1389 
1390     crm_trace("cleanup");
1391 
1392     if (cib_op_modifies(call_type) == FALSE && output != current_cib) {
1393         free_xml(output);
1394         output = NULL;
1395     }
1396 
1397     if (call_type >= 0) {
1398         cib_op_cleanup(call_type, call_options, &input, &output);
1399     }
1400 
1401     crm_trace("done");
1402     return rc;
1403 }
1404 
1405 gint
1406 cib_GCompareFunc(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
1407 {
1408     const xmlNode *a_msg = a;
1409     const xmlNode *b_msg = b;
1410 
1411     int msg_a_id = 0;
1412     int msg_b_id = 0;
1413     const char *value = NULL;
1414 
1415     value = crm_element_value_const(a_msg, F_CIB_CALLID);
1416     msg_a_id = crm_parse_int(value, NULL);
1417 
1418     value = crm_element_value_const(b_msg, F_CIB_CALLID);
1419     msg_b_id = crm_parse_int(value, NULL);
1420 
1421     if (msg_a_id == msg_b_id) {
1422         return 0;
1423     } else if (msg_a_id < msg_b_id) {
1424         return -1;
1425     }
1426     return 1;
1427 }
1428 
1429 #if SUPPORT_HEARTBEAT
1430 void
1431 cib_ha_peer_callback(HA_Message * msg, void *private_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1432 {
1433     xmlNode *xml = convert_ha_message(NULL, msg, __FUNCTION__);
1434 
1435     cib_peer_callback(xml, private_data);
1436     free_xml(xml);
1437 }
1438 #endif
1439 
1440 void
1441 cib_peer_callback(xmlNode * msg, void *private_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1442 {
1443     const char *reason = NULL;
1444     const char *originator = crm_element_value(msg, F_ORIG);
1445 
1446     if (cib_legacy_mode() && (originator == NULL || crm_str_eq(originator, cib_our_uname, TRUE))) {
1447         /* message is from ourselves */
1448         int bcast_id = 0;
1449 
1450         if (!(crm_element_value_int(msg, F_CIB_LOCAL_NOTIFY_ID, &bcast_id))) {
1451             check_local_notify(bcast_id);
1452         }
1453         return;
1454 
1455     } else if (crm_peer_cache == NULL) {
1456         reason = "membership not established";
1457         goto bail;
1458     }
1459 
1460     if (crm_element_value(msg, F_CIB_CLIENTNAME) == NULL) {
1461         crm_xml_add(msg, F_CIB_CLIENTNAME, originator);
1462     }
1463 
1464     /* crm_log_xml_trace("Peer[inbound]", msg); */
1465     cib_process_request(msg, FALSE, TRUE, TRUE, NULL);
1466     return;
1467 
1468   bail:
1469     if (reason) {
1470         const char *seq = crm_element_value(msg, F_SEQ);
1471         const char *op = crm_element_value(msg, F_CIB_OPERATION);
1472 
1473         crm_warn("Discarding %s message (%s) from %s: %s", op, seq, originator, reason);
1474     }
1475 }
1476 
1477 #if SUPPORT_HEARTBEAT
1478 extern oc_ev_t *cib_ev_token;
1479 static void *ccm_library = NULL;
1480 int (*ccm_api_callback_done) (void *cookie) = NULL;
1481 int (*ccm_api_handle_event) (const oc_ev_t * token) = NULL;
1482 
1483 void
1484 cib_client_status_callback(const char *node, const char *client, const char *status, void *private)
     /* [previous][next][first][last][top][bottom][index][help] */
1485 {
1486     /* Heartbeat only */
1487     crm_node_t *peer = NULL;
1488 
1489     if (safe_str_eq(client, CRM_SYSTEM_CIB)) {
1490         peer = crm_get_peer(0, node);
1491         if (safe_str_neq(peer->state, CRM_NODE_MEMBER)) {
1492             crm_warn("This peer is not a ccm member (yet). "
1493                 "Status ignored: Client %s/%s announced status [%s]",
1494                 node, client, status);
1495             return;
1496         }
1497 
1498         crm_info("Status update: Client %s/%s now has status [%s]", node, client, status);
1499 
1500         if (safe_str_eq(status, JOINSTATUS)) {
1501             status = ONLINESTATUS;
1502 
1503         } else if (safe_str_eq(status, LEAVESTATUS)) {
1504             status = OFFLINESTATUS;
1505         }
1506 
1507         crm_update_peer_proc(__FUNCTION__, peer, crm_proc_cib, status);
1508     }
1509     return;
1510 }
1511 
1512 int
1513 cib_ccm_dispatch(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1514 {
1515     int rc = 0;
1516     oc_ev_t *ccm_token = (oc_ev_t *) user_data;
1517 
1518     crm_trace("received callback");
1519 
1520     if (ccm_api_handle_event == NULL) {
1521         ccm_api_handle_event =
1522             find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_handle_event", 1);
1523     }
1524 
1525     rc = (*ccm_api_handle_event) (ccm_token);
1526     if (0 == rc) {
1527         return 0;
1528     }
1529 
1530     crm_err("CCM connection appears to have failed: rc=%d.", rc);
1531 
1532     /* eventually it might be nice to recover and reconnect... but until then... */
1533     crm_err("Exiting to recover from CCM connection failure");
1534     return crm_exit(ENOTCONN);
1535 }
1536 
1537 int current_instance = 0;
1538 void
1539 cib_ccm_msg_callback(oc_ed_t event, void *cookie, size_t size, const void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
1540 {
1541     gboolean update_id = FALSE;
1542     const oc_ev_membership_t *membership = data;
1543 
1544     CRM_ASSERT(membership != NULL);
1545 
1546     crm_info("Processing CCM event=%s (id=%d)", ccm_event_name(event), membership->m_instance);
1547 
1548     if (current_instance > membership->m_instance) {
1549         crm_err("Membership instance ID went backwards! %d->%d",
1550                 current_instance, membership->m_instance);
1551         CRM_ASSERT(current_instance <= membership->m_instance);
1552     }
1553 
1554     switch (event) {
1555         case OC_EV_MS_NEW_MEMBERSHIP:
1556         case OC_EV_MS_INVALID:
1557             update_id = TRUE;
1558             break;
1559         case OC_EV_MS_PRIMARY_RESTORED:
1560             update_id = TRUE;
1561             break;
1562         case OC_EV_MS_NOT_PRIMARY:
1563             crm_trace("Ignoring transitional CCM event: %s", ccm_event_name(event));
1564             break;
1565         case OC_EV_MS_EVICTED:
1566             crm_err("Evicted from CCM: %s", ccm_event_name(event));
1567             break;
1568         default:
1569             crm_err("Unknown CCM event: %d", event);
1570     }
1571 
1572     if (update_id) {
1573         unsigned int lpc = 0;
1574 
1575         CRM_CHECK(membership != NULL, return);
1576 
1577         current_instance = membership->m_instance;
1578 
1579         for (lpc = 0; lpc < membership->m_n_out; lpc++) {
1580             crm_update_ccm_node(membership, lpc + membership->m_out_idx, CRM_NODE_LOST,
1581                                 current_instance);
1582         }
1583 
1584         for (lpc = 0; lpc < membership->m_n_member; lpc++) {
1585             crm_update_ccm_node(membership, lpc + membership->m_memb_idx, CRM_NODE_MEMBER,
1586                                 current_instance);
1587         }
1588         heartbeat_cluster->llc_ops->client_status(heartbeat_cluster, NULL, crm_system_name, 0);
1589     }
1590 
1591     if (ccm_api_callback_done == NULL) {
1592         ccm_api_callback_done =
1593             find_library_function(&ccm_library, CCM_LIBRARY, "oc_ev_callback_done", 1);
1594     }
1595     (*ccm_api_callback_done) (cookie);
1596     return;
1597 }
1598 #endif
1599 
1600 gboolean
1601 can_write(int flags)
     /* [previous][next][first][last][top][bottom][index][help] */
1602 {
1603     return TRUE;
1604 }
1605 
1606 static gboolean
1607 cib_force_exit(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
1608 {
1609     crm_notice("Forcing exit!");
1610     terminate_cib(__FUNCTION__, -1);
1611     return FALSE;
1612 }
1613 
1614 static void
1615 disconnect_remote_client(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1616 {
1617     crm_client_t *a_client = value;
1618 
1619     crm_err("Disconnecting %s... Not implemented", crm_str(a_client->name));
1620 }
1621 
1622 void
1623 cib_shutdown(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
1624 {
1625     struct qb_ipcs_stats srv_stats;
1626 
1627     if (cib_shutdown_flag == FALSE) {
1628         int disconnects = 0;
1629         qb_ipcs_connection_t *c = NULL;
1630 
1631         cib_shutdown_flag = TRUE;
1632 
1633         c = qb_ipcs_connection_first_get(ipcs_rw);
1634         while (c != NULL) {
1635             qb_ipcs_connection_t *last = c;
1636 
1637             c = qb_ipcs_connection_next_get(ipcs_rw, last);
1638 
1639             crm_debug("Disconnecting r/w client %p...", last);
1640             qb_ipcs_disconnect(last);
1641             qb_ipcs_connection_unref(last);
1642             disconnects++;
1643         }
1644 
1645         c = qb_ipcs_connection_first_get(ipcs_ro);
1646         while (c != NULL) {
1647             qb_ipcs_connection_t *last = c;
1648 
1649             c = qb_ipcs_connection_next_get(ipcs_ro, last);
1650 
1651             crm_debug("Disconnecting r/o client %p...", last);
1652             qb_ipcs_disconnect(last);
1653             qb_ipcs_connection_unref(last);
1654             disconnects++;
1655         }
1656 
1657         c = qb_ipcs_connection_first_get(ipcs_shm);
1658         while (c != NULL) {
1659             qb_ipcs_connection_t *last = c;
1660 
1661             c = qb_ipcs_connection_next_get(ipcs_shm, last);
1662 
1663             crm_debug("Disconnecting non-blocking r/w client %p...", last);
1664             qb_ipcs_disconnect(last);
1665             qb_ipcs_connection_unref(last);
1666             disconnects++;
1667         }
1668 
1669         disconnects += crm_hash_table_size(client_connections);
1670 
1671         crm_debug("Disconnecting %d remote clients", crm_hash_table_size(client_connections));
1672         g_hash_table_foreach(client_connections, disconnect_remote_client, NULL);
1673         crm_info("Disconnected %d clients", disconnects);
1674     }
1675 
1676     qb_ipcs_stats_get(ipcs_rw, &srv_stats, QB_FALSE);
1677 
1678     if (crm_hash_table_size(client_connections) == 0) {
1679         crm_info("All clients disconnected (%d)", srv_stats.active_connections);
1680         initiate_exit();
1681 
1682     } else {
1683         crm_info("Waiting on %d clients to disconnect (%d)",
1684                  crm_hash_table_size(client_connections), srv_stats.active_connections);
1685     }
1686 }
1687 
1688 void
1689 initiate_exit(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1690 {
1691     int active = 0;
1692     xmlNode *leaving = NULL;
1693 
1694     active = crm_active_peers();
1695     if (active < 2) {
1696         terminate_cib(__FUNCTION__, 0);
1697         return;
1698     }
1699 
1700     crm_info("Sending disconnect notification to %d peers...", active);
1701 
1702     leaving = create_xml_node(NULL, "exit-notification");
1703     crm_xml_add(leaving, F_TYPE, "cib");
1704     crm_xml_add(leaving, F_CIB_OPERATION, "cib_shutdown_req");
1705 
1706     send_cluster_message(NULL, crm_msg_cib, leaving, TRUE);
1707     free_xml(leaving);
1708 
1709     g_timeout_add(crm_get_msec("5s"), cib_force_exit, NULL);
1710 }
1711 
1712 extern int remote_fd;
1713 extern int remote_tls_fd;
1714 
1715 /*!
1716  * \internal
1717  * \brief Close remote sockets, free the global CIB and quit
1718  *
1719  * \param[in] caller           Name of calling function (for log message)
1720  * \param[in] fast             If 1, skip disconnect; if -1, also exit error
1721  */
1722 void
1723 terminate_cib(const char *caller, int fast)
     /* [previous][next][first][last][top][bottom][index][help] */
1724 {
1725     crm_info("%s: Exiting%s...", caller,
1726              (fast < 0)? " fast" : mainloop ? " from mainloop" : "");
1727 
1728     if (remote_fd > 0) {
1729         close(remote_fd);
1730         remote_fd = 0;
1731     }
1732     if (remote_tls_fd > 0) {
1733         close(remote_tls_fd);
1734         remote_tls_fd = 0;
1735     }
1736 
1737     uninitializeCib();
1738 
1739     if (fast < 0) {
1740         /* Quit fast on error */
1741         cib_ipc_servers_destroy(ipcs_ro, ipcs_rw, ipcs_shm);
1742         crm_exit(EINVAL);
1743 
1744     } else if ((mainloop != NULL) && g_main_is_running(mainloop)) {
1745         /* Quit via returning from the main loop. If fast == 1, we skip the
1746          * disconnect here, and it will be done when the main loop returns
1747          * (this allows the peer status callback to avoid messing with the
1748          * peer caches).
1749          */
1750         if (fast == 0) {
1751             crm_cluster_disconnect(&crm_cluster);
1752         }
1753         g_main_quit(mainloop);
1754 
1755     } else {
1756         /* Quit via clean exit. Even the peer status callback can disconnect
1757          * here, because we're not returning control to the caller. */
1758         crm_cluster_disconnect(&crm_cluster);
1759         cib_ipc_servers_destroy(ipcs_ro, ipcs_rw, ipcs_shm);
1760         crm_exit(pcmk_ok);
1761     }
1762 }

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