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

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