root/lib/fencing/st_client.c

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

DEFINITIONS

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

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

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