root/lib/fencing/st_client.c

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

DEFINITIONS

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

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