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. log_action
  5. foreach_notify_entry
  6. stonith_connection_destroy
  7. create_device_registration_xml
  8. stonith_api_register_device
  9. stonith_api_remove_device
  10. stonith_api_remove_level_full
  11. stonith_api_remove_level
  12. create_level_registration_xml
  13. stonith_api_register_level_full
  14. stonith_api_register_level
  15. append_config_arg
  16. make_args
  17. stonith__destroy_action
  18. stonith__action_result
  19. stonith_action_create
  20. update_remaining_timeout
  21. svc_action_to_errno
  22. stonith_action_async_done
  23. stonith_action_async_forked
  24. internal_stonith_action_execute
  25. stonith_action_execute_async
  26. stonith__execute
  27. stonith_api_device_list
  28. stonith_api_device_metadata
  29. stonith_api_query
  30. stonith_api_call
  31. stonith_api_list
  32. stonith_api_monitor
  33. stonith_api_status
  34. stonith_api_fence_with_delay
  35. stonith_api_fence
  36. stonith_api_confirm
  37. stonith_api_history
  38. stonith_history_free
  39. stonithlib_GCompareFunc
  40. stonith_create_op
  41. stonith_destroy_op_callback
  42. stonith_api_signoff
  43. stonith_api_del_callback
  44. invoke_callback
  45. stonith_perform_callback
  46. stonith_async_timeout_handler
  47. set_callback_timeout
  48. update_callback_timeout
  49. stonith_dispatch_internal
  50. stonith_api_signon
  51. stonith_set_notification
  52. stonith_api_add_notification
  53. stonith_api_del_notification
  54. stonith_api_add_callback
  55. stonith_dump_pending_op
  56. stonith_dump_pending_callbacks
  57. xml_to_event
  58. event_free
  59. stonith_send_notification
  60. stonith_send_command
  61. stonith_dispatch
  62. stonith_api_free
  63. stonith_api_delete
  64. stonith_api_validate
  65. stonith_api_new
  66. stonith_api_connect_retry
  67. stonith_key_value_add
  68. stonith_key_value_freeall
  69. stonith_api_kick
  70. stonith_api_time
  71. stonith_agent_exists
  72. stonith_action_str
  73. parse_list_line
  74. stonith__parse_targets
  75. stonith__later_succeeded
  76. stonith__sort_history
  77. stonith_op_state_str
  78. stonith__first_matching_event
  79. stonith__event_state_pending
  80. stonith__event_state_eq
  81. stonith__event_state_neq
  82. stonith__device_parameter_flags
  83. get_stonith_provider

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

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