root/lib/fencing/st_client.c

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

DEFINITIONS

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

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

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