root/lib/fencing/st_client.c

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

DEFINITIONS

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

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

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