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