root/lib/fencing/st_client.c

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

DEFINITIONS

This source file includes following definitions.
  1. stonith_text2namespace
  2. stonith_namespace2text
  3. stonith_get_namespace
  4. stonith__watchdog_fencing_enabled_for_node_api
  5. stonith__watchdog_fencing_enabled_for_node
  6. foreach_notify_entry
  7. stonith_connection_destroy
  8. create_device_registration_xml
  9. stonith_api_register_device
  10. stonith_api_remove_device
  11. stonith_api_remove_level_full
  12. stonith_api_remove_level
  13. create_level_registration_xml
  14. stonith_api_register_level_full
  15. stonith_api_register_level
  16. stonith_api_device_list
  17. stonith_api_device_metadata
  18. stonith_api_query
  19. stonith_api_call
  20. stonith_api_list
  21. stonith_api_monitor
  22. stonith_api_status
  23. stonith_api_fence_with_delay
  24. stonith_api_fence
  25. stonith_api_confirm
  26. stonith_api_history
  27. stonith_history_free
  28. stonithlib_GCompareFunc
  29. stonith_create_op
  30. stonith_destroy_op_callback
  31. stonith_api_signoff
  32. stonith_api_del_callback
  33. invoke_fence_action_callback
  34. invoke_registered_callbacks
  35. stonith_async_timeout_handler
  36. set_callback_timeout
  37. update_callback_timeout
  38. stonith_dispatch_internal
  39. stonith_api_signon
  40. stonith_set_notification
  41. stonith_api_add_notification
  42. del_notify_entry
  43. stonith_api_del_notification
  44. stonith_api_add_callback
  45. stonith_dump_pending_op
  46. stonith_dump_pending_callbacks
  47. get_event_data_xml
  48. xml_to_event
  49. event_free
  50. stonith_send_notification
  51. stonith_send_command
  52. stonith_dispatch
  53. stonith_api_free
  54. stonith_api_delete
  55. stonith_api_validate
  56. stonith_api_new
  57. stonith_api_connect_retry
  58. stonith_key_value_add
  59. stonith_key_value_freeall
  60. stonith_api_kick
  61. stonith_api_time
  62. stonith_agent_exists
  63. stonith_action_str
  64. parse_list_line
  65. stonith__parse_targets
  66. stonith__later_succeeded
  67. stonith__sort_history
  68. stonith_op_state_str
  69. stonith__first_matching_event
  70. stonith__event_state_pending
  71. stonith__event_state_eq
  72. stonith__event_state_neq
  73. stonith__device_parameter_flags
  74. stonith__exit_status
  75. stonith__execution_status
  76. stonith__exit_reason
  77. stonith__event_exit_status
  78. stonith__event_execution_status
  79. stonith__event_exit_reason
  80. stonith__event_description
  81. get_stonith_provider

   1 /*
   2  * Copyright 2004-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 Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdlib.h>
  13 #include <stdio.h>
  14 #include <stdbool.h>
  15 #include <string.h>
  16 #include <ctype.h>
  17 #include <inttypes.h>
  18 #include <sys/types.h>
  19 #include <glib.h>
  20 
  21 #include <crm/crm.h>
  22 #include <crm/stonith-ng.h>
  23 #include <crm/fencing/internal.h>
  24 #include <crm/msg_xml.h>
  25 
  26 #include <crm/common/mainloop.h>
  27 
  28 #include "fencing_private.h"
  29 
  30 CRM_TRACE_INIT_DATA(stonith);
  31 
  32 // Used as stonith_t:st_private
  33 typedef struct stonith_private_s {
  34     char *token;
  35     crm_ipc_t *ipc;
  36     mainloop_io_t *source;
  37     GHashTable *stonith_op_callback_table;
  38     GList *notify_list;
  39     int notify_refcnt;
  40     bool notify_deletes;
  41 
  42     void (*op_callback) (stonith_t * st, stonith_callback_data_t * data);
  43 
  44 } stonith_private_t;
  45 
  46 // Used as stonith_event_t:opaque
  47 struct event_private {
  48     pcmk__action_result_t result;
  49 };
  50 
  51 typedef struct stonith_notify_client_s {
  52     const char *event;
  53     const char *obj_id;         /* implement one day */
  54     const char *obj_type;       /* implement one day */
  55     void (*notify) (stonith_t * st, stonith_event_t * e);
  56     bool delete;
  57 
  58 } stonith_notify_client_t;
  59 
  60 typedef struct stonith_callback_client_s {
  61     void (*callback) (stonith_t * st, stonith_callback_data_t * data);
  62     const char *id;
  63     void *user_data;
  64     gboolean only_success;
  65     gboolean allow_timeout_updates;
  66     struct timer_rec_s *timer;
  67 
  68 } stonith_callback_client_t;
  69 
  70 struct notify_blob_s {
  71     stonith_t *stonith;
  72     xmlNode *xml;
  73 };
  74 
  75 struct timer_rec_s {
  76     int call_id;
  77     int timeout;
  78     guint ref;
  79     stonith_t *stonith;
  80 };
  81 
  82 typedef int (*stonith_op_t) (const char *, int, const char *, xmlNode *,
  83                              xmlNode *, xmlNode *, xmlNode **, xmlNode **);
  84 
  85 bool stonith_dispatch(stonith_t * st);
  86 xmlNode *stonith_create_op(int call_id, const char *token, const char *op, xmlNode * data,
  87                            int call_options);
  88 static int stonith_send_command(stonith_t *stonith, const char *op,
  89                                 xmlNode *data, xmlNode **output_data,
  90                                 int call_options, int timeout);
  91 
  92 static void stonith_connection_destroy(gpointer user_data);
  93 static void stonith_send_notification(gpointer data, gpointer user_data);
  94 static int stonith_api_del_notification(stonith_t *stonith,
  95                                         const char *event);
  96 /*!
  97  * \brief Get agent namespace by name
  98  *
  99  * \param[in] namespace_s  Name of namespace as string
 100  *
 101  * \return Namespace as enum value
 102  */
 103 enum stonith_namespace
 104 stonith_text2namespace(const char *namespace_s)
     /* [previous][next][first][last][top][bottom][index][help] */
 105 {
 106     if (pcmk__str_eq(namespace_s, "any", pcmk__str_null_matches)) {
 107         return st_namespace_any;
 108 
 109     } else if (!strcmp(namespace_s, "redhat")
 110                || !strcmp(namespace_s, "stonith-ng")) {
 111         return st_namespace_rhcs;
 112 
 113     } else if (!strcmp(namespace_s, "internal")) {
 114         return st_namespace_internal;
 115 
 116     } else if (!strcmp(namespace_s, "heartbeat")) {
 117         return st_namespace_lha;
 118     }
 119     return st_namespace_invalid;
 120 }
 121 
 122 /*!
 123  * \brief Get agent namespace name
 124  *
 125  * \param[in] namespace  Namespace as enum value
 126  *
 127  * \return Namespace name as string
 128  */
 129 const char *
 130 stonith_namespace2text(enum stonith_namespace st_namespace)
     /* [previous][next][first][last][top][bottom][index][help] */
 131 {
 132     switch (st_namespace) {
 133         case st_namespace_any:      return "any";
 134         case st_namespace_rhcs:     return "stonith-ng";
 135         case st_namespace_internal: return "internal";
 136         case st_namespace_lha:      return "heartbeat";
 137         default:                    break;
 138     }
 139     return "unsupported";
 140 }
 141 
 142 /*!
 143  * \brief Determine namespace of a fence agent
 144  *
 145  * \param[in] agent        Fence agent type
 146  * \param[in] namespace_s  Name of agent namespace as string, if known
 147  *
 148  * \return Namespace of specified agent, as enum value
 149  */
 150 enum stonith_namespace
 151 stonith_get_namespace(const char *agent, const char *namespace_s)
     /* [previous][next][first][last][top][bottom][index][help] */
 152 {
 153     if (pcmk__str_eq(namespace_s, "internal", pcmk__str_casei)) {
 154         return st_namespace_internal;
 155     }
 156 
 157     if (stonith__agent_is_rhcs(agent)) {
 158         return st_namespace_rhcs;
 159     }
 160 
 161 #if HAVE_STONITH_STONITH_H
 162     if (stonith__agent_is_lha(agent)) {
 163         return st_namespace_lha;
 164     }
 165 #endif
 166 
 167     crm_err("Unknown fence agent: %s", agent);
 168     return st_namespace_invalid;
 169 }
 170 
 171 gboolean
 172 stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 173 {
 174     gboolean rv = FALSE;
 175     stonith_t *stonith_api = st?st:stonith_api_new();
 176     char *list = NULL;
 177 
 178     if(stonith_api) {
 179         if (stonith_api->state == stonith_disconnected) {
 180             int rc = stonith_api->cmds->connect(stonith_api, "stonith-api", NULL);
 181 
 182             if (rc != pcmk_ok) {
 183                 crm_err("Failed connecting to Stonith-API for watchdog-fencing-query.");
 184             }
 185         }
 186 
 187         if (stonith_api->state != stonith_disconnected) {
 188             /* caveat!!!
 189              * this might fail when when stonithd is just updating the device-list
 190              * probably something we should fix as well for other api-calls */
 191             int rc = stonith_api->cmds->list(stonith_api, st_opt_sync_call, STONITH_WATCHDOG_ID, &list, 0);
 192             if ((rc != pcmk_ok) || (list == NULL)) {
 193                 /* due to the race described above it can happen that
 194                  * we drop in here - so as not to make remote nodes
 195                  * panic on that answer
 196                  */
 197                 if (rc == -ENODEV) {
 198                     crm_notice("Cluster does not have watchdog fencing device");
 199                 } else {
 200                     crm_warn("Could not check for watchdog fencing device: %s",
 201                              pcmk_strerror(rc));
 202                 }
 203             } else if (list[0] == '\0') {
 204                 rv = TRUE;
 205             } else {
 206                 GList *targets = stonith__parse_targets(list);
 207                 rv = pcmk__str_in_list(node, targets, pcmk__str_casei);
 208                 g_list_free_full(targets, free);
 209             }
 210             free(list);
 211             if (!st) {
 212                 /* if we're provided the api we still might have done the
 213                  * connection - but let's assume the caller won't bother
 214                  */
 215                 stonith_api->cmds->disconnect(stonith_api);
 216             }
 217         }
 218 
 219         if (!st) {
 220             stonith_api_delete(stonith_api);
 221         }
 222     } else {
 223         crm_err("Stonith-API for watchdog-fencing-query couldn't be created.");
 224     }
 225     crm_trace("Pacemaker assumes node %s %sto do watchdog-fencing.",
 226               node, rv?"":"not ");
 227     return rv;
 228 }
 229 
 230 gboolean
 231 stonith__watchdog_fencing_enabled_for_node(const char *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 232 {
 233     return stonith__watchdog_fencing_enabled_for_node_api(NULL, node);
 234 }
 235 
 236 /* when cycling through the list we don't want to delete items
 237    so just mark them and when we know nobody is using the list
 238    loop over it to remove the marked items
 239  */
 240 static void
 241 foreach_notify_entry (stonith_private_t *private,
     /* [previous][next][first][last][top][bottom][index][help] */
 242                 GFunc func,
 243                 gpointer user_data)
 244 {
 245     private->notify_refcnt++;
 246     g_list_foreach(private->notify_list, func, user_data);
 247     private->notify_refcnt--;
 248     if ((private->notify_refcnt == 0) &&
 249         private->notify_deletes) {
 250         GList *list_item = private->notify_list;
 251 
 252         private->notify_deletes = FALSE;
 253         while (list_item != NULL)
 254         {
 255             stonith_notify_client_t *list_client = list_item->data;
 256             GList *next = g_list_next(list_item);
 257 
 258             if (list_client->delete) {
 259                 free(list_client);
 260                 private->notify_list =
 261                     g_list_delete_link(private->notify_list, list_item);
 262             }
 263             list_item = next;
 264         }
 265     }
 266 }
 267 
 268 static void
 269 stonith_connection_destroy(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 270 {
 271     stonith_t *stonith = user_data;
 272     stonith_private_t *native = NULL;
 273     struct notify_blob_s blob;
 274 
 275     crm_trace("Sending destroyed notification");
 276     blob.stonith = stonith;
 277     blob.xml = create_xml_node(NULL, "notify");
 278 
 279     native = stonith->st_private;
 280     native->ipc = NULL;
 281     native->source = NULL;
 282 
 283     free(native->token); native->token = NULL;
 284     stonith->state = stonith_disconnected;
 285     crm_xml_add(blob.xml, F_TYPE, T_STONITH_NOTIFY);
 286     crm_xml_add(blob.xml, F_SUBTYPE, T_STONITH_NOTIFY_DISCONNECT);
 287 
 288     foreach_notify_entry(native, stonith_send_notification, &blob);
 289     free_xml(blob.xml);
 290 }
 291 
 292 xmlNode *
 293 create_device_registration_xml(const char *id, enum stonith_namespace namespace,
     /* [previous][next][first][last][top][bottom][index][help] */
 294                                const char *agent, stonith_key_value_t *params,
 295                                const char *rsc_provides)
 296 {
 297     xmlNode *data = create_xml_node(NULL, F_STONITH_DEVICE);
 298     xmlNode *args = create_xml_node(data, XML_TAG_ATTRS);
 299 
 300 #if HAVE_STONITH_STONITH_H
 301     if (namespace == st_namespace_any) {
 302         namespace = stonith_get_namespace(agent, NULL);
 303     }
 304     if (namespace == st_namespace_lha) {
 305         hash2field((gpointer) "plugin", (gpointer) agent, args);
 306         agent = "fence_legacy";
 307     }
 308 #endif
 309 
 310     crm_xml_add(data, XML_ATTR_ID, id);
 311     crm_xml_add(data, F_STONITH_ORIGIN, __func__);
 312     crm_xml_add(data, "agent", agent);
 313     if ((namespace != st_namespace_any) && (namespace != st_namespace_invalid)) {
 314         crm_xml_add(data, "namespace", stonith_namespace2text(namespace));
 315     }
 316     if (rsc_provides) {
 317         crm_xml_add(data, "rsc_provides", rsc_provides);
 318     }
 319 
 320     for (; params; params = params->next) {
 321         hash2field((gpointer) params->key, (gpointer) params->value, args);
 322     }
 323 
 324     return data;
 325 }
 326 
 327 static int
 328 stonith_api_register_device(stonith_t * st, int call_options,
     /* [previous][next][first][last][top][bottom][index][help] */
 329                             const char *id, const char *namespace, const char *agent,
 330                             stonith_key_value_t * params)
 331 {
 332     int rc = 0;
 333     xmlNode *data = NULL;
 334 
 335     data = create_device_registration_xml(id, stonith_text2namespace(namespace),
 336                                           agent, params, NULL);
 337 
 338     rc = stonith_send_command(st, STONITH_OP_DEVICE_ADD, data, NULL, call_options, 0);
 339     free_xml(data);
 340 
 341     return rc;
 342 }
 343 
 344 static int
 345 stonith_api_remove_device(stonith_t * st, int call_options, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 346 {
 347     int rc = 0;
 348     xmlNode *data = NULL;
 349 
 350     data = create_xml_node(NULL, F_STONITH_DEVICE);
 351     crm_xml_add(data, F_STONITH_ORIGIN, __func__);
 352     crm_xml_add(data, XML_ATTR_ID, name);
 353     rc = stonith_send_command(st, STONITH_OP_DEVICE_DEL, data, NULL, call_options, 0);
 354     free_xml(data);
 355 
 356     return rc;
 357 }
 358 
 359 static int
 360 stonith_api_remove_level_full(stonith_t *st, int options,
     /* [previous][next][first][last][top][bottom][index][help] */
 361                               const char *node, const char *pattern,
 362                               const char *attr, const char *value, int level)
 363 {
 364     int rc = 0;
 365     xmlNode *data = NULL;
 366 
 367     CRM_CHECK(node || pattern || (attr && value), return -EINVAL);
 368 
 369     data = create_xml_node(NULL, XML_TAG_FENCING_LEVEL);
 370     crm_xml_add(data, F_STONITH_ORIGIN, __func__);
 371 
 372     if (node) {
 373         crm_xml_add(data, XML_ATTR_STONITH_TARGET, node);
 374 
 375     } else if (pattern) {
 376         crm_xml_add(data, XML_ATTR_STONITH_TARGET_PATTERN, pattern);
 377 
 378     } else {
 379         crm_xml_add(data, XML_ATTR_STONITH_TARGET_ATTRIBUTE, attr);
 380         crm_xml_add(data, XML_ATTR_STONITH_TARGET_VALUE, value);
 381     }
 382 
 383     crm_xml_add_int(data, XML_ATTR_STONITH_INDEX, level);
 384     rc = stonith_send_command(st, STONITH_OP_LEVEL_DEL, data, NULL, options, 0);
 385     free_xml(data);
 386 
 387     return rc;
 388 }
 389 
 390 static int
 391 stonith_api_remove_level(stonith_t * st, int options, const char *node, int level)
     /* [previous][next][first][last][top][bottom][index][help] */
 392 {
 393     return stonith_api_remove_level_full(st, options, node,
 394                                          NULL, NULL, NULL, level);
 395 }
 396 
 397 /*!
 398  * \internal
 399  * \brief Create XML for fence topology level registration request
 400  *
 401  * \param[in] node        If not NULL, target level by this node name
 402  * \param[in] pattern     If not NULL, target by node name using this regex
 403  * \param[in] attr        If not NULL, target by this node attribute
 404  * \param[in] value       If not NULL, target by this node attribute value
 405  * \param[in] level       Index number of level to register
 406  * \param[in] device_list List of devices in level
 407  *
 408  * \return Newly allocated XML tree on success, NULL otherwise
 409  *
 410  * \note The caller should set only one of node, pattern or attr/value.
 411  */
 412 xmlNode *
 413 create_level_registration_xml(const char *node, const char *pattern,
     /* [previous][next][first][last][top][bottom][index][help] */
 414                               const char *attr, const char *value,
 415                               int level, stonith_key_value_t *device_list)
 416 {
 417     size_t len = 0;
 418     char *list = NULL;
 419     xmlNode *data;
 420 
 421     CRM_CHECK(node || pattern || (attr && value), return NULL);
 422 
 423     data = create_xml_node(NULL, XML_TAG_FENCING_LEVEL);
 424     CRM_CHECK(data, return NULL);
 425 
 426     crm_xml_add(data, F_STONITH_ORIGIN, __func__);
 427     crm_xml_add_int(data, XML_ATTR_ID, level);
 428     crm_xml_add_int(data, XML_ATTR_STONITH_INDEX, level);
 429 
 430     if (node) {
 431         crm_xml_add(data, XML_ATTR_STONITH_TARGET, node);
 432 
 433     } else if (pattern) {
 434         crm_xml_add(data, XML_ATTR_STONITH_TARGET_PATTERN, pattern);
 435 
 436     } else {
 437         crm_xml_add(data, XML_ATTR_STONITH_TARGET_ATTRIBUTE, attr);
 438         crm_xml_add(data, XML_ATTR_STONITH_TARGET_VALUE, value);
 439     }
 440 
 441     // cppcheck seems not to understand the abort logic behind pcmk__realloc
 442     // cppcheck-suppress memleak
 443     for (; device_list; device_list = device_list->next) {
 444         pcmk__add_separated_word(&list, &len, device_list->value, ",");
 445     }
 446 
 447     crm_xml_add(data, XML_ATTR_STONITH_DEVICES, list);
 448 
 449     free(list);
 450     return data;
 451 }
 452 
 453 static int
 454 stonith_api_register_level_full(stonith_t * st, int options, const char *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 455                                 const char *pattern,
 456                                 const char *attr, const char *value,
 457                                 int level, stonith_key_value_t *device_list)
 458 {
 459     int rc = 0;
 460     xmlNode *data = create_level_registration_xml(node, pattern, attr, value,
 461                                                   level, device_list);
 462     CRM_CHECK(data != NULL, return -EINVAL);
 463 
 464     rc = stonith_send_command(st, STONITH_OP_LEVEL_ADD, data, NULL, options, 0);
 465     free_xml(data);
 466 
 467     return rc;
 468 }
 469 
 470 static int
 471 stonith_api_register_level(stonith_t * st, int options, const char *node, int level,
     /* [previous][next][first][last][top][bottom][index][help] */
 472                            stonith_key_value_t * device_list)
 473 {
 474     return stonith_api_register_level_full(st, options, node, NULL, NULL, NULL,
 475                                            level, device_list);
 476 }
 477 
 478 static int
 479 stonith_api_device_list(stonith_t * stonith, int call_options, const char *namespace,
     /* [previous][next][first][last][top][bottom][index][help] */
 480                         stonith_key_value_t ** devices, int timeout)
 481 {
 482     int count = 0;
 483     enum stonith_namespace ns = stonith_text2namespace(namespace);
 484 
 485     if (devices == NULL) {
 486         crm_err("Parameter error: stonith_api_device_list");
 487         return -EFAULT;
 488     }
 489 
 490 #if HAVE_STONITH_STONITH_H
 491     // Include Linux-HA agents if requested
 492     if ((ns == st_namespace_any) || (ns == st_namespace_lha)) {
 493         count += stonith__list_lha_agents(devices);
 494     }
 495 #endif
 496 
 497     // Include Red Hat agents if requested
 498     if ((ns == st_namespace_any) || (ns == st_namespace_rhcs)) {
 499         count += stonith__list_rhcs_agents(devices);
 500     }
 501 
 502     return count;
 503 }
 504 
 505 static int
 506 stonith_api_device_metadata(stonith_t * stonith, int call_options, const char *agent,
     /* [previous][next][first][last][top][bottom][index][help] */
 507                             const char *namespace, char **output, int timeout)
 508 {
 509     /* By executing meta-data directly, we can get it from stonith_admin when
 510      * the cluster is not running, which is important for higher-level tools.
 511      */
 512 
 513     enum stonith_namespace ns = stonith_get_namespace(agent, namespace);
 514 
 515     crm_trace("Looking up metadata for %s agent %s",
 516               stonith_namespace2text(ns), agent);
 517 
 518     switch (ns) {
 519         case st_namespace_rhcs:
 520             return stonith__rhcs_metadata(agent, timeout, output);
 521 
 522 #if HAVE_STONITH_STONITH_H
 523         case st_namespace_lha:
 524             return stonith__lha_metadata(agent, timeout, output);
 525 #endif
 526 
 527         default:
 528             crm_err("Can't get fence agent '%s' meta-data: No such agent",
 529                     agent);
 530             break;
 531     }
 532     return -ENODEV;
 533 }
 534 
 535 static int
 536 stonith_api_query(stonith_t * stonith, int call_options, const char *target,
     /* [previous][next][first][last][top][bottom][index][help] */
 537                   stonith_key_value_t ** devices, int timeout)
 538 {
 539     int rc = 0, lpc = 0, max = 0;
 540 
 541     xmlNode *data = NULL;
 542     xmlNode *output = NULL;
 543     xmlXPathObjectPtr xpathObj = NULL;
 544 
 545     CRM_CHECK(devices != NULL, return -EINVAL);
 546 
 547     data = create_xml_node(NULL, F_STONITH_DEVICE);
 548     crm_xml_add(data, F_STONITH_ORIGIN, __func__);
 549     crm_xml_add(data, F_STONITH_TARGET, target);
 550     crm_xml_add(data, F_STONITH_ACTION, "off");
 551     rc = stonith_send_command(stonith, STONITH_OP_QUERY, data, &output, call_options, timeout);
 552 
 553     if (rc < 0) {
 554         return rc;
 555     }
 556 
 557     xpathObj = xpath_search(output, "//@agent");
 558     if (xpathObj) {
 559         max = numXpathResults(xpathObj);
 560 
 561         for (lpc = 0; lpc < max; lpc++) {
 562             xmlNode *match = getXpathResult(xpathObj, lpc);
 563 
 564             CRM_LOG_ASSERT(match != NULL);
 565             if(match != NULL) {
 566                 xmlChar *match_path = xmlGetNodePath(match);
 567 
 568                 crm_info("%s[%d] = %s", "//@agent", lpc, match_path);
 569                 free(match_path);
 570                 *devices = stonith_key_value_add(*devices, NULL, crm_element_value(match, XML_ATTR_ID));
 571             }
 572         }
 573 
 574         freeXpathObject(xpathObj);
 575     }
 576 
 577     free_xml(output);
 578     free_xml(data);
 579     return max;
 580 }
 581 
 582 static int
 583 stonith_api_call(stonith_t * stonith,
     /* [previous][next][first][last][top][bottom][index][help] */
 584                  int call_options,
 585                  const char *id,
 586                  const char *action, const char *victim, int timeout, xmlNode ** output)
 587 {
 588     int rc = 0;
 589     xmlNode *data = NULL;
 590 
 591     data = create_xml_node(NULL, F_STONITH_DEVICE);
 592     crm_xml_add(data, F_STONITH_ORIGIN, __func__);
 593     crm_xml_add(data, F_STONITH_DEVICE, id);
 594     crm_xml_add(data, F_STONITH_ACTION, action);
 595     crm_xml_add(data, F_STONITH_TARGET, victim);
 596 
 597     rc = stonith_send_command(stonith, STONITH_OP_EXEC, data, output, call_options, timeout);
 598     free_xml(data);
 599 
 600     return rc;
 601 }
 602 
 603 static int
 604 stonith_api_list(stonith_t * stonith, int call_options, const char *id, char **list_info,
     /* [previous][next][first][last][top][bottom][index][help] */
 605                  int timeout)
 606 {
 607     int rc;
 608     xmlNode *output = NULL;
 609 
 610     rc = stonith_api_call(stonith, call_options, id, "list", NULL, timeout, &output);
 611 
 612     if (output && list_info) {
 613         const char *list_str;
 614 
 615         list_str = crm_element_value(output, F_STONITH_OUTPUT);
 616 
 617         if (list_str) {
 618             *list_info = strdup(list_str);
 619         }
 620     }
 621 
 622     if (output) {
 623         free_xml(output);
 624     }
 625 
 626     return rc;
 627 }
 628 
 629 static int
 630 stonith_api_monitor(stonith_t * stonith, int call_options, const char *id, int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
 631 {
 632     return stonith_api_call(stonith, call_options, id, "monitor", NULL, timeout, NULL);
 633 }
 634 
 635 static int
 636 stonith_api_status(stonith_t * stonith, int call_options, const char *id, const char *port,
     /* [previous][next][first][last][top][bottom][index][help] */
 637                    int timeout)
 638 {
 639     return stonith_api_call(stonith, call_options, id, "status", port, timeout, NULL);
 640 }
 641 
 642 static int
 643 stonith_api_fence_with_delay(stonith_t * stonith, int call_options, const char *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 644                              const char *action, int timeout, int tolerance, int delay)
 645 {
 646     int rc = 0;
 647     xmlNode *data = NULL;
 648 
 649     data = create_xml_node(NULL, __func__);
 650     crm_xml_add(data, F_STONITH_TARGET, node);
 651     crm_xml_add(data, F_STONITH_ACTION, action);
 652     crm_xml_add_int(data, F_STONITH_TIMEOUT, timeout);
 653     crm_xml_add_int(data, F_STONITH_TOLERANCE, tolerance);
 654     crm_xml_add_int(data, F_STONITH_DELAY, delay);
 655 
 656     rc = stonith_send_command(stonith, STONITH_OP_FENCE, data, NULL, call_options, timeout);
 657     free_xml(data);
 658 
 659     return rc;
 660 }
 661 
 662 static int
 663 stonith_api_fence(stonith_t * stonith, int call_options, const char *node, const char *action,
     /* [previous][next][first][last][top][bottom][index][help] */
 664                   int timeout, int tolerance)
 665 {
 666     return stonith_api_fence_with_delay(stonith, call_options, node, action,
 667                                         timeout, tolerance, 0);
 668 }
 669 
 670 static int
 671 stonith_api_confirm(stonith_t * stonith, int call_options, const char *target)
     /* [previous][next][first][last][top][bottom][index][help] */
 672 {
 673     stonith__set_call_options(call_options, target, st_opt_manual_ack);
 674     return stonith_api_fence(stonith, call_options, target, "off", 0, 0);
 675 }
 676 
 677 static int
 678 stonith_api_history(stonith_t * stonith, int call_options, const char *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 679                     stonith_history_t ** history, int timeout)
 680 {
 681     int rc = 0;
 682     xmlNode *data = NULL;
 683     xmlNode *output = NULL;
 684     stonith_history_t *last = NULL;
 685 
 686     *history = NULL;
 687 
 688     if (node) {
 689         data = create_xml_node(NULL, __func__);
 690         crm_xml_add(data, F_STONITH_TARGET, node);
 691     }
 692 
 693     stonith__set_call_options(call_options, node, st_opt_sync_call);
 694     rc = stonith_send_command(stonith, STONITH_OP_FENCE_HISTORY, data, &output,
 695                               call_options, timeout);
 696     free_xml(data);
 697 
 698     if (rc == 0) {
 699         xmlNode *op = NULL;
 700         xmlNode *reply = get_xpath_object("//" F_STONITH_HISTORY_LIST, output,
 701                                           LOG_NEVER);
 702 
 703         for (op = pcmk__xml_first_child(reply); op != NULL;
 704              op = pcmk__xml_next(op)) {
 705             stonith_history_t *kvp;
 706             long long completed;
 707             long long completed_nsec = 0L;
 708             pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
 709 
 710             kvp = calloc(1, sizeof(stonith_history_t));
 711             kvp->target = crm_element_value_copy(op, F_STONITH_TARGET);
 712             kvp->action = crm_element_value_copy(op, F_STONITH_ACTION);
 713             kvp->origin = crm_element_value_copy(op, F_STONITH_ORIGIN);
 714             kvp->delegate = crm_element_value_copy(op, F_STONITH_DELEGATE);
 715             kvp->client = crm_element_value_copy(op, F_STONITH_CLIENTNAME);
 716             crm_element_value_ll(op, F_STONITH_DATE, &completed);
 717             kvp->completed = (time_t) completed;
 718             crm_element_value_ll(op, F_STONITH_DATE_NSEC, &completed_nsec);
 719             kvp->completed_nsec = completed_nsec;
 720             crm_element_value_int(op, F_STONITH_STATE, &kvp->state);
 721 
 722             stonith__xe_get_result(op, &result);
 723             kvp->exit_reason = result.exit_reason;
 724             result.exit_reason = NULL;
 725             pcmk__reset_result(&result);
 726 
 727             if (last) {
 728                 last->next = kvp;
 729             } else {
 730                 *history = kvp;
 731             }
 732             last = kvp;
 733         }
 734     }
 735 
 736     free_xml(output);
 737 
 738     return rc;
 739 }
 740 
 741 void stonith_history_free(stonith_history_t *history)
     /* [previous][next][first][last][top][bottom][index][help] */
 742 {
 743     stonith_history_t *hp, *hp_old;
 744 
 745     for (hp = history; hp; hp_old = hp, hp = hp->next, free(hp_old)) {
 746         free(hp->target);
 747         free(hp->action);
 748         free(hp->origin);
 749         free(hp->delegate);
 750         free(hp->client);
 751         free(hp->exit_reason);
 752     }
 753 }
 754 
 755 static gint
 756 stonithlib_GCompareFunc(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 757 {
 758     int rc = 0;
 759     const stonith_notify_client_t *a_client = a;
 760     const stonith_notify_client_t *b_client = b;
 761 
 762     if (a_client->delete || b_client->delete) {
 763         /* make entries marked for deletion not findable */
 764         return -1;
 765     }
 766     CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0);
 767     rc = strcmp(a_client->event, b_client->event);
 768     if (rc == 0) {
 769         if (a_client->notify == NULL || b_client->notify == NULL) {
 770             return 0;
 771 
 772         } else if (a_client->notify == b_client->notify) {
 773             return 0;
 774 
 775         } else if (((long)a_client->notify) < ((long)b_client->notify)) {
 776             crm_err("callbacks for %s are not equal: %p vs. %p",
 777                     a_client->event, a_client->notify, b_client->notify);
 778             return -1;
 779         }
 780         crm_err("callbacks for %s are not equal: %p vs. %p",
 781                 a_client->event, a_client->notify, b_client->notify);
 782         return 1;
 783     }
 784     return rc;
 785 }
 786 
 787 xmlNode *
 788 stonith_create_op(int call_id, const char *token, const char *op, xmlNode * data, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 789 {
 790     xmlNode *op_msg = create_xml_node(NULL, "stonith_command");
 791 
 792     CRM_CHECK(op_msg != NULL, return NULL);
 793     CRM_CHECK(token != NULL, return NULL);
 794 
 795     crm_xml_add(op_msg, F_XML_TAGNAME, "stonith_command");
 796 
 797     crm_xml_add(op_msg, F_TYPE, T_STONITH_NG);
 798     crm_xml_add(op_msg, F_STONITH_CALLBACK_TOKEN, token);
 799     crm_xml_add(op_msg, F_STONITH_OPERATION, op);
 800     crm_xml_add_int(op_msg, F_STONITH_CALLID, call_id);
 801     crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
 802     crm_xml_add_int(op_msg, F_STONITH_CALLOPTS, call_options);
 803 
 804     if (data != NULL) {
 805         add_message_xml(op_msg, F_STONITH_CALLDATA, data);
 806     }
 807 
 808     return op_msg;
 809 }
 810 
 811 static void
 812 stonith_destroy_op_callback(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 813 {
 814     stonith_callback_client_t *blob = data;
 815 
 816     if (blob->timer && blob->timer->ref > 0) {
 817         g_source_remove(blob->timer->ref);
 818     }
 819     free(blob->timer);
 820     free(blob);
 821 }
 822 
 823 static int
 824 stonith_api_signoff(stonith_t * stonith)
     /* [previous][next][first][last][top][bottom][index][help] */
 825 {
 826     stonith_private_t *native = stonith->st_private;
 827 
 828     crm_debug("Disconnecting from the fencer");
 829 
 830     if (native->source != NULL) {
 831         /* Attached to mainloop */
 832         mainloop_del_ipc_client(native->source);
 833         native->source = NULL;
 834         native->ipc = NULL;
 835 
 836     } else if (native->ipc) {
 837         /* Not attached to mainloop */
 838         crm_ipc_t *ipc = native->ipc;
 839 
 840         native->ipc = NULL;
 841         crm_ipc_close(ipc);
 842         crm_ipc_destroy(ipc);
 843     }
 844 
 845     free(native->token); native->token = NULL;
 846     stonith->state = stonith_disconnected;
 847     return pcmk_ok;
 848 }
 849 
 850 static int
 851 stonith_api_del_callback(stonith_t * stonith, int call_id, bool all_callbacks)
     /* [previous][next][first][last][top][bottom][index][help] */
 852 {
 853     stonith_private_t *private = stonith->st_private;
 854 
 855     if (all_callbacks) {
 856         private->op_callback = NULL;
 857         g_hash_table_destroy(private->stonith_op_callback_table);
 858         private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
 859 
 860     } else if (call_id == 0) {
 861         private->op_callback = NULL;
 862 
 863     } else {
 864         pcmk__intkey_table_remove(private->stonith_op_callback_table, call_id);
 865     }
 866     return pcmk_ok;
 867 }
 868 
 869 /*!
 870  * \internal
 871  * \brief Invoke a (single) specified fence action callback
 872  *
 873  * \param[in] st        Fencer API connection
 874  * \param[in] call_id   If positive, call ID of completed fence action, otherwise
 875  *                      legacy return code for early action failure
 876  * \param[in] result    Full result for action
 877  * \param[in] userdata  User data to pass to callback
 878  * \param[in] callback  Fence action callback to invoke
 879  */
 880 static void
 881 invoke_fence_action_callback(stonith_t *st, int call_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 882                              pcmk__action_result_t *result,
 883                              void *userdata,
 884                              void (*callback) (stonith_t *st,
 885                                                stonith_callback_data_t *data))
 886 {
 887     stonith_callback_data_t data = { 0, };
 888 
 889     data.call_id = call_id;
 890     data.rc = pcmk_rc2legacy(stonith__result2rc(result));
 891     data.userdata = userdata;
 892     data.opaque = (void *) result;
 893 
 894     callback(st, &data);
 895 }
 896 
 897 /*!
 898  * \internal
 899  * \brief Invoke any callbacks registered for a specified fence action result
 900  *
 901  * Given a fence action result from the fencer, invoke any callback registered
 902  * for that action, as well as any global callback registered.
 903  *
 904  * \param[in] st        Fencer API connection
 905  * \param[in] msg       If non-NULL, fencer reply
 906  * \param[in] call_id   If \p msg is NULL, call ID of action that timed out
 907  */
 908 static void
 909 invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 910 {
 911     stonith_private_t *private = NULL;
 912     stonith_callback_client_t *cb_info = NULL;
 913     pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
 914 
 915     CRM_CHECK(stonith != NULL, return);
 916     CRM_CHECK(stonith->st_private != NULL, return);
 917 
 918     private = stonith->st_private;
 919 
 920     if (msg == NULL) {
 921         // Fencer didn't reply in time
 922         pcmk__set_result(&result, CRM_EX_ERROR, PCMK_EXEC_TIMEOUT,
 923                          "Fencer accepted request but did not reply in time");
 924         CRM_LOG_ASSERT(call_id > 0);
 925 
 926     } else {
 927         // We have the fencer reply
 928         if ((crm_element_value_int(msg, F_STONITH_CALLID, &call_id) != 0)
 929             || (call_id <= 0)) {
 930             crm_log_xml_warn(msg, "Bad fencer reply");
 931         }
 932         stonith__xe_get_result(msg, &result);
 933     }
 934 
 935     if (call_id > 0) {
 936         cb_info = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
 937                                             call_id);
 938     }
 939 
 940     if ((cb_info != NULL) && (cb_info->callback != NULL)
 941         && (pcmk__result_ok(&result) || !(cb_info->only_success))) {
 942         crm_trace("Invoking callback %s for call %d",
 943                   crm_str(cb_info->id), call_id);
 944         invoke_fence_action_callback(stonith, call_id, &result,
 945                                      cb_info->user_data, cb_info->callback);
 946 
 947     } else if ((private->op_callback == NULL) && !pcmk__result_ok(&result)) {
 948         crm_warn("Fencing action without registered callback failed: %d (%s%s%s)",
 949                  result.exit_status,
 950                  pcmk_exec_status_str(result.execution_status),
 951                  ((result.exit_reason == NULL)? "" : ": "),
 952                  ((result.exit_reason == NULL)? "" : result.exit_reason));
 953         crm_log_xml_debug(msg, "Failed fence update");
 954     }
 955 
 956     if (private->op_callback != NULL) {
 957         crm_trace("Invoking global callback for call %d", call_id);
 958         invoke_fence_action_callback(stonith, call_id, &result, NULL,
 959                                      private->op_callback);
 960     }
 961 
 962     if (cb_info != NULL) {
 963         stonith_api_del_callback(stonith, call_id, FALSE);
 964     }
 965     pcmk__reset_result(&result);
 966 }
 967 
 968 static gboolean
 969 stonith_async_timeout_handler(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 970 {
 971     struct timer_rec_s *timer = data;
 972 
 973     crm_err("Async call %d timed out after %dms", timer->call_id, timer->timeout);
 974     invoke_registered_callbacks(timer->stonith, NULL, timer->call_id);
 975 
 976     /* Always return TRUE, never remove the handler
 977      * We do that in stonith_del_callback()
 978      */
 979     return TRUE;
 980 }
 981 
 982 static void
 983 set_callback_timeout(stonith_callback_client_t * callback, stonith_t * stonith, int call_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 984                      int timeout)
 985 {
 986     struct timer_rec_s *async_timer = callback->timer;
 987 
 988     if (timeout <= 0) {
 989         return;
 990     }
 991 
 992     if (!async_timer) {
 993         async_timer = calloc(1, sizeof(struct timer_rec_s));
 994         callback->timer = async_timer;
 995     }
 996 
 997     async_timer->stonith = stonith;
 998     async_timer->call_id = call_id;
 999     /* Allow a fair bit of grace to allow the server to tell us of a timeout
1000      * This is only a fallback
1001      */
1002     async_timer->timeout = (timeout + 60) * 1000;
1003     if (async_timer->ref) {
1004         g_source_remove(async_timer->ref);
1005     }
1006     async_timer->ref =
1007         g_timeout_add(async_timer->timeout, stonith_async_timeout_handler, async_timer);
1008 }
1009 
1010 static void
1011 update_callback_timeout(int call_id, int timeout, stonith_t * st)
     /* [previous][next][first][last][top][bottom][index][help] */
1012 {
1013     stonith_callback_client_t *callback = NULL;
1014     stonith_private_t *private = st->st_private;
1015 
1016     callback = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
1017                                          call_id);
1018     if (!callback || !callback->allow_timeout_updates) {
1019         return;
1020     }
1021 
1022     set_callback_timeout(callback, st, call_id, timeout);
1023 }
1024 
1025 static int
1026 stonith_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
1027 {
1028     const char *type = NULL;
1029     struct notify_blob_s blob;
1030 
1031     stonith_t *st = userdata;
1032     stonith_private_t *private = NULL;
1033 
1034     CRM_ASSERT(st != NULL);
1035     private = st->st_private;
1036 
1037     blob.stonith = st;
1038     blob.xml = string2xml(buffer);
1039     if (blob.xml == NULL) {
1040         crm_warn("Received malformed message from fencer: %s", buffer);
1041         return 0;
1042     }
1043 
1044     /* do callbacks */
1045     type = crm_element_value(blob.xml, F_TYPE);
1046     crm_trace("Activating %s callbacks...", type);
1047 
1048     if (pcmk__str_eq(type, T_STONITH_NG, pcmk__str_casei)) {
1049         invoke_registered_callbacks(st, blob.xml, 0);
1050 
1051     } else if (pcmk__str_eq(type, T_STONITH_NOTIFY, pcmk__str_casei)) {
1052         foreach_notify_entry(private, stonith_send_notification, &blob);
1053     } else if (pcmk__str_eq(type, T_STONITH_TIMEOUT_VALUE, pcmk__str_casei)) {
1054         int call_id = 0;
1055         int timeout = 0;
1056 
1057         crm_element_value_int(blob.xml, F_STONITH_TIMEOUT, &timeout);
1058         crm_element_value_int(blob.xml, F_STONITH_CALLID, &call_id);
1059 
1060         update_callback_timeout(call_id, timeout, st);
1061     } else {
1062         crm_err("Unknown message type: %s", type);
1063         crm_log_xml_warn(blob.xml, "BadReply");
1064     }
1065 
1066     free_xml(blob.xml);
1067     return 1;
1068 }
1069 
1070 static int
1071 stonith_api_signon(stonith_t * stonith, const char *name, int *stonith_fd)
     /* [previous][next][first][last][top][bottom][index][help] */
1072 {
1073     int rc = pcmk_ok;
1074     stonith_private_t *native = NULL;
1075     const char *display_name = name? name : "client";
1076 
1077     struct ipc_client_callbacks st_callbacks = {
1078         .dispatch = stonith_dispatch_internal,
1079         .destroy = stonith_connection_destroy
1080     };
1081 
1082     CRM_CHECK(stonith != NULL, return -EINVAL);
1083 
1084     native = stonith->st_private;
1085     CRM_ASSERT(native != NULL);
1086 
1087     crm_debug("Attempting fencer connection by %s with%s mainloop",
1088               display_name, (stonith_fd? "out" : ""));
1089 
1090     stonith->state = stonith_connected_command;
1091     if (stonith_fd) {
1092         /* No mainloop */
1093         native->ipc = crm_ipc_new("stonith-ng", 0);
1094 
1095         if (native->ipc && crm_ipc_connect(native->ipc)) {
1096             *stonith_fd = crm_ipc_get_fd(native->ipc);
1097         } else if (native->ipc) {
1098             crm_ipc_close(native->ipc);
1099             crm_ipc_destroy(native->ipc);
1100             native->ipc = NULL;
1101         }
1102 
1103     } else {
1104         /* With mainloop */
1105         native->source =
1106             mainloop_add_ipc_client("stonith-ng", G_PRIORITY_MEDIUM, 0, stonith, &st_callbacks);
1107         native->ipc = mainloop_get_ipc_client(native->source);
1108     }
1109 
1110     if (native->ipc == NULL) {
1111         rc = -ENOTCONN;
1112     } else {
1113         xmlNode *reply = NULL;
1114         xmlNode *hello = create_xml_node(NULL, "stonith_command");
1115 
1116         crm_xml_add(hello, F_TYPE, T_STONITH_NG);
1117         crm_xml_add(hello, F_STONITH_OPERATION, CRM_OP_REGISTER);
1118         crm_xml_add(hello, F_STONITH_CLIENTNAME, name);
1119         rc = crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, &reply);
1120 
1121         if (rc < 0) {
1122             crm_debug("Couldn't register with the fencer: %s "
1123                       CRM_XS " rc=%d", pcmk_strerror(rc), rc);
1124             rc = -ECOMM;
1125 
1126         } else if (reply == NULL) {
1127             crm_debug("Couldn't register with the fencer: no reply");
1128             rc = -EPROTO;
1129 
1130         } else {
1131             const char *msg_type = crm_element_value(reply, F_STONITH_OPERATION);
1132 
1133             native->token = crm_element_value_copy(reply, F_STONITH_CLIENTID);
1134             if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) {
1135                 crm_debug("Couldn't register with the fencer: invalid reply type '%s'",
1136                           (msg_type? msg_type : "(missing)"));
1137                 crm_log_xml_debug(reply, "Invalid fencer reply");
1138                 rc = -EPROTO;
1139 
1140             } else if (native->token == NULL) {
1141                 crm_debug("Couldn't register with the fencer: no token in reply");
1142                 crm_log_xml_debug(reply, "Invalid fencer reply");
1143                 rc = -EPROTO;
1144 
1145             } else {
1146                 crm_debug("Connection to fencer by %s succeeded (registration token: %s)",
1147                           display_name, native->token);
1148                 rc = pcmk_ok;
1149             }
1150         }
1151 
1152         free_xml(reply);
1153         free_xml(hello);
1154     }
1155 
1156     if (rc != pcmk_ok) {
1157         crm_debug("Connection attempt to fencer by %s failed: %s "
1158                   CRM_XS " rc=%d", display_name, pcmk_strerror(rc), rc);
1159         stonith->cmds->disconnect(stonith);
1160     }
1161     return rc;
1162 }
1163 
1164 static int
1165 stonith_set_notification(stonith_t * stonith, const char *callback, int enabled)
     /* [previous][next][first][last][top][bottom][index][help] */
1166 {
1167     int rc = pcmk_ok;
1168     xmlNode *notify_msg = create_xml_node(NULL, __func__);
1169     stonith_private_t *native = stonith->st_private;
1170 
1171     if (stonith->state != stonith_disconnected) {
1172 
1173         crm_xml_add(notify_msg, F_STONITH_OPERATION, T_STONITH_NOTIFY);
1174         if (enabled) {
1175             crm_xml_add(notify_msg, F_STONITH_NOTIFY_ACTIVATE, callback);
1176         } else {
1177             crm_xml_add(notify_msg, F_STONITH_NOTIFY_DEACTIVATE, callback);
1178         }
1179 
1180         rc = crm_ipc_send(native->ipc, notify_msg, crm_ipc_client_response, -1, NULL);
1181         if (rc < 0) {
1182             crm_perror(LOG_DEBUG, "Couldn't register for fencing notifications: %d", rc);
1183             rc = -ECOMM;
1184         } else {
1185             rc = pcmk_ok;
1186         }
1187     }
1188 
1189     free_xml(notify_msg);
1190     return rc;
1191 }
1192 
1193 static int
1194 stonith_api_add_notification(stonith_t * stonith, const char *event,
     /* [previous][next][first][last][top][bottom][index][help] */
1195                              void (*callback) (stonith_t * stonith, stonith_event_t * e))
1196 {
1197     GList *list_item = NULL;
1198     stonith_notify_client_t *new_client = NULL;
1199     stonith_private_t *private = NULL;
1200 
1201     private = stonith->st_private;
1202     crm_trace("Adding callback for %s events (%d)", event, g_list_length(private->notify_list));
1203 
1204     new_client = calloc(1, sizeof(stonith_notify_client_t));
1205     new_client->event = event;
1206     new_client->notify = callback;
1207 
1208     list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
1209 
1210     if (list_item != NULL) {
1211         crm_warn("Callback already present");
1212         free(new_client);
1213         return -ENOTUNIQ;
1214 
1215     } else {
1216         private->notify_list = g_list_append(private->notify_list, new_client);
1217 
1218         stonith_set_notification(stonith, event, 1);
1219 
1220         crm_trace("Callback added (%d)", g_list_length(private->notify_list));
1221     }
1222     return pcmk_ok;
1223 }
1224 
1225 static void
1226 del_notify_entry(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1227 {
1228     stonith_notify_client_t *entry = data;
1229     stonith_t * stonith = user_data;
1230 
1231     if (!entry->delete) {
1232         crm_debug("Removing callback for %s events", entry->event);
1233         stonith_api_del_notification(stonith, entry->event);
1234     }
1235 }
1236 
1237 static int
1238 stonith_api_del_notification(stonith_t * stonith, const char *event)
     /* [previous][next][first][last][top][bottom][index][help] */
1239 {
1240     GList *list_item = NULL;
1241     stonith_notify_client_t *new_client = NULL;
1242     stonith_private_t *private = stonith->st_private;
1243 
1244     if (event == NULL) {
1245         foreach_notify_entry(private, del_notify_entry, stonith);
1246         crm_trace("Removed callback");
1247 
1248         return pcmk_ok;
1249     }
1250 
1251     crm_debug("Removing callback for %s events", event);
1252 
1253     new_client = calloc(1, sizeof(stonith_notify_client_t));
1254     new_client->event = event;
1255     new_client->notify = NULL;
1256 
1257     list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
1258 
1259     stonith_set_notification(stonith, event, 0);
1260 
1261     if (list_item != NULL) {
1262         stonith_notify_client_t *list_client = list_item->data;
1263 
1264         if (private->notify_refcnt) {
1265             list_client->delete = TRUE;
1266             private->notify_deletes = TRUE;
1267         } else {
1268             private->notify_list = g_list_remove(private->notify_list, list_client);
1269             free(list_client);
1270         }
1271 
1272         crm_trace("Removed callback");
1273 
1274     } else {
1275         crm_trace("Callback not present");
1276     }
1277     free(new_client);
1278     return pcmk_ok;
1279 }
1280 
1281 static int
1282 stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int options,
     /* [previous][next][first][last][top][bottom][index][help] */
1283                          void *user_data, const char *callback_name,
1284                          void (*callback) (stonith_t * st, stonith_callback_data_t * data))
1285 {
1286     stonith_callback_client_t *blob = NULL;
1287     stonith_private_t *private = NULL;
1288 
1289     CRM_CHECK(stonith != NULL, return -EINVAL);
1290     CRM_CHECK(stonith->st_private != NULL, return -EINVAL);
1291     private = stonith->st_private;
1292 
1293     if (call_id == 0) { // Add global callback
1294         private->op_callback = callback;
1295 
1296     } else if (call_id < 0) { // Call failed immediately, so call callback now
1297         if (!(options & st_opt_report_only_success)) {
1298             pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
1299 
1300             crm_trace("Call failed, calling %s: %s", callback_name, pcmk_strerror(call_id));
1301             pcmk__set_result(&result, CRM_EX_ERROR,
1302                              stonith__legacy2status(call_id), NULL);
1303             invoke_fence_action_callback(stonith, call_id, &result,
1304                                          user_data, callback);
1305         } else {
1306             crm_warn("Fencer call failed: %s", pcmk_strerror(call_id));
1307         }
1308         return FALSE;
1309     }
1310 
1311     blob = calloc(1, sizeof(stonith_callback_client_t));
1312     blob->id = callback_name;
1313     blob->only_success = (options & st_opt_report_only_success) ? TRUE : FALSE;
1314     blob->user_data = user_data;
1315     blob->callback = callback;
1316     blob->allow_timeout_updates = (options & st_opt_timeout_updates) ? TRUE : FALSE;
1317 
1318     if (timeout > 0) {
1319         set_callback_timeout(blob, stonith, call_id, timeout);
1320     }
1321 
1322     pcmk__intkey_table_insert(private->stonith_op_callback_table, call_id,
1323                               blob);
1324     crm_trace("Added callback to %s for call %d", callback_name, call_id);
1325 
1326     return TRUE;
1327 }
1328 
1329 static void
1330 stonith_dump_pending_op(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1331 {
1332     int call = GPOINTER_TO_INT(key);
1333     stonith_callback_client_t *blob = value;
1334 
1335     crm_debug("Call %d (%s): pending", call, crm_str(blob->id));
1336 }
1337 
1338 void
1339 stonith_dump_pending_callbacks(stonith_t * stonith)
     /* [previous][next][first][last][top][bottom][index][help] */
1340 {
1341     stonith_private_t *private = stonith->st_private;
1342 
1343     if (private->stonith_op_callback_table == NULL) {
1344         return;
1345     }
1346     return g_hash_table_foreach(private->stonith_op_callback_table, stonith_dump_pending_op, NULL);
1347 }
1348 
1349 /*!
1350  * \internal
1351  * \brief Get the data section of a fencer notification
1352  *
1353  * \param[in] msg    Notification XML
1354  * \param[in] ntype  Notification type
1355  */
1356 static xmlNode *
1357 get_event_data_xml(xmlNode *msg, const char *ntype)
     /* [previous][next][first][last][top][bottom][index][help] */
1358 {
1359     char *data_addr = crm_strdup_printf("//%s", ntype);
1360     xmlNode *data = get_xpath_object(data_addr, msg, LOG_DEBUG);
1361 
1362     free(data_addr);
1363     return data;
1364 }
1365 
1366 /*
1367  <notify t="st_notify" subt="st_device_register" st_op="st_device_register" st_rc="0" >
1368    <st_calldata >
1369      <stonith_command t="stonith-ng" st_async_id="088fb640-431a-48b9-b2fc-c4ff78d0a2d9" st_op="st_device_register" st_callid="2" st_callopt="4096" st_timeout="0" st_clientid="088fb640-431a-48b9-b2fc-c4ff78d0a2d9" st_clientname="cts-fence-helper" >
1370        <st_calldata >
1371          <st_device_id id="test-id" origin="create_device_registration_xml" agent="fence_virsh" namespace="stonith-ng" >
1372            <attributes ipaddr="localhost" pcmk-portmal="some-host=pcmk-1 pcmk-3=3,4" login="root" identity_file="/root/.ssh/id_dsa" />
1373          </st_device_id>
1374        </st_calldata>
1375      </stonith_command>
1376    </st_calldata>
1377  </notify>
1378 
1379  <notify t="st_notify" subt="st_notify_fence" st_op="st_notify_fence" st_rc="0" >
1380    <st_calldata >
1381      <st_notify_fence st_rc="0" st_target="some-host" st_op="st_fence" st_delegate="test-id" st_origin="61dd7759-e229-4be7-b1f8-ef49dd14d9f0" />
1382    </st_calldata>
1383  </notify>
1384 */
1385 static stonith_event_t *
1386 xml_to_event(xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
1387 {
1388     stonith_event_t *event = calloc(1, sizeof(stonith_event_t));
1389     const char *ntype = crm_element_value(msg, F_SUBTYPE);
1390     struct event_private *event_private = NULL;
1391 
1392     CRM_ASSERT(event != NULL);
1393 
1394     event->opaque = calloc(1, sizeof(struct event_private));
1395     CRM_ASSERT(event->opaque != NULL);
1396     event_private = (struct event_private *) event->opaque;
1397 
1398     crm_log_xml_trace(msg, "stonith_notify");
1399 
1400     // All notification types have the operation result
1401     stonith__xe_get_result(msg, &event_private->result);
1402 
1403     // @COMPAT The API originally provided the result as a legacy return code
1404     event->result = pcmk_rc2legacy(stonith__result2rc(&event_private->result));
1405 
1406     // Fence notifications have additional information
1407     if (pcmk__str_eq(ntype, T_STONITH_NOTIFY_FENCE, pcmk__str_casei)) {
1408         xmlNode *data = get_event_data_xml(msg, ntype);
1409 
1410         if (data == NULL) {
1411             crm_err("No data for %s event", ntype);
1412             crm_log_xml_notice(msg, "BadEvent");
1413         } else {
1414             event->origin = crm_element_value_copy(data, F_STONITH_ORIGIN);
1415             event->action = crm_element_value_copy(data, F_STONITH_ACTION);
1416             event->target = crm_element_value_copy(data, F_STONITH_TARGET);
1417             event->executioner = crm_element_value_copy(data, F_STONITH_DELEGATE);
1418             event->id = crm_element_value_copy(data, F_STONITH_REMOTE_OP_ID);
1419             event->client_origin = crm_element_value_copy(data, F_STONITH_CLIENTNAME);
1420             event->device = crm_element_value_copy(data, F_STONITH_DEVICE);
1421         }
1422         event->operation = crm_element_value_copy(msg, F_STONITH_OPERATION);
1423     }
1424 
1425     return event;
1426 }
1427 
1428 static void
1429 event_free(stonith_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help] */
1430 {
1431     struct event_private *event_private = event->opaque;
1432 
1433     free(event->id);
1434     free(event->type);
1435     free(event->message);
1436     free(event->operation);
1437     free(event->origin);
1438     free(event->action);
1439     free(event->target);
1440     free(event->executioner);
1441     free(event->device);
1442     free(event->client_origin);
1443     pcmk__reset_result(&event_private->result);
1444     free(event->opaque);
1445     free(event);
1446 }
1447 
1448 static void
1449 stonith_send_notification(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1450 {
1451     struct notify_blob_s *blob = user_data;
1452     stonith_notify_client_t *entry = data;
1453     stonith_event_t *st_event = NULL;
1454     const char *event = NULL;
1455 
1456     if (blob->xml == NULL) {
1457         crm_warn("Skipping callback - NULL message");
1458         return;
1459     }
1460 
1461     event = crm_element_value(blob->xml, F_SUBTYPE);
1462 
1463     if (entry == NULL) {
1464         crm_warn("Skipping callback - NULL callback client");
1465         return;
1466 
1467     } else if (entry->delete) {
1468         crm_trace("Skipping callback - marked for deletion");
1469         return;
1470 
1471     } else if (entry->notify == NULL) {
1472         crm_warn("Skipping callback - NULL callback");
1473         return;
1474 
1475     } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
1476         crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
1477         return;
1478     }
1479 
1480     st_event = xml_to_event(blob->xml);
1481 
1482     crm_trace("Invoking callback for %p/%s event...", entry, event);
1483     entry->notify(blob->stonith, st_event);
1484     crm_trace("Callback invoked...");
1485 
1486     event_free(st_event);
1487 }
1488 
1489 /*!
1490  * \internal
1491  * \brief Create and send an API request
1492  *
1493  * \param[in]  stonith       Stonith connection
1494  * \param[in]  op            API operation to request
1495  * \param[in]  data          Data to attach to request
1496  * \param[out] output_data   If not NULL, will be set to reply if synchronous
1497  * \param[in]  call_options  Bitmask of stonith_call_options to use
1498  * \param[in]  timeout       Error if not completed within this many seconds
1499  *
1500  * \return pcmk_ok (for synchronous requests) or positive call ID
1501  *         (for asynchronous requests) on success, -errno otherwise
1502  */
1503 static int
1504 stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNode ** output_data,
     /* [previous][next][first][last][top][bottom][index][help] */
1505                      int call_options, int timeout)
1506 {
1507     int rc = 0;
1508     int reply_id = -1;
1509 
1510     xmlNode *op_msg = NULL;
1511     xmlNode *op_reply = NULL;
1512     stonith_private_t *native = NULL;
1513 
1514     CRM_ASSERT(stonith && stonith->st_private && op);
1515     native = stonith->st_private;
1516 
1517     if (output_data != NULL) {
1518         *output_data = NULL;
1519     }
1520 
1521     if ((stonith->state == stonith_disconnected) || (native->token == NULL)) {
1522         return -ENOTCONN;
1523     }
1524 
1525     /* Increment the call ID, which must be positive to avoid conflicting with
1526      * error codes. This shouldn't be a problem unless the client mucked with
1527      * it or the counter wrapped around.
1528      */
1529     stonith->call_id++;
1530     if (stonith->call_id < 1) {
1531         stonith->call_id = 1;
1532     }
1533 
1534     op_msg = stonith_create_op(stonith->call_id, native->token, op, data, call_options);
1535     if (op_msg == NULL) {
1536         return -EINVAL;
1537     }
1538 
1539     crm_xml_add_int(op_msg, F_STONITH_TIMEOUT, timeout);
1540     crm_trace("Sending %s message to fencer with timeout %ds", op, timeout);
1541 
1542     if (data) {
1543         const char *delay_s = crm_element_value(data, F_STONITH_DELAY);
1544 
1545         if (delay_s) {
1546             crm_xml_add(op_msg, F_STONITH_DELAY, delay_s);
1547         }
1548     }
1549 
1550     {
1551         enum crm_ipc_flags ipc_flags = crm_ipc_flags_none;
1552 
1553         if (call_options & st_opt_sync_call) {
1554             pcmk__set_ipc_flags(ipc_flags, "stonith command",
1555                                 crm_ipc_client_response);
1556         }
1557         rc = crm_ipc_send(native->ipc, op_msg, ipc_flags,
1558                           1000 * (timeout + 60), &op_reply);
1559     }
1560     free_xml(op_msg);
1561 
1562     if (rc < 0) {
1563         crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%ds): %d", op, timeout, rc);
1564         rc = -ECOMM;
1565         goto done;
1566     }
1567 
1568     crm_log_xml_trace(op_reply, "Reply");
1569 
1570     if (!(call_options & st_opt_sync_call)) {
1571         crm_trace("Async call %d, returning", stonith->call_id);
1572         free_xml(op_reply);
1573         return stonith->call_id;
1574     }
1575 
1576     rc = pcmk_ok;
1577     crm_element_value_int(op_reply, F_STONITH_CALLID, &reply_id);
1578 
1579     if (reply_id == stonith->call_id) {
1580         pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
1581 
1582         crm_trace("Synchronous reply %d received", reply_id);
1583 
1584         stonith__xe_get_result(op_reply, &result);
1585         rc = pcmk_rc2legacy(stonith__result2rc(&result));
1586         pcmk__reset_result(&result);
1587 
1588         if ((call_options & st_opt_discard_reply) || output_data == NULL) {
1589             crm_trace("Discarding reply");
1590 
1591         } else {
1592             *output_data = op_reply;
1593             op_reply = NULL;    /* Prevent subsequent free */
1594         }
1595 
1596     } else if (reply_id <= 0) {
1597         crm_err("Received bad reply: No id set");
1598         crm_log_xml_err(op_reply, "Bad reply");
1599         free_xml(op_reply);
1600         rc = -ENOMSG;
1601 
1602     } else {
1603         crm_err("Received bad reply: %d (wanted %d)", reply_id, stonith->call_id);
1604         crm_log_xml_err(op_reply, "Old reply");
1605         free_xml(op_reply);
1606         rc = -ENOMSG;
1607     }
1608 
1609   done:
1610     if (crm_ipc_connected(native->ipc) == FALSE) {
1611         crm_err("Fencer disconnected");
1612         free(native->token); native->token = NULL;
1613         stonith->state = stonith_disconnected;
1614     }
1615 
1616     free_xml(op_reply);
1617     return rc;
1618 }
1619 
1620 /* Not used with mainloop */
1621 bool
1622 stonith_dispatch(stonith_t * st)
     /* [previous][next][first][last][top][bottom][index][help] */
1623 {
1624     gboolean stay_connected = TRUE;
1625     stonith_private_t *private = NULL;
1626 
1627     CRM_ASSERT(st != NULL);
1628     private = st->st_private;
1629 
1630     while (crm_ipc_ready(private->ipc)) {
1631 
1632         if (crm_ipc_read(private->ipc) > 0) {
1633             const char *msg = crm_ipc_buffer(private->ipc);
1634 
1635             stonith_dispatch_internal(msg, strlen(msg), st);
1636         }
1637 
1638         if (crm_ipc_connected(private->ipc) == FALSE) {
1639             crm_err("Connection closed");
1640             stay_connected = FALSE;
1641         }
1642     }
1643 
1644     return stay_connected;
1645 }
1646 
1647 static int
1648 stonith_api_free(stonith_t * stonith)
     /* [previous][next][first][last][top][bottom][index][help] */
1649 {
1650     int rc = pcmk_ok;
1651 
1652     crm_trace("Destroying %p", stonith);
1653 
1654     if (stonith->state != stonith_disconnected) {
1655         crm_trace("Disconnecting %p first", stonith);
1656         rc = stonith->cmds->disconnect(stonith);
1657     }
1658 
1659     if (stonith->state == stonith_disconnected) {
1660         stonith_private_t *private = stonith->st_private;
1661 
1662         crm_trace("Removing %d callbacks", g_hash_table_size(private->stonith_op_callback_table));
1663         g_hash_table_destroy(private->stonith_op_callback_table);
1664 
1665         crm_trace("Destroying %d notification clients", g_list_length(private->notify_list));
1666         g_list_free_full(private->notify_list, free);
1667 
1668         free(stonith->st_private);
1669         free(stonith->cmds);
1670         free(stonith);
1671 
1672     } else {
1673         crm_err("Not free'ing active connection: %s (%d)", pcmk_strerror(rc), rc);
1674     }
1675 
1676     return rc;
1677 }
1678 
1679 void
1680 stonith_api_delete(stonith_t * stonith)
     /* [previous][next][first][last][top][bottom][index][help] */
1681 {
1682     crm_trace("Destroying %p", stonith);
1683     if(stonith) {
1684         stonith->cmds->free(stonith);
1685     }
1686 }
1687 
1688 static int
1689 stonith_api_validate(stonith_t *st, int call_options, const char *rsc_id,
     /* [previous][next][first][last][top][bottom][index][help] */
1690                      const char *namespace_s, const char *agent,
1691                      stonith_key_value_t *params, int timeout, char **output,
1692                      char **error_output)
1693 {
1694     /* Validation should be done directly via the agent, so we can get it from
1695      * stonith_admin when the cluster is not running, which is important for
1696      * higher-level tools.
1697      */
1698 
1699     int rc = pcmk_ok;
1700 
1701     /* Use a dummy node name in case the agent requires a target. We assume the
1702      * actual target doesn't matter for validation purposes (if in practice,
1703      * that is incorrect, we will need to allow the caller to pass the target).
1704      */
1705     const char *target = "node1";
1706     const char *host_arg = NULL;
1707 
1708     GHashTable *params_table = pcmk__strkey_table(free, free);
1709 
1710     // Convert parameter list to a hash table
1711     for (; params; params = params->next) {
1712         if (pcmk__str_eq(params->key, PCMK_STONITH_HOST_ARGUMENT,
1713                          pcmk__str_casei)) {
1714             host_arg = params->value;
1715         }
1716         if (!pcmk_stonith_param(params->key)) {
1717             g_hash_table_insert(params_table, strdup(params->key),
1718                                 strdup(params->value));
1719         }
1720     }
1721 
1722 #if SUPPORT_CIBSECRETS
1723     rc = pcmk__substitute_secrets(rsc_id, params_table);
1724     if (rc != pcmk_rc_ok) {
1725         crm_warn("Could not replace secret parameters for validation of %s: %s",
1726                  agent, pcmk_rc_str(rc));
1727         // rc is standard return value, don't return it in this function
1728     }
1729 #endif
1730 
1731     if (output) {
1732         *output = NULL;
1733     }
1734     if (error_output) {
1735         *error_output = NULL;
1736     }
1737 
1738     switch (stonith_get_namespace(agent, namespace_s)) {
1739         case st_namespace_rhcs:
1740             rc = stonith__rhcs_validate(st, call_options, target, agent,
1741                                         params_table, host_arg, timeout,
1742                                         output, error_output);
1743             break;
1744 
1745 #if HAVE_STONITH_STONITH_H
1746         case st_namespace_lha:
1747             rc = stonith__lha_validate(st, call_options, target, agent,
1748                                        params_table, timeout, output,
1749                                        error_output);
1750             break;
1751 #endif
1752 
1753         default:
1754             rc = -EINVAL;
1755             errno = EINVAL;
1756             crm_perror(LOG_ERR,
1757                        "Agent %s not found or does not support validation",
1758                        agent);
1759             break;
1760     }
1761     g_hash_table_destroy(params_table);
1762     return rc;
1763 }
1764 
1765 stonith_t *
1766 stonith_api_new(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1767 {
1768     stonith_t *new_stonith = NULL;
1769     stonith_private_t *private = NULL;
1770 
1771     new_stonith = calloc(1, sizeof(stonith_t));
1772     if (new_stonith == NULL) {
1773         return NULL;
1774     }
1775 
1776     private = calloc(1, sizeof(stonith_private_t));
1777     if (private == NULL) {
1778         free(new_stonith);
1779         return NULL;
1780     }
1781     new_stonith->st_private = private;
1782 
1783     private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
1784     private->notify_list = NULL;
1785     private->notify_refcnt = 0;
1786     private->notify_deletes = FALSE;
1787 
1788     new_stonith->call_id = 1;
1789     new_stonith->state = stonith_disconnected;
1790 
1791     new_stonith->cmds = calloc(1, sizeof(stonith_api_operations_t));
1792     if (new_stonith->cmds == NULL) {
1793         free(new_stonith->st_private);
1794         free(new_stonith);
1795         return NULL;
1796     }
1797 
1798 /* *INDENT-OFF* */
1799     new_stonith->cmds->free       = stonith_api_free;
1800     new_stonith->cmds->connect    = stonith_api_signon;
1801     new_stonith->cmds->disconnect = stonith_api_signoff;
1802 
1803     new_stonith->cmds->list       = stonith_api_list;
1804     new_stonith->cmds->monitor    = stonith_api_monitor;
1805     new_stonith->cmds->status     = stonith_api_status;
1806     new_stonith->cmds->fence      = stonith_api_fence;
1807     new_stonith->cmds->fence_with_delay = stonith_api_fence_with_delay;
1808     new_stonith->cmds->confirm    = stonith_api_confirm;
1809     new_stonith->cmds->history    = stonith_api_history;
1810 
1811     new_stonith->cmds->list_agents  = stonith_api_device_list;
1812     new_stonith->cmds->metadata     = stonith_api_device_metadata;
1813 
1814     new_stonith->cmds->query           = stonith_api_query;
1815     new_stonith->cmds->remove_device   = stonith_api_remove_device;
1816     new_stonith->cmds->register_device = stonith_api_register_device;
1817 
1818     new_stonith->cmds->remove_level          = stonith_api_remove_level;
1819     new_stonith->cmds->remove_level_full     = stonith_api_remove_level_full;
1820     new_stonith->cmds->register_level        = stonith_api_register_level;
1821     new_stonith->cmds->register_level_full   = stonith_api_register_level_full;
1822 
1823     new_stonith->cmds->remove_callback       = stonith_api_del_callback;
1824     new_stonith->cmds->register_callback     = stonith_api_add_callback;
1825     new_stonith->cmds->remove_notification   = stonith_api_del_notification;
1826     new_stonith->cmds->register_notification = stonith_api_add_notification;
1827 
1828     new_stonith->cmds->validate              = stonith_api_validate;
1829 /* *INDENT-ON* */
1830 
1831     return new_stonith;
1832 }
1833 
1834 /*!
1835  * \brief Make a blocking connection attempt to the fencer
1836  *
1837  * \param[in,out] st            Fencer API object
1838  * \param[in]     name          Client name to use with fencer
1839  * \param[in]     max_attempts  Return error if this many attempts fail
1840  *
1841  * \return pcmk_ok on success, result of last attempt otherwise
1842  */
1843 int
1844 stonith_api_connect_retry(stonith_t *st, const char *name, int max_attempts)
     /* [previous][next][first][last][top][bottom][index][help] */
1845 {
1846     int rc = -EINVAL; // if max_attempts is not positive
1847 
1848     for (int attempt = 1; attempt <= max_attempts; attempt++) {
1849         rc = st->cmds->connect(st, name, NULL);
1850         if (rc == pcmk_ok) {
1851             return pcmk_ok;
1852         } else if (attempt < max_attempts) {
1853             crm_notice("Fencer connection attempt %d of %d failed (retrying in 2s): %s "
1854                        CRM_XS " rc=%d",
1855                        attempt, max_attempts, pcmk_strerror(rc), rc);
1856             sleep(2);
1857         }
1858     }
1859     crm_notice("Could not connect to fencer: %s " CRM_XS " rc=%d",
1860                pcmk_strerror(rc), rc);
1861     return rc;
1862 }
1863 
1864 stonith_key_value_t *
1865 stonith_key_value_add(stonith_key_value_t * head, const char *key, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
1866 {
1867     stonith_key_value_t *p, *end;
1868 
1869     p = calloc(1, sizeof(stonith_key_value_t));
1870     pcmk__str_update(&p->key, key);
1871     pcmk__str_update(&p->value, value);
1872 
1873     end = head;
1874     while (end && end->next) {
1875         end = end->next;
1876     }
1877 
1878     if (end) {
1879         end->next = p;
1880     } else {
1881         head = p;
1882     }
1883 
1884     return head;
1885 }
1886 
1887 void
1888 stonith_key_value_freeall(stonith_key_value_t * head, int keys, int values)
     /* [previous][next][first][last][top][bottom][index][help] */
1889 {
1890     stonith_key_value_t *p;
1891 
1892     while (head) {
1893         p = head->next;
1894         if (keys) {
1895             free(head->key);
1896         }
1897         if (values) {
1898             free(head->value);
1899         }
1900         free(head);
1901         head = p;
1902     }
1903 }
1904 
1905 #define api_log_open() openlog("stonith-api", LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON)
1906 #define api_log(level, fmt, args...) syslog(level, "%s: "fmt, __func__, args)
1907 
1908 int
1909 stonith_api_kick(uint32_t nodeid, const char *uname, int timeout, bool off)
     /* [previous][next][first][last][top][bottom][index][help] */
1910 {
1911     int rc = pcmk_ok;
1912     stonith_t *st = stonith_api_new();
1913     const char *action = off? "off" : "reboot";
1914 
1915     api_log_open();
1916     if (st == NULL) {
1917         api_log(LOG_ERR, "API initialization failed, could not kick (%s) node %u/%s",
1918                 action, nodeid, uname);
1919         return -EPROTO;
1920     }
1921 
1922     rc = st->cmds->connect(st, "stonith-api", NULL);
1923     if (rc != pcmk_ok) {
1924         api_log(LOG_ERR, "Connection failed, could not kick (%s) node %u/%s : %s (%d)",
1925                 action, nodeid, uname, pcmk_strerror(rc), rc);
1926     } else {
1927         char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
1928         int opts = 0;
1929 
1930         stonith__set_call_options(opts, name,
1931                                   st_opt_sync_call|st_opt_allow_suicide);
1932         if ((uname == NULL) && (nodeid > 0)) {
1933             stonith__set_call_options(opts, name, st_opt_cs_nodeid);
1934         }
1935         rc = st->cmds->fence(st, opts, name, action, timeout, 0);
1936         free(name);
1937 
1938         if (rc != pcmk_ok) {
1939             api_log(LOG_ERR, "Could not kick (%s) node %u/%s : %s (%d)",
1940                     action, nodeid, uname, pcmk_strerror(rc), rc);
1941         } else {
1942             api_log(LOG_NOTICE, "Node %u/%s kicked: %s", nodeid, uname, action);
1943         }
1944     }
1945 
1946     stonith_api_delete(st);
1947     return rc;
1948 }
1949 
1950 time_t
1951 stonith_api_time(uint32_t nodeid, const char *uname, bool in_progress)
     /* [previous][next][first][last][top][bottom][index][help] */
1952 {
1953     int rc = pcmk_ok;
1954     time_t when = 0;
1955     stonith_t *st = stonith_api_new();
1956     stonith_history_t *history = NULL, *hp = NULL;
1957 
1958     if (st == NULL) {
1959         api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: "
1960                 "API initialization failed", nodeid, uname);
1961         return when;
1962     }
1963 
1964     rc = st->cmds->connect(st, "stonith-api", NULL);
1965     if (rc != pcmk_ok) {
1966         api_log(LOG_NOTICE, "Connection failed: %s (%d)", pcmk_strerror(rc), rc);
1967     } else {
1968         int entries = 0;
1969         int progress = 0;
1970         int completed = 0;
1971         int opts = 0;
1972         char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
1973 
1974         stonith__set_call_options(opts, name, st_opt_sync_call);
1975         if ((uname == NULL) && (nodeid > 0)) {
1976             stonith__set_call_options(opts, name, st_opt_cs_nodeid);
1977         }
1978         rc = st->cmds->history(st, opts, name, &history, 120);
1979         free(name);
1980 
1981         for (hp = history; hp; hp = hp->next) {
1982             entries++;
1983             if (in_progress) {
1984                 progress++;
1985                 if (hp->state != st_done && hp->state != st_failed) {
1986                     when = time(NULL);
1987                 }
1988 
1989             } else if (hp->state == st_done) {
1990                 completed++;
1991                 if (hp->completed > when) {
1992                     when = hp->completed;
1993                 }
1994             }
1995         }
1996 
1997         stonith_history_free(history);
1998 
1999         if(rc == pcmk_ok) {
2000             api_log(LOG_INFO, "Found %d entries for %u/%s: %d in progress, %d completed", entries, nodeid, uname, progress, completed);
2001         } else {
2002             api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: %s (%d)", nodeid, uname, pcmk_strerror(rc), rc);
2003         }
2004     }
2005 
2006     stonith_api_delete(st);
2007 
2008     if(when) {
2009         api_log(LOG_INFO, "Node %u/%s last kicked at: %ld", nodeid, uname, (long int)when);
2010     }
2011     return when;
2012 }
2013 
2014 bool
2015 stonith_agent_exists(const char *agent, int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
2016 {
2017     stonith_t *st = NULL;
2018     stonith_key_value_t *devices = NULL;
2019     stonith_key_value_t *dIter = NULL;
2020     bool rc = FALSE;
2021 
2022     if (agent == NULL) {
2023         return rc;
2024     }
2025 
2026     st = stonith_api_new();
2027     if (st == NULL) {
2028         crm_err("Could not list fence agents: API memory allocation failed");
2029         return FALSE;
2030     }
2031     st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, timeout == 0 ? 120 : timeout);
2032 
2033     for (dIter = devices; dIter != NULL; dIter = dIter->next) {
2034         if (pcmk__str_eq(dIter->value, agent, pcmk__str_none)) {
2035             rc = TRUE;
2036             break;
2037         }
2038     }
2039 
2040     stonith_key_value_freeall(devices, 1, 1);
2041     stonith_api_delete(st);
2042     return rc;
2043 }
2044 
2045 const char *
2046 stonith_action_str(const char *action)
     /* [previous][next][first][last][top][bottom][index][help] */
2047 {
2048     if (action == NULL) {
2049         return "fencing";
2050     } else if (!strcmp(action, "on")) {
2051         return "unfencing";
2052     } else if (!strcmp(action, "off")) {
2053         return "turning off";
2054     } else {
2055         return action;
2056     }
2057 }
2058 
2059 /*!
2060  * \internal
2061  * \brief Parse a target name from one line of a target list string
2062  *
2063  * \param[in]     line    One line of a target list string
2064  * \param[in]     len     String length of line
2065  * \param[in,out] output  List to add newly allocated target name to
2066  */
2067 static void
2068 parse_list_line(const char *line, int len, GList **output)
     /* [previous][next][first][last][top][bottom][index][help] */
2069 {
2070     size_t i = 0;
2071     size_t entry_start = 0;
2072 
2073     /* Skip complaints about additional parameters device doesn't understand
2074      *
2075      * @TODO Document or eliminate the implied restriction of target names
2076      */
2077     if (strstr(line, "invalid") || strstr(line, "variable")) {
2078         crm_debug("Skipping list output line: %s", line);
2079         return;
2080     }
2081 
2082     // Process line content, character by character
2083     for (i = 0; i <= len; i++) {
2084 
2085         if (isspace(line[i]) || (line[i] == ',') || (line[i] == ';')
2086             || (line[i] == '\0')) {
2087             // We've found a separator (i.e. the end of an entry)
2088 
2089             int rc = 0;
2090             char *entry = NULL;
2091 
2092             if (i == entry_start) {
2093                 // Skip leading and sequential separators
2094                 entry_start = i + 1;
2095                 continue;
2096             }
2097 
2098             entry = calloc(i - entry_start + 1, sizeof(char));
2099             CRM_ASSERT(entry != NULL);
2100 
2101             /* Read entry, stopping at first separator
2102              *
2103              * @TODO Document or eliminate these character restrictions
2104              */
2105             rc = sscanf(line + entry_start, "%[a-zA-Z0-9_-.]", entry);
2106             if (rc != 1) {
2107                 crm_warn("Could not parse list output entry: %s "
2108                          CRM_XS " entry_start=%d position=%d",
2109                          line + entry_start, entry_start, i);
2110                 free(entry);
2111 
2112             } else if (pcmk__strcase_any_of(entry, "on", "off", NULL)) {
2113                 /* Some agents print the target status in the list output,
2114                  * though none are known now (the separate list-status command
2115                  * is used for this, but it can also print "UNKNOWN"). To handle
2116                  * this possibility, skip such entries.
2117                  *
2118                  * @TODO Document or eliminate the implied restriction of target
2119                  * names.
2120                  */
2121                 free(entry);
2122 
2123             } else {
2124                 // We have a valid entry
2125                 *output = g_list_append(*output, entry);
2126             }
2127             entry_start = i + 1;
2128         }
2129     }
2130 }
2131 
2132 /*!
2133  * \internal
2134  * \brief Parse a list of targets from a string
2135  *
2136  * \param[in] list_output  Target list as a string
2137  *
2138  * \return List of target names
2139  * \note The target list string format is flexible, to allow for user-specified
2140  *       lists such pcmk_host_list and the output of an agent's list action
2141  *       (whether direct or via the API, which escapes newlines). There may be
2142  *       multiple lines, separated by either a newline or an escaped newline
2143  *       (backslash n). Each line may have one or more target names, separated
2144  *       by any combination of whitespace, commas, and semi-colons. Lines
2145  *       containing "invalid" or "variable" will be ignored entirely. Target
2146  *       names "on" or "off" (case-insensitive) will be ignored. Target names
2147  *       may contain only alphanumeric characters, underbars (_), dashes (-),
2148  *       and dots (.) (if any other character occurs in the name, it and all
2149  *       subsequent characters in the name will be ignored).
2150  * \note The caller is responsible for freeing the result with
2151  *       g_list_free_full(result, free).
2152  */
2153 GList *
2154 stonith__parse_targets(const char *target_spec)
     /* [previous][next][first][last][top][bottom][index][help] */
2155 {
2156     GList *targets = NULL;
2157 
2158     if (target_spec != NULL) {
2159         size_t out_len = strlen(target_spec);
2160         size_t line_start = 0; // Starting index of line being processed
2161 
2162         for (size_t i = 0; i <= out_len; ++i) {
2163             if ((target_spec[i] == '\n') || (target_spec[i] == '\0')
2164                 || ((target_spec[i] == '\\') && (target_spec[i + 1] == 'n'))) {
2165                 // We've reached the end of one line of output
2166 
2167                 int len = i - line_start;
2168 
2169                 if (len > 0) {
2170                     char *line = strndup(target_spec + line_start, len);
2171 
2172                     line[len] = '\0'; // Because it might be a newline
2173                     parse_list_line(line, len, &targets);
2174                     free(line);
2175                 }
2176                 if (target_spec[i] == '\\') {
2177                     ++i; // backslash-n takes up two positions
2178                 }
2179                 line_start = i + 1;
2180             }
2181         }
2182     }
2183     return targets;
2184 }
2185 
2186 /*!
2187  * \internal
2188  * \brief Determine if a later stonith event succeeded.
2189  *
2190  * \note Before calling this function, use stonith__sort_history() to sort the
2191  *       top_history argument.
2192  */
2193 gboolean
2194 stonith__later_succeeded(stonith_history_t *event, stonith_history_t *top_history)
     /* [previous][next][first][last][top][bottom][index][help] */
2195 {
2196      gboolean ret = FALSE;
2197 
2198      for (stonith_history_t *prev_hp = top_history; prev_hp; prev_hp = prev_hp->next) {
2199         if (prev_hp == event) {
2200             break;
2201         }
2202 
2203          if ((prev_hp->state == st_done) &&
2204             pcmk__str_eq(event->target, prev_hp->target, pcmk__str_casei) &&
2205             pcmk__str_eq(event->action, prev_hp->action, pcmk__str_casei) &&
2206             pcmk__str_eq(event->delegate, prev_hp->delegate, pcmk__str_casei) &&
2207             ((event->completed < prev_hp->completed) || 
2208              ((event->completed == prev_hp->completed) && (event->completed_nsec < prev_hp->completed_nsec)))) {
2209             ret = TRUE;
2210             break;
2211         }
2212     }
2213     return ret;
2214 }
2215 
2216 /*!
2217  * \internal
2218  * \brief Sort the stonith-history
2219  *        sort by competed most current on the top
2220  *        pending actions lacking a completed-stamp are gathered at the top
2221  *
2222  * \param[in] history    List of stonith actions
2223  *
2224  */
2225 stonith_history_t *
2226 stonith__sort_history(stonith_history_t *history)
     /* [previous][next][first][last][top][bottom][index][help] */
2227 {
2228     stonith_history_t *new = NULL, *pending = NULL, *hp, *np, *tmp;
2229 
2230     for (hp = history; hp; ) {
2231         tmp = hp->next;
2232         if ((hp->state == st_done) || (hp->state == st_failed)) {
2233             /* sort into new */
2234             if ((!new) || (hp->completed > new->completed) || 
2235                 ((hp->completed == new->completed) && (hp->completed_nsec > new->completed_nsec))) {
2236                 hp->next = new;
2237                 new = hp;
2238             } else {
2239                 np = new;
2240                 do {
2241                     if ((!np->next) || (hp->completed > np->next->completed) ||
2242                         ((hp->completed == np->next->completed) && (hp->completed_nsec > np->next->completed_nsec))) {
2243                         hp->next = np->next;
2244                         np->next = hp;
2245                         break;
2246                     }
2247                     np = np->next;
2248                 } while (1);
2249             }
2250         } else {
2251             /* put into pending */
2252             hp->next = pending;
2253             pending = hp;
2254         }
2255         hp = tmp;
2256     }
2257 
2258     /* pending actions don't have a completed-stamp so make them go front */
2259     if (pending) {
2260         stonith_history_t *last_pending = pending;
2261 
2262         while (last_pending->next) {
2263             last_pending = last_pending->next;
2264         }
2265 
2266         last_pending->next = new;
2267         new = pending;
2268     }
2269     return new;
2270 }
2271 
2272 /*!
2273  * \brief Return string equivalent of an operation state value
2274  *
2275  * \param[in] state  Fencing operation state value
2276  *
2277  * \return Human-friendly string equivalent of state
2278  */
2279 const char *
2280 stonith_op_state_str(enum op_state state)
     /* [previous][next][first][last][top][bottom][index][help] */
2281 {
2282     switch (state) {
2283         case st_query:      return "querying";
2284         case st_exec:       return "executing";
2285         case st_done:       return "completed";
2286         case st_duplicate:  return "duplicate";
2287         case st_failed:     return "failed";
2288     }
2289     return "unknown";
2290 }
2291 
2292 stonith_history_t *
2293 stonith__first_matching_event(stonith_history_t *history,
     /* [previous][next][first][last][top][bottom][index][help] */
2294                               bool (*matching_fn)(stonith_history_t *, void *),
2295                               void *user_data)
2296 {
2297     for (stonith_history_t *hp = history; hp; hp = hp->next) {
2298         if (matching_fn(hp, user_data)) {
2299             return hp;
2300         }
2301     }
2302 
2303     return NULL;
2304 }
2305 
2306 bool
2307 stonith__event_state_pending(stonith_history_t *history, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
2308 {
2309     return history->state != st_failed && history->state != st_done;
2310 }
2311 
2312 bool
2313 stonith__event_state_eq(stonith_history_t *history, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
2314 {
2315     return history->state == GPOINTER_TO_INT(user_data);
2316 }
2317 
2318 bool
2319 stonith__event_state_neq(stonith_history_t *history, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
2320 {
2321     return history->state != GPOINTER_TO_INT(user_data);
2322 }
2323 
2324 void
2325 stonith__device_parameter_flags(uint32_t *device_flags, const char *device_name,
     /* [previous][next][first][last][top][bottom][index][help] */
2326                                 xmlNode *metadata)
2327 {
2328     xmlXPathObjectPtr xpath = NULL;
2329     int max = 0;
2330     int lpc = 0;
2331 
2332     CRM_CHECK((device_flags != NULL) && (metadata != NULL), return);
2333 
2334     xpath = xpath_search(metadata, "//parameter");
2335     max = numXpathResults(xpath);
2336 
2337     if (max <= 0) {
2338         freeXpathObject(xpath);
2339         return;
2340     }
2341 
2342     for (lpc = 0; lpc < max; lpc++) {
2343         const char *parameter = NULL;
2344         xmlNode *match = getXpathResult(xpath, lpc);
2345 
2346         CRM_LOG_ASSERT(match != NULL);
2347         if (match == NULL) {
2348             continue;
2349         }
2350 
2351         parameter = crm_element_value(match, "name");
2352 
2353         if (pcmk__str_eq(parameter, "plug", pcmk__str_casei)) {
2354             stonith__set_device_flags(*device_flags, device_name,
2355                                       st_device_supports_parameter_plug);
2356 
2357         } else if (pcmk__str_eq(parameter, "port", pcmk__str_casei)) {
2358             stonith__set_device_flags(*device_flags, device_name,
2359                                       st_device_supports_parameter_port);
2360         }
2361     }
2362 
2363     freeXpathObject(xpath);
2364 }
2365 
2366 /*!
2367  * \internal
2368  * \brief Return the exit status from an async action callback
2369  *
2370  * \param[in] data  Callback data
2371  *
2372  * \return Exit status from callback data
2373  */
2374 int
2375 stonith__exit_status(stonith_callback_data_t *data)
     /* [previous][next][first][last][top][bottom][index][help] */
2376 {
2377     if ((data == NULL) || (data->opaque == NULL)) {
2378         return CRM_EX_ERROR;
2379     }
2380     return ((pcmk__action_result_t *) data->opaque)->exit_status;
2381 }
2382 
2383 /*!
2384  * \internal
2385  * \brief Return the execution status from an async action callback
2386  *
2387  * \param[in] data  Callback data
2388  *
2389  * \return Execution status from callback data
2390  */
2391 int
2392 stonith__execution_status(stonith_callback_data_t *data)
     /* [previous][next][first][last][top][bottom][index][help] */
2393 {
2394     if ((data == NULL) || (data->opaque == NULL)) {
2395         return PCMK_EXEC_UNKNOWN;
2396     }
2397     return ((pcmk__action_result_t *) data->opaque)->execution_status;
2398 }
2399 
2400 /*!
2401  * \internal
2402  * \brief Return the exit reason from an async action callback
2403  *
2404  * \param[in] data  Callback data
2405  *
2406  * \return Exit reason from callback data
2407  */
2408 const char *
2409 stonith__exit_reason(stonith_callback_data_t *data)
     /* [previous][next][first][last][top][bottom][index][help] */
2410 {
2411     if ((data == NULL) || (data->opaque == NULL)) {
2412         return NULL;
2413     }
2414     return ((pcmk__action_result_t *) data->opaque)->exit_reason;
2415 }
2416 
2417 /*!
2418  * \internal
2419  * \brief Return the exit status from an event notification
2420  *
2421  * \param[in] event  Event
2422  *
2423  * \return Exit status from event
2424  */
2425 int
2426 stonith__event_exit_status(stonith_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help] */
2427 {
2428     if ((event == NULL) || (event->opaque == NULL)) {
2429         return CRM_EX_ERROR;
2430     } else {
2431         struct event_private *event_private = event->opaque;
2432 
2433         return event_private->result.exit_status;
2434     }
2435 }
2436 
2437 /*!
2438  * \internal
2439  * \brief Return the execution status from an event notification
2440  *
2441  * \param[in] event  Event
2442  *
2443  * \return Execution status from event
2444  */
2445 int
2446 stonith__event_execution_status(stonith_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help] */
2447 {
2448     if ((event == NULL) || (event->opaque == NULL)) {
2449         return PCMK_EXEC_UNKNOWN;
2450     } else {
2451         struct event_private *event_private = event->opaque;
2452 
2453         return event_private->result.execution_status;
2454     }
2455 }
2456 
2457 /*!
2458  * \internal
2459  * \brief Return the exit reason from an event notification
2460  *
2461  * \param[in] event  Event
2462  *
2463  * \return Exit reason from event
2464  */
2465 const char *
2466 stonith__event_exit_reason(stonith_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help] */
2467 {
2468     if ((event == NULL) || (event->opaque == NULL)) {
2469         return NULL;
2470     } else {
2471         struct event_private *event_private = event->opaque;
2472 
2473         return event_private->result.exit_reason;
2474     }
2475 }
2476 
2477 /*!
2478  * \internal
2479  * \brief Return a human-friendly description of a fencing event
2480  *
2481  * \param[in] event  Event to describe
2482  *
2483  * \return Newly allocated string with description of \p event
2484  * \note The caller is responsible for freeing the return value.
2485  *       This function asserts on memory errors and never returns NULL.
2486  * \note This currently is useful only for events of type
2487  *       T_STONITH_NOTIFY_FENCE.
2488  */
2489 char *
2490 stonith__event_description(stonith_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help] */
2491 {
2492     const char *origin = event->client_origin;
2493     const char *executioner = event->executioner;
2494     const char *reason = stonith__event_exit_reason(event);
2495     const char *status;
2496 
2497     if (origin == NULL) {
2498         origin = "a client";
2499     }
2500     if (executioner == NULL) {
2501         executioner = "the cluster";
2502     }
2503 
2504     if (stonith__event_execution_status(event) != PCMK_EXEC_DONE) {
2505         status = pcmk_exec_status_str(stonith__event_execution_status(event));
2506     } else if (stonith__event_exit_status(event) != CRM_EX_OK) {
2507         status = pcmk_exec_status_str(PCMK_EXEC_ERROR);
2508     } else {
2509         status = crm_exit_str(CRM_EX_OK);
2510     }
2511 
2512     return crm_strdup_printf("Operation %s of %s by %s for %s@%s: %s%s%s%s (ref=%s)",
2513                              event->action, event->target, executioner, origin,
2514                              event->origin, status,
2515                              ((reason == NULL)? "" : " ("),
2516                              ((reason == NULL)? "" : reason),
2517                              ((reason == NULL)? "" : ")"),
2518                              event->id);
2519 }
2520 
2521 
2522 // Deprecated functions kept only for backward API compatibility
2523 // LCOV_EXCL_START
2524 
2525 const char *get_stonith_provider(const char *agent, const char *provider);
2526 
2527 const char *
2528 get_stonith_provider(const char *agent, const char *provider)
     /* [previous][next][first][last][top][bottom][index][help] */
2529 {
2530     return stonith_namespace2text(stonith_get_namespace(agent, provider));
2531 }
2532 
2533 // LCOV_EXCL_STOP
2534 // End deprecated API

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