root/lib/fencing/st_client.c

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

DEFINITIONS

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

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

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