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

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