1    	/*
2    	 * Copyright 2009-2026 the Pacemaker project contributors
3    	 *
4    	 * The version control history for this file may have further details.
5    	 *
6    	 * This source code is licensed under the GNU General Public License version 2
7    	 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <stdbool.h>
13   	
14   	#include <crm/common/mainloop.h>
15   	#include <crm/common/output.h>
16   	#include <crm/common/results.h>
17   	#include <crm/stonith-ng.h>         // stonith_t, stonith_history_t, etc.
18   	#include <crm/fencing/internal.h>   // stonith__*
19   	
20   	#include <glib.h>
21   	#include <libxml/tree.h>
22   	#include <pacemaker.h>
23   	#include <pacemaker-internal.h>
24   	
25   	#include "libpacemaker_private.h"
26   	
27   	static const int st_opts = st_opt_sync_call|st_opt_allow_self_fencing;
28   	
29   	static GMainLoop *mainloop = NULL;
30   	
31   	static struct {
32   	    stonith_t *st;
33   	    const char *target;
34   	    const char *action;
35   	    char *name;
36   	    unsigned int timeout;
37   	    unsigned int tolerance;
38   	    int delay;
39   	    pcmk__action_result_t result;
40   	} async_fence_data = { NULL, };
41   	
42   	static int
43   	handle_level(stonith_t *st, const char *target, int fence_level, GList *devices,
44   	             bool added)
45   	{
46   	    const char *node = NULL;
47   	    const char *pattern = NULL;
48   	
49   	    gchar **name_value = NULL;
50   	    const gchar *name = NULL;
51   	    const gchar *value = NULL;
52   	    int rc = pcmk_rc_ok;
53   	    char *target_copy = NULL;
54   	
(1) Event path: Condition "target == NULL", taking false branch.
55   	    if (target == NULL) {
56   	        // Not really possible, but makes static analysis happy
57   	        return EINVAL;
58   	    }
59   	
(2) Event alloc_fn: Storage is returned from allocation function "pcmk__str_copy_as". [details]
(3) Event var_assign: Assigning: "target_copy" = storage returned from "pcmk__str_copy_as("pcmk_fence.c", <anonymous>, 60U, target)".
Also see events: [leaked_storage]
60   	    target_copy = pcmk__str_copy(target);
61   	
62   	    /* Determine if targeting by attribute, node name pattern or node name */
63   	    name_value = g_strsplit(target, "=", 2);
64   	
(4) Event path: Condition "g_strv_length(name_value) == 2", taking true branch.
65   	    if (g_strv_length(name_value) == 2) {
66   	        name = name_value[0];
67   	        value = name_value[1];
68   	
(5) Event path: Falling through to end of if statement.
69   	    } else if (*target == '@') {
70   	        pattern = target + 1;
71   	
72   	    } else {
73   	        node = target_copy;
74   	    }
75   	
76   	    /* Register or unregister level as appropriate */
(6) Event path: Condition "added", taking true branch.
77   	    if (added) {
78   	        stonith_key_value_t *kvs = NULL;
79   	
(7) Event path: Condition "iter != NULL", taking true branch.
(9) Event path: Condition "iter != NULL", taking false branch.
80   	        for (GList *iter = devices; iter != NULL; iter = iter->next) {
81   	            kvs = stonith__key_value_add(kvs, NULL, iter->data);
(8) Event path: Jumping back to the beginning of the loop.
82   	        }
83   	
84   	        rc = st->cmds->register_level_full(st, st_opts, node, pattern, name,
85   	                                           value, fence_level, kvs);
86   	        stonith__key_value_freeall(kvs, false, true);
(10) Event path: Falling through to end of if statement.
87   	    } else {
88   	        rc = st->cmds->remove_level_full(st, st_opts, node, pattern,
89   	                                         name, value, fence_level);
90   	    }
91   	
92   	    g_strfreev(name_value);
CID (unavailable; MK=55016abac81e0011238823f90dfbdc58) (#1 of 1): Resource leak (RESOURCE_LEAK):
(11) Event leaked_storage: Variable "target_copy" going out of scope leaks the storage it points to.
Also see events: [alloc_fn][var_assign]
93   	    return pcmk_legacy2rc(rc);
94   	}
95   	
96   	static stonith_history_t *
97   	reduce_fence_history(stonith_history_t *history)
98   	{
99   	    stonith_history_t *new, *hp, *np;
100  	
101  	    if (!history) {
102  	        return history;
103  	    }
104  	
105  	    new = history;
106  	    hp = new->next;
107  	    new->next = NULL;
108  	
109  	    while (hp) {
110  	        stonith_history_t *hp_next = hp->next;
111  	
112  	        hp->next = NULL;
113  	
114  	        for (np = new; ; np = np->next) {
115  	            if ((hp->state == st_done) || (hp->state == st_failed)) {
116  	                /* action not in progress */
117  	                if (pcmk__str_eq(hp->target, np->target, pcmk__str_casei)
118  	                    && pcmk__str_eq(hp->action, np->action, pcmk__str_none)
119  	                    && (hp->state == np->state)
120  	                    && ((hp->state == st_done)
121  	                        || pcmk__str_eq(hp->delegate, np->delegate,
122  	                                        pcmk__str_casei))) {
123  	                        /* purge older hp */
124  	                        stonith__history_free(hp);
125  	                        break;
126  	                }
127  	            }
128  	
129  	            if (!np->next) {
130  	                np->next = hp;
131  	                break;
132  	            }
133  	        }
134  	        hp = hp_next;
135  	    }
136  	
137  	    return new;
138  	}
139  	
140  	static void
141  	notify_callback(stonith_t * st, stonith_event_t * e)
142  	{
143  	    if (pcmk__str_eq(async_fence_data.target, e->target, pcmk__str_casei)
144  	        && pcmk__str_eq(async_fence_data.action, e->action, pcmk__str_none)) {
145  	
146  	        pcmk__set_result(&async_fence_data.result,
147  	                         stonith__event_exit_status(e),
148  	                         stonith__event_execution_status(e),
149  	                         stonith__event_exit_reason(e));
150  	        g_main_loop_quit(mainloop);
151  	    }
152  	}
153  	
154  	static void
155  	fence_callback(stonith_t * stonith, stonith_callback_data_t * data)
156  	{
157  	    pcmk__set_result(&async_fence_data.result, stonith__exit_status(data),
158  	                     stonith__execution_status(data),
159  	                     stonith__exit_reason(data));
160  	    g_main_loop_quit(mainloop);
161  	}
162  	
163  	static gboolean
164  	async_fence_helper(gpointer user_data)
165  	{
166  	    stonith_t *st = async_fence_data.st;
167  	    int call_id = 0;
168  	    int rc = stonith__api_connect_retry(st, async_fence_data.name, 10);
169  	    int timeout = 0;
170  	
171  	    if (rc != pcmk_rc_ok) {
172  	        g_main_loop_quit(mainloop);
173  	        pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR,
174  	                         PCMK_EXEC_NOT_CONNECTED, pcmk_rc_str(rc));
175  	        return TRUE;
176  	    }
177  	
178  	    st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_FENCE,
179  	                                    notify_callback);
180  	
181  	    call_id = st->cmds->fence_with_delay(st,
182  	                                         st_opt_allow_self_fencing,
183  	                                         async_fence_data.target,
184  	                                         async_fence_data.action,
185  	                                         pcmk__timeout_ms2s(async_fence_data.timeout),
186  	                                         pcmk__timeout_ms2s(async_fence_data.tolerance),
187  	                                         async_fence_data.delay);
188  	
189  	    if (call_id < 0) {
190  	        g_main_loop_quit(mainloop);
191  	        pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR,
192  	                         PCMK_EXEC_ERROR, pcmk_strerror(call_id));
193  	        return TRUE;
194  	    }
195  	
196  	    timeout = pcmk__timeout_ms2s(async_fence_data.timeout);
197  	    if (async_fence_data.delay > 0) {
198  	        timeout += async_fence_data.delay;
199  	    }
200  	    st->cmds->register_callback(st, call_id, timeout, st_opt_timeout_updates,
201  	                                NULL, "callback", fence_callback);
202  	    return TRUE;
203  	}
204  	
205  	int
206  	pcmk__request_fencing(stonith_t *st, const char *target, const char *action,
207  	                      const char *name, unsigned int timeout,
208  	                      unsigned int tolerance, int delay, char **reason)
209  	{
210  	    crm_trigger_t *trig;
211  	    int rc = pcmk_rc_ok;
212  	
213  	    async_fence_data.st = st;
214  	    async_fence_data.name = strdup(name);
215  	    async_fence_data.target = target;
216  	    async_fence_data.action = action;
217  	    async_fence_data.timeout = timeout;
218  	    async_fence_data.tolerance = tolerance;
219  	    async_fence_data.delay = delay;
220  	    pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR, PCMK_EXEC_UNKNOWN,
221  	                     NULL);
222  	
223  	    trig = mainloop_add_trigger(G_PRIORITY_HIGH, async_fence_helper, NULL);
224  	    mainloop_set_trigger(trig);
225  	
226  	    mainloop = g_main_loop_new(NULL, FALSE);
227  	    g_main_loop_run(mainloop);
228  	
229  	    free(async_fence_data.name);
230  	
231  	    if (reason != NULL) {
232  	        // Give the caller ownership of the exit reason
233  	        *reason = async_fence_data.result.exit_reason;
234  	        async_fence_data.result.exit_reason = NULL;
235  	    }
236  	    rc = stonith__result2rc(&async_fence_data.result);
237  	    pcmk__reset_result(&async_fence_data.result);
238  	    return rc;
239  	}
240  	
241  	int
242  	pcmk_request_fencing(xmlNodePtr *xml, const char *target, const char *action,
243  	                     const char *name, unsigned int timeout,
244  	                     unsigned int tolerance, int delay, char **reason)
245  	{
246  	    stonith_t *st = NULL;
247  	    pcmk__output_t *out = NULL;
248  	    int rc = pcmk_rc_ok;
249  	
250  	    rc = pcmk__setup_output_fencing(&out, &st, xml);
251  	    if (rc != pcmk_rc_ok) {
252  	        return rc;
253  	    }
254  	
255  	    rc = pcmk__request_fencing(st, target, action, name, timeout, tolerance,
256  	                               delay, reason);
257  	    pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
258  	
259  	    st->cmds->disconnect(st);
260  	    stonith__api_free(st);
261  	    return rc;
262  	}
263  	
264  	int
265  	pcmk__fence_history(pcmk__output_t *out, stonith_t *st, const char *target,
266  	                    unsigned int timeout, int verbose, bool broadcast,
267  	                    bool cleanup)
268  	{
269  	    stonith_history_t *history = NULL;
270  	    stonith_history_t *latest = NULL;
271  	    int rc = pcmk_rc_ok;
272  	    int opts = 0;
273  	
274  	    if (cleanup) {
275  	        out->info(out, "cleaning up fencing-history%s%s",
276  	                  target ? " for node " : "", target ? target : "");
277  	    }
278  	
279  	    if (broadcast) {
280  	        out->info(out, "gather fencing-history from all nodes");
281  	    }
282  	
283  	    stonith__set_call_options(opts, target, st_opts);
284  	
285  	    if (cleanup) {
286  	        stonith__set_call_options(opts, target, st_opt_cleanup);
287  	    }
288  	
289  	    if (broadcast) {
290  	        stonith__set_call_options(opts, target, st_opt_broadcast);
291  	    }
292  	
293  	    if (pcmk__str_eq(target, "*", pcmk__str_none)) {
294  	        target = NULL;
295  	    }
296  	
297  	    rc = st->cmds->history(st, opts, target, &history, pcmk__timeout_ms2s(timeout));
298  	
299  	    if (cleanup) {
300  	        // Cleanup doesn't return a history list
301  	        stonith__history_free(history);
302  	        return pcmk_legacy2rc(rc);
303  	    }
304  	
305  	    out->begin_list(out, "event", "events", "Fencing history");
306  	
307  	    history = stonith__sort_history(history);
308  	    for (stonith_history_t *hp = history; hp != NULL; hp = hp->next) {
309  	        if (hp->state == st_done) {
310  	            latest = hp;
311  	        }
312  	
313  	        if (out->is_quiet(out) || !verbose) {
314  	            continue;
315  	        }
316  	
317  	        out->message(out, "stonith-event", hp, true, false,
318  	                     stonith__later_succeeded(hp, history),
319  	                     (uint32_t) pcmk_show_failed_detail);
320  	        out->increment_list(out);
321  	    }
322  	
323  	    if (latest) {
324  	        if (out->is_quiet(out)) {
325  	            out->message(out, "stonith-event", latest, false, true, NULL,
326  	                         (uint32_t) pcmk_show_failed_detail);
327  	        } else if (!verbose) { // already printed if verbose
328  	            out->message(out, "stonith-event", latest, false, false, NULL,
329  	                         (uint32_t) pcmk_show_failed_detail);
330  	            out->increment_list(out);
331  	        }
332  	    }
333  	
334  	    out->end_list(out);
335  	
336  	    stonith__history_free(history);
337  	    return pcmk_legacy2rc(rc);
338  	}
339  	
340  	int
341  	pcmk_fence_history(xmlNodePtr *xml, const char *target, unsigned int timeout,
342  	                   bool quiet, int verbose, bool broadcast, bool cleanup)
343  	{
344  	    stonith_t *st = NULL;
345  	    pcmk__output_t *out = NULL;
346  	    int rc = pcmk_rc_ok;
347  	
348  	    rc = pcmk__setup_output_fencing(&out, &st, xml);
349  	    if (rc != pcmk_rc_ok) {
350  	        return rc;
351  	    }
352  	
353  	    out->quiet = quiet;
354  	
355  	    rc = pcmk__fence_history(out, st, target, timeout, verbose, broadcast,
356  	                             cleanup);
357  	    pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
358  	
359  	    st->cmds->disconnect(st);
360  	    stonith__api_free(st);
361  	    return rc;
362  	}
363  	
364  	int
365  	pcmk__fence_installed(pcmk__output_t *out, stonith_t *st)
366  	{
367  	    stonith_key_value_t *devices = NULL;
368  	    int rc = pcmk_rc_ok;
369  	
370  	    rc = st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, 0);
371  	    // rc is a negative error code or a positive number of agents
372  	    if (rc < 0) {
373  	        return pcmk_legacy2rc(rc);
374  	    }
375  	
376  	    out->begin_list(out, "fence device", "fence devices",
377  	                    "Installed fence devices");
378  	    for (stonith_key_value_t *iter = devices; iter != NULL; iter = iter->next) {
379  	        out->list_item(out, "device", "%s", iter->value);
380  	    }
381  	    out->end_list(out);
382  	
383  	    stonith__key_value_freeall(devices, true, true);
384  	    return pcmk_rc_ok;
385  	}
386  	
387  	int
388  	pcmk_fence_installed(xmlNodePtr *xml, unsigned int timeout)
389  	{
390  	    stonith_t *st = NULL;
391  	    pcmk__output_t *out = NULL;
392  	    int rc = pcmk_rc_ok;
393  	
394  	    rc = pcmk__setup_output_fencing(&out, &st, xml);
395  	    if (rc != pcmk_rc_ok) {
396  	        return rc;
397  	    }
398  	
399  	    rc = pcmk__fence_installed(out, st);
400  	    pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
401  	
402  	    st->cmds->disconnect(st);
403  	    stonith__api_free(st);
404  	    return rc;
405  	}
406  	
407  	int
408  	pcmk__fence_last(pcmk__output_t *out, const char *target, bool as_nodeid)
409  	{
410  	    time_t when = 0;
411  	
412  	    if (target == NULL) {
413  	        return pcmk_rc_ok;
414  	    }
415  	
416  	    if (as_nodeid) {
417  	        when = stonith_api_time(atol(target), NULL, FALSE);
418  	    } else {
419  	        when = stonith_api_time(0, target, FALSE);
420  	    }
421  	
422  	    return out->message(out, "last-fenced", target, when);
423  	}
424  	
425  	int
426  	pcmk_fence_last(xmlNodePtr *xml, const char *target, bool as_nodeid)
427  	{
428  	    pcmk__output_t *out = NULL;
429  	    int rc = pcmk_rc_ok;
430  	
431  	    rc = pcmk__xml_output_new(&out, xml);
432  	    if (rc != pcmk_rc_ok) {
433  	        return rc;
434  	    }
435  	
436  	    stonith__register_messages(out);
437  	
438  	    rc = pcmk__fence_last(out, target, as_nodeid);
439  	    pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
440  	    return rc;
441  	}
442  	
443  	int
444  	pcmk__fence_list_targets(pcmk__output_t *out, stonith_t *st,
445  	                         const char *device_id, unsigned int timeout)
446  	{
447  	    GList *targets = NULL;
448  	    char *lists = NULL;
449  	    int rc = pcmk_rc_ok;
450  	
451  	    rc = st->cmds->list(st, st_opts, device_id, &lists, pcmk__timeout_ms2s(timeout));
452  	    if (rc != pcmk_rc_ok) {
453  	        return pcmk_legacy2rc(rc);
454  	    }
455  	
456  	    targets = stonith__parse_targets(lists);
457  	
458  	    out->begin_list(out, "fence target", "fence targets", "Fence Targets");
459  	    while (targets != NULL) {
460  	        out->list_item(out, NULL, "%s", (const char *) targets->data);
461  	        targets = targets->next;
462  	    }
463  	    out->end_list(out);
464  	
465  	    free(lists);
466  	    return rc;
467  	}
468  	
469  	int
470  	pcmk_fence_list_targets(xmlNodePtr *xml, const char *device_id, unsigned int timeout)
471  	{
472  	    stonith_t *st = NULL;
473  	    pcmk__output_t *out = NULL;
474  	    int rc = pcmk_rc_ok;
475  	
476  	    rc = pcmk__setup_output_fencing(&out, &st, xml);
477  	    if (rc != pcmk_rc_ok) {
478  	        return rc;
479  	    }
480  	
481  	    rc = pcmk__fence_list_targets(out, st, device_id, timeout);
482  	    pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
483  	
484  	    st->cmds->disconnect(st);
485  	    stonith__api_free(st);
486  	    return rc;
487  	}
488  	
489  	int
490  	pcmk__fence_metadata(pcmk__output_t *out, stonith_t *st, const char *agent,
491  	                     unsigned int timeout)
492  	{
493  	    char *buffer = NULL;
494  	    int rc = st->cmds->metadata(st, st_opt_sync_call, agent, NULL, &buffer,
495  	                                pcmk__timeout_ms2s(timeout));
496  	
497  	    if (rc != pcmk_rc_ok) {
498  	        return pcmk_legacy2rc(rc);
499  	    }
500  	
501  	    out->output_xml(out, PCMK_XE_METADATA, buffer);
502  	    free(buffer);
503  	    return rc;
504  	}
505  	
506  	int
507  	pcmk_fence_metadata(xmlNodePtr *xml, const char *agent, unsigned int timeout)
508  	{
509  	    stonith_t *st = NULL;
510  	    pcmk__output_t *out = NULL;
511  	    int rc = pcmk_rc_ok;
512  	
513  	    rc = pcmk__setup_output_fencing(&out, &st, xml);
514  	    if (rc != pcmk_rc_ok) {
515  	        return rc;
516  	    }
517  	
518  	    rc = pcmk__fence_metadata(out, st, agent, timeout);
519  	    pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
520  	
521  	    st->cmds->disconnect(st);
522  	    stonith__api_free(st);
523  	    return rc;
524  	}
525  	
526  	int
527  	pcmk__fence_registered(pcmk__output_t *out, stonith_t *st, const char *target,
528  	                       unsigned int timeout)
529  	{
530  	    stonith_key_value_t *devices = NULL;
531  	    int rc = pcmk_rc_ok;
532  	
533  	    rc = st->cmds->query(st, st_opts, target, &devices, pcmk__timeout_ms2s(timeout));
534  	    /* query returns a negative error code or a positive number of results. */
535  	    if (rc < 0) {
536  	        return pcmk_legacy2rc(rc);
537  	    }
538  	
539  	    out->begin_list(out, "fence device", "fence devices",
540  	                    "Registered fence devices");
541  	    for (stonith_key_value_t *iter = devices; iter != NULL; iter = iter->next) {
542  	        out->list_item(out, "device", "%s", iter->value);
543  	    }
544  	    out->end_list(out);
545  	
546  	    stonith__key_value_freeall(devices, true, true);
547  	
548  	    /* Return pcmk_rc_ok here, not the number of results.  Callers probably
549  	     * don't care.
550  	     */
551  	    return pcmk_rc_ok;
552  	}
553  	
554  	int
555  	pcmk_fence_registered(xmlNodePtr *xml, const char *target, unsigned int timeout)
556  	{
557  	    stonith_t *st = NULL;
558  	    pcmk__output_t *out = NULL;
559  	    int rc = pcmk_rc_ok;
560  	
561  	    rc = pcmk__setup_output_fencing(&out, &st, xml);
562  	    if (rc != pcmk_rc_ok) {
563  	        return rc;
564  	    }
565  	
566  	    rc = pcmk__fence_registered(out, st, target, timeout);
567  	    pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
568  	
569  	    st->cmds->disconnect(st);
570  	    stonith__api_free(st);
571  	    return rc;
572  	}
573  	
574  	int
575  	pcmk__fence_register_level(stonith_t *st, const char *target, int fence_level,
576  	                           GList *devices)
577  	{
578  	    return handle_level(st, target, fence_level, devices, true);
579  	}
580  	
581  	int
582  	pcmk_fence_register_level(xmlNodePtr *xml, const char *target, int fence_level,
583  	                          GList *devices)
584  	{
585  	    stonith_t* st = NULL;
586  	    pcmk__output_t *out = NULL;
587  	    int rc = pcmk_rc_ok;
588  	
589  	    rc = pcmk__setup_output_fencing(&out, &st, xml);
590  	    if (rc != pcmk_rc_ok) {
591  	        return rc;
592  	    }
593  	
594  	    rc = pcmk__fence_register_level(st, target, fence_level, devices);
595  	    pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
596  	
597  	    st->cmds->disconnect(st);
598  	    stonith__api_free(st);
599  	    return rc;
600  	}
601  	
602  	int
603  	pcmk__fence_unregister_level(stonith_t *st, const char *target, int fence_level)
604  	{
605  	    return handle_level(st, target, fence_level, NULL, false);
606  	}
607  	
608  	int
609  	pcmk_fence_unregister_level(xmlNodePtr *xml, const char *target, int fence_level)
610  	{
611  	    stonith_t* st = NULL;
612  	    pcmk__output_t *out = NULL;
613  	    int rc = pcmk_rc_ok;
614  	
615  	    rc = pcmk__setup_output_fencing(&out, &st, xml);
616  	    if (rc != pcmk_rc_ok) {
617  	        return rc;
618  	    }
619  	
620  	    rc = pcmk__fence_unregister_level(st, target, fence_level);
621  	    pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
622  	
623  	    st->cmds->disconnect(st);
624  	    stonith__api_free(st);
625  	    return rc;
626  	}
627  	
628  	int
629  	pcmk__fence_validate(pcmk__output_t *out, stonith_t *st, const char *agent,
630  	                     const char *id, GHashTable *params, unsigned int timeout)
631  	{
632  	    char *output = NULL;
633  	    char *error_output = NULL;
634  	    int rc;
635  	
636  	    rc  = stonith__validate(st, st_opt_sync_call, id, agent, params,
637  	                            pcmk__timeout_ms2s(timeout), &output, &error_output);
638  	    out->message(out, "validate", agent, id, output, error_output, rc);
639  	    return pcmk_legacy2rc(rc);
640  	}
641  	
642  	int
643  	pcmk_fence_validate(xmlNodePtr *xml, const char *agent, const char *id,
644  	                    GHashTable *params, unsigned int timeout)
645  	{
646  	    stonith_t *st = NULL;
647  	    pcmk__output_t *out = NULL;
648  	    int rc = pcmk_rc_ok;
649  	
650  	    rc = pcmk__setup_output_fencing(&out, &st, xml);
651  	    if (rc != pcmk_rc_ok) {
652  	        return rc;
653  	    }
654  	
655  	    rc = pcmk__fence_validate(out, st, agent, id, params, timeout);
656  	    pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
657  	
658  	    st->cmds->disconnect(st);
659  	    stonith__api_free(st);
660  	    return rc;
661  	}
662  	
663  	int
664  	pcmk__get_fencing_history(stonith_t *st, stonith_history_t **stonith_history,
665  	                          enum pcmk__fence_history fence_history)
666  	{
667  	    int rc = pcmk_rc_ok;
668  	
669  	    if ((st == NULL) || (st->state == stonith_disconnected)) {
670  	        rc = ENOTCONN;
671  	    } else if (fence_history != pcmk__fence_history_none) {
672  	        rc = st->cmds->history(st, st_opt_sync_call, NULL, stonith_history,
673  	                               120);
674  	
675  	        rc = pcmk_legacy2rc(rc);
676  	        if (rc != pcmk_rc_ok) {
677  	            return rc;
678  	        }
679  	
680  	        *stonith_history = stonith__sort_history(*stonith_history);
681  	        if (fence_history == pcmk__fence_history_reduced) {
682  	            *stonith_history = reduce_fence_history(*stonith_history);
683  	        }
684  	    }
685  	
686  	    return rc;
687  	}
688