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     CRM_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     CRM_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     CRM_ASSERT(stonith && stonith->st_private && op);
1558     native = stonith->st_private;
1559 
1560     if (output_data != NULL) {
1561         *output_data = NULL;
1562     }
1563 
1564     if ((stonith->state == stonith_disconnected) || (native->token == NULL)) {
1565         return -ENOTCONN;
1566     }
1567 
1568     /* Increment the call ID, which must be positive to avoid conflicting with
1569      * error codes. This shouldn't be a problem unless the client mucked with
1570      * it or the counter wrapped around.
1571      */
1572     stonith->call_id++;
1573     if (stonith->call_id < 1) {
1574         stonith->call_id = 1;
1575     }
1576 
1577     op_msg = stonith_create_op(stonith->call_id, native->token, op, data, call_options);
1578     if (op_msg == NULL) {
1579         return -EINVAL;
1580     }
1581 
1582     crm_xml_add_int(op_msg, PCMK__XA_ST_TIMEOUT, timeout);
1583     crm_trace("Sending %s message to fencer with timeout %ds", op, timeout);
1584 
1585     if (data) {
1586         const char *delay_s = crm_element_value(data, PCMK__XA_ST_DELAY);
1587 
1588         if (delay_s) {
1589             crm_xml_add(op_msg, PCMK__XA_ST_DELAY, delay_s);
1590         }
1591     }
1592 
1593     {
1594         enum crm_ipc_flags ipc_flags = crm_ipc_flags_none;
1595 
1596         if (call_options & st_opt_sync_call) {
1597             pcmk__set_ipc_flags(ipc_flags, "stonith command",
1598                                 crm_ipc_client_response);
1599         }
1600         rc = crm_ipc_send(native->ipc, op_msg, ipc_flags,
1601                           1000 * (timeout + 60), &op_reply);
1602     }
1603     free_xml(op_msg);
1604 
1605     if (rc < 0) {
1606         crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%ds): %d", op, timeout, rc);
1607         rc = -ECOMM;
1608         goto done;
1609     }
1610 
1611     crm_log_xml_trace(op_reply, "Reply");
1612 
1613     if (!(call_options & st_opt_sync_call)) {
1614         crm_trace("Async call %d, returning", stonith->call_id);
1615         free_xml(op_reply);
1616         return stonith->call_id;
1617     }
1618 
1619     crm_element_value_int(op_reply, PCMK__XA_ST_CALLID, &reply_id);
1620 
1621     if (reply_id == stonith->call_id) {
1622         pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
1623 
1624         crm_trace("Synchronous reply %d received", reply_id);
1625 
1626         stonith__xe_get_result(op_reply, &result);
1627         rc = pcmk_rc2legacy(stonith__result2rc(&result));
1628         pcmk__reset_result(&result);
1629 
1630         if ((call_options & st_opt_discard_reply) || output_data == NULL) {
1631             crm_trace("Discarding reply");
1632 
1633         } else {
1634             *output_data = op_reply;
1635             op_reply = NULL;    /* Prevent subsequent free */
1636         }
1637 
1638     } else if (reply_id <= 0) {
1639         crm_err("Received bad reply: No id set");
1640         crm_log_xml_err(op_reply, "Bad reply");
1641         free_xml(op_reply);
1642         op_reply = NULL;
1643         rc = -ENOMSG;
1644 
1645     } else {
1646         crm_err("Received bad reply: %d (wanted %d)", reply_id, stonith->call_id);
1647         crm_log_xml_err(op_reply, "Old reply");
1648         free_xml(op_reply);
1649         op_reply = NULL;
1650         rc = -ENOMSG;
1651     }
1652 
1653   done:
1654     if (!crm_ipc_connected(native->ipc)) {
1655         crm_err("Fencer disconnected");
1656         free(native->token); native->token = NULL;
1657         stonith->state = stonith_disconnected;
1658     }
1659 
1660     free_xml(op_reply);
1661     return rc;
1662 }
1663 
1664 /* Not used with mainloop */
1665 bool
1666 stonith_dispatch(stonith_t * st)
     /* [previous][next][first][last][top][bottom][index][help] */
1667 {
1668     gboolean stay_connected = TRUE;
1669     stonith_private_t *private = NULL;
1670 
1671     CRM_ASSERT(st != NULL);
1672     private = st->st_private;
1673 
1674     while (crm_ipc_ready(private->ipc)) {
1675 
1676         if (crm_ipc_read(private->ipc) > 0) {
1677             const char *msg = crm_ipc_buffer(private->ipc);
1678 
1679             stonith_dispatch_internal(msg, strlen(msg), st);
1680         }
1681 
1682         if (!crm_ipc_connected(private->ipc)) {
1683             crm_err("Connection closed");
1684             stay_connected = FALSE;
1685         }
1686     }
1687 
1688     return stay_connected;
1689 }
1690 
1691 static int
1692 stonith_api_free(stonith_t * stonith)
     /* [previous][next][first][last][top][bottom][index][help] */
1693 {
1694     int rc = pcmk_ok;
1695 
1696     crm_trace("Destroying %p", stonith);
1697 
1698     if (stonith->state != stonith_disconnected) {
1699         crm_trace("Unregistering notifications and disconnecting %p first",
1700                   stonith);
1701         stonith->cmds->remove_notification(stonith, NULL);
1702         rc = stonith->cmds->disconnect(stonith);
1703     }
1704 
1705     if (stonith->state == stonith_disconnected) {
1706         stonith_private_t *private = stonith->st_private;
1707 
1708         crm_trace("Removing %d callbacks", g_hash_table_size(private->stonith_op_callback_table));
1709         g_hash_table_destroy(private->stonith_op_callback_table);
1710 
1711         crm_trace("Destroying %d notification clients", g_list_length(private->notify_list));
1712         g_list_free_full(private->notify_list, free);
1713 
1714         free(stonith->st_private);
1715         free(stonith->cmds);
1716         free(stonith);
1717 
1718     } else {
1719         crm_err("Not free'ing active connection: %s (%d)", pcmk_strerror(rc), rc);
1720     }
1721 
1722     return rc;
1723 }
1724 
1725 void
1726 stonith_api_delete(stonith_t * stonith)
     /* [previous][next][first][last][top][bottom][index][help] */
1727 {
1728     crm_trace("Destroying %p", stonith);
1729     if(stonith) {
1730         stonith->cmds->free(stonith);
1731     }
1732 }
1733 
1734 static int
1735 stonith_api_validate(stonith_t *st, int call_options, const char *rsc_id,
     /* [previous][next][first][last][top][bottom][index][help] */
1736                      const char *namespace_s, const char *agent,
1737                      const stonith_key_value_t *params, int timeout_sec,
1738                      char **output, char **error_output)
1739 {
1740     /* Validation should be done directly via the agent, so we can get it from
1741      * stonith_admin when the cluster is not running, which is important for
1742      * higher-level tools.
1743      */
1744 
1745     int rc = pcmk_ok;
1746 
1747     /* Use a dummy node name in case the agent requires a target. We assume the
1748      * actual target doesn't matter for validation purposes (if in practice,
1749      * that is incorrect, we will need to allow the caller to pass the target).
1750      */
1751     const char *target = "node1";
1752     const char *host_arg = NULL;
1753 
1754     GHashTable *params_table = pcmk__strkey_table(free, free);
1755 
1756     // Convert parameter list to a hash table
1757     for (; params; params = params->next) {
1758         if (pcmk__str_eq(params->key, PCMK_STONITH_HOST_ARGUMENT,
1759                          pcmk__str_none)) {
1760             host_arg = params->value;
1761         }
1762         if (!pcmk_stonith_param(params->key)) {
1763             pcmk__insert_dup(params_table, params->key, params->value);
1764         }
1765     }
1766 
1767 #if SUPPORT_CIBSECRETS
1768     rc = pcmk__substitute_secrets(rsc_id, params_table);
1769     if (rc != pcmk_rc_ok) {
1770         crm_warn("Could not replace secret parameters for validation of %s: %s",
1771                  agent, pcmk_rc_str(rc));
1772         // rc is standard return value, don't return it in this function
1773     }
1774 #endif
1775 
1776     if (output) {
1777         *output = NULL;
1778     }
1779     if (error_output) {
1780         *error_output = NULL;
1781     }
1782 
1783     if (timeout_sec <= 0) {
1784         timeout_sec = PCMK_DEFAULT_METADATA_TIMEOUT_MS; // Questionable
1785     }
1786 
1787     switch (stonith_get_namespace(agent, namespace_s)) {
1788         case st_namespace_rhcs:
1789             rc = stonith__rhcs_validate(st, call_options, target, agent,
1790                                         params_table, host_arg, timeout_sec,
1791                                         output, error_output);
1792             break;
1793 
1794 #if HAVE_STONITH_STONITH_H
1795         case st_namespace_lha:
1796             rc = stonith__lha_validate(st, call_options, target, agent,
1797                                        params_table, timeout_sec, output,
1798                                        error_output);
1799             break;
1800 #endif
1801 
1802         case st_namespace_invalid:
1803             errno = ENOENT;
1804             rc = -errno;
1805 
1806             if (error_output) {
1807                 *error_output = crm_strdup_printf("Agent %s not found", agent);
1808             } else {
1809                 crm_err("Agent %s not found", agent);
1810             }
1811 
1812             break;
1813 
1814         default:
1815             errno = EOPNOTSUPP;
1816             rc = -errno;
1817 
1818             if (error_output) {
1819                 *error_output = crm_strdup_printf("Agent %s does not support validation",
1820                                                   agent);
1821             } else {
1822                 crm_err("Agent %s does not support validation", agent);
1823             }
1824 
1825             break;
1826     }
1827 
1828     g_hash_table_destroy(params_table);
1829     return rc;
1830 }
1831 
1832 stonith_t *
1833 stonith_api_new(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1834 {
1835     stonith_t *new_stonith = NULL;
1836     stonith_private_t *private = NULL;
1837 
1838     new_stonith = calloc(1, sizeof(stonith_t));
1839     if (new_stonith == NULL) {
1840         return NULL;
1841     }
1842 
1843     private = calloc(1, sizeof(stonith_private_t));
1844     if (private == NULL) {
1845         free(new_stonith);
1846         return NULL;
1847     }
1848     new_stonith->st_private = private;
1849 
1850     private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
1851     private->notify_list = NULL;
1852     private->notify_refcnt = 0;
1853     private->notify_deletes = FALSE;
1854 
1855     new_stonith->call_id = 1;
1856     new_stonith->state = stonith_disconnected;
1857 
1858     new_stonith->cmds = calloc(1, sizeof(stonith_api_operations_t));
1859     if (new_stonith->cmds == NULL) {
1860         free(new_stonith->st_private);
1861         free(new_stonith);
1862         return NULL;
1863     }
1864 
1865 /* *INDENT-OFF* */
1866     new_stonith->cmds->free       = stonith_api_free;
1867     new_stonith->cmds->connect    = stonith_api_signon;
1868     new_stonith->cmds->disconnect = stonith_api_signoff;
1869 
1870     new_stonith->cmds->list       = stonith_api_list;
1871     new_stonith->cmds->monitor    = stonith_api_monitor;
1872     new_stonith->cmds->status     = stonith_api_status;
1873     new_stonith->cmds->fence      = stonith_api_fence;
1874     new_stonith->cmds->fence_with_delay = stonith_api_fence_with_delay;
1875     new_stonith->cmds->confirm    = stonith_api_confirm;
1876     new_stonith->cmds->history    = stonith_api_history;
1877 
1878     new_stonith->cmds->list_agents  = stonith_api_device_list;
1879     new_stonith->cmds->metadata     = stonith_api_device_metadata;
1880 
1881     new_stonith->cmds->query           = stonith_api_query;
1882     new_stonith->cmds->remove_device   = stonith_api_remove_device;
1883     new_stonith->cmds->register_device = stonith_api_register_device;
1884 
1885     new_stonith->cmds->remove_level          = stonith_api_remove_level;
1886     new_stonith->cmds->remove_level_full     = stonith_api_remove_level_full;
1887     new_stonith->cmds->register_level        = stonith_api_register_level;
1888     new_stonith->cmds->register_level_full   = stonith_api_register_level_full;
1889 
1890     new_stonith->cmds->remove_callback       = stonith_api_del_callback;
1891     new_stonith->cmds->register_callback     = stonith_api_add_callback;
1892     new_stonith->cmds->remove_notification   = stonith_api_del_notification;
1893     new_stonith->cmds->register_notification = stonith_api_add_notification;
1894 
1895     new_stonith->cmds->validate              = stonith_api_validate;
1896 /* *INDENT-ON* */
1897 
1898     return new_stonith;
1899 }
1900 
1901 /*!
1902  * \brief Make a blocking connection attempt to the fencer
1903  *
1904  * \param[in,out] st            Fencer API object
1905  * \param[in]     name          Client name to use with fencer
1906  * \param[in]     max_attempts  Return error if this many attempts fail
1907  *
1908  * \return pcmk_ok on success, result of last attempt otherwise
1909  */
1910 int
1911 stonith_api_connect_retry(stonith_t *st, const char *name, int max_attempts)
     /* [previous][next][first][last][top][bottom][index][help] */
1912 {
1913     int rc = -EINVAL; // if max_attempts is not positive
1914 
1915     for (int attempt = 1; attempt <= max_attempts; attempt++) {
1916         rc = st->cmds->connect(st, name, NULL);
1917         if (rc == pcmk_ok) {
1918             return pcmk_ok;
1919         } else if (attempt < max_attempts) {
1920             crm_notice("Fencer connection attempt %d of %d failed (retrying in 2s): %s "
1921                        CRM_XS " rc=%d",
1922                        attempt, max_attempts, pcmk_strerror(rc), rc);
1923             sleep(2);
1924         }
1925     }
1926     crm_notice("Could not connect to fencer: %s " CRM_XS " rc=%d",
1927                pcmk_strerror(rc), rc);
1928     return rc;
1929 }
1930 
1931 stonith_key_value_t *
1932 stonith_key_value_add(stonith_key_value_t * head, const char *key, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
1933 {
1934     stonith_key_value_t *p, *end;
1935 
1936     p = pcmk__assert_alloc(1, sizeof(stonith_key_value_t));
1937     p->key = pcmk__str_copy(key);
1938     p->value = pcmk__str_copy(value);
1939 
1940     end = head;
1941     while (end && end->next) {
1942         end = end->next;
1943     }
1944 
1945     if (end) {
1946         end->next = p;
1947     } else {
1948         head = p;
1949     }
1950 
1951     return head;
1952 }
1953 
1954 void
1955 stonith_key_value_freeall(stonith_key_value_t * head, int keys, int values)
     /* [previous][next][first][last][top][bottom][index][help] */
1956 {
1957     stonith_key_value_t *p;
1958 
1959     while (head) {
1960         p = head->next;
1961         if (keys) {
1962             free(head->key);
1963         }
1964         if (values) {
1965             free(head->value);
1966         }
1967         free(head);
1968         head = p;
1969     }
1970 }
1971 
1972 #define api_log_open() openlog("stonith-api", LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON)
1973 #define api_log(level, fmt, args...) syslog(level, "%s: "fmt, __func__, args)
1974 
1975 int
1976 stonith_api_kick(uint32_t nodeid, const char *uname, int timeout, bool off)
     /* [previous][next][first][last][top][bottom][index][help] */
1977 {
1978     int rc = pcmk_ok;
1979     stonith_t *st = stonith_api_new();
1980     const char *action = off? PCMK_ACTION_OFF : PCMK_ACTION_REBOOT;
1981 
1982     api_log_open();
1983     if (st == NULL) {
1984         api_log(LOG_ERR, "API initialization failed, could not kick (%s) node %u/%s",
1985                 action, nodeid, uname);
1986         return -EPROTO;
1987     }
1988 
1989     rc = st->cmds->connect(st, "stonith-api", NULL);
1990     if (rc != pcmk_ok) {
1991         api_log(LOG_ERR, "Connection failed, could not kick (%s) node %u/%s : %s (%d)",
1992                 action, nodeid, uname, pcmk_strerror(rc), rc);
1993     } else {
1994         char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
1995         int opts = 0;
1996 
1997         stonith__set_call_options(opts, name,
1998                                   st_opt_sync_call|st_opt_allow_suicide);
1999         if ((uname == NULL) && (nodeid > 0)) {
2000             stonith__set_call_options(opts, name, st_opt_cs_nodeid);
2001         }
2002         rc = st->cmds->fence(st, opts, name, action, timeout, 0);
2003         free(name);
2004 
2005         if (rc != pcmk_ok) {
2006             api_log(LOG_ERR, "Could not kick (%s) node %u/%s : %s (%d)",
2007                     action, nodeid, uname, pcmk_strerror(rc), rc);
2008         } else {
2009             api_log(LOG_NOTICE, "Node %u/%s kicked: %s", nodeid, uname, action);
2010         }
2011     }
2012 
2013     stonith_api_delete(st);
2014     return rc;
2015 }
2016 
2017 time_t
2018 stonith_api_time(uint32_t nodeid, const char *uname, bool in_progress)
     /* [previous][next][first][last][top][bottom][index][help] */
2019 {
2020     int rc = pcmk_ok;
2021     time_t when = 0;
2022     stonith_t *st = stonith_api_new();
2023     stonith_history_t *history = NULL, *hp = NULL;
2024 
2025     if (st == NULL) {
2026         api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: "
2027                 "API initialization failed", nodeid, uname);
2028         return when;
2029     }
2030 
2031     rc = st->cmds->connect(st, "stonith-api", NULL);
2032     if (rc != pcmk_ok) {
2033         api_log(LOG_NOTICE, "Connection failed: %s (%d)", pcmk_strerror(rc), rc);
2034     } else {
2035         int entries = 0;
2036         int progress = 0;
2037         int completed = 0;
2038         int opts = 0;
2039         char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
2040 
2041         stonith__set_call_options(opts, name, st_opt_sync_call);
2042         if ((uname == NULL) && (nodeid > 0)) {
2043             stonith__set_call_options(opts, name, st_opt_cs_nodeid);
2044         }
2045         rc = st->cmds->history(st, opts, name, &history, 120);
2046         free(name);
2047 
2048         for (hp = history; hp; hp = hp->next) {
2049             entries++;
2050             if (in_progress) {
2051                 progress++;
2052                 if (hp->state != st_done && hp->state != st_failed) {
2053                     when = time(NULL);
2054                 }
2055 
2056             } else if (hp->state == st_done) {
2057                 completed++;
2058                 if (hp->completed > when) {
2059                     when = hp->completed;
2060                 }
2061             }
2062         }
2063 
2064         stonith_history_free(history);
2065 
2066         if(rc == pcmk_ok) {
2067             api_log(LOG_INFO, "Found %d entries for %u/%s: %d in progress, %d completed", entries, nodeid, uname, progress, completed);
2068         } else {
2069             api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: %s (%d)", nodeid, uname, pcmk_strerror(rc), rc);
2070         }
2071     }
2072 
2073     stonith_api_delete(st);
2074 
2075     if(when) {
2076         api_log(LOG_INFO, "Node %u/%s last kicked at: %ld", nodeid, uname, (long int)when);
2077     }
2078     return when;
2079 }
2080 
2081 bool
2082 stonith_agent_exists(const char *agent, int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
2083 {
2084     stonith_t *st = NULL;
2085     stonith_key_value_t *devices = NULL;
2086     stonith_key_value_t *dIter = NULL;
2087     bool rc = FALSE;
2088 
2089     if (agent == NULL) {
2090         return rc;
2091     }
2092 
2093     st = stonith_api_new();
2094     if (st == NULL) {
2095         crm_err("Could not list fence agents: API memory allocation failed");
2096         return FALSE;
2097     }
2098     st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, timeout == 0 ? 120 : timeout);
2099 
2100     for (dIter = devices; dIter != NULL; dIter = dIter->next) {
2101         if (pcmk__str_eq(dIter->value, agent, pcmk__str_none)) {
2102             rc = TRUE;
2103             break;
2104         }
2105     }
2106 
2107     stonith_key_value_freeall(devices, 1, 1);
2108     stonith_api_delete(st);
2109     return rc;
2110 }
2111 
2112 const char *
2113 stonith_action_str(const char *action)
     /* [previous][next][first][last][top][bottom][index][help] */
2114 {
2115     if (action == NULL) {
2116         return "fencing";
2117     } else if (strcmp(action, PCMK_ACTION_ON) == 0) {
2118         return "unfencing";
2119     } else if (strcmp(action, PCMK_ACTION_OFF) == 0) {
2120         return "turning off";
2121     } else {
2122         return action;
2123     }
2124 }
2125 
2126 /*!
2127  * \internal
2128  * \brief Parse a target name from one line of a target list string
2129  *
2130  * \param[in]     line    One line of a target list string
2131  * \param[in]     len     String length of line
2132  * \param[in,out] output  List to add newly allocated target name to
2133  */
2134 static void
2135 parse_list_line(const char *line, int len, GList **output)
     /* [previous][next][first][last][top][bottom][index][help] */
2136 {
2137     size_t i = 0;
2138     size_t entry_start = 0;
2139 
2140     /* Skip complaints about additional parameters device doesn't understand
2141      *
2142      * @TODO Document or eliminate the implied restriction of target names
2143      */
2144     if (strstr(line, "invalid") || strstr(line, "variable")) {
2145         crm_debug("Skipping list output line: %s", line);
2146         return;
2147     }
2148 
2149     // Process line content, character by character
2150     for (i = 0; i <= len; i++) {
2151 
2152         if (isspace(line[i]) || (line[i] == ',') || (line[i] == ';')
2153             || (line[i] == '\0')) {
2154             // We've found a separator (i.e. the end of an entry)
2155 
2156             int rc = 0;
2157             char *entry = NULL;
2158 
2159             if (i == entry_start) {
2160                 // Skip leading and sequential separators
2161                 entry_start = i + 1;
2162                 continue;
2163             }
2164 
2165             entry = pcmk__assert_alloc(i - entry_start + 1, sizeof(char));
2166 
2167             /* Read entry, stopping at first separator
2168              *
2169              * @TODO Document or eliminate these character restrictions
2170              */
2171             rc = sscanf(line + entry_start, "%[a-zA-Z0-9_-.]", entry);
2172             if (rc != 1) {
2173                 crm_warn("Could not parse list output entry: %s "
2174                          CRM_XS " entry_start=%d position=%d",
2175                          line + entry_start, entry_start, i);
2176                 free(entry);
2177 
2178             } else if (pcmk__strcase_any_of(entry, PCMK_ACTION_ON,
2179                                             PCMK_ACTION_OFF, NULL)) {
2180                 /* Some agents print the target status in the list output,
2181                  * though none are known now (the separate list-status command
2182                  * is used for this, but it can also print "UNKNOWN"). To handle
2183                  * this possibility, skip such entries.
2184                  *
2185                  * @TODO Document or eliminate the implied restriction of target
2186                  * names.
2187                  */
2188                 free(entry);
2189 
2190             } else {
2191                 // We have a valid entry
2192                 *output = g_list_append(*output, entry);
2193             }
2194             entry_start = i + 1;
2195         }
2196     }
2197 }
2198 
2199 /*!
2200  * \internal
2201  * \brief Parse a list of targets from a string
2202  *
2203  * \param[in] list_output  Target list as a string
2204  *
2205  * \return List of target names
2206  * \note The target list string format is flexible, to allow for user-specified
2207  *       lists such pcmk_host_list and the output of an agent's list action
2208  *       (whether direct or via the API, which escapes newlines). There may be
2209  *       multiple lines, separated by either a newline or an escaped newline
2210  *       (backslash n). Each line may have one or more target names, separated
2211  *       by any combination of whitespace, commas, and semi-colons. Lines
2212  *       containing "invalid" or "variable" will be ignored entirely. Target
2213  *       names "on" or "off" (case-insensitive) will be ignored. Target names
2214  *       may contain only alphanumeric characters, underbars (_), dashes (-),
2215  *       and dots (.) (if any other character occurs in the name, it and all
2216  *       subsequent characters in the name will be ignored).
2217  * \note The caller is responsible for freeing the result with
2218  *       g_list_free_full(result, free).
2219  */
2220 GList *
2221 stonith__parse_targets(const char *target_spec)
     /* [previous][next][first][last][top][bottom][index][help] */
2222 {
2223     GList *targets = NULL;
2224 
2225     if (target_spec != NULL) {
2226         size_t out_len = strlen(target_spec);
2227         size_t line_start = 0; // Starting index of line being processed
2228 
2229         for (size_t i = 0; i <= out_len; ++i) {
2230             if ((target_spec[i] == '\n') || (target_spec[i] == '\0')
2231                 || ((target_spec[i] == '\\') && (target_spec[i + 1] == 'n'))) {
2232                 // We've reached the end of one line of output
2233 
2234                 int len = i - line_start;
2235 
2236                 if (len > 0) {
2237                     char *line = strndup(target_spec + line_start, len);
2238 
2239                     line[len] = '\0'; // Because it might be a newline
2240                     parse_list_line(line, len, &targets);
2241                     free(line);
2242                 }
2243                 if (target_spec[i] == '\\') {
2244                     ++i; // backslash-n takes up two positions
2245                 }
2246                 line_start = i + 1;
2247             }
2248         }
2249     }
2250     return targets;
2251 }
2252 
2253 /*!
2254  * \internal
2255  * \brief Check whether a fencing failure was followed by an equivalent success
2256  *
2257  * \param[in] event        Fencing failure
2258  * \param[in] top_history  Complete fencing history (must be sorted by
2259  *                         stonith__sort_history() beforehand)
2260  *
2261  * \return The name of the node that executed the fencing if a later successful
2262  *         event exists, or NULL if no such event exists
2263  */
2264 const char *
2265 stonith__later_succeeded(const stonith_history_t *event,
     /* [previous][next][first][last][top][bottom][index][help] */
2266                          const stonith_history_t *top_history)
2267 {
2268     const char *other = NULL;
2269 
2270      for (const stonith_history_t *prev_hp = top_history;
2271           prev_hp != NULL; prev_hp = prev_hp->next) {
2272         if (prev_hp == event) {
2273             break;
2274         }
2275         if ((prev_hp->state == st_done) &&
2276             pcmk__str_eq(event->target, prev_hp->target, pcmk__str_casei) &&
2277             pcmk__str_eq(event->action, prev_hp->action, pcmk__str_none) &&
2278             ((event->completed < prev_hp->completed) ||
2279              ((event->completed == prev_hp->completed) && (event->completed_nsec < prev_hp->completed_nsec)))) {
2280 
2281             if ((event->delegate == NULL)
2282                 || pcmk__str_eq(event->delegate, prev_hp->delegate,
2283                                 pcmk__str_casei)) {
2284                 // Prefer equivalent fencing by same executioner
2285                 return prev_hp->delegate;
2286 
2287             } else if (other == NULL) {
2288                 // Otherwise remember first successful executioner
2289                 other = (prev_hp->delegate == NULL)? "some node" : prev_hp->delegate;
2290             }
2291         }
2292     }
2293     return other;
2294 }
2295 
2296 /*!
2297  * \internal
2298  * \brief Sort fencing history, pending first then by most recently completed
2299  *
2300  * \param[in,out] history    List of stonith actions
2301  *
2302  * \return New head of sorted \p history
2303  */
2304 stonith_history_t *
2305 stonith__sort_history(stonith_history_t *history)
     /* [previous][next][first][last][top][bottom][index][help] */
2306 {
2307     stonith_history_t *new = NULL, *pending = NULL, *hp, *np, *tmp;
2308 
2309     for (hp = history; hp; ) {
2310         tmp = hp->next;
2311         if ((hp->state == st_done) || (hp->state == st_failed)) {
2312             /* sort into new */
2313             if ((!new) || (hp->completed > new->completed) || 
2314                 ((hp->completed == new->completed) && (hp->completed_nsec > new->completed_nsec))) {
2315                 hp->next = new;
2316                 new = hp;
2317             } else {
2318                 np = new;
2319                 do {
2320                     if ((!np->next) || (hp->completed > np->next->completed) ||
2321                         ((hp->completed == np->next->completed) && (hp->completed_nsec > np->next->completed_nsec))) {
2322                         hp->next = np->next;
2323                         np->next = hp;
2324                         break;
2325                     }
2326                     np = np->next;
2327                 } while (1);
2328             }
2329         } else {
2330             /* put into pending */
2331             hp->next = pending;
2332             pending = hp;
2333         }
2334         hp = tmp;
2335     }
2336 
2337     /* pending actions don't have a completed-stamp so make them go front */
2338     if (pending) {
2339         stonith_history_t *last_pending = pending;
2340 
2341         while (last_pending->next) {
2342             last_pending = last_pending->next;
2343         }
2344 
2345         last_pending->next = new;
2346         new = pending;
2347     }
2348     return new;
2349 }
2350 
2351 /*!
2352  * \brief Return string equivalent of an operation state value
2353  *
2354  * \param[in] state  Fencing operation state value
2355  *
2356  * \return Human-friendly string equivalent of state
2357  */
2358 const char *
2359 stonith_op_state_str(enum op_state state)
     /* [previous][next][first][last][top][bottom][index][help] */
2360 {
2361     switch (state) {
2362         case st_query:      return "querying";
2363         case st_exec:       return "executing";
2364         case st_done:       return "completed";
2365         case st_duplicate:  return "duplicate";
2366         case st_failed:     return "failed";
2367     }
2368     return "unknown";
2369 }
2370 
2371 stonith_history_t *
2372 stonith__first_matching_event(stonith_history_t *history,
     /* [previous][next][first][last][top][bottom][index][help] */
2373                               bool (*matching_fn)(stonith_history_t *, void *),
2374                               void *user_data)
2375 {
2376     for (stonith_history_t *hp = history; hp; hp = hp->next) {
2377         if (matching_fn(hp, user_data)) {
2378             return hp;
2379         }
2380     }
2381 
2382     return NULL;
2383 }
2384 
2385 bool
2386 stonith__event_state_pending(stonith_history_t *history, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
2387 {
2388     return history->state != st_failed && history->state != st_done;
2389 }
2390 
2391 bool
2392 stonith__event_state_eq(stonith_history_t *history, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
2393 {
2394     return history->state == GPOINTER_TO_INT(user_data);
2395 }
2396 
2397 bool
2398 stonith__event_state_neq(stonith_history_t *history, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
2399 {
2400     return history->state != GPOINTER_TO_INT(user_data);
2401 }
2402 
2403 void
2404 stonith__device_parameter_flags(uint32_t *device_flags, const char *device_name,
     /* [previous][next][first][last][top][bottom][index][help] */
2405                                 xmlNode *metadata)
2406 {
2407     xmlXPathObjectPtr xpath = NULL;
2408     int max = 0;
2409     int lpc = 0;
2410 
2411     CRM_CHECK((device_flags != NULL) && (metadata != NULL), return);
2412 
2413     xpath = xpath_search(metadata, "//" PCMK_XE_PARAMETER);
2414     max = numXpathResults(xpath);
2415 
2416     if (max <= 0) {
2417         freeXpathObject(xpath);
2418         return;
2419     }
2420 
2421     for (lpc = 0; lpc < max; lpc++) {
2422         const char *parameter = NULL;
2423         xmlNode *match = getXpathResult(xpath, lpc);
2424 
2425         CRM_LOG_ASSERT(match != NULL);
2426         if (match == NULL) {
2427             continue;
2428         }
2429 
2430         parameter = crm_element_value(match, PCMK_XA_NAME);
2431 
2432         if (pcmk__str_eq(parameter, "plug", pcmk__str_casei)) {
2433             stonith__set_device_flags(*device_flags, device_name,
2434                                       st_device_supports_parameter_plug);
2435 
2436         } else if (pcmk__str_eq(parameter, "port", pcmk__str_casei)) {
2437             stonith__set_device_flags(*device_flags, device_name,
2438                                       st_device_supports_parameter_port);
2439         }
2440     }
2441 
2442     freeXpathObject(xpath);
2443 }
2444 
2445 /*!
2446  * \internal
2447  * \brief Retrieve fence agent meta-data asynchronously
2448  *
2449  * \param[in]     agent        Agent to execute
2450  * \param[in]     timeout_sec  Error if not complete within this time
2451  * \param[in]     callback     Function to call with result (this will always be
2452  *                             called, whether by this function directly or
2453  *                             later via the main loop, and on success the
2454  *                             metadata will be in its result argument's
2455  *                             action_stdout)
2456  * \param[in,out] user_data    User data to pass to callback
2457  *
2458  * \return Standard Pacemaker return code
2459  * \note The caller must use a main loop. This function is not a
2460  *       stonith_api_operations_t method because it does not need a stonith_t
2461  *       object and does not go through the fencer, but executes the agent
2462  *       directly.
2463  */
2464 int
2465 stonith__metadata_async(const char *agent, int timeout_sec,
     /* [previous][next][first][last][top][bottom][index][help] */
2466                         void (*callback)(int pid,
2467                                          const pcmk__action_result_t *result,
2468                                          void *user_data),
2469                         void *user_data)
2470 {
2471     switch (stonith_get_namespace(agent, NULL)) {
2472         case st_namespace_rhcs:
2473             {
2474                 stonith_action_t *action = NULL;
2475                 int rc = pcmk_ok;
2476 
2477                 action = stonith__action_create(agent, PCMK_ACTION_METADATA,
2478                                                 NULL, 0, timeout_sec, NULL,
2479                                                 NULL, NULL);
2480 
2481                 rc = stonith__execute_async(action, user_data, callback, NULL);
2482                 if (rc != pcmk_ok) {
2483                     callback(0, stonith__action_result(action), user_data);
2484                     stonith__destroy_action(action);
2485                 }
2486                 return pcmk_legacy2rc(rc);
2487             }
2488 
2489 #if HAVE_STONITH_STONITH_H
2490         case st_namespace_lha:
2491             // LHA metadata is simply synthesized, so simulate async
2492             {
2493                 pcmk__action_result_t result = {
2494                     .exit_status = CRM_EX_OK,
2495                     .execution_status = PCMK_EXEC_DONE,
2496                     .exit_reason = NULL,
2497                     .action_stdout = NULL,
2498                     .action_stderr = NULL,
2499                 };
2500 
2501                 stonith__lha_metadata(agent, timeout_sec,
2502                                       &result.action_stdout);
2503                 callback(0, &result, user_data);
2504                 pcmk__reset_result(&result);
2505                 return pcmk_rc_ok;
2506             }
2507 #endif
2508 
2509         default:
2510             {
2511                 pcmk__action_result_t result = {
2512                     .exit_status = CRM_EX_NOSUCH,
2513                     .execution_status = PCMK_EXEC_ERROR_HARD,
2514                     .exit_reason = crm_strdup_printf("No such agent '%s'",
2515                                                      agent),
2516                     .action_stdout = NULL,
2517                     .action_stderr = NULL,
2518                 };
2519 
2520                 callback(0, &result, user_data);
2521                 pcmk__reset_result(&result);
2522                 return ENOENT;
2523             }
2524     }
2525 }
2526 
2527 /*!
2528  * \internal
2529  * \brief Return the exit status from an async action callback
2530  *
2531  * \param[in] data  Callback data
2532  *
2533  * \return Exit status from callback data
2534  */
2535 int
2536 stonith__exit_status(const stonith_callback_data_t *data)
     /* [previous][next][first][last][top][bottom][index][help] */
2537 {
2538     if ((data == NULL) || (data->opaque == NULL)) {
2539         return CRM_EX_ERROR;
2540     }
2541     return ((pcmk__action_result_t *) data->opaque)->exit_status;
2542 }
2543 
2544 /*!
2545  * \internal
2546  * \brief Return the execution status from an async action callback
2547  *
2548  * \param[in] data  Callback data
2549  *
2550  * \return Execution status from callback data
2551  */
2552 int
2553 stonith__execution_status(const stonith_callback_data_t *data)
     /* [previous][next][first][last][top][bottom][index][help] */
2554 {
2555     if ((data == NULL) || (data->opaque == NULL)) {
2556         return PCMK_EXEC_UNKNOWN;
2557     }
2558     return ((pcmk__action_result_t *) data->opaque)->execution_status;
2559 }
2560 
2561 /*!
2562  * \internal
2563  * \brief Return the exit reason from an async action callback
2564  *
2565  * \param[in] data  Callback data
2566  *
2567  * \return Exit reason from callback data
2568  */
2569 const char *
2570 stonith__exit_reason(const stonith_callback_data_t *data)
     /* [previous][next][first][last][top][bottom][index][help] */
2571 {
2572     if ((data == NULL) || (data->opaque == NULL)) {
2573         return NULL;
2574     }
2575     return ((pcmk__action_result_t *) data->opaque)->exit_reason;
2576 }
2577 
2578 /*!
2579  * \internal
2580  * \brief Return the exit status from an event notification
2581  *
2582  * \param[in] event  Event
2583  *
2584  * \return Exit status from event
2585  */
2586 int
2587 stonith__event_exit_status(const stonith_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help] */
2588 {
2589     if ((event == NULL) || (event->opaque == NULL)) {
2590         return CRM_EX_ERROR;
2591     } else {
2592         struct event_private *event_private = event->opaque;
2593 
2594         return event_private->result.exit_status;
2595     }
2596 }
2597 
2598 /*!
2599  * \internal
2600  * \brief Return the execution status from an event notification
2601  *
2602  * \param[in] event  Event
2603  *
2604  * \return Execution status from event
2605  */
2606 int
2607 stonith__event_execution_status(const stonith_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help] */
2608 {
2609     if ((event == NULL) || (event->opaque == NULL)) {
2610         return PCMK_EXEC_UNKNOWN;
2611     } else {
2612         struct event_private *event_private = event->opaque;
2613 
2614         return event_private->result.execution_status;
2615     }
2616 }
2617 
2618 /*!
2619  * \internal
2620  * \brief Return the exit reason from an event notification
2621  *
2622  * \param[in] event  Event
2623  *
2624  * \return Exit reason from event
2625  */
2626 const char *
2627 stonith__event_exit_reason(const stonith_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help] */
2628 {
2629     if ((event == NULL) || (event->opaque == NULL)) {
2630         return NULL;
2631     } else {
2632         struct event_private *event_private = event->opaque;
2633 
2634         return event_private->result.exit_reason;
2635     }
2636 }
2637 
2638 /*!
2639  * \internal
2640  * \brief Return a human-friendly description of a fencing event
2641  *
2642  * \param[in] event  Event to describe
2643  *
2644  * \return Newly allocated string with description of \p event
2645  * \note The caller is responsible for freeing the return value.
2646  *       This function asserts on memory errors and never returns NULL.
2647  */
2648 char *
2649 stonith__event_description(const stonith_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help] */
2650 {
2651     // Use somewhat readable defaults
2652     const char *origin = pcmk__s(event->client_origin, "a client");
2653     const char *origin_node = pcmk__s(event->origin, "a node");
2654     const char *executioner = pcmk__s(event->executioner, "the cluster");
2655     const char *device = pcmk__s(event->device, "unknown");
2656     const char *action = pcmk__s(event->action, event->operation);
2657     const char *target = pcmk__s(event->target, "no node");
2658     const char *reason = stonith__event_exit_reason(event);
2659     const char *status;
2660 
2661     if (action == NULL) {
2662         action = "(unknown)";
2663     }
2664 
2665     if (stonith__event_execution_status(event) != PCMK_EXEC_DONE) {
2666         status = pcmk_exec_status_str(stonith__event_execution_status(event));
2667     } else if (stonith__event_exit_status(event) != CRM_EX_OK) {
2668         status = pcmk_exec_status_str(PCMK_EXEC_ERROR);
2669     } else {
2670         status = crm_exit_str(CRM_EX_OK);
2671     }
2672 
2673     if (pcmk__str_eq(event->operation, PCMK__VALUE_ST_NOTIFY_HISTORY,
2674                      pcmk__str_none)) {
2675         return crm_strdup_printf("Fencing history may have changed");
2676 
2677     } else if (pcmk__str_eq(event->operation, STONITH_OP_DEVICE_ADD,
2678                             pcmk__str_none)) {
2679         return crm_strdup_printf("A fencing device (%s) was added", device);
2680 
2681     } else if (pcmk__str_eq(event->operation, STONITH_OP_DEVICE_DEL,
2682                             pcmk__str_none)) {
2683         return crm_strdup_printf("A fencing device (%s) was removed", device);
2684 
2685     } else if (pcmk__str_eq(event->operation, STONITH_OP_LEVEL_ADD,
2686                             pcmk__str_none)) {
2687         return crm_strdup_printf("A fencing topology level (%s) was added",
2688                                  device);
2689 
2690     } else if (pcmk__str_eq(event->operation, STONITH_OP_LEVEL_DEL,
2691                             pcmk__str_none)) {
2692         return crm_strdup_printf("A fencing topology level (%s) was removed",
2693                                  device);
2694     }
2695 
2696     // event->operation should be PCMK__VALUE_ST_NOTIFY_FENCE at this point
2697 
2698     return crm_strdup_printf("Operation %s of %s by %s for %s@%s: %s%s%s%s (ref=%s)",
2699                              action, target, executioner, origin, origin_node,
2700                              status,
2701                              ((reason == NULL)? "" : " ("), pcmk__s(reason, ""),
2702                              ((reason == NULL)? "" : ")"),
2703                              pcmk__s(event->id, "(none)"));
2704 }
2705 
2706 
2707 // Deprecated functions kept only for backward API compatibility
2708 // LCOV_EXCL_START
2709 
2710 const char *get_stonith_provider(const char *agent, const char *provider);
2711 
2712 const char *
2713 get_stonith_provider(const char *agent, const char *provider)
     /* [previous][next][first][last][top][bottom][index][help] */
2714 {
2715     return stonith_namespace2text(stonith_get_namespace(agent, provider));
2716 }
2717 
2718 // LCOV_EXCL_STOP
2719 // End deprecated API

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