root/daemons/fenced/pacemaker-fenced.c

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

DEFINITIONS

This source file includes following definitions.
  1. st_ipc_accept
  2. st_ipc_dispatch
  3. st_ipc_closed
  4. st_ipc_destroy
  5. stonith_peer_callback
  6. stonith_peer_ais_callback
  7. stonith_peer_cs_destroy
  8. do_local_reply
  9. get_stonith_flag
  10. stonith_notify_client
  11. do_stonith_async_timeout_update
  12. fenced_send_notification
  13. send_config_notification
  14. fenced_send_device_notification
  15. fenced_send_level_notification
  16. topology_remove_helper
  17. remove_cib_device
  18. remove_topology_level
  19. add_topology_level
  20. remove_fencing_topology
  21. register_fencing_topology
  22. fencing_topology_init
  23. our_node_allowed_for
  24. watchdog_device_update
  25. update_stonith_watchdog_timeout_ms
  26. cib_device_update
  27. cib_devices_update
  28. update_cib_stonith_devices_v2
  29. update_cib_stonith_devices_v1
  30. update_cib_stonith_devices
  31. node_has_attr
  32. node_does_watchdog_fencing
  33. update_fencing_topology
  34. update_cib_cache_cb
  35. init_cib_cache_cb
  36. stonith_shutdown
  37. cib_connection_destroy
  38. stonith_cleanup
  39. setup_cib
  40. st_peer_update_callback
  41. fencer_metadata
  42. main

   1 /*
   2  * Copyright 2009-2022 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <sys/param.h>
  13 #include <stdio.h>
  14 #include <sys/types.h>
  15 #include <sys/stat.h>
  16 #include <unistd.h>
  17 #include <sys/utsname.h>
  18 
  19 #include <stdlib.h>
  20 #include <errno.h>
  21 #include <fcntl.h>
  22 #include <inttypes.h>  // PRIu32, PRIx32
  23 
  24 #include <crm/crm.h>
  25 #include <crm/msg_xml.h>
  26 #include <crm/common/ipc.h>
  27 #include <crm/common/ipc_internal.h>
  28 #include <crm/cluster/internal.h>
  29 
  30 #include <crm/stonith-ng.h>
  31 #include <crm/fencing/internal.h>
  32 #include <crm/common/xml.h>
  33 #include <crm/common/xml_internal.h>
  34 
  35 #include <crm/common/mainloop.h>
  36 
  37 #include <crm/cib/internal.h>
  38 #include <crm/pengine/status.h>
  39 #include <pacemaker-internal.h>
  40 
  41 #include <pacemaker-fenced.h>
  42 
  43 char *stonith_our_uname = NULL;
  44 long stonith_watchdog_timeout_ms = 0;
  45 GList *stonith_watchdog_targets = NULL;
  46 
  47 static GMainLoop *mainloop = NULL;
  48 
  49 gboolean stand_alone = FALSE;
  50 static gboolean no_cib_connect = FALSE;
  51 static gboolean stonith_shutdown_flag = FALSE;
  52 
  53 static qb_ipcs_service_t *ipcs = NULL;
  54 static xmlNode *local_cib = NULL;
  55 static pe_working_set_t *fenced_data_set = NULL;
  56 static const unsigned long long data_set_flags = pe_flag_quick_location
  57                                                  | pe_flag_no_compat
  58                                                  | pe_flag_no_counts;
  59 
  60 static cib_t *cib_api = NULL;
  61 
  62 static pcmk__output_t *out = NULL;
  63 
  64 pcmk__supported_format_t formats[] = {
  65     PCMK__SUPPORTED_FORMAT_LOG,
  66     PCMK__SUPPORTED_FORMAT_NONE,
  67     PCMK__SUPPORTED_FORMAT_TEXT,
  68     { NULL, NULL, NULL }
  69 };
  70 
  71 static void stonith_shutdown(int nsig);
  72 static void stonith_cleanup(void);
  73 
  74 static int32_t
  75 st_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
  76 {
  77     if (stonith_shutdown_flag) {
  78         crm_info("Ignoring new client [%d] during shutdown",
  79                  pcmk__client_pid(c));
  80         return -EPERM;
  81     }
  82 
  83     if (pcmk__new_client(c, uid, gid) == NULL) {
  84         return -EIO;
  85     }
  86     return 0;
  87 }
  88 
  89 /* Exit code means? */
  90 static int32_t
  91 st_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
  92 {
  93     uint32_t id = 0;
  94     uint32_t flags = 0;
  95     int call_options = 0;
  96     xmlNode *request = NULL;
  97     pcmk__client_t *c = pcmk__find_client(qbc);
  98     const char *op = NULL;
  99 
 100     if (c == NULL) {
 101         crm_info("Invalid client: %p", qbc);
 102         return 0;
 103     }
 104 
 105     request = pcmk__client_data2xml(c, data, &id, &flags);
 106     if (request == NULL) {
 107         pcmk__ipc_send_ack(c, id, flags, "nack", NULL, CRM_EX_PROTOCOL);
 108         return 0;
 109     }
 110 
 111 
 112     op = crm_element_value(request, F_CRM_TASK);
 113     if(pcmk__str_eq(op, CRM_OP_RM_NODE_CACHE, pcmk__str_casei)) {
 114         crm_xml_add(request, F_TYPE, T_STONITH_NG);
 115         crm_xml_add(request, F_STONITH_OPERATION, op);
 116         crm_xml_add(request, F_STONITH_CLIENTID, c->id);
 117         crm_xml_add(request, F_STONITH_CLIENTNAME, pcmk__client_name(c));
 118         crm_xml_add(request, F_STONITH_CLIENTNODE, stonith_our_uname);
 119 
 120         send_cluster_message(NULL, crm_msg_stonith_ng, request, FALSE);
 121         free_xml(request);
 122         return 0;
 123     }
 124 
 125     if (c->name == NULL) {
 126         const char *value = crm_element_value(request, F_STONITH_CLIENTNAME);
 127 
 128         if (value == NULL) {
 129             value = "unknown";
 130         }
 131         c->name = crm_strdup_printf("%s.%u", value, c->pid);
 132     }
 133 
 134     crm_element_value_int(request, F_STONITH_CALLOPTS, &call_options);
 135     crm_trace("Flags %#08" PRIx32 "/%#08x for command %" PRIu32
 136               " from client %s", flags, call_options, id, pcmk__client_name(c));
 137 
 138     if (pcmk_is_set(call_options, st_opt_sync_call)) {
 139         CRM_ASSERT(flags & crm_ipc_client_response);
 140         CRM_LOG_ASSERT(c->request_id == 0);     /* This means the client has two synchronous events in-flight */
 141         c->request_id = id;     /* Reply only to the last one */
 142     }
 143 
 144     crm_xml_add(request, F_STONITH_CLIENTID, c->id);
 145     crm_xml_add(request, F_STONITH_CLIENTNAME, pcmk__client_name(c));
 146     crm_xml_add(request, F_STONITH_CLIENTNODE, stonith_our_uname);
 147 
 148     crm_log_xml_trace(request, "ipc-received");
 149     stonith_command(c, id, flags, request, NULL);
 150 
 151     free_xml(request);
 152     return 0;
 153 }
 154 
 155 /* Error code means? */
 156 static int32_t
 157 st_ipc_closed(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 158 {
 159     pcmk__client_t *client = pcmk__find_client(c);
 160 
 161     if (client == NULL) {
 162         return 0;
 163     }
 164 
 165     crm_trace("Connection %p closed", c);
 166     pcmk__free_client(client);
 167 
 168     /* 0 means: yes, go ahead and destroy the connection */
 169     return 0;
 170 }
 171 
 172 static void
 173 st_ipc_destroy(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 174 {
 175     crm_trace("Connection %p destroyed", c);
 176     st_ipc_closed(c);
 177 }
 178 
 179 static void
 180 stonith_peer_callback(xmlNode * msg, void *private_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 181 {
 182     const char *remote_peer = crm_element_value(msg, F_ORIG);
 183     const char *op = crm_element_value(msg, F_STONITH_OPERATION);
 184 
 185     if (pcmk__str_eq(op, "poke", pcmk__str_none)) {
 186         return;
 187     }
 188 
 189     crm_log_xml_trace(msg, "Peer[inbound]");
 190     stonith_command(NULL, 0, 0, msg, remote_peer);
 191 }
 192 
 193 #if SUPPORT_COROSYNC
 194 static void
 195 stonith_peer_ais_callback(cpg_handle_t handle,
     /* [previous][next][first][last][top][bottom][index][help] */
 196                           const struct cpg_name *groupName,
 197                           uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
 198 {
 199     uint32_t kind = 0;
 200     xmlNode *xml = NULL;
 201     const char *from = NULL;
 202     char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
 203 
 204     if(data == NULL) {
 205         return;
 206     }
 207     if (kind == crm_class_cluster) {
 208         xml = string2xml(data);
 209         if (xml == NULL) {
 210             crm_err("Invalid XML: '%.120s'", data);
 211             free(data);
 212             return;
 213         }
 214         crm_xml_add(xml, F_ORIG, from);
 215         /* crm_xml_add_int(xml, F_SEQ, wrapper->id); */
 216         stonith_peer_callback(xml, NULL);
 217     }
 218 
 219     free_xml(xml);
 220     free(data);
 221     return;
 222 }
 223 
 224 static void
 225 stonith_peer_cs_destroy(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 226 {
 227     crm_crit("Lost connection to cluster layer, shutting down");
 228     stonith_shutdown(0);
 229 }
 230 #endif
 231 
 232 void
 233 do_local_reply(xmlNode *notify_src, pcmk__client_t *client, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 234 {
 235     /* send callback to originating child */
 236     int local_rc = pcmk_rc_ok;
 237     int rid = 0;
 238     uint32_t ipc_flags = crm_ipc_server_event;
 239 
 240     if (pcmk_is_set(call_options, st_opt_sync_call)) {
 241         CRM_LOG_ASSERT(client->request_id);
 242         rid = client->request_id;
 243         client->request_id = 0;
 244         ipc_flags = crm_ipc_flags_none;
 245     }
 246 
 247     local_rc = pcmk__ipc_send_xml(client, rid, notify_src, ipc_flags);
 248     if (local_rc == pcmk_rc_ok) {
 249         crm_trace("Sent response %d to client %s",
 250                   rid, pcmk__client_name(client));
 251     } else {
 252         crm_warn("%synchronous reply to client %s failed: %s",
 253                  (pcmk_is_set(call_options, st_opt_sync_call)? "S" : "As"),
 254                  pcmk__client_name(client), pcmk_rc_str(local_rc));
 255     }
 256 }
 257 
 258 uint64_t
 259 get_stonith_flag(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 260 {
 261     if (pcmk__str_eq(name, T_STONITH_NOTIFY_FENCE, pcmk__str_casei)) {
 262         return st_callback_notify_fence;
 263 
 264     } else if (pcmk__str_eq(name, STONITH_OP_DEVICE_ADD, pcmk__str_casei)) {
 265         return st_callback_device_add;
 266 
 267     } else if (pcmk__str_eq(name, STONITH_OP_DEVICE_DEL, pcmk__str_casei)) {
 268         return st_callback_device_del;
 269 
 270     } else if (pcmk__str_eq(name, T_STONITH_NOTIFY_HISTORY, pcmk__str_casei)) {
 271         return st_callback_notify_history;
 272 
 273     } else if (pcmk__str_eq(name, T_STONITH_NOTIFY_HISTORY_SYNCED, pcmk__str_casei)) {
 274         return st_callback_notify_history_synced;
 275 
 276     }
 277     return st_callback_unknown;
 278 }
 279 
 280 static void
 281 stonith_notify_client(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 282 {
 283 
 284     xmlNode *update_msg = user_data;
 285     pcmk__client_t *client = value;
 286     const char *type = NULL;
 287 
 288     CRM_CHECK(client != NULL, return);
 289     CRM_CHECK(update_msg != NULL, return);
 290 
 291     type = crm_element_value(update_msg, F_SUBTYPE);
 292     CRM_CHECK(type != NULL, crm_log_xml_err(update_msg, "notify"); return);
 293 
 294     if (client->ipcs == NULL) {
 295         crm_trace("Skipping client with NULL channel");
 296         return;
 297     }
 298 
 299     if (pcmk_is_set(client->flags, get_stonith_flag(type))) {
 300         int rc = pcmk__ipc_send_xml(client, 0, update_msg,
 301                                     crm_ipc_server_event);
 302 
 303         if (rc != pcmk_rc_ok) {
 304             crm_warn("%s notification of client %s failed: %s "
 305                      CRM_XS " id=%.8s rc=%d", type, pcmk__client_name(client),
 306                      pcmk_rc_str(rc), client->id, rc);
 307         } else {
 308             crm_trace("Sent %s notification to client %s",
 309                       type, pcmk__client_name(client));
 310         }
 311     }
 312 }
 313 
 314 void
 315 do_stonith_async_timeout_update(const char *client_id, const char *call_id, int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
 316 {
 317     pcmk__client_t *client = NULL;
 318     xmlNode *notify_data = NULL;
 319 
 320     if (!timeout || !call_id || !client_id) {
 321         return;
 322     }
 323 
 324     client = pcmk__find_client_by_id(client_id);
 325     if (!client) {
 326         return;
 327     }
 328 
 329     notify_data = create_xml_node(NULL, T_STONITH_TIMEOUT_VALUE);
 330     crm_xml_add(notify_data, F_TYPE, T_STONITH_TIMEOUT_VALUE);
 331     crm_xml_add(notify_data, F_STONITH_CALLID, call_id);
 332     crm_xml_add_int(notify_data, F_STONITH_TIMEOUT, timeout);
 333 
 334     crm_trace("timeout update is %d for client %s and call id %s", timeout, client_id, call_id);
 335 
 336     if (client) {
 337         pcmk__ipc_send_xml(client, 0, notify_data, crm_ipc_server_event);
 338     }
 339 
 340     free_xml(notify_data);
 341 }
 342 
 343 /*!
 344  * \internal
 345  * \brief Notify relevant IPC clients of a fencing operation result
 346  *
 347  * \param[in] type     Notification type
 348  * \param[in] result   Result of fencing operation (assume success if NULL)
 349  * \param[in] data     If not NULL, add to notification as call data
 350  */
 351 void
 352 fenced_send_notification(const char *type, const pcmk__action_result_t *result,
     /* [previous][next][first][last][top][bottom][index][help] */
 353                          xmlNode *data)
 354 {
 355     /* TODO: Standardize the contents of data */
 356     xmlNode *update_msg = create_xml_node(NULL, "notify");
 357 
 358     CRM_LOG_ASSERT(type != NULL);
 359 
 360     crm_xml_add(update_msg, F_TYPE, T_STONITH_NOTIFY);
 361     crm_xml_add(update_msg, F_SUBTYPE, type);
 362     crm_xml_add(update_msg, F_STONITH_OPERATION, type);
 363     stonith__xe_set_result(update_msg, result);
 364 
 365     if (data != NULL) {
 366         add_message_xml(update_msg, F_STONITH_CALLDATA, data);
 367     }
 368 
 369     crm_trace("Notifying clients");
 370     pcmk__foreach_ipc_client(stonith_notify_client, update_msg);
 371     free_xml(update_msg);
 372     crm_trace("Notify complete");
 373 }
 374 
 375 /*!
 376  * \internal
 377  * \brief Send notifications for a configuration change to subscribed clients
 378  *
 379  * \param[in] op      Notification type (STONITH_OP_DEVICE_ADD,
 380  *                    STONITH_OP_DEVICE_DEL, STONITH_OP_LEVEL_ADD, or
 381  *                    STONITH_OP_LEVEL_DEL)
 382  * \param[in] result  Operation result
 383  * \param[in] desc    Description of what changed
 384  * \param[in] active  Current number of devices or topologies in use
 385  */
 386 static void
 387 send_config_notification(const char *op, const pcmk__action_result_t *result,
     /* [previous][next][first][last][top][bottom][index][help] */
 388                          const char *desc, int active)
 389 {
 390     xmlNode *notify_data = create_xml_node(NULL, op);
 391 
 392     CRM_CHECK(notify_data != NULL, return);
 393 
 394     crm_xml_add(notify_data, F_STONITH_DEVICE, desc);
 395     crm_xml_add_int(notify_data, F_STONITH_ACTIVE, active);
 396 
 397     fenced_send_notification(op, result, notify_data);
 398     free_xml(notify_data);
 399 }
 400 
 401 /*!
 402  * \internal
 403  * \brief Send notifications for a device change to subscribed clients
 404  *
 405  * \param[in] op      Notification type (STONITH_OP_DEVICE_ADD or
 406  *                    STONITH_OP_DEVICE_DEL)
 407  * \param[in] result  Operation result
 408  * \param[in] desc    ID of device that changed
 409  */
 410 void
 411 fenced_send_device_notification(const char *op,
     /* [previous][next][first][last][top][bottom][index][help] */
 412                                 const pcmk__action_result_t *result,
 413                                 const char *desc)
 414 {
 415     send_config_notification(op, result, desc, g_hash_table_size(device_list));
 416 }
 417 
 418 /*!
 419  * \internal
 420  * \brief Send notifications for a topology level change to subscribed clients
 421  *
 422  * \param[in] op      Notification type (STONITH_OP_LEVEL_ADD or
 423  *                    STONITH_OP_LEVEL_DEL)
 424  * \param[in] result  Operation result
 425  * \param[in] desc    String representation of level (<target>[<level_index>])
 426  */
 427 void
 428 fenced_send_level_notification(const char *op,
     /* [previous][next][first][last][top][bottom][index][help] */
 429                                const pcmk__action_result_t *result,
 430                                const char *desc)
 431 {
 432     send_config_notification(op, result, desc, g_hash_table_size(topology));
 433 }
 434 
 435 static void
 436 topology_remove_helper(const char *node, int level)
     /* [previous][next][first][last][top][bottom][index][help] */
 437 {
 438     char *desc = NULL;
 439     pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
 440     xmlNode *data = create_xml_node(NULL, XML_TAG_FENCING_LEVEL);
 441 
 442     crm_xml_add(data, F_STONITH_ORIGIN, __func__);
 443     crm_xml_add_int(data, XML_ATTR_STONITH_INDEX, level);
 444     crm_xml_add(data, XML_ATTR_STONITH_TARGET, node);
 445 
 446     fenced_unregister_level(data, &desc, &result);
 447     fenced_send_level_notification(STONITH_OP_LEVEL_DEL, &result, desc);
 448     pcmk__reset_result(&result);
 449     free_xml(data);
 450     free(desc);
 451 }
 452 
 453 static void
 454 remove_cib_device(xmlXPathObjectPtr xpathObj)
     /* [previous][next][first][last][top][bottom][index][help] */
 455 {
 456     int max = numXpathResults(xpathObj), lpc = 0;
 457 
 458     for (lpc = 0; lpc < max; lpc++) {
 459         const char *rsc_id = NULL;
 460         const char *standard = NULL;
 461         xmlNode *match = getXpathResult(xpathObj, lpc);
 462 
 463         CRM_LOG_ASSERT(match != NULL);
 464         if(match != NULL) {
 465             standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
 466         }
 467 
 468         if (!pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
 469             continue;
 470         }
 471 
 472         rsc_id = crm_element_value(match, XML_ATTR_ID);
 473 
 474         stonith_device_remove(rsc_id, true);
 475     }
 476 }
 477 
 478 static void
 479 remove_topology_level(xmlNode *match)
     /* [previous][next][first][last][top][bottom][index][help] */
 480 {
 481     int index = 0;
 482     char *key = NULL;
 483 
 484     CRM_CHECK(match != NULL, return);
 485 
 486     key = stonith_level_key(match, fenced_target_by_unknown);
 487     crm_element_value_int(match, XML_ATTR_STONITH_INDEX, &index);
 488     topology_remove_helper(key, index);
 489     free(key);
 490 }
 491 
 492 static void
 493 add_topology_level(xmlNode *match)
     /* [previous][next][first][last][top][bottom][index][help] */
 494 {
 495     char *desc = NULL;
 496     pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
 497 
 498     CRM_CHECK(match != NULL, return);
 499 
 500     fenced_register_level(match, &desc, &result);
 501     fenced_send_level_notification(STONITH_OP_LEVEL_ADD, &result, desc);
 502     pcmk__reset_result(&result);
 503     free(desc);
 504 }
 505 
 506 static void
 507 remove_fencing_topology(xmlXPathObjectPtr xpathObj)
     /* [previous][next][first][last][top][bottom][index][help] */
 508 {
 509     int max = numXpathResults(xpathObj), lpc = 0;
 510 
 511     for (lpc = 0; lpc < max; lpc++) {
 512         xmlNode *match = getXpathResult(xpathObj, lpc);
 513 
 514         CRM_LOG_ASSERT(match != NULL);
 515         if (match && crm_element_value(match, XML_DIFF_MARKER)) {
 516             /* Deletion */
 517             int index = 0;
 518             char *target = stonith_level_key(match, fenced_target_by_unknown);
 519 
 520             crm_element_value_int(match, XML_ATTR_STONITH_INDEX, &index);
 521             if (target == NULL) {
 522                 crm_err("Invalid fencing target in element %s", ID(match));
 523 
 524             } else if (index <= 0) {
 525                 crm_err("Invalid level for %s in element %s", target, ID(match));
 526 
 527             } else {
 528                 topology_remove_helper(target, index);
 529             }
 530             /* } else { Deal with modifications during the 'addition' stage */
 531         }
 532     }
 533 }
 534 
 535 static void
 536 register_fencing_topology(xmlXPathObjectPtr xpathObj)
     /* [previous][next][first][last][top][bottom][index][help] */
 537 {
 538     int max = numXpathResults(xpathObj), lpc = 0;
 539 
 540     for (lpc = 0; lpc < max; lpc++) {
 541         xmlNode *match = getXpathResult(xpathObj, lpc);
 542 
 543         remove_topology_level(match);
 544         add_topology_level(match);
 545     }
 546 }
 547 
 548 /* Fencing
 549 <diff crm_feature_set="3.0.6">
 550   <diff-removed>
 551     <fencing-topology>
 552       <fencing-level id="f-p1.1" target="pcmk-1" index="1" devices="poison-pill" __crm_diff_marker__="removed:top"/>
 553       <fencing-level id="f-p1.2" target="pcmk-1" index="2" devices="power" __crm_diff_marker__="removed:top"/>
 554       <fencing-level devices="disk,network" id="f-p2.1"/>
 555     </fencing-topology>
 556   </diff-removed>
 557   <diff-added>
 558     <fencing-topology>
 559       <fencing-level id="f-p.1" target="pcmk-1" index="1" devices="poison-pill" __crm_diff_marker__="added:top"/>
 560       <fencing-level id="f-p2.1" target="pcmk-2" index="1" devices="disk,something"/>
 561       <fencing-level id="f-p3.1" target="pcmk-2" index="2" devices="power" __crm_diff_marker__="added:top"/>
 562     </fencing-topology>
 563   </diff-added>
 564 </diff>
 565 */
 566 
 567 static void
 568 fencing_topology_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 569 {
 570     xmlXPathObjectPtr xpathObj = NULL;
 571     const char *xpath = "//" XML_TAG_FENCING_LEVEL;
 572 
 573     crm_trace("Full topology refresh");
 574     free_topology_list();
 575     init_topology_list();
 576 
 577     /* Grab everything */
 578     xpathObj = xpath_search(local_cib, xpath);
 579     register_fencing_topology(xpathObj);
 580 
 581     freeXpathObject(xpathObj);
 582 }
 583 
 584 #define rsc_name(x) x->clone_name?x->clone_name:x->id
 585 
 586 /*!
 587  * \internal
 588  * \brief Check whether our uname is in a resource's allowed node list
 589  *
 590  * \param[in] rsc  Resource to check
 591  *
 592  * \return Pointer to node object if found, NULL otherwise
 593  */
 594 static pe_node_t *
 595 our_node_allowed_for(const pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 596 {
 597     GHashTableIter iter;
 598     pe_node_t *node = NULL;
 599 
 600     if (rsc && stonith_our_uname) {
 601         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
 602         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 603             if (node && strcmp(node->details->uname, stonith_our_uname) == 0) {
 604                 break;
 605             }
 606             node = NULL;
 607         }
 608     }
 609     return node;
 610 }
 611 
 612 static void
 613 watchdog_device_update(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 614 {
 615     if (stonith_watchdog_timeout_ms > 0) {
 616         if (!g_hash_table_lookup(device_list, STONITH_WATCHDOG_ID) &&
 617             !stonith_watchdog_targets) {
 618             /* getting here watchdog-fencing enabled, no device there yet
 619                and reason isn't stonith_watchdog_targets preventing that
 620              */
 621             int rc;
 622             xmlNode *xml;
 623 
 624             xml = create_device_registration_xml(
 625                     STONITH_WATCHDOG_ID,
 626                     st_namespace_internal,
 627                     STONITH_WATCHDOG_AGENT,
 628                     NULL, /* stonith_device_register will add our
 629                              own name as PCMK_STONITH_HOST_LIST param
 630                              so we can skip that here
 631                            */
 632                     NULL);
 633             rc = stonith_device_register(xml, TRUE);
 634             free_xml(xml);
 635             if (rc != pcmk_ok) {
 636                 crm_crit("Cannot register watchdog pseudo fence agent");
 637                 crm_exit(CRM_EX_FATAL);
 638             }
 639         }
 640 
 641     } else {
 642         /* be silent if no device - todo parameter to stonith_device_remove */
 643         if (g_hash_table_lookup(device_list, STONITH_WATCHDOG_ID)) {
 644             stonith_device_remove(STONITH_WATCHDOG_ID, true);
 645         }
 646     }
 647 }
 648 
 649 static void
 650 update_stonith_watchdog_timeout_ms(xmlNode *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 651 {
 652     long timeout_ms = 0;
 653     xmlNode *stonith_watchdog_xml = NULL;
 654     const char *value = NULL;
 655 
 656     stonith_watchdog_xml = get_xpath_object("//nvpair[@name='stonith-watchdog-timeout']",
 657                                             cib, LOG_NEVER);
 658     if (stonith_watchdog_xml) {
 659         value = crm_element_value(stonith_watchdog_xml, XML_NVPAIR_ATTR_VALUE);
 660     }
 661     if (value) {
 662         timeout_ms = crm_get_msec(value);
 663     }
 664 
 665     if (timeout_ms < 0) {
 666         timeout_ms = pcmk__auto_watchdog_timeout();
 667     }
 668 
 669     stonith_watchdog_timeout_ms = timeout_ms;
 670 }
 671 
 672 /*!
 673  * \internal
 674  * \brief If a resource or any of its children are STONITH devices, update their
 675  *        definitions given a cluster working set.
 676  *
 677  * \param[in,out] rsc       Resource to check
 678  * \param[in,out] data_set  Cluster working set with device information
 679  */
 680 static void
 681 cib_device_update(pe_resource_t *rsc, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 682 {
 683     pe_node_t *node = NULL;
 684     const char *value = NULL;
 685     const char *rclass = NULL;
 686     pe_node_t *parent = NULL;
 687 
 688     /* If this is a complex resource, check children rather than this resource itself. */
 689     if(rsc->children) {
 690         GList *gIter = NULL;
 691         for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 692             cib_device_update(gIter->data, data_set);
 693             if(pe_rsc_is_clone(rsc)) {
 694                 crm_trace("Only processing one copy of the clone %s", rsc->id);
 695                 break;
 696             }
 697         }
 698         return;
 699     }
 700 
 701     /* We only care about STONITH resources. */
 702     rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
 703     if (!pcmk__str_eq(rclass, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
 704         return;
 705     }
 706 
 707     /* If this STONITH resource is disabled, remove it. */
 708     if (pe__resource_is_disabled(rsc)) {
 709         crm_info("Device %s has been disabled", rsc->id);
 710         return;
 711     }
 712 
 713     /* if watchdog-fencing is disabled handle any watchdog-fence
 714        resource as if it was disabled
 715      */
 716     if ((stonith_watchdog_timeout_ms <= 0) &&
 717         pcmk__str_eq(rsc->id, STONITH_WATCHDOG_ID, pcmk__str_none)) {
 718         crm_info("Watchdog-fencing disabled thus handling "
 719                  "device %s as disabled", rsc->id);
 720         return;
 721     }
 722 
 723     /* Check whether our node is allowed for this resource (and its parent if in a group) */
 724     node = our_node_allowed_for(rsc);
 725     if (rsc->parent && (rsc->parent->variant == pe_group)) {
 726         parent = our_node_allowed_for(rsc->parent);
 727     }
 728 
 729     if(node == NULL) {
 730         /* Our node is disallowed, so remove the device */
 731         GHashTableIter iter;
 732 
 733         crm_info("Device %s has been disabled on %s: unknown", rsc->id, stonith_our_uname);
 734         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
 735         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 736             crm_trace("Available: %s = %d", pe__node_name(node), node->weight);
 737         }
 738 
 739         return;
 740 
 741     } else if(node->weight < 0 || (parent && parent->weight < 0)) {
 742         /* Our node (or its group) is disallowed by score, so remove the device */
 743         int score = (node->weight < 0)? node->weight : parent->weight;
 744 
 745         crm_info("Device %s has been disabled on %s: score=%s",
 746                  rsc->id, stonith_our_uname, pcmk_readable_score(score));
 747         return;
 748 
 749     } else {
 750         /* Our node is allowed, so update the device information */
 751         int rc;
 752         xmlNode *data;
 753         GHashTable *rsc_params = NULL;
 754         GHashTableIter gIter;
 755         stonith_key_value_t *params = NULL;
 756 
 757         const char *name = NULL;
 758         const char *agent = crm_element_value(rsc->xml, XML_EXPR_ATTR_TYPE);
 759         const char *rsc_provides = NULL;
 760 
 761         crm_debug("Device %s is allowed on %s: score=%d", rsc->id, stonith_our_uname, node->weight);
 762         rsc_params = pe_rsc_params(rsc, node, data_set);
 763         get_meta_attributes(rsc->meta, rsc, node, data_set);
 764 
 765         rsc_provides = g_hash_table_lookup(rsc->meta, PCMK_STONITH_PROVIDES);
 766 
 767         g_hash_table_iter_init(&gIter, rsc_params);
 768         while (g_hash_table_iter_next(&gIter, (gpointer *) & name, (gpointer *) & value)) {
 769             if (!name || !value) {
 770                 continue;
 771             }
 772             params = stonith_key_value_add(params, name, value);
 773             crm_trace(" %s=%s", name, value);
 774         }
 775 
 776         data = create_device_registration_xml(rsc_name(rsc), st_namespace_any,
 777                                               agent, params, rsc_provides);
 778         stonith_key_value_freeall(params, 1, 1);
 779         rc = stonith_device_register(data, TRUE);
 780         CRM_ASSERT(rc == pcmk_ok);
 781         free_xml(data);
 782     }
 783 }
 784 
 785 /*!
 786  * \internal
 787  * \brief Update all STONITH device definitions based on current CIB
 788  */
 789 static void
 790 cib_devices_update(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 791 {
 792     GHashTableIter iter;
 793     stonith_device_t *device = NULL;
 794 
 795     crm_info("Updating devices to version %s.%s.%s",
 796              crm_element_value(local_cib, XML_ATTR_GENERATION_ADMIN),
 797              crm_element_value(local_cib, XML_ATTR_GENERATION),
 798              crm_element_value(local_cib, XML_ATTR_NUMUPDATES));
 799 
 800     if (fenced_data_set->now != NULL) {
 801         crm_time_free(fenced_data_set->now);
 802         fenced_data_set->now = NULL;
 803     }
 804     fenced_data_set->localhost = stonith_our_uname;
 805     pcmk__schedule_actions(local_cib, data_set_flags, fenced_data_set);
 806 
 807     g_hash_table_iter_init(&iter, device_list);
 808     while (g_hash_table_iter_next(&iter, NULL, (void **)&device)) {
 809         if (device->cib_registered) {
 810             device->dirty = TRUE;
 811         }
 812     }
 813 
 814     /* have list repopulated if cib has a watchdog-fencing-resource
 815        TODO: keep a cached list for queries happening while we are refreshing
 816      */
 817     g_list_free_full(stonith_watchdog_targets, free);
 818     stonith_watchdog_targets = NULL;
 819     g_list_foreach(fenced_data_set->resources, (GFunc) cib_device_update, fenced_data_set);
 820 
 821     g_hash_table_iter_init(&iter, device_list);
 822     while (g_hash_table_iter_next(&iter, NULL, (void **)&device)) {
 823         if (device->dirty) {
 824             g_hash_table_iter_remove(&iter);
 825         }
 826     }
 827 
 828     fenced_data_set->input = NULL; // Wasn't a copy, so don't let API free it
 829     pe_reset_working_set(fenced_data_set);
 830 }
 831 
 832 static void
 833 update_cib_stonith_devices_v2(const char *event, xmlNode * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 834 {
 835     xmlNode *change = NULL;
 836     char *reason = NULL;
 837     bool needs_update = FALSE;
 838     xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
 839 
 840     for (change = pcmk__xml_first_child(patchset); change != NULL;
 841          change = pcmk__xml_next(change)) {
 842         const char *op = crm_element_value(change, XML_DIFF_OP);
 843         const char *xpath = crm_element_value(change, XML_DIFF_PATH);
 844         const char *shortpath = NULL;
 845 
 846         if ((op == NULL) ||
 847             (strcmp(op, "move") == 0) ||
 848             strstr(xpath, "/"XML_CIB_TAG_STATUS)) {
 849             continue;
 850         } else if (pcmk__str_eq(op, "delete", pcmk__str_casei) && strstr(xpath, "/"XML_CIB_TAG_RESOURCE)) {
 851             const char *rsc_id = NULL;
 852             char *search = NULL;
 853             char *mutable = NULL;
 854 
 855             if (strstr(xpath, XML_TAG_ATTR_SETS) ||
 856                 strstr(xpath, XML_TAG_META_SETS)) {
 857                 needs_update = TRUE;
 858                 reason = strdup("(meta) attribute deleted from resource");
 859                 break;
 860             } 
 861             mutable = strdup(xpath);
 862             rsc_id = strstr(mutable, "primitive[@id=\'");
 863             if (rsc_id != NULL) {
 864                 rsc_id += strlen("primitive[@id=\'");
 865                 search = strchr(rsc_id, '\'');
 866             }
 867             if (search != NULL) {
 868                 *search = 0;
 869                 stonith_device_remove(rsc_id, true);
 870                 /* watchdog_device_update called afterwards
 871                    to fall back to implicit definition if needed */
 872             } else {
 873                 crm_warn("Ignoring malformed CIB update (resource deletion)");
 874             }
 875             free(mutable);
 876 
 877         } else if (strstr(xpath, "/"XML_CIB_TAG_RESOURCES) ||
 878                    strstr(xpath, "/"XML_CIB_TAG_CONSTRAINTS) ||
 879                    strstr(xpath, "/"XML_CIB_TAG_RSCCONFIG)) {
 880             shortpath = strrchr(xpath, '/'); CRM_ASSERT(shortpath);
 881             reason = crm_strdup_printf("%s %s", op, shortpath+1);
 882             needs_update = TRUE;
 883             break;
 884         }
 885     }
 886 
 887     if(needs_update) {
 888         crm_info("Updating device list from CIB: %s", reason);
 889         cib_devices_update();
 890     } else {
 891         crm_trace("No updates for device list found in CIB");
 892     }
 893     free(reason);
 894 }
 895 
 896 
 897 static void
 898 update_cib_stonith_devices_v1(const char *event, xmlNode * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 899 {
 900     const char *reason = "none";
 901     gboolean needs_update = FALSE;
 902     xmlXPathObjectPtr xpath_obj = NULL;
 903 
 904     /* process new constraints */
 905     xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_CONS_TAG_RSC_LOCATION);
 906     if (numXpathResults(xpath_obj) > 0) {
 907         int max = numXpathResults(xpath_obj), lpc = 0;
 908 
 909         /* Safest and simplest to always recompute */
 910         needs_update = TRUE;
 911         reason = "new location constraint";
 912 
 913         for (lpc = 0; lpc < max; lpc++) {
 914             xmlNode *match = getXpathResult(xpath_obj, lpc);
 915 
 916             crm_log_xml_trace(match, "new constraint");
 917         }
 918     }
 919     freeXpathObject(xpath_obj);
 920 
 921     /* process deletions */
 922     xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_CIB_TAG_RESOURCE);
 923     if (numXpathResults(xpath_obj) > 0) {
 924         remove_cib_device(xpath_obj);
 925     }
 926     freeXpathObject(xpath_obj);
 927 
 928     /* process additions */
 929     xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_CIB_TAG_RESOURCE);
 930     if (numXpathResults(xpath_obj) > 0) {
 931         int max = numXpathResults(xpath_obj), lpc = 0;
 932 
 933         for (lpc = 0; lpc < max; lpc++) {
 934             const char *rsc_id = NULL;
 935             const char *standard = NULL;
 936             xmlNode *match = getXpathResult(xpath_obj, lpc);
 937 
 938             rsc_id = crm_element_value(match, XML_ATTR_ID);
 939             standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
 940 
 941             if (!pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
 942                 continue;
 943             }
 944 
 945             crm_trace("Fencing resource %s was added or modified", rsc_id);
 946             reason = "new resource";
 947             needs_update = TRUE;
 948         }
 949     }
 950     freeXpathObject(xpath_obj);
 951 
 952     if(needs_update) {
 953         crm_info("Updating device list from CIB: %s", reason);
 954         cib_devices_update();
 955     }
 956 }
 957 
 958 static void
 959 update_cib_stonith_devices(const char *event, xmlNode * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 960 {
 961     int format = 1;
 962     xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
 963 
 964     CRM_ASSERT(patchset);
 965     crm_element_value_int(patchset, "format", &format);
 966     switch(format) {
 967         case 1:
 968             update_cib_stonith_devices_v1(event, msg);
 969             break;
 970         case 2:
 971             update_cib_stonith_devices_v2(event, msg);
 972             break;
 973         default:
 974             crm_warn("Unknown patch format: %d", format);
 975     }
 976 }
 977 
 978 /*!
 979  * \internal
 980  * \brief Check whether a node has a specific attribute name/value
 981  *
 982  * \param[in] node    Name of node to check
 983  * \param[in] name    Name of an attribute to look for
 984  * \param[in] value   The value the named attribute needs to be set to in order to be considered a match
 985  *
 986  * \return TRUE if the locally cached CIB has the specified node attribute
 987  */
 988 gboolean
 989 node_has_attr(const char *node, const char *name, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 990 {
 991     GString *xpath = NULL;
 992     xmlNode *match;
 993 
 994     CRM_CHECK((local_cib != NULL) && (node != NULL) && (name != NULL)
 995               && (value != NULL), return FALSE);
 996 
 997     /* Search for the node's attributes in the CIB. While the schema allows
 998      * multiple sets of instance attributes, and allows instance attributes to
 999      * use id-ref to reference values elsewhere, that is intended for resources,
1000      * so we ignore that here.
1001      */
1002     xpath = g_string_sized_new(256);
1003     pcmk__g_strcat(xpath,
1004                    "//" XML_CIB_TAG_NODES "/" XML_CIB_TAG_NODE
1005                    "[@" XML_ATTR_UNAME "='", node, "']/" XML_TAG_ATTR_SETS
1006                    "/" XML_CIB_TAG_NVPAIR
1007                    "[@" XML_NVPAIR_ATTR_NAME "='", name, "' "
1008                    "and @" XML_NVPAIR_ATTR_VALUE "='", value, "']", NULL);
1009 
1010     match = get_xpath_object((const char *) xpath->str, local_cib, LOG_NEVER);
1011 
1012     g_string_free(xpath, TRUE);
1013     return (match != NULL);
1014 }
1015 
1016 /*!
1017  * \internal
1018  * \brief Check whether a node does watchdog-fencing
1019  *
1020  * \param[in] node    Name of node to check
1021  *
1022  * \return TRUE if node found in stonith_watchdog_targets
1023  *         or stonith_watchdog_targets is empty indicating
1024  *         all nodes are doing watchdog-fencing
1025  */
1026 gboolean
1027 node_does_watchdog_fencing(const char *node)
     /* [previous][next][first][last][top][bottom][index][help] */
1028 {
1029     return ((stonith_watchdog_targets == NULL) ||
1030             pcmk__str_in_list(node, stonith_watchdog_targets, pcmk__str_casei));
1031 }
1032 
1033 
1034 static void
1035 update_fencing_topology(const char *event, xmlNode * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
1036 {
1037     int format = 1;
1038     const char *xpath;
1039     xmlXPathObjectPtr xpathObj = NULL;
1040     xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
1041 
1042     CRM_ASSERT(patchset);
1043     crm_element_value_int(patchset, "format", &format);
1044 
1045     if(format == 1) {
1046         /* Process deletions (only) */
1047         xpath = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_TAG_FENCING_LEVEL;
1048         xpathObj = xpath_search(msg, xpath);
1049 
1050         remove_fencing_topology(xpathObj);
1051         freeXpathObject(xpathObj);
1052 
1053         /* Process additions and changes */
1054         xpath = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_TAG_FENCING_LEVEL;
1055         xpathObj = xpath_search(msg, xpath);
1056 
1057         register_fencing_topology(xpathObj);
1058         freeXpathObject(xpathObj);
1059 
1060     } else if(format == 2) {
1061         xmlNode *change = NULL;
1062         int add[] = { 0, 0, 0 };
1063         int del[] = { 0, 0, 0 };
1064 
1065         xml_patch_versions(patchset, add, del);
1066 
1067         for (change = pcmk__xml_first_child(patchset); change != NULL;
1068              change = pcmk__xml_next(change)) {
1069             const char *op = crm_element_value(change, XML_DIFF_OP);
1070             const char *xpath = crm_element_value(change, XML_DIFF_PATH);
1071 
1072             if(op == NULL) {
1073                 continue;
1074 
1075             } else if(strstr(xpath, "/" XML_TAG_FENCING_LEVEL) != NULL) {
1076                 /* Change to a specific entry */
1077 
1078                 crm_trace("Handling %s operation %d.%d.%d for %s", op, add[0], add[1], add[2], xpath);
1079                 if(strcmp(op, "move") == 0) {
1080                     continue;
1081 
1082                 } else if(strcmp(op, "create") == 0) {
1083                     add_topology_level(change->children);
1084 
1085                 } else if(strcmp(op, "modify") == 0) {
1086                     xmlNode *match = first_named_child(change, XML_DIFF_RESULT);
1087 
1088                     if(match) {
1089                         remove_topology_level(match->children);
1090                         add_topology_level(match->children);
1091                     }
1092 
1093                 } else if(strcmp(op, "delete") == 0) {
1094                     /* Nuclear option, all we have is the path and an id... not enough to remove a specific entry */
1095                     crm_info("Re-initializing fencing topology after %s operation %d.%d.%d for %s",
1096                              op, add[0], add[1], add[2], xpath);
1097                     fencing_topology_init();
1098                     return;
1099                 }
1100 
1101             } else if (strstr(xpath, "/" XML_TAG_FENCING_TOPOLOGY) != NULL) {
1102                 /* Change to the topology in general */
1103                 crm_info("Re-initializing fencing topology after top-level %s operation  %d.%d.%d for %s",
1104                          op, add[0], add[1], add[2], xpath);
1105                 fencing_topology_init();
1106                 return;
1107 
1108             } else if (strstr(xpath, "/" XML_CIB_TAG_CONFIGURATION)) {
1109                 /* Changes to the whole config section, possibly including the topology as a whild */
1110                 if(first_named_child(change, XML_TAG_FENCING_TOPOLOGY) == NULL) {
1111                     crm_trace("Nothing for us in %s operation %d.%d.%d for %s.",
1112                               op, add[0], add[1], add[2], xpath);
1113 
1114                 } else if(strcmp(op, "delete") == 0 || strcmp(op, "create") == 0) {
1115                     crm_info("Re-initializing fencing topology after top-level %s operation %d.%d.%d for %s.",
1116                              op, add[0], add[1], add[2], xpath);
1117                     fencing_topology_init();
1118                     return;
1119                 }
1120 
1121             } else {
1122                 crm_trace("Nothing for us in %s operation %d.%d.%d for %s",
1123                           op, add[0], add[1], add[2], xpath);
1124             }
1125         }
1126 
1127     } else {
1128         crm_warn("Unknown patch format: %d", format);
1129     }
1130 }
1131 static bool have_cib_devices = FALSE;
1132 
1133 static void
1134 update_cib_cache_cb(const char *event, xmlNode * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
1135 {
1136     int rc = pcmk_ok;
1137     long timeout_ms_saved = stonith_watchdog_timeout_ms;
1138     gboolean need_full_refresh = FALSE;
1139 
1140     if(!have_cib_devices) {
1141         crm_trace("Skipping updates until we get a full dump");
1142         return;
1143 
1144     } else if(msg == NULL) {
1145         crm_trace("Missing %s update", event);
1146         return;
1147     }
1148 
1149     /* Maintain a local copy of the CIB so that we have full access
1150      * to device definitions, location constraints, and node attributes
1151      */
1152     if (local_cib != NULL) {
1153         int rc = pcmk_ok;
1154         xmlNode *patchset = NULL;
1155 
1156         crm_element_value_int(msg, F_CIB_RC, &rc);
1157         if (rc != pcmk_ok) {
1158             return;
1159         }
1160 
1161         patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
1162         xml_log_patchset(LOG_TRACE, "Config update", patchset);
1163         rc = xml_apply_patchset(local_cib, patchset, TRUE);
1164         switch (rc) {
1165             case pcmk_ok:
1166             case -pcmk_err_old_data:
1167                 break;
1168             case -pcmk_err_diff_resync:
1169             case -pcmk_err_diff_failed:
1170                 crm_notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc);
1171                 free_xml(local_cib);
1172                 local_cib = NULL;
1173                 break;
1174             default:
1175                 crm_warn("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc);
1176                 free_xml(local_cib);
1177                 local_cib = NULL;
1178         }
1179     }
1180 
1181     if (local_cib == NULL) {
1182         crm_trace("Re-requesting full CIB");
1183         rc = cib_api->cmds->query(cib_api, NULL, &local_cib, cib_scope_local | cib_sync_call);
1184         if(rc != pcmk_ok) {
1185             crm_err("Couldn't retrieve the CIB: %s (%d)", pcmk_strerror(rc), rc);
1186             return;
1187         }
1188         CRM_ASSERT(local_cib != NULL);
1189         need_full_refresh = TRUE;
1190     }
1191 
1192     pcmk__refresh_node_caches_from_cib(local_cib);
1193     update_stonith_watchdog_timeout_ms(local_cib);
1194 
1195     if (timeout_ms_saved != stonith_watchdog_timeout_ms) {
1196             need_full_refresh = TRUE;
1197     } else {
1198             update_fencing_topology(event, msg);
1199             update_cib_stonith_devices(event, msg);
1200             watchdog_device_update();
1201     }
1202 
1203     if (need_full_refresh) {
1204         fencing_topology_init();
1205         cib_devices_update();
1206         watchdog_device_update();
1207     }
1208 }
1209 
1210 static void
1211 init_cib_cache_cb(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1212 {
1213     crm_info("Updating device list from CIB");
1214     have_cib_devices = TRUE;
1215     local_cib = copy_xml(output);
1216 
1217     pcmk__refresh_node_caches_from_cib(local_cib);
1218     update_stonith_watchdog_timeout_ms(local_cib);
1219 
1220     fencing_topology_init();
1221     cib_devices_update();
1222     watchdog_device_update();
1223 }
1224 
1225 static void
1226 stonith_shutdown(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
1227 {
1228     crm_info("Terminating with %d clients", pcmk__ipc_client_count());
1229     stonith_shutdown_flag = TRUE;
1230     if (mainloop != NULL && g_main_loop_is_running(mainloop)) {
1231         g_main_loop_quit(mainloop);
1232     } else {
1233         stonith_cleanup();
1234         crm_exit(CRM_EX_OK);
1235     }
1236 }
1237 
1238 static void
1239 cib_connection_destroy(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1240 {
1241     if (stonith_shutdown_flag) {
1242         crm_info("Connection to the CIB manager closed");
1243         return;
1244     } else {
1245         crm_crit("Lost connection to the CIB manager, shutting down");
1246     }
1247     if (cib_api) {
1248         cib_api->cmds->signoff(cib_api);
1249     }
1250     stonith_shutdown(0);
1251 }
1252 
1253 static void
1254 stonith_cleanup(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1255 {
1256     if (cib_api) {
1257         cib_api->cmds->del_notify_callback(cib_api, T_CIB_DIFF_NOTIFY, update_cib_cache_cb);
1258         cib_api->cmds->signoff(cib_api);
1259     }
1260 
1261     if (ipcs) {
1262         qb_ipcs_destroy(ipcs);
1263     }
1264 
1265     crm_peer_destroy();
1266     pcmk__client_cleanup();
1267     free_stonith_remote_op_list();
1268     free_topology_list();
1269     free_device_list();
1270     free_metadata_cache();
1271     fenced_unregister_handlers();
1272 
1273     free(stonith_our_uname);
1274     stonith_our_uname = NULL;
1275 
1276     free_xml(local_cib);
1277     local_cib = NULL;
1278 }
1279 
1280 static pcmk__cli_option_t long_options[] = {
1281     // long option, argument type, storage, short option, description, flags
1282     {
1283         "stand-alone", no_argument, 0, 's',
1284         "\tDeprecated (will be removed in a future release)",
1285         pcmk__option_default
1286     },
1287     {
1288         "stand-alone-w-cpg", no_argument, 0, 'c',
1289         "\tIntended for use in regression testing only",
1290         pcmk__option_default
1291     },
1292     {
1293         "logfile", required_argument, 0, 'l',
1294         NULL, pcmk__option_default
1295     },
1296     {
1297         "verbose", no_argument, 0, 'V',
1298         NULL, pcmk__option_default
1299     },
1300     {
1301         "version", no_argument, 0, '$',
1302         NULL, pcmk__option_default
1303     },
1304     {
1305         "help", no_argument, 0, '?',
1306         NULL, pcmk__option_default
1307     },
1308     { 0, 0, 0, 0 }
1309 };
1310 
1311 static void
1312 setup_cib(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1313 {
1314     int rc, retries = 0;
1315 
1316     cib_api = cib_new();
1317     if (cib_api == NULL) {
1318         crm_err("No connection to the CIB manager");
1319         return;
1320     }
1321 
1322     do {
1323         sleep(retries);
1324         rc = cib_api->cmds->signon(cib_api, CRM_SYSTEM_STONITHD, cib_command);
1325     } while (rc == -ENOTCONN && ++retries < 5);
1326 
1327     if (rc != pcmk_ok) {
1328         crm_err("Could not connect to the CIB manager: %s (%d)", pcmk_strerror(rc), rc);
1329 
1330     } else if (pcmk_ok !=
1331                cib_api->cmds->add_notify_callback(cib_api, T_CIB_DIFF_NOTIFY, update_cib_cache_cb)) {
1332         crm_err("Could not set CIB notification callback");
1333 
1334     } else {
1335         rc = cib_api->cmds->query(cib_api, NULL, NULL, cib_scope_local);
1336         cib_api->cmds->register_callback(cib_api, rc, 120, FALSE, NULL, "init_cib_cache_cb",
1337                                          init_cib_cache_cb);
1338         cib_api->cmds->set_connection_dnotify(cib_api, cib_connection_destroy);
1339         crm_info("Watching for fencing topology changes");
1340     }
1341 }
1342 
1343 struct qb_ipcs_service_handlers ipc_callbacks = {
1344     .connection_accept = st_ipc_accept,
1345     .connection_created = NULL,
1346     .msg_process = st_ipc_dispatch,
1347     .connection_closed = st_ipc_closed,
1348     .connection_destroyed = st_ipc_destroy
1349 };
1350 
1351 /*!
1352  * \internal
1353  * \brief Callback for peer status changes
1354  *
1355  * \param[in] type  What changed
1356  * \param[in] node  What peer had the change
1357  * \param[in] data  Previous value of what changed
1358  */
1359 static void
1360 st_peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
1361 {
1362     if ((type != crm_status_processes)
1363         && !pcmk_is_set(node->flags, crm_remote_node)) {
1364         /*
1365          * This is a hack until we can send to a nodeid and/or we fix node name lookups
1366          * These messages are ignored in stonith_peer_callback()
1367          */
1368         xmlNode *query = create_xml_node(NULL, "stonith_command");
1369 
1370         crm_xml_add(query, F_XML_TAGNAME, "stonith_command");
1371         crm_xml_add(query, F_TYPE, T_STONITH_NG);
1372         crm_xml_add(query, F_STONITH_OPERATION, "poke");
1373 
1374         crm_debug("Broadcasting our uname because of node %u", node->id);
1375         send_cluster_message(NULL, crm_msg_stonith_ng, query, FALSE);
1376 
1377         free_xml(query);
1378     }
1379 }
1380 
1381 static pcmk__cluster_option_t fencer_options[] = {
1382     /* name, old name, type, allowed values,
1383      * default value, validator,
1384      * short description,
1385      * long description
1386      */
1387     {
1388         PCMK_STONITH_HOST_ARGUMENT, NULL, "string", NULL, "port", NULL,
1389         N_("Advanced use only: An alternate parameter to supply instead of 'port'"),
1390         N_("some devices do not support the "
1391            "standard 'port' parameter or may provide additional ones. Use "
1392            "this to specify an alternate, device-specific, parameter "
1393            "that should indicate the machine to be fenced. A value of "
1394            "none can be used to tell the cluster not to supply any "
1395            "additional parameters.")
1396     },
1397     {
1398         PCMK_STONITH_HOST_MAP,NULL, "string", NULL, "", NULL,
1399         N_("A mapping of host names to ports numbers for devices that do not support host names."),
1400         N_("Eg. node1:1;node2:2,3 would tell the cluster to use port 1 for node1 and ports 2 and 3 for node2")
1401     },
1402     {
1403         PCMK_STONITH_HOST_LIST,NULL, "string", NULL, "", NULL,
1404         N_("Eg. node1,node2,node3"),
1405         N_("A list of machines controlled by "
1406                "this device (Optional unless pcmk_host_list=static-list)")
1407     },
1408     {
1409         PCMK_STONITH_HOST_CHECK,NULL, "string", NULL, "dynamic-list", NULL,
1410         N_("How to determine which machines are controlled by the device."),
1411         N_("Allowed values: dynamic-list "
1412                "(query the device via the 'list' command), static-list "
1413                "(check the pcmk_host_list attribute), status "
1414                "(query the device via the 'status' command), "
1415                "none (assume every device can fence every "
1416                "machine)")
1417     },
1418     {
1419         PCMK_STONITH_DELAY_MAX,NULL, "time", NULL, "0s", NULL,
1420         N_("Enable a base delay for fencing actions and specify base delay value."),
1421         N_("Enable a delay of no more than the "
1422                "time specified before executing fencing actions. Pacemaker "
1423                "derives the overall delay by taking the value of "
1424                "pcmk_delay_base and adding a random delay value such "
1425                "that the sum is kept below this maximum.")
1426     },
1427     {
1428         PCMK_STONITH_DELAY_BASE,NULL, "string", NULL, "0s", NULL,
1429         N_("Enable a base delay for "
1430                "fencing actions and specify base delay value."),
1431         N_("This enables a static delay for "
1432                "fencing actions, which can help avoid \"death matches\" where "
1433                "two nodes try to fence each other at the same time. If "
1434                "pcmk_delay_max  is also used, a random delay will be "
1435                "added such that the total delay is kept below that value."
1436                "This can be set to a single time value to apply to any node "
1437                "targeted by this device (useful if a separate device is "
1438                "configured for each target), or to a node map (for example, "
1439                "\"node1:1s;node2:5\") to set a different value per target.")
1440     },
1441     {
1442         PCMK_STONITH_ACTION_LIMIT,NULL, "integer", NULL, "1", NULL,
1443         N_("The maximum number of actions can be performed in parallel on this device"),
1444         N_("Cluster property concurrent-fencing=true needs to be configured first."
1445              "Then use this to specify the maximum number of actions can be performed in parallel on this device. -1 is unlimited.")
1446     },
1447     {
1448         "pcmk_reboot_action",NULL, "string", NULL, "reboot", NULL,
1449         N_("Advanced use only: An alternate command to run instead of 'reboot'"),
1450         N_("Some devices do not support the standard commands or may provide additional ones.\n"
1451                  "Use this to specify an alternate, device-specific, command that implements the \'reboot\' action.")
1452     },
1453     {
1454         "pcmk_reboot_timeout",NULL, "time", NULL, "60s", NULL,
1455         N_("Advanced use only: Specify an alternate timeout to use for reboot actions instead of stonith-timeout"),
1456         N_("Some devices need much more/less time to complete than normal."
1457            "Use this to specify an alternate, device-specific, timeout for \'reboot\' actions.")
1458     },
1459     {
1460         "pcmk_reboot_retries",NULL, "integer", NULL, "2", NULL,
1461         N_("Advanced use only: The maximum number of times to retry the 'reboot' command within the timeout period"),
1462         N_("Some devices do not support multiple connections."
1463            " Operations may 'fail' if the device is busy with another task so Pacemaker will automatically retry the operation,      if there is time remaining."
1464            " Use this option to alter the number of times Pacemaker retries \'reboot\' actions before giving up.")
1465     },
1466     {
1467         "pcmk_off_action",NULL, "string", NULL, "off", NULL,
1468         N_("Advanced use only: An alternate command to run instead of \'off\'"),
1469         N_("Some devices do not support the standard commands or may provide additional ones."
1470                  "Use this to specify an alternate, device-specific, command that implements the \'off\' action.")
1471     },
1472     {
1473         "pcmk_off_timeout",NULL, "time", NULL, "60s", NULL,
1474         N_("Advanced use only: Specify an alternate timeout to use for off actions instead of stonith-timeout"),
1475         N_("Some devices need much more/less time to complete than normal."
1476            "Use this to specify an alternate, device-specific, timeout for \'off\' actions.")
1477     },
1478     {
1479         "pcmk_off_retries",NULL, "integer", NULL, "2", NULL,
1480         N_("Advanced use only: The maximum number of times to retry the 'off' command within the timeout period"),
1481         N_("Some devices do not support multiple connections."
1482            " Operations may 'fail' if the device is busy with another task so Pacemaker will automatically retry the operation,      if there is time remaining."
1483            " Use this option to alter the number of times Pacemaker retries \'off\' actions before giving up.")
1484     },
1485     {
1486         "pcmk_on_action",NULL, "string", NULL, "on", NULL,
1487         N_("Advanced use only: An alternate command to run instead of 'on'"),
1488         N_("Some devices do not support the standard commands or may provide additional ones."
1489                  "Use this to specify an alternate, device-specific, command that implements the \'on\' action.")
1490     },
1491     {
1492         "pcmk_on_timeout",NULL, "time", NULL, "60s", NULL,
1493         N_("Advanced use only: Specify an alternate timeout to use for on actions instead of stonith-timeout"),
1494         N_("Some devices need much more/less time to complete than normal."
1495            "Use this to specify an alternate, device-specific, timeout for \'on\' actions.")
1496     },
1497     {
1498         "pcmk_on_retries",NULL, "integer", NULL, "2", NULL,
1499         N_("Advanced use only: The maximum number of times to retry the 'on' command within the timeout period"),
1500         N_("Some devices do not support multiple connections."
1501            " Operations may 'fail' if the device is busy with another task so Pacemaker will automatically retry the operation,      if there is time remaining."
1502            " Use this option to alter the number of times Pacemaker retries \'on\' actions before giving up.")
1503     },
1504     {
1505         "pcmk_list_action",NULL, "string", NULL, "list", NULL,
1506         N_("Advanced use only: An alternate command to run instead of \'list\'"),
1507         N_("Some devices do not support the standard commands or may provide additional ones."
1508                  "Use this to specify an alternate, device-specific, command that implements the \'list\' action.")
1509     },
1510     {
1511         "pcmk_list_timeout",NULL, "time", NULL, "60s", NULL,
1512         N_("Advanced use only: Specify an alternate timeout to use for list actions instead of stonith-timeout"),
1513         N_("Some devices need much more/less time to complete than normal."
1514            "Use this to specify an alternate, device-specific, timeout for \'list\' actions.")
1515     },
1516     {
1517         "pcmk_list_retries",NULL, "integer", NULL, "2", NULL,
1518         N_("Advanced use only: The maximum number of times to retry the \'list\' command within the timeout period"),
1519         N_("Some devices do not support multiple connections."
1520            " Operations may 'fail' if the device is busy with another task so Pacemaker will automatically retry the operation,      if there is time remaining."
1521            " Use this option to alter the number of times Pacemaker retries \'list\' actions before giving up.")
1522     },
1523     {
1524         "pcmk_monitor_action",NULL, "string", NULL, "monitor", NULL,
1525         N_("Advanced use only: An alternate command to run instead of \'monitor\'"),
1526         N_("Some devices do not support the standard commands or may provide additional ones."
1527                  "Use this to specify an alternate, device-specific, command that implements the \'monitor\' action.")
1528     },
1529     {
1530         "pcmk_monitor_timeout",NULL, "time", NULL, "60s", NULL,
1531         N_("Advanced use only: Specify an alternate timeout to use for monitor actions instead of stonith-timeout"),
1532         N_("Some devices need much more/less time to complete than normal.\n"
1533            "Use this to specify an alternate, device-specific, timeout for \'monitor\' actions.")
1534     },
1535     {
1536         "pcmk_monitor_retries",NULL, "integer", NULL, "2", NULL,
1537         N_("Advanced use only: The maximum number of times to retry the \'monitor\' command within the timeout period"),
1538         N_("Some devices do not support multiple connections."
1539            " Operations may 'fail' if the device is busy with another task so Pacemaker will automatically retry the operation,      if there is time remaining."
1540            " Use this option to alter the number of times Pacemaker retries \'monitor\' actions before giving up.")
1541     },
1542     {
1543         "pcmk_status_action",NULL, "string", NULL, "status", NULL,
1544         N_("Advanced use only: An alternate command to run instead of \'status\'"),
1545         N_("Some devices do not support the standard commands or may provide additional ones."
1546                  "Use this to specify an alternate, device-specific, command that implements the \'status\' action.")
1547     },
1548     {
1549         "pcmk_status_timeout",NULL, "time", NULL, "60s", NULL,
1550         N_("Advanced use only: Specify an alternate timeout to use for status actions instead of stonith-timeout"),
1551         N_("Some devices need much more/less time to complete than normal."
1552            "Use this to specify an alternate, device-specific, timeout for \'status\' actions.")
1553     },
1554     {
1555         "pcmk_status_retries",NULL, "integer", NULL, "2", NULL,
1556         N_("Advanced use only: The maximum number of times to retry the \'status\' command within the timeout period"),
1557         N_("Some devices do not support multiple connections."
1558            " Operations may 'fail' if the device is busy with another task so Pacemaker will automatically retry the operation,      if there is time remaining."
1559            " Use this option to alter the number of times Pacemaker retries \'status\' actions before giving up.")
1560     },
1561 };
1562 
1563 void
1564 fencer_metadata(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1565 {
1566     const char *desc_short = N_("Instance attributes available for all "
1567                              "\"stonith\"-class resources");
1568     const char *desc_long = N_("Instance attributes available for all \"stonith\"-"
1569                             "class resources and used by Pacemaker's fence "
1570                             "daemon, formerly known as stonithd");
1571 
1572     gchar *s = pcmk__format_option_metadata("pacemaker-fenced", desc_short,
1573                                             desc_long, fencer_options,
1574                                             PCMK__NELEM(fencer_options));
1575     printf("%s", s);
1576     g_free(s);
1577 }
1578 
1579 /*
1580 static const char *
1581 fenceder_options(GHashTable *options, const char *name)
1582 {
1583     return pcmk__cluster_option(options, fenceder_options,
1584                                 PCMK__NELEM(fenceder_options), name);
1585 }
1586 */
1587 int
1588 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
1589 {
1590     int flag;
1591     int argerr = 0;
1592     int option_index = 0;
1593     crm_cluster_t *cluster = NULL;
1594     crm_ipc_t *old_instance = NULL;
1595     int rc = pcmk_rc_ok;
1596 
1597     crm_log_preinit(NULL, argc, argv);
1598     pcmk__set_cli_options(NULL, "[options]", long_options,
1599                           "daemon for executing fencing devices in a "
1600                           "Pacemaker cluster");
1601 
1602     while (1) {
1603         flag = pcmk__next_cli_option(argc, argv, &option_index, NULL);
1604         if (flag == -1) {
1605             break;
1606         }
1607 
1608         switch (flag) {
1609             case 'V':
1610                 crm_bump_log_level(argc, argv);
1611                 break;
1612             case 'l':
1613                 {
1614                     int rc = pcmk__add_logfile(optarg);
1615 
1616                     if (rc != pcmk_rc_ok) {
1617                         /* Logging has not yet been initialized, so stderr is
1618                          * the only way to get information out
1619                          */
1620                         fprintf(stderr, "Logging to %s is disabled: %s\n",
1621                                 optarg, pcmk_rc_str(rc));
1622                     }
1623                 }
1624                 break;
1625             case 's':
1626                 stand_alone = TRUE;
1627                 break;
1628             case 'c':
1629                 stand_alone = FALSE;
1630                 no_cib_connect = TRUE;
1631                 break;
1632             case '$':
1633             case '?':
1634                 pcmk__cli_help(flag, CRM_EX_OK);
1635                 break;
1636             default:
1637                 ++argerr;
1638                 break;
1639         }
1640     }
1641 
1642     if (argc - optind == 1 && pcmk__str_eq("metadata", argv[optind], pcmk__str_casei)) {
1643         fencer_metadata();
1644         return CRM_EX_OK;
1645     }    
1646     if (optind != argc) {
1647         ++argerr;
1648     }
1649 
1650     if (argerr) {
1651         pcmk__cli_help('?', CRM_EX_USAGE);
1652     }
1653 
1654     crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
1655 
1656     crm_notice("Starting Pacemaker fencer");
1657 
1658     old_instance = crm_ipc_new("stonith-ng", 0);
1659     if (crm_ipc_connect(old_instance)) {
1660         // IPC end-point already up 
1661         crm_ipc_close(old_instance);
1662         crm_ipc_destroy(old_instance);
1663         crm_err("pacemaker-fenced is already active, aborting startup");
1664         crm_exit(CRM_EX_OK);
1665     } else {
1666         // not up or not authentic, we'll proceed either way 
1667         crm_ipc_destroy(old_instance);
1668         old_instance = NULL;
1669     }
1670 
1671     mainloop_add_signal(SIGTERM, stonith_shutdown);
1672 
1673     crm_peer_init();
1674 
1675     fenced_data_set = pe_new_working_set();
1676     CRM_ASSERT(fenced_data_set != NULL);
1677 
1678     cluster = calloc(1, sizeof(crm_cluster_t));
1679     CRM_ASSERT(cluster != NULL);
1680 
1681     if (stand_alone == FALSE) {
1682 
1683         if (is_corosync_cluster()) {
1684 #if SUPPORT_COROSYNC
1685             cluster->destroy = stonith_peer_cs_destroy;
1686             cluster->cpg.cpg_deliver_fn = stonith_peer_ais_callback;
1687             cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
1688 #endif
1689         }
1690 
1691         crm_set_status_callback(&st_peer_update_callback);
1692 
1693         if (crm_cluster_connect(cluster) == FALSE) {
1694             crm_crit("Cannot sign in to the cluster... terminating");
1695             crm_exit(CRM_EX_FATAL);
1696         }
1697         stonith_our_uname = strdup(cluster->uname);
1698 
1699         if (no_cib_connect == FALSE) {
1700             setup_cib();
1701         }
1702 
1703     } else {
1704         stonith_our_uname = strdup("localhost");
1705         crm_warn("Stand-alone mode is deprecated and will be removed "
1706                  "in a future release");
1707     }
1708 
1709     init_device_list();
1710     init_topology_list();
1711 
1712     pcmk__serve_fenced_ipc(&ipcs, &ipc_callbacks);
1713 
1714     pcmk__register_formats(NULL, formats);
1715     rc = pcmk__output_new(&out, "log", NULL, argv);
1716     if ((rc != pcmk_rc_ok) || (out == NULL)) {
1717         crm_err("Can't log resource details due to internal error: %s\n",
1718                 pcmk_rc_str(rc));
1719         crm_exit(CRM_EX_FATAL);
1720     }
1721 
1722     pe__register_messages(out);
1723     pcmk__register_lib_messages(out);
1724 
1725     pcmk__output_set_log_level(out, LOG_TRACE);
1726     fenced_data_set->priv = out;
1727 
1728     // Create the mainloop and run it... 
1729     mainloop = g_main_loop_new(NULL, FALSE);
1730     crm_notice("Pacemaker fencer successfully started and accepting connections");
1731     g_main_loop_run(mainloop);
1732 
1733     stonith_cleanup();
1734     free(cluster->uuid);
1735     free(cluster->uname);
1736     free(cluster);
1737     pe_free_working_set(fenced_data_set);
1738 
1739     out->finish(out, CRM_EX_OK, true, NULL);
1740     pcmk__output_free(out);
1741     pcmk__unregister_formats();
1742 
1743     crm_exit(CRM_EX_OK);
1744 }

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