root/lib/fencing/st_client.c

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

DEFINITIONS

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

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

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