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