root/lib/fencing/st_client.c

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

DEFINITIONS

This source file includes following definitions.
  1. parse_namespace
  2. namespace_text
  3. get_namespace_from_agent
  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. get_event_data_xml
  46. xml_to_event
  47. event_free
  48. stonith_send_notification
  49. stonith_send_command
  50. stonith__api_dispatch
  51. free_stonith_api
  52. is_stonith_param
  53. stonith__validate
  54. stonith_api_validate
  55. stonith__api_new
  56. stonith__api_free
  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. parse_list_line
  64. stonith__parse_targets
  65. stonith__later_succeeded
  66. stonith__sort_history
  67. stonith__op_state_text
  68. stonith__first_matching_event
  69. stonith__event_state_pending
  70. stonith__event_state_eq
  71. stonith__event_state_neq
  72. param_is_supported
  73. stonith__default_host_arg
  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. stonith_api_new
  83. stonith_api_delete
  84. stonith_dump_pending_op
  85. stonith_dump_pending_callbacks
  86. stonith_dispatch
  87. stonith_key_value_add
  88. stonith_key_value_freeall
  89. stonith_history_free
  90. stonith_api_connect_retry
  91. stonith_op_state_str
  92. stonith_agent_exists
  93. stonith_action_str
  94. stonith_text2namespace
  95. stonith_namespace2text
  96. stonith_get_namespace

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

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