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