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