root/daemons/controld/controld_messages.c

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

DEFINITIONS

This source file includes following definitions.
  1. register_fsa_error_adv
  2. register_fsa_input_adv
  3. fsa_dump_queue
  4. copy_ha_msg_input
  5. delete_fsa_input
  6. get_message
  7. fsa_typed_data_adv
  8. do_msg_route
  9. route_message
  10. relay_message
  11. authorize_version
  12. controld_authorize_ipc_message
  13. handle_message
  14. handle_failcount_op
  15. handle_lrm_delete
  16. handle_remote_state
  17. create_ping_reply
  18. handle_ping
  19. handle_node_list
  20. handle_node_info_request
  21. verify_feature_set
  22. handle_shutdown_self_ack
  23. handle_shutdown_ack
  24. handle_request
  25. handle_response
  26. handle_shutdown_request
  27. send_msg_via_ipc
  28. delete_ha_msg_input
  29. broadcast_remote_state_message

   1 /*
   2  * Copyright 2004-2025 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 <inttypes.h>                   // PRIx64
  13 #include <stdint.h>                     // uint64_t
  14 #include <string.h>
  15 #include <sys/param.h>
  16 #include <time.h>
  17 
  18 #include <crm/crm.h>
  19 #include <crm/common/xml.h>
  20 #include <crm/cluster/internal.h>
  21 #include <crm/cib.h>
  22 #include <crm/common/ipc_internal.h>
  23 
  24 #include <pacemaker-controld.h>
  25 
  26 static enum crmd_fsa_input handle_message(xmlNode *msg,
  27                                           enum crmd_fsa_cause cause);
  28 static xmlNode* create_ping_reply(const xmlNode *msg);
  29 static void handle_response(xmlNode *stored_msg);
  30 static enum crmd_fsa_input handle_request(xmlNode *stored_msg,
  31                                           enum crmd_fsa_cause cause);
  32 static enum crmd_fsa_input handle_shutdown_request(xmlNode *stored_msg);
  33 static void send_msg_via_ipc(xmlNode * msg, const char *sys, const char *src);
  34 
  35 /* debug only, can wrap all it likes */
  36 static int last_data_id = 0;
  37 
  38 void
  39 register_fsa_error_adv(enum crmd_fsa_cause cause, enum crmd_fsa_input input,
     /* [previous][next][first][last][top][bottom][index][help] */
  40                        fsa_data_t * cur_data, void *new_data, const char *raised_from)
  41 {
  42     /* save the current actions if any */
  43     if (controld_globals.fsa_actions != A_NOTHING) {
  44         register_fsa_input_adv(cur_data ? cur_data->fsa_cause : C_FSA_INTERNAL,
  45                                I_NULL, cur_data ? cur_data->data : NULL,
  46                                controld_globals.fsa_actions, TRUE, __func__);
  47     }
  48 
  49     /* reset the action list */
  50     crm_info("Resetting the current action list");
  51     fsa_dump_actions(controld_globals.fsa_actions, "Drop");
  52     controld_globals.fsa_actions = A_NOTHING;
  53 
  54     /* register the error */
  55     register_fsa_input_adv(cause, input, new_data, A_NOTHING, TRUE, raised_from);
  56 }
  57 
  58 void
  59 register_fsa_input_adv(enum crmd_fsa_cause cause, enum crmd_fsa_input input,
     /* [previous][next][first][last][top][bottom][index][help] */
  60                        void *data, uint64_t with_actions,
  61                        gboolean prepend, const char *raised_from)
  62 {
  63     unsigned old_len = g_list_length(controld_globals.fsa_message_queue);
  64     fsa_data_t *fsa_data = NULL;
  65 
  66     if (raised_from == NULL) {
  67         raised_from = "<unknown>";
  68     }
  69 
  70     if (input == I_NULL && with_actions == A_NOTHING /* && data == NULL */ ) {
  71         /* no point doing anything */
  72         crm_err("Cannot add entry to queue: no input and no action");
  73         return;
  74     }
  75 
  76     if (input == I_WAIT_FOR_EVENT) {
  77         controld_set_global_flags(controld_fsa_is_stalled);
  78         crm_debug("Stalling the FSA pending further input: source=%s cause=%s data=%p queue=%d",
  79                   raised_from, fsa_cause2string(cause), data, old_len);
  80 
  81         if (old_len > 0) {
  82             fsa_dump_queue(LOG_TRACE);
  83             prepend = FALSE;
  84         }
  85 
  86         if (data == NULL) {
  87             controld_set_fsa_action_flags(with_actions);
  88             fsa_dump_actions(with_actions, "Restored");
  89             return;
  90         }
  91 
  92         /* Store everything in the new event and reset
  93          * controld_globals.fsa_actions
  94          */
  95         with_actions |= controld_globals.fsa_actions;
  96         controld_globals.fsa_actions = A_NOTHING;
  97     }
  98 
  99     last_data_id++;
 100     crm_trace("%s %s FSA input %d (%s) due to %s, %s data",
 101               raised_from, (prepend? "prepended" : "appended"), last_data_id,
 102               fsa_input2string(input), fsa_cause2string(cause),
 103               (data? "with" : "without"));
 104 
 105     fsa_data = pcmk__assert_alloc(1, sizeof(fsa_data_t));
 106     fsa_data->id = last_data_id;
 107     fsa_data->fsa_input = input;
 108     fsa_data->fsa_cause = cause;
 109     fsa_data->origin = raised_from;
 110     fsa_data->data = NULL;
 111     fsa_data->data_type = fsa_dt_none;
 112     fsa_data->actions = with_actions;
 113 
 114     if (with_actions != A_NOTHING) {
 115         crm_trace("Adding actions %.16" PRIx64 " to input", with_actions);
 116     }
 117 
 118     if (data != NULL) {
 119         switch (cause) {
 120             case C_FSA_INTERNAL:
 121             case C_CRMD_STATUS_CALLBACK:
 122             case C_IPC_MESSAGE:
 123             case C_HA_MESSAGE:
 124                 CRM_CHECK(((ha_msg_input_t *) data)->msg != NULL,
 125                           crm_err("Bogus data from %s", raised_from));
 126                 crm_trace("Copying %s data from %s as cluster message data",
 127                           fsa_cause2string(cause), raised_from);
 128                 fsa_data->data = copy_ha_msg_input(data);
 129                 fsa_data->data_type = fsa_dt_ha_msg;
 130                 break;
 131 
 132             case C_LRM_OP_CALLBACK:
 133                 crm_trace("Copying %s data from %s as lrmd_event_data_t",
 134                           fsa_cause2string(cause), raised_from);
 135                 fsa_data->data = lrmd_copy_event((lrmd_event_data_t *) data);
 136                 fsa_data->data_type = fsa_dt_lrm;
 137                 break;
 138 
 139             case C_TIMER_POPPED:
 140             case C_SHUTDOWN:
 141             case C_UNKNOWN:
 142             case C_STARTUP:
 143                 crm_crit("Copying %s data (from %s) is not yet implemented",
 144                          fsa_cause2string(cause), raised_from);
 145                 crmd_exit(CRM_EX_SOFTWARE);
 146                 break;
 147         }
 148     }
 149 
 150     /* make sure to free it properly later */
 151     if (prepend) {
 152         controld_globals.fsa_message_queue
 153             = g_list_prepend(controld_globals.fsa_message_queue, fsa_data);
 154     } else {
 155         controld_globals.fsa_message_queue
 156             = g_list_append(controld_globals.fsa_message_queue, fsa_data);
 157     }
 158 
 159     crm_trace("FSA message queue length is %d",
 160               g_list_length(controld_globals.fsa_message_queue));
 161 
 162     /* fsa_dump_queue(LOG_TRACE); */
 163 
 164     if (old_len == g_list_length(controld_globals.fsa_message_queue)) {
 165         crm_err("Couldn't add message to the queue");
 166     }
 167 
 168     if (input != I_WAIT_FOR_EVENT) {
 169         controld_trigger_fsa();
 170     }
 171 }
 172 
 173 void
 174 fsa_dump_queue(int log_level)
     /* [previous][next][first][last][top][bottom][index][help] */
 175 {
 176     int offset = 0;
 177 
 178     for (GList *iter = controld_globals.fsa_message_queue; iter != NULL;
 179          iter = iter->next) {
 180         fsa_data_t *data = (fsa_data_t *) iter->data;
 181 
 182         do_crm_log_unlikely(log_level,
 183                             "queue[%d.%d]: input %s raised by %s(%p.%d)\t(cause=%s)",
 184                             offset++, data->id, fsa_input2string(data->fsa_input),
 185                             data->origin, data->data, data->data_type,
 186                             fsa_cause2string(data->fsa_cause));
 187     }
 188 }
 189 
 190 ha_msg_input_t *
 191 copy_ha_msg_input(ha_msg_input_t * orig)
     /* [previous][next][first][last][top][bottom][index][help] */
 192 {
 193     xmlNode *wrapper = NULL;
 194 
 195     ha_msg_input_t *copy = pcmk__assert_alloc(1, sizeof(ha_msg_input_t));
 196 
 197     copy->msg = (orig != NULL)? pcmk__xml_copy(NULL, orig->msg) : NULL;
 198 
 199     wrapper = pcmk__xe_first_child(copy->msg, PCMK__XE_CRM_XML, NULL, NULL);
 200     copy->xml = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 201     return copy;
 202 }
 203 
 204 void
 205 delete_fsa_input(fsa_data_t * fsa_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 206 {
 207     lrmd_event_data_t *op = NULL;
 208     xmlNode *foo = NULL;
 209 
 210     if (fsa_data == NULL) {
 211         return;
 212     }
 213     crm_trace("About to free %s data", fsa_cause2string(fsa_data->fsa_cause));
 214 
 215     if (fsa_data->data != NULL) {
 216         switch (fsa_data->data_type) {
 217             case fsa_dt_ha_msg:
 218                 delete_ha_msg_input(fsa_data->data);
 219                 break;
 220 
 221             case fsa_dt_xml:
 222                 foo = fsa_data->data;
 223                 pcmk__xml_free(foo);
 224                 break;
 225 
 226             case fsa_dt_lrm:
 227                 op = (lrmd_event_data_t *) fsa_data->data;
 228                 lrmd_free_event(op);
 229                 break;
 230 
 231             case fsa_dt_none:
 232                 if (fsa_data->data != NULL) {
 233                     crm_err("Don't know how to free %s data from %s",
 234                             fsa_cause2string(fsa_data->fsa_cause), fsa_data->origin);
 235                     crmd_exit(CRM_EX_SOFTWARE);
 236                 }
 237                 break;
 238         }
 239         crm_trace("%s data freed", fsa_cause2string(fsa_data->fsa_cause));
 240     }
 241 
 242     free(fsa_data);
 243 }
 244 
 245 /* returns the next message */
 246 fsa_data_t *
 247 get_message(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 248 {
 249     fsa_data_t *message
 250         = (fsa_data_t *) controld_globals.fsa_message_queue->data;
 251 
 252     controld_globals.fsa_message_queue
 253         = g_list_remove(controld_globals.fsa_message_queue, message);
 254     crm_trace("Processing input %d", message->id);
 255     return message;
 256 }
 257 
 258 void *
 259 fsa_typed_data_adv(fsa_data_t * fsa_data, enum fsa_data_type a_type, const char *caller)
     /* [previous][next][first][last][top][bottom][index][help] */
 260 {
 261     void *ret_val = NULL;
 262 
 263     if (fsa_data == NULL) {
 264         crm_err("%s: No FSA data available", caller);
 265 
 266     } else if (fsa_data->data == NULL) {
 267         crm_err("%s: No message data available. Origin: %s", caller, fsa_data->origin);
 268 
 269     } else if (fsa_data->data_type != a_type) {
 270         crm_crit("%s: Message data was the wrong type! %d vs. requested=%d.  Origin: %s",
 271                  caller, fsa_data->data_type, a_type, fsa_data->origin);
 272         pcmk__assert(fsa_data->data_type == a_type);
 273     } else {
 274         ret_val = fsa_data->data;
 275     }
 276 
 277     return ret_val;
 278 }
 279 
 280 /*      A_MSG_ROUTE     */
 281 void
 282 do_msg_route(long long action,
     /* [previous][next][first][last][top][bottom][index][help] */
 283              enum crmd_fsa_cause cause,
 284              enum crmd_fsa_state cur_state,
 285              enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 286 {
 287     ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg);
 288 
 289     route_message(msg_data->fsa_cause, input->msg);
 290 }
 291 
 292 void
 293 route_message(enum crmd_fsa_cause cause, xmlNode * input)
     /* [previous][next][first][last][top][bottom][index][help] */
 294 {
 295     ha_msg_input_t fsa_input;
 296     enum crmd_fsa_input result = I_NULL;
 297 
 298     fsa_input.msg = input;
 299     CRM_CHECK(cause == C_IPC_MESSAGE || cause == C_HA_MESSAGE, return);
 300 
 301     /* try passing the buck first */
 302     if (relay_message(input, cause == C_IPC_MESSAGE)) {
 303         return;
 304     }
 305 
 306     /* handle locally */
 307     result = handle_message(input, cause);
 308 
 309     /* done or process later? */
 310     switch (result) {
 311         case I_NULL:
 312         case I_ROUTER:
 313         case I_NODE_JOIN:
 314         case I_JOIN_REQUEST:
 315         case I_JOIN_RESULT:
 316             break;
 317         default:
 318             /* Defering local processing of message */
 319             register_fsa_input_later(cause, result, &fsa_input);
 320             return;
 321     }
 322 
 323     if (result != I_NULL) {
 324         /* add to the front of the queue */
 325         register_fsa_input(cause, result, &fsa_input);
 326     }
 327 }
 328 
 329 gboolean
 330 relay_message(xmlNode * msg, gboolean originated_locally)
     /* [previous][next][first][last][top][bottom][index][help] */
 331 {
 332     enum pcmk_ipc_server dest = pcmk_ipc_unknown;
 333     bool is_for_dc = false;
 334     bool is_for_dcib = false;
 335     bool is_for_te = false;
 336     bool is_for_crm = false;
 337     bool is_for_cib = false;
 338     bool is_local = false;
 339     bool broadcast = false;
 340     const char *host_to = NULL;
 341     const char *sys_to = NULL;
 342     const char *sys_from = NULL;
 343     const char *type = NULL;
 344     const char *task = NULL;
 345     const char *ref = NULL;
 346     pcmk__node_status_t *node_to = NULL;
 347 
 348     CRM_CHECK(msg != NULL, return TRUE);
 349 
 350     host_to = crm_element_value(msg, PCMK__XA_CRM_HOST_TO);
 351     sys_to = crm_element_value(msg, PCMK__XA_CRM_SYS_TO);
 352     sys_from = crm_element_value(msg, PCMK__XA_CRM_SYS_FROM);
 353     type = crm_element_value(msg, PCMK__XA_T);
 354     task = crm_element_value(msg, PCMK__XA_CRM_TASK);
 355     ref = crm_element_value(msg, PCMK_XA_REFERENCE);
 356 
 357     broadcast = pcmk__str_empty(host_to);
 358 
 359     if (ref == NULL) {
 360         ref = "without reference ID";
 361     }
 362 
 363     if (pcmk__str_eq(task, CRM_OP_HELLO, pcmk__str_casei)) {
 364         crm_trace("Received hello %s from %s (no processing needed)",
 365                   ref, pcmk__s(sys_from, "unidentified source"));
 366         crm_log_xml_trace(msg, "hello");
 367         return TRUE;
 368     }
 369 
 370     // Require message type (set by pcmk__new_request())
 371     if (!pcmk__str_eq(type, PCMK__VALUE_CRMD, pcmk__str_none)) {
 372         crm_warn("Ignoring invalid message %s with type '%s' "
 373                  "(not '" PCMK__VALUE_CRMD "')",
 374                  ref, pcmk__s(type, ""));
 375         crm_log_xml_trace(msg, "ignored");
 376         return TRUE;
 377     }
 378 
 379     // Require a destination subsystem (also set by pcmk__new_request())
 380     if (sys_to == NULL) {
 381         crm_warn("Ignoring invalid message %s with no " PCMK__XA_CRM_SYS_TO,
 382                  ref);
 383         crm_log_xml_trace(msg, "ignored");
 384         return TRUE;
 385     }
 386 
 387     // Get the message type appropriate to the destination subsystem
 388     if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
 389         dest = pcmk__parse_server(sys_to);
 390         if (dest == pcmk_ipc_unknown) {
 391             /* Unrecognized value, use a sane default
 392              *
 393              * @TODO Maybe we should bail instead
 394              */
 395             dest = pcmk_ipc_controld;
 396         }
 397     }
 398 
 399     is_for_dc = (strcasecmp(CRM_SYSTEM_DC, sys_to) == 0);
 400     is_for_dcib = (strcasecmp(CRM_SYSTEM_DCIB, sys_to) == 0);
 401     is_for_te = (strcasecmp(CRM_SYSTEM_TENGINE, sys_to) == 0);
 402     is_for_cib = (strcasecmp(CRM_SYSTEM_CIB, sys_to) == 0);
 403     is_for_crm = (strcasecmp(CRM_SYSTEM_CRMD, sys_to) == 0);
 404 
 405     // Check whether message should be processed locally
 406     is_local = false;
 407     if (broadcast) {
 408         if (is_for_dc || is_for_te) {
 409             is_local = false;
 410 
 411         } else if (is_for_crm) {
 412             if (pcmk__strcase_any_of(task, CRM_OP_NODE_INFO,
 413                                      PCMK__CONTROLD_CMD_NODES, NULL)) {
 414                 /* Node info requests do not specify a host, which is normally
 415                  * treated as "all hosts", because the whole point is that the
 416                  * client may not know the local node name. Always handle these
 417                  * requests locally.
 418                  */
 419                 is_local = true;
 420             } else {
 421                 is_local = !originated_locally;
 422             }
 423 
 424         } else {
 425             is_local = true;
 426         }
 427 
 428     } else if (controld_is_local_node(host_to)) {
 429         is_local = true;
 430 
 431     } else if (is_for_crm && pcmk__str_eq(task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
 432         xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CRM_XML, NULL,
 433                                                 NULL);
 434         xmlNode *msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 435         const char *mode = crm_element_value(msg_data, PCMK__XA_MODE);
 436 
 437         if (pcmk__str_eq(mode, PCMK__VALUE_CIB, pcmk__str_none)) {
 438             // Local delete of an offline node's resource history
 439             is_local = true;
 440         }
 441     }
 442 
 443     // If is for DC and DC is not yet selected
 444     if (is_for_dc && pcmk__str_eq(task, CRM_OP_PING, pcmk__str_casei) 
 445         && (controld_globals.dc_name == NULL)) {
 446             
 447         xmlNode *reply = create_ping_reply(msg);
 448         sys_to = crm_element_value(reply, PCMK__XA_CRM_SYS_TO);
 449         // Explicitly leave src empty. It indicates that dc is "not yet selected"
 450         send_msg_via_ipc(reply, sys_to, NULL);
 451         pcmk__xml_free(reply);
 452         return TRUE;
 453     }
 454 
 455     // Check whether message should be relayed
 456 
 457     if (is_for_dc || is_for_dcib || is_for_te) {
 458         if (AM_I_DC) {
 459             if (is_for_te) {
 460                 crm_trace("Route message %s locally as transition request",
 461                           ref);
 462                 crm_log_xml_trace(msg, sys_to);
 463                 send_msg_via_ipc(msg, sys_to, controld_globals.cluster->priv->node_name);
 464 
 465                 return TRUE; // No further processing of message is needed
 466             }
 467             crm_trace("Route message %s locally as DC request", ref);
 468             return FALSE; // More to be done by caller
 469         }
 470 
 471         if (originated_locally
 472             && !pcmk__strcase_any_of(sys_from, CRM_SYSTEM_PENGINE,
 473                                      CRM_SYSTEM_TENGINE, NULL)) {
 474             crm_trace("Relay message %s to DC (via %s)",
 475                       ref, pcmk__s(host_to, "broadcast"));
 476             crm_log_xml_trace(msg, "relayed");
 477             if (!broadcast) {
 478                 node_to = pcmk__get_node(0, host_to, NULL,
 479                                          pcmk__node_search_cluster_member);
 480             }
 481             pcmk__cluster_send_message(node_to, dest, msg);
 482             return TRUE;
 483         }
 484 
 485         /* Transition engine and scheduler messages are sent only to the DC on
 486          * the same node. If we are no longer the DC, discard this message.
 487          */
 488         crm_trace("Ignoring message %s because we are no longer DC", ref);
 489         crm_log_xml_trace(msg, "ignored");
 490         return TRUE; // No further processing of message is needed
 491     }
 492 
 493     if (is_local) {
 494         if (is_for_crm || is_for_cib) {
 495             crm_trace("Route message %s locally as controller request", ref);
 496             return FALSE; // More to be done by caller
 497         }
 498         crm_trace("Relay message %s locally to %s", ref, sys_to);
 499         crm_log_xml_trace(msg, "IPC-relay");
 500         send_msg_via_ipc(msg, sys_to, controld_globals.cluster->priv->node_name);
 501         return TRUE;
 502     }
 503 
 504     if (!broadcast) {
 505         node_to = pcmk__search_node_caches(0, host_to, NULL,
 506                                            pcmk__node_search_cluster_member);
 507         if (node_to == NULL) {
 508             crm_warn("Ignoring message %s because node %s is unknown",
 509                      ref, host_to);
 510             crm_log_xml_trace(msg, "ignored");
 511             return TRUE;
 512         }
 513     }
 514 
 515     crm_trace("Relay message %s to %s",
 516               ref, pcmk__s(host_to, "all peers"));
 517     crm_log_xml_trace(msg, "relayed");
 518     pcmk__cluster_send_message(node_to, dest, msg);
 519     return TRUE;
 520 }
 521 
 522 // Return true if field contains a positive integer
 523 static bool
 524 authorize_version(xmlNode *message_data, const char *field,
     /* [previous][next][first][last][top][bottom][index][help] */
 525                   const char *client_name, const char *ref, const char *uuid)
 526 {
 527     const char *version = crm_element_value(message_data, field);
 528     long long version_num;
 529 
 530     if ((pcmk__scan_ll(version, &version_num, -1LL) != pcmk_rc_ok)
 531         || (version_num < 0LL)) {
 532 
 533         crm_warn("Rejected IPC hello from %s: '%s' is not a valid protocol %s "
 534                  QB_XS " ref=%s uuid=%s",
 535                  client_name, ((version == NULL)? "" : version),
 536                  field, (ref? ref : "none"), uuid);
 537         return false;
 538     }
 539     return true;
 540 }
 541 
 542 /*!
 543  * \internal
 544  * \brief Check whether a client IPC message is acceptable
 545  *
 546  * If a given client IPC message is a hello, "authorize" it by ensuring it has
 547  * valid information such as a protocol version, and return false indicating
 548  * that nothing further needs to be done with the message. If the message is not
 549  * a hello, just return true to indicate it needs further processing.
 550  *
 551  * \param[in]     client_msg     XML of IPC message
 552  * \param[in,out] curr_client    If IPC is not proxied, client that sent message
 553  * \param[in]     proxy_session  If IPC is proxied, the session ID
 554  *
 555  * \return true if message needs further processing, false if it doesn't
 556  */
 557 bool
 558 controld_authorize_ipc_message(const xmlNode *client_msg, pcmk__client_t *curr_client,
     /* [previous][next][first][last][top][bottom][index][help] */
 559                                const char *proxy_session)
 560 {
 561     xmlNode *wrapper = NULL;
 562     xmlNode *message_data = NULL;
 563     const char *client_name = NULL;
 564     const char *op = crm_element_value(client_msg, PCMK__XA_CRM_TASK);
 565     const char *ref = crm_element_value(client_msg, PCMK_XA_REFERENCE);
 566     const char *uuid = (curr_client? curr_client->id : proxy_session);
 567 
 568     if (uuid == NULL) {
 569         crm_warn("IPC message from client rejected: No client identifier "
 570                  QB_XS " ref=%s", (ref? ref : "none"));
 571         goto rejected;
 572     }
 573 
 574     if (!pcmk__str_eq(CRM_OP_HELLO, op, pcmk__str_casei)) {
 575         // Only hello messages need to be authorized
 576         return true;
 577     }
 578 
 579     wrapper = pcmk__xe_first_child(client_msg, PCMK__XE_CRM_XML, NULL, NULL);
 580     message_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 581 
 582     client_name = crm_element_value(message_data, PCMK__XA_CLIENT_NAME);
 583     if (pcmk__str_empty(client_name)) {
 584         crm_warn("IPC hello from client rejected: No client name",
 585                  QB_XS " ref=%s uuid=%s", (ref? ref : "none"), uuid);
 586         goto rejected;
 587     }
 588     if (!authorize_version(message_data, PCMK__XA_MAJOR_VERSION, client_name,
 589                            ref, uuid)) {
 590         goto rejected;
 591     }
 592     if (!authorize_version(message_data, PCMK__XA_MINOR_VERSION, client_name,
 593                            ref, uuid)) {
 594         goto rejected;
 595     }
 596 
 597     crm_trace("Validated IPC hello from client %s", client_name);
 598     crm_log_xml_trace(client_msg, "hello");
 599     if (curr_client) {
 600         curr_client->userdata = pcmk__str_copy(client_name);
 601     }
 602     controld_trigger_fsa();
 603     return false;
 604 
 605 rejected:
 606     crm_log_xml_trace(client_msg, "rejected");
 607     if (curr_client) {
 608         qb_ipcs_disconnect(curr_client->ipcs);
 609     }
 610     return false;
 611 }
 612 
 613 static enum crmd_fsa_input
 614 handle_message(xmlNode *msg, enum crmd_fsa_cause cause)
     /* [previous][next][first][last][top][bottom][index][help] */
 615 {
 616     const char *type = NULL;
 617 
 618     CRM_CHECK(msg != NULL, return I_NULL);
 619 
 620     type = crm_element_value(msg, PCMK__XA_SUBT);
 621     if (pcmk__str_eq(type, PCMK__VALUE_REQUEST, pcmk__str_none)) {
 622         return handle_request(msg, cause);
 623     }
 624 
 625     if (pcmk__str_eq(type, PCMK__VALUE_RESPONSE, pcmk__str_none)) {
 626         handle_response(msg);
 627         return I_NULL;
 628     }
 629 
 630     crm_warn("Ignoring message with unknown " PCMK__XA_SUBT" '%s'",
 631              pcmk__s(type, ""));
 632     crm_log_xml_trace(msg, "bad");
 633     return I_NULL;
 634 }
 635 
 636 static enum crmd_fsa_input
 637 handle_failcount_op(xmlNode * stored_msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 638 {
 639     const char *rsc = NULL;
 640     const char *uname = NULL;
 641     const char *op = NULL;
 642     char *interval_spec = NULL;
 643     guint interval_ms = 0;
 644     gboolean is_remote_node = FALSE;
 645 
 646     xmlNode *wrapper = pcmk__xe_first_child(stored_msg, PCMK__XE_CRM_XML, NULL,
 647                                             NULL);
 648     xmlNode *xml_op = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 649 
 650     if (xml_op) {
 651         xmlNode *xml_rsc = pcmk__xe_first_child(xml_op, PCMK_XE_PRIMITIVE, NULL,
 652                                                 NULL);
 653         xmlNode *xml_attrs = pcmk__xe_first_child(xml_op, PCMK__XE_ATTRIBUTES,
 654                                                   NULL, NULL);
 655 
 656         if (xml_rsc) {
 657             rsc = pcmk__xe_id(xml_rsc);
 658         }
 659         if (xml_attrs) {
 660             op = crm_element_value(xml_attrs,
 661                                    CRM_META "_" PCMK__META_CLEAR_FAILURE_OP);
 662             crm_element_value_ms(xml_attrs,
 663                                  CRM_META "_" PCMK__META_CLEAR_FAILURE_INTERVAL,
 664                                  &interval_ms);
 665         }
 666     }
 667     uname = crm_element_value(xml_op, PCMK__META_ON_NODE);
 668 
 669     if ((rsc == NULL) || (uname == NULL)) {
 670         crm_log_xml_warn(stored_msg, "invalid failcount op");
 671         return I_NULL;
 672     }
 673 
 674     if (crm_element_value(xml_op, PCMK__XA_ROUTER_NODE)) {
 675         is_remote_node = TRUE;
 676     }
 677 
 678     crm_debug("Clearing failures for %s-interval %s on %s "
 679               "from attribute manager, CIB, and executor state",
 680               pcmk__readable_interval(interval_ms), rsc, uname);
 681 
 682     if (interval_ms) {
 683         interval_spec = crm_strdup_printf("%ums", interval_ms);
 684     }
 685     update_attrd_clear_failures(uname, rsc, op, interval_spec, is_remote_node);
 686     free(interval_spec);
 687 
 688     controld_cib_delete_last_failure(rsc, uname, op, interval_ms);
 689 
 690     lrm_clear_last_failure(rsc, uname, op, interval_ms);
 691 
 692     return I_NULL;
 693 }
 694 
 695 static enum crmd_fsa_input
 696 handle_lrm_delete(xmlNode *stored_msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 697 {
 698     const char *mode = NULL;
 699     xmlNode *wrapper = pcmk__xe_first_child(stored_msg, PCMK__XE_CRM_XML, NULL,
 700                                             NULL);
 701     xmlNode *msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 702 
 703     CRM_CHECK(msg_data != NULL, return I_NULL);
 704 
 705     /* CRM_OP_LRM_DELETE has two distinct modes. The default behavior is to
 706      * relay the operation to the affected node, which will unregister the
 707      * resource from the local executor, clear the resource's history from the
 708      * CIB, and do some bookkeeping in the controller.
 709      *
 710      * However, if the affected node is offline, the client will specify
 711      * mode=PCMK__VALUE_CIB which means the controller receiving the operation
 712      * should clear the resource's history from the CIB and nothing else. This
 713      * is used to clear shutdown locks.
 714      */
 715     mode = crm_element_value(msg_data, PCMK__XA_MODE);
 716     if (!pcmk__str_eq(mode, PCMK__VALUE_CIB, pcmk__str_none)) {
 717         // Relay to affected node
 718         crm_xml_add(stored_msg, PCMK__XA_CRM_SYS_TO, CRM_SYSTEM_LRMD);
 719         return I_ROUTER;
 720 
 721     } else {
 722         // Delete CIB history locally (compare with do_lrm_delete())
 723         const char *from_sys = NULL;
 724         const char *user_name = NULL;
 725         const char *rsc_id = NULL;
 726         const char *node = NULL;
 727         xmlNode *rsc_xml = NULL;
 728         int rc = pcmk_rc_ok;
 729 
 730         rsc_xml = pcmk__xe_first_child(msg_data, PCMK_XE_PRIMITIVE, NULL, NULL);
 731         CRM_CHECK(rsc_xml != NULL, return I_NULL);
 732 
 733         rsc_id = pcmk__xe_id(rsc_xml);
 734         from_sys = crm_element_value(stored_msg, PCMK__XA_CRM_SYS_FROM);
 735         node = crm_element_value(msg_data, PCMK__META_ON_NODE);
 736         user_name = pcmk__update_acl_user(stored_msg, PCMK__XA_CRM_USER, NULL);
 737         crm_debug("Handling " CRM_OP_LRM_DELETE " for %s on %s locally%s%s "
 738                   "(clearing CIB resource history only)", rsc_id, node,
 739                   (user_name? " for user " : ""), (user_name? user_name : ""));
 740         rc = controld_delete_resource_history(rsc_id, node, user_name,
 741                                               cib_dryrun|cib_sync_call);
 742         if (rc == pcmk_rc_ok) {
 743             rc = controld_delete_resource_history(rsc_id, node, user_name,
 744                                                   crmd_cib_smart_opt());
 745         }
 746 
 747         /* Notify client. Also notify tengine if mode=PCMK__VALUE_CIB and
 748          * op=CRM_OP_LRM_DELETE.
 749          */
 750         if (from_sys) {
 751             lrmd_event_data_t *op = NULL;
 752             const char *from_host = crm_element_value(stored_msg, PCMK__XA_SRC);
 753             const char *transition;
 754 
 755             if (strcmp(from_sys, CRM_SYSTEM_TENGINE)) {
 756                 transition = crm_element_value(msg_data,
 757                                                PCMK__XA_TRANSITION_KEY);
 758             } else {
 759                 transition = crm_element_value(stored_msg,
 760                                                PCMK__XA_TRANSITION_KEY);
 761             }
 762 
 763             crm_info("Notifying %s on %s that %s was%s deleted",
 764                      from_sys, (from_host? from_host : "local node"), rsc_id,
 765                      ((rc == pcmk_rc_ok)? "" : " not"));
 766             op = lrmd_new_event(rsc_id, PCMK_ACTION_DELETE, 0);
 767             op->type = lrmd_event_exec_complete;
 768             op->user_data = pcmk__str_copy(pcmk__s(transition, FAKE_TE_ID));
 769             op->params = pcmk__strkey_table(free, free);
 770             pcmk__insert_dup(op->params, PCMK_XA_CRM_FEATURE_SET,
 771                              CRM_FEATURE_SET);
 772             controld_rc2event(op, rc);
 773             controld_ack_event_directly(from_host, from_sys, NULL, op, rsc_id);
 774             lrmd_free_event(op);
 775             controld_trigger_delete_refresh(from_sys, rsc_id);
 776         }
 777         return I_NULL;
 778     }
 779 }
 780 
 781 /*!
 782  * \brief Handle a CRM_OP_REMOTE_STATE message by updating remote peer cache
 783  *
 784  * \param[in] msg  Message XML
 785  *
 786  * \return Next FSA input
 787  */
 788 static enum crmd_fsa_input
 789 handle_remote_state(const xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 790 {
 791     const char *conn_host = NULL;
 792     const char *remote_uname = pcmk__xe_id(msg);
 793     pcmk__node_status_t *remote_peer;
 794     bool remote_is_up = false;
 795     int rc = pcmk_rc_ok;
 796 
 797     rc = pcmk__xe_get_bool_attr(msg, PCMK__XA_IN_CCM, &remote_is_up);
 798 
 799     CRM_CHECK(remote_uname && rc == pcmk_rc_ok, return I_NULL);
 800 
 801     remote_peer = pcmk__cluster_lookup_remote_node(remote_uname);
 802     CRM_CHECK(remote_peer, return I_NULL);
 803 
 804     pcmk__update_peer_state(__func__, remote_peer,
 805                             remote_is_up ? PCMK_VALUE_MEMBER : PCMK__VALUE_LOST,
 806                             0);
 807 
 808     conn_host = crm_element_value(msg, PCMK__XA_CONNECTION_HOST);
 809     if (conn_host) {
 810         pcmk__str_update(&remote_peer->conn_host, conn_host);
 811     } else if (remote_peer->conn_host) {
 812         free(remote_peer->conn_host);
 813         remote_peer->conn_host = NULL;
 814     }
 815 
 816     return I_NULL;
 817 }
 818 
 819 /*!
 820  * \brief Handle a CRM_OP_PING message
 821  *
 822  * \param[in] msg  Message XML
 823  *
 824  * \return Next FSA input
 825  */
 826 static xmlNode*
 827 create_ping_reply(const xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 828 {
 829     const char *value = NULL;
 830     xmlNode *ping = NULL;
 831     xmlNode *reply = NULL;
 832 
 833     // Build reply
 834 
 835     ping = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE);
 836     value = crm_element_value(msg, PCMK__XA_CRM_SYS_TO);
 837     crm_xml_add(ping, PCMK__XA_CRM_SUBSYSTEM, value);
 838 
 839     // Add controller state
 840     value = fsa_state2string(controld_globals.fsa_state);
 841     crm_xml_add(ping, PCMK__XA_CRMD_STATE, value);
 842     crm_notice("Current ping state: %s", value); // CTS needs this
 843 
 844     // Add controller health
 845     // @TODO maybe do some checks to determine meaningful status
 846     crm_xml_add(ping, PCMK_XA_RESULT, "ok");
 847 
 848     reply = pcmk__new_reply(msg, ping);
 849     pcmk__xml_free(ping);
 850     return reply;
 851 }
 852 
 853 static enum crmd_fsa_input
 854 handle_ping(const xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 855 {
 856     xmlNode *reply = create_ping_reply(msg);
 857     if (reply != NULL) {
 858         (void) relay_message(reply, TRUE);
 859         pcmk__xml_free(reply);
 860     }
 861 
 862     // Nothing further to do
 863     return I_NULL;
 864 }
 865 
 866 /*!
 867  * \brief Handle a PCMK__CONTROLD_CMD_NODES message
 868  *
 869  * \param[in] request  Message XML
 870  *
 871  * \return Next FSA input
 872  */
 873 static enum crmd_fsa_input
 874 handle_node_list(const xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 875 {
 876     GHashTableIter iter;
 877     pcmk__node_status_t *node = NULL;
 878     xmlNode *reply = NULL;
 879     xmlNode *reply_data = NULL;
 880 
 881     // Create message data for reply
 882     reply_data = pcmk__xe_create(NULL, PCMK_XE_NODES);
 883     g_hash_table_iter_init(&iter, pcmk__peer_cache);
 884     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
 885         xmlNode *xml = pcmk__xe_create(reply_data, PCMK_XE_NODE);
 886 
 887         crm_xml_add_ll(xml, PCMK_XA_ID,
 888                        (long long) node->cluster_layer_id); // uint32_t
 889         crm_xml_add(xml, PCMK_XA_UNAME, node->name);
 890         crm_xml_add(xml, PCMK__XA_IN_CCM, node->state);
 891     }
 892 
 893     // Create and send reply
 894     reply = pcmk__new_reply(request, reply_data);
 895     pcmk__xml_free(reply_data);
 896     if (reply) {
 897         (void) relay_message(reply, TRUE);
 898         pcmk__xml_free(reply);
 899     }
 900 
 901     // Nothing further to do
 902     return I_NULL;
 903 }
 904 
 905 /*!
 906  * \brief Handle a CRM_OP_NODE_INFO request
 907  *
 908  * \param[in] msg  Message XML
 909  *
 910  * \return Next FSA input
 911  */
 912 static enum crmd_fsa_input
 913 handle_node_info_request(const xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 914 {
 915     const char *value = NULL;
 916     pcmk__node_status_t *node = NULL;
 917     int node_id = 0;
 918     xmlNode *reply = NULL;
 919     xmlNode *reply_data = NULL;
 920 
 921     // Build reply
 922 
 923     reply_data = pcmk__xe_create(NULL, PCMK_XE_NODE);
 924     crm_xml_add(reply_data, PCMK__XA_CRM_SUBSYSTEM, CRM_SYSTEM_CRMD);
 925 
 926     // Add whether current partition has quorum
 927     pcmk__xe_set_bool_attr(reply_data, PCMK_XA_HAVE_QUORUM,
 928                            pcmk_is_set(controld_globals.flags,
 929                                        controld_has_quorum));
 930 
 931     /* Check whether client requested node info by ID and/or name
 932      *
 933      * @TODO A Corosync-layer node ID is of type uint32_t. We should be able to
 934      * handle legitimate node IDs greater than INT_MAX, but currently we do not.
 935      */
 936     crm_element_value_int(msg, PCMK_XA_ID, &node_id);
 937     if (node_id < 0) {
 938         node_id = 0;
 939     }
 940     value = crm_element_value(msg, PCMK_XA_UNAME);
 941 
 942     // Default to local node if none given
 943     if ((node_id == 0) && (value == NULL)) {
 944         value = controld_globals.cluster->priv->node_name;
 945     }
 946 
 947     node = pcmk__search_node_caches(node_id, value, NULL,
 948                                     pcmk__node_search_any);
 949     if (node) {
 950         crm_xml_add(reply_data, PCMK_XA_ID, node->xml_id);
 951         crm_xml_add(reply_data, PCMK_XA_UNAME, node->name);
 952         crm_xml_add(reply_data, PCMK_XA_CRMD, node->state);
 953         pcmk__xe_set_bool_attr(reply_data, PCMK_XA_REMOTE_NODE,
 954                                pcmk_is_set(node->flags,
 955                                            pcmk__node_status_remote));
 956     }
 957 
 958     // Send reply
 959     reply = pcmk__new_reply(msg, reply_data);
 960     pcmk__xml_free(reply_data);
 961     if (reply != NULL) {
 962         (void) relay_message(reply, TRUE);
 963         pcmk__xml_free(reply);
 964     }
 965 
 966     // Nothing further to do
 967     return I_NULL;
 968 }
 969 
 970 static void
 971 verify_feature_set(xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 972 {
 973     const char *dc_version = crm_element_value(msg, PCMK_XA_CRM_FEATURE_SET);
 974 
 975     if (dc_version == NULL) {
 976         /* All we really know is that the DC feature set is older than 3.1.0,
 977          * but that's also all that really matters.
 978          */
 979         dc_version = "3.0.14";
 980     }
 981 
 982     if (feature_set_compatible(dc_version, CRM_FEATURE_SET)) {
 983         crm_trace("Local feature set (%s) is compatible with DC's (%s)",
 984                   CRM_FEATURE_SET, dc_version);
 985     } else {
 986         crm_err("Local feature set (%s) is incompatible with DC's (%s)",
 987                 CRM_FEATURE_SET, dc_version);
 988 
 989         // Nothing is likely to improve without administrator involvement
 990         controld_set_fsa_input_flags(R_STAYDOWN);
 991         crmd_exit(CRM_EX_FATAL);
 992     }
 993 }
 994 
 995 // DC gets own shutdown all-clear
 996 static enum crmd_fsa_input
 997 handle_shutdown_self_ack(xmlNode *stored_msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 998 {
 999     const char *host_from = crm_element_value(stored_msg, PCMK__XA_SRC);
1000 
1001     if (pcmk_is_set(controld_globals.fsa_input_register, R_SHUTDOWN)) {
1002         // The expected case -- we initiated own shutdown sequence
1003         crm_info("Shutting down controller");
1004         return I_STOP;
1005     }
1006 
1007     if (pcmk__str_eq(host_from, controld_globals.dc_name, pcmk__str_casei)) {
1008         // Must be logic error -- DC confirming its own unrequested shutdown
1009         crm_err("Shutting down controller immediately due to "
1010                 "unexpected shutdown confirmation");
1011         return I_TERMINATE;
1012     }
1013 
1014     if (controld_globals.fsa_state != S_STOPPING) {
1015         // Shouldn't happen -- non-DC confirming unrequested shutdown
1016         crm_err("Starting new DC election because %s is "
1017                 "confirming shutdown we did not request",
1018                 (host_from? host_from : "another node"));
1019         return I_ELECTION;
1020     }
1021 
1022     // Shouldn't happen, but we are already stopping anyway
1023     crm_debug("Ignoring unexpected shutdown confirmation from %s",
1024               (host_from? host_from : "another node"));
1025     return I_NULL;
1026 }
1027 
1028 // Non-DC gets shutdown all-clear from DC
1029 static enum crmd_fsa_input
1030 handle_shutdown_ack(xmlNode *stored_msg)
     /* [previous][next][first][last][top][bottom][index][help] */
1031 {
1032     const char *host_from = crm_element_value(stored_msg, PCMK__XA_SRC);
1033 
1034     if (host_from == NULL) {
1035         crm_warn("Ignoring shutdown request without origin specified");
1036         return I_NULL;
1037     }
1038 
1039     if (pcmk__str_eq(host_from, controld_globals.dc_name,
1040                      pcmk__str_null_matches|pcmk__str_casei)) {
1041 
1042         if (pcmk_is_set(controld_globals.fsa_input_register, R_SHUTDOWN)) {
1043             crm_info("Shutting down controller after confirmation from %s",
1044                      host_from);
1045         } else {
1046             crm_err("Shutting down controller after unexpected "
1047                     "shutdown request from %s", host_from);
1048             controld_set_fsa_input_flags(R_STAYDOWN);
1049         }
1050         return I_STOP;
1051     }
1052 
1053     crm_warn("Ignoring shutdown request from %s because DC is %s",
1054              host_from, controld_globals.dc_name);
1055     return I_NULL;
1056 }
1057 
1058 static enum crmd_fsa_input
1059 handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause)
     /* [previous][next][first][last][top][bottom][index][help] */
1060 {
1061     xmlNode *msg = NULL;
1062     const char *op = crm_element_value(stored_msg, PCMK__XA_CRM_TASK);
1063 
1064     /* Optimize this for the DC - it has the most to do */
1065 
1066     crm_log_xml_trace(stored_msg, "request");
1067     if (op == NULL) {
1068         crm_warn("Ignoring request without " PCMK__XA_CRM_TASK);
1069         return I_NULL;
1070     }
1071 
1072     if (strcmp(op, CRM_OP_SHUTDOWN_REQ) == 0) {
1073         const char *from = crm_element_value(stored_msg, PCMK__XA_SRC);
1074         pcmk__node_status_t *node =
1075             pcmk__search_node_caches(0, from, NULL,
1076                                      pcmk__node_search_cluster_member);
1077 
1078         pcmk__update_peer_expected(__func__, node, CRMD_JOINSTATE_DOWN);
1079         if(AM_I_DC == FALSE) {
1080             return I_NULL; /* Done */
1081         }
1082     }
1083 
1084     /*========== DC-Only Actions ==========*/
1085     if (AM_I_DC) {
1086         if (strcmp(op, CRM_OP_JOIN_ANNOUNCE) == 0) {
1087             return I_NODE_JOIN;
1088 
1089         } else if (strcmp(op, CRM_OP_JOIN_REQUEST) == 0) {
1090             return I_JOIN_REQUEST;
1091 
1092         } else if (strcmp(op, CRM_OP_JOIN_CONFIRM) == 0) {
1093             return I_JOIN_RESULT;
1094 
1095         } else if (strcmp(op, CRM_OP_SHUTDOWN) == 0) {
1096             return handle_shutdown_self_ack(stored_msg);
1097 
1098         } else if (strcmp(op, CRM_OP_SHUTDOWN_REQ) == 0) {
1099             // Another controller wants to shut down its node
1100             return handle_shutdown_request(stored_msg);
1101         }
1102     }
1103 
1104     /*========== common actions ==========*/
1105     if (strcmp(op, CRM_OP_NOVOTE) == 0) {
1106         ha_msg_input_t fsa_input;
1107 
1108         fsa_input.msg = stored_msg;
1109         register_fsa_input_adv(C_HA_MESSAGE, I_NULL, &fsa_input,
1110                                A_ELECTION_COUNT | A_ELECTION_CHECK, FALSE,
1111                                __func__);
1112 
1113     } else if (strcmp(op, CRM_OP_REMOTE_STATE) == 0) {
1114         /* a remote connection host is letting us know the node state */
1115         return handle_remote_state(stored_msg);
1116 
1117     } else if (strcmp(op, CRM_OP_THROTTLE) == 0) {
1118         throttle_update(stored_msg);
1119         if (AM_I_DC && (controld_globals.transition_graph != NULL)
1120             && !controld_globals.transition_graph->complete) {
1121 
1122             crm_debug("The throttle changed. Trigger a graph.");
1123             trigger_graph();
1124         }
1125         return I_NULL;
1126 
1127     } else if (strcmp(op, CRM_OP_CLEAR_FAILCOUNT) == 0) {
1128         return handle_failcount_op(stored_msg);
1129 
1130     } else if (strcmp(op, CRM_OP_VOTE) == 0) {
1131         /* count the vote and decide what to do after that */
1132         ha_msg_input_t fsa_input;
1133 
1134         fsa_input.msg = stored_msg;
1135         register_fsa_input_adv(C_HA_MESSAGE, I_NULL, &fsa_input,
1136                                A_ELECTION_COUNT | A_ELECTION_CHECK, FALSE,
1137                                __func__);
1138 
1139         /* Sometimes we _must_ go into S_ELECTION */
1140         if (controld_globals.fsa_state == S_HALT) {
1141             crm_debug("Forcing an election from S_HALT");
1142             return I_ELECTION;
1143         }
1144 
1145     } else if (strcmp(op, CRM_OP_JOIN_OFFER) == 0) {
1146         verify_feature_set(stored_msg);
1147         crm_debug("Raising I_JOIN_OFFER: join-%s",
1148                   crm_element_value(stored_msg, PCMK__XA_JOIN_ID));
1149         return I_JOIN_OFFER;
1150 
1151     } else if (strcmp(op, CRM_OP_JOIN_ACKNAK) == 0) {
1152         crm_debug("Raising I_JOIN_RESULT: join-%s",
1153                   crm_element_value(stored_msg, PCMK__XA_JOIN_ID));
1154         return I_JOIN_RESULT;
1155 
1156     } else if (strcmp(op, CRM_OP_LRM_DELETE) == 0) {
1157         return handle_lrm_delete(stored_msg);
1158 
1159     } else if ((strcmp(op, CRM_OP_LRM_FAIL) == 0)
1160                || (strcmp(op, CRM_OP_REPROBE) == 0)) {
1161 
1162         crm_xml_add(stored_msg, PCMK__XA_CRM_SYS_TO, CRM_SYSTEM_LRMD);
1163         return I_ROUTER;
1164 
1165     } else if (strcmp(op, CRM_OP_NOOP) == 0) {
1166         return I_NULL;
1167 
1168     } else if (strcmp(op, CRM_OP_PING) == 0) {
1169         return handle_ping(stored_msg);
1170 
1171     } else if (strcmp(op, CRM_OP_NODE_INFO) == 0) {
1172         return handle_node_info_request(stored_msg);
1173 
1174     } else if (strcmp(op, CRM_OP_RM_NODE_CACHE) == 0) {
1175         int id = 0;
1176         const char *name = NULL;
1177 
1178         crm_element_value_int(stored_msg, PCMK_XA_ID, &id);
1179         name = crm_element_value(stored_msg, PCMK_XA_UNAME);
1180 
1181         if(cause == C_IPC_MESSAGE) {
1182             msg = pcmk__new_request(pcmk_ipc_controld, CRM_SYSTEM_CRMD, NULL,
1183                                     CRM_SYSTEM_CRMD, CRM_OP_RM_NODE_CACHE,
1184                                     NULL);
1185             if (!pcmk__cluster_send_message(NULL, pcmk_ipc_controld, msg)) {
1186                 crm_err("Could not instruct peers to remove references to node %s/%u", name, id);
1187             } else {
1188                 crm_notice("Instructing peers to remove references to node %s/%u", name, id);
1189             }
1190             pcmk__xml_free(msg);
1191 
1192         } else {
1193             pcmk__cluster_forget_cluster_node(id, name);
1194 
1195             /* If we're forgetting this node, also forget any failures to fence
1196              * it, so we don't carry that over to any node added later with the
1197              * same name.
1198              */
1199             st_fail_count_reset(name);
1200         }
1201 
1202     } else if (strcmp(op, CRM_OP_MAINTENANCE_NODES) == 0) {
1203         xmlNode *wrapper = pcmk__xe_first_child(stored_msg, PCMK__XE_CRM_XML,
1204                                                 NULL, NULL);
1205         xmlNode *xml = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
1206 
1207         remote_ra_process_maintenance_nodes(xml);
1208 
1209     } else if (strcmp(op, PCMK__CONTROLD_CMD_NODES) == 0) {
1210         return handle_node_list(stored_msg);
1211 
1212         /*========== (NOT_DC)-Only Actions ==========*/
1213     } else if (!AM_I_DC) {
1214 
1215         if (strcmp(op, CRM_OP_SHUTDOWN) == 0) {
1216             return handle_shutdown_ack(stored_msg);
1217         }
1218 
1219     } else {
1220         crm_err("Unexpected request (%s) sent to %s", op, AM_I_DC ? "the DC" : "non-DC node");
1221         crm_log_xml_err(stored_msg, "Unexpected");
1222     }
1223 
1224     return I_NULL;
1225 }
1226 
1227 static void
1228 handle_response(xmlNode *stored_msg)
     /* [previous][next][first][last][top][bottom][index][help] */
1229 {
1230     const char *op = crm_element_value(stored_msg, PCMK__XA_CRM_TASK);
1231 
1232     crm_log_xml_trace(stored_msg, "reply");
1233     if (op == NULL) {
1234         crm_warn("Ignoring reply without " PCMK__XA_CRM_TASK);
1235 
1236     } else if (AM_I_DC && strcmp(op, CRM_OP_PECALC) == 0) {
1237         // Check whether scheduler answer been superseded by subsequent request
1238         const char *msg_ref = crm_element_value(stored_msg, PCMK_XA_REFERENCE);
1239 
1240         if (msg_ref == NULL) {
1241             crm_err("%s - Ignoring calculation with no reference", op);
1242 
1243         } else if (pcmk__str_eq(msg_ref, controld_globals.fsa_pe_ref,
1244                                 pcmk__str_none)) {
1245             ha_msg_input_t fsa_input;
1246 
1247             controld_stop_sched_timer();
1248             fsa_input.msg = stored_msg;
1249             register_fsa_input_later(C_IPC_MESSAGE, I_PE_SUCCESS, &fsa_input);
1250 
1251         } else {
1252             crm_info("%s calculation %s is obsolete", op, msg_ref);
1253         }
1254 
1255     } else if (strcmp(op, CRM_OP_VOTE) == 0
1256                || strcmp(op, CRM_OP_SHUTDOWN_REQ) == 0 || strcmp(op, CRM_OP_SHUTDOWN) == 0) {
1257 
1258     } else {
1259         const char *host_from = crm_element_value(stored_msg, PCMK__XA_SRC);
1260 
1261         crm_err("Unexpected response (op=%s, src=%s) sent to the %s",
1262                 op, host_from, AM_I_DC ? "DC" : "controller");
1263     }
1264 }
1265 
1266 static enum crmd_fsa_input
1267 handle_shutdown_request(xmlNode * stored_msg)
     /* [previous][next][first][last][top][bottom][index][help] */
1268 {
1269     /* handle here to avoid potential version issues
1270      *   where the shutdown message/procedure may have
1271      *   been changed in later versions.
1272      *
1273      * This way the DC is always in control of the shutdown
1274      */
1275 
1276     char *now_s = NULL;
1277     const char *host_from = crm_element_value(stored_msg, PCMK__XA_SRC);
1278 
1279     if (host_from == NULL) {
1280         /* we're shutting down and the DC */
1281         host_from = controld_globals.cluster->priv->node_name;
1282     }
1283 
1284     crm_info("Creating shutdown request for %s (state=%s)", host_from,
1285              fsa_state2string(controld_globals.fsa_state));
1286     crm_log_xml_trace(stored_msg, "message");
1287 
1288     now_s = pcmk__ttoa(time(NULL));
1289     update_attrd(host_from, PCMK__NODE_ATTR_SHUTDOWN, now_s, NULL, FALSE);
1290     free(now_s);
1291 
1292     /* will be picked up by the TE as long as its running */
1293     return I_NULL;
1294 }
1295 
1296 static void
1297 send_msg_via_ipc(xmlNode * msg, const char *sys, const char *src)
     /* [previous][next][first][last][top][bottom][index][help] */
1298 {
1299     pcmk__client_t *client_channel = NULL;
1300 
1301     CRM_CHECK(sys != NULL, return);
1302 
1303     client_channel = pcmk__find_client_by_id(sys);
1304 
1305     if (crm_element_value(msg, PCMK__XA_SRC) == NULL) {
1306         crm_xml_add(msg, PCMK__XA_SRC, src);
1307     }
1308 
1309     if (client_channel != NULL) {
1310         /* Transient clients such as crmadmin */
1311         pcmk__ipc_send_xml(client_channel, 0, msg, crm_ipc_server_event);
1312 
1313     } else if (pcmk__str_eq(sys, CRM_SYSTEM_TENGINE, pcmk__str_none)) {
1314         xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CRM_XML, NULL,
1315                                                 NULL);
1316         xmlNode *data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
1317 
1318         process_te_message(msg, data);
1319 
1320     } else if (pcmk__str_eq(sys, CRM_SYSTEM_LRMD, pcmk__str_none)) {
1321         fsa_data_t fsa_data;
1322         ha_msg_input_t fsa_input;
1323         xmlNode *wrapper = NULL;
1324 
1325         fsa_input.msg = msg;
1326 
1327         wrapper = pcmk__xe_first_child(msg, PCMK__XE_CRM_XML, NULL, NULL);
1328         fsa_input.xml = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
1329 
1330         fsa_data.id = 0;
1331         fsa_data.actions = 0;
1332         fsa_data.data = &fsa_input;
1333         fsa_data.fsa_input = I_MESSAGE;
1334         fsa_data.fsa_cause = C_IPC_MESSAGE;
1335         fsa_data.origin = __func__;
1336         fsa_data.data_type = fsa_dt_ha_msg;
1337 
1338         do_lrm_invoke(A_LRM_INVOKE, C_IPC_MESSAGE, controld_globals.fsa_state,
1339                       I_MESSAGE, &fsa_data);
1340 
1341     } else if (crmd_is_proxy_session(sys)) {
1342         crmd_proxy_send(sys, msg);
1343 
1344     } else {
1345         crm_info("Received invalid request: unknown subsystem '%s'", sys);
1346     }
1347 }
1348 
1349 void
1350 delete_ha_msg_input(ha_msg_input_t * orig)
     /* [previous][next][first][last][top][bottom][index][help] */
1351 {
1352     if (orig == NULL) {
1353         return;
1354     }
1355     pcmk__xml_free(orig->msg);
1356     free(orig);
1357 }
1358 
1359 /*!
1360  * \internal
1361  * \brief Notify the cluster of a remote node state change
1362  *
1363  * \param[in] node_name  Node's name
1364  * \param[in] node_up    true if node is up, false if down
1365  */
1366 void
1367 broadcast_remote_state_message(const char *node_name, bool node_up)
     /* [previous][next][first][last][top][bottom][index][help] */
1368 {
1369     xmlNode *msg = pcmk__new_request(pcmk_ipc_controld, CRM_SYSTEM_CRMD, NULL,
1370                                      CRM_SYSTEM_CRMD, CRM_OP_REMOTE_STATE,
1371                                      NULL);
1372 
1373     crm_info("Notifying cluster of Pacemaker Remote node %s %s",
1374              node_name, node_up? "coming up" : "going down");
1375 
1376     crm_xml_add(msg, PCMK_XA_ID, node_name);
1377     pcmk__xe_set_bool_attr(msg, PCMK__XA_IN_CCM, node_up);
1378 
1379     if (node_up) {
1380         crm_xml_add(msg, PCMK__XA_CONNECTION_HOST,
1381                     controld_globals.cluster->priv->node_name);
1382     }
1383 
1384     pcmk__cluster_send_message(NULL, pcmk_ipc_controld, msg);
1385     pcmk__xml_free(msg);
1386 }

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