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

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