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 <sys/param.h>
13   	#include <stdio.h>
14   	#include <sys/types.h>
15   	#include <sys/stat.h>
16   	#include <unistd.h>
17   	#include <sys/utsname.h>
18   	
19   	#include <errno.h>
20   	#include <fcntl.h>
21   	#include <stdbool.h>
22   	#include <stdlib.h>
23   	#include <string.h>
24   	
25   	#include <glib.h>                   // gboolean, gchar, etc.
26   	
27   	#include <crm/crm.h>
28   	#include <crm/common/ipc.h>
29   	#include <crm/cluster/internal.h>
30   	
31   	#include <crm/stonith-ng.h>
32   	#include <crm/fencing/internal.h>   // stonith__register_messages()
33   	#include <crm/cib.h>
34   	#include <crm/pengine/status.h>
35   	
36   	#include <crm/common/xml.h>
37   	#include <pacemaker-internal.h>
38   	
39   	#define SUMMARY "stonith_admin - Access the Pacemaker fencing API"
40   	
41   	char action = 0;
42   	
43   	struct {
44   	    gboolean as_nodeid;
45   	    gboolean broadcast;
46   	    gboolean cleanup;
47   	    gboolean installed;
48   	    gboolean metadata;
49   	    gboolean registered;
50   	    gboolean validate_cfg;
51   	    GList *devices;
52   	    GHashTable *params;
53   	    int fence_level;
54   	    int timeout ;
55   	    unsigned int tolerance_ms;
56   	    int delay;
57   	    char *agent;
58   	    char *confirm_host;
59   	    char *fence_host;
60   	    char *history;
61   	    char *last_fenced;
62   	    char *query;
63   	    char *reboot_host;
64   	    char *register_dev;
65   	    char *register_level;
66   	    char *targets;
67   	    char *terminate;
68   	    char *unfence_host;
69   	    char *unregister_dev;
70   	    char *unregister_level;
71   	} options = {
72   	    .timeout = 120,
73   	    .delay = 0
74   	};
75   	
76   	gboolean add_env_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
77   	gboolean add_fencing_device(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
78   	gboolean add_fencing_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
79   	gboolean add_tolerance(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
80   	gboolean set_tag(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
81   	
82   	#define INDENT "                                    "
83   	
84   	/* *INDENT-OFF* */
85   	static GOptionEntry defn_entries[] = {
86   	    { "register", 'R', 0, G_OPTION_ARG_STRING, &options.register_dev,
87   	      "Register the named fencing device. Requires: --agent.\n"
88   	      INDENT "Optional: --option, --env-option.",
89   	      "DEVICE" },
90   	    { "deregister", 'D', 0, G_OPTION_ARG_STRING, &options.unregister_dev,
91   	      "De-register the named fencing device.",
92   	      "DEVICE" },
93   	    { "register-level", 'r', 0, G_OPTION_ARG_STRING, &options.register_level,
94   	      "Register a fencing level for the named target,\n"
95   	      INDENT "specified as one of NAME, @PATTERN, or ATTR=VALUE.\n"
96   	      INDENT "Requires: --index and one or more --device entries.",
97   	      "TARGET" },
98   	    { "deregister-level", 'd', 0, G_OPTION_ARG_STRING, &options.unregister_level,
99   	      "Unregister a fencing level for the named target,\n"
100  	      INDENT "specified as for --register-level. Requires: --index",
101  	      "TARGET" },
102  	
103  	    { NULL }
104  	};
105  	
106  	static GOptionEntry query_entries[] = {
107  	    { "list", 'l', 0, G_OPTION_ARG_STRING, &options.terminate,
108  	      "List devices that can terminate the specified host.\n"
109  	      INDENT "Optional: --timeout",
110  	      "HOST" },
111  	    { "list-registered", 'L', 0, G_OPTION_ARG_NONE, &options.registered,
112  	      "List all registered devices. Optional: --timeout.",
113  	      NULL },
114  	    { "list-installed", 'I', 0, G_OPTION_ARG_NONE, &options.installed,
115  	      "List all installed devices. Optional: --timeout.",
116  	      NULL },
117  	    { "list-targets", 's', 0, G_OPTION_ARG_STRING, &options.targets,
118  	      "List the targets that can be fenced by the\n"
119  	      INDENT "named device. Optional: --timeout.",
120  	      "DEVICE" },
121  	    { "metadata", 'M', 0, G_OPTION_ARG_NONE, &options.metadata,
122  	      "Show agent metadata. Requires: --agent.\n"
123  	      INDENT "Optional: --timeout.",
124  	      NULL },
125  	    { "query", 'Q', 0, G_OPTION_ARG_STRING, &options.query,
126  	      "Check the named device's status. Optional: --timeout.",
127  	      "DEVICE" },
128  	    { "history", 'H', 0, G_OPTION_ARG_STRING, &options.history,
129  	      "Show last successful fencing operation for named node\n"
130  	      INDENT "(or '*' for all nodes). Optional: --timeout, --cleanup,\n"
131  	      INDENT "--quiet (show only the operation's epoch timestamp),\n"
132  	      INDENT "--verbose (show all recorded and pending operations),\n"
133  	      INDENT "--broadcast (update history from all nodes available).",
134  	      "NODE" },
135  	    { "last", 'h', 0, G_OPTION_ARG_STRING, &options.last_fenced,
136  	      "Indicate when the named node was last fenced.\n"
137  	      INDENT "Optional: --as-node-id.",
138  	      "NODE" },
139  	    { "validate", 'K', 0, G_OPTION_ARG_NONE, &options.validate_cfg,
140  	      "Validate a fence device configuration.\n"
141  	      INDENT "Requires: --agent. Optional: --option, --env-option,\n"
142  	      INDENT "--quiet (print no output, only return status).",
143  	      NULL },
144  	
145  	    { NULL }
146  	};
147  	
148  	static GOptionEntry fence_entries[] = {
149  	    { "fence", 'F', 0, G_OPTION_ARG_STRING, &options.fence_host,
150  	      "Fence named host. Optional: --timeout, --tolerance, --delay.",
151  	      "HOST" },
152  	    { "unfence", 'U', 0, G_OPTION_ARG_STRING, &options.unfence_host,
153  	      "Unfence named host. Optional: --timeout, --tolerance, --delay.",
154  	      "HOST" },
155  	    { "reboot", 'B', 0, G_OPTION_ARG_STRING, &options.reboot_host,
156  	      "Reboot named host. Optional: --timeout, --tolerance, --delay.",
157  	      "HOST" },
158  	    { "confirm", 'C', 0, G_OPTION_ARG_STRING, &options.confirm_host,
159  	      "Tell cluster that named host is now safely down.",
160  	      "HOST", },
161  	
162  	    { NULL }
163  	};
164  	
165  	static GOptionEntry addl_entries[] = {
166  	    { "cleanup", 'c', 0, G_OPTION_ARG_NONE, &options.cleanup,
167  	      "Cleanup wherever appropriate. Requires --history.",
168  	      NULL },
169  	    { "broadcast", 'b', 0, G_OPTION_ARG_NONE, &options.broadcast,
170  	      "Broadcast wherever appropriate.",
171  	      NULL },
172  	    { "agent", 'a', 0, G_OPTION_ARG_STRING, &options.agent,
173  	      "The agent to use (for example, fence_xvm;\n"
174  	      INDENT "with --register, --metadata, --validate).",
175  	      "AGENT" },
176  	    { "option", 'o', 0, G_OPTION_ARG_CALLBACK, add_fencing_params,
177  	      "Specify a device configuration parameter as NAME=VALUE\n"
178  	      INDENT "(may be specified multiple times; with --register,\n"
179  	      INDENT "--validate).",
180  	      "PARAM" },
181  	    { "env-option", 'e', 0, G_OPTION_ARG_CALLBACK, add_env_params,
182  	      "Specify a device configuration parameter with the\n"
183  	      INDENT "specified name, using the value of the\n"
184  	      INDENT "environment variable of the same name prefixed with\n"
185  	      INDENT "OCF_RESKEY_ (may be specified multiple times;\n"
186  	      INDENT "with --register, --validate).",
187  	      "PARAM" },
188  	    { "tag", 'T', 0, G_OPTION_ARG_CALLBACK, set_tag,
189  	      "Identify fencing operations in logs with the specified\n"
190  	      INDENT "tag; useful when multiple entities might invoke\n"
191  	      INDENT "stonith_admin (used with most commands).",
192  	      "TAG" },
193  	    { "device", 'v', 0, G_OPTION_ARG_CALLBACK, add_fencing_device,
194  	      "Device ID (with --register-level, device to associate with\n"
195  	      INDENT "a given host and level; may be specified multiple times)"
196  	#if PCMK__ENABLE_CIBSECRETS
197  	      "\n" INDENT "(with --validate, name to use to load CIB secrets)"
198  	#endif
199  	      ".",
200  	      "DEVICE" },
201  	    { "index", 'i', 0, G_OPTION_ARG_INT, &options.fence_level,
202  	      "The fencing level (1-9) (with --register-level,\n"
203  	      INDENT "--deregister-level).",
204  	      "LEVEL" },
205  	    { "timeout", 't', 0, G_OPTION_ARG_INT, &options.timeout,
206  	      "Operation timeout in seconds (default 120;\n"
207  	      INDENT "used with most commands).",
208  	      "SECONDS" },
209  	    { "delay", 'y', 0, G_OPTION_ARG_INT, &options.delay,
210  	      "Apply a fencing delay in seconds. Any static/random delays from\n"
211  	      INDENT "pcmk_delay_base/max will be added, otherwise all\n"
212  	      INDENT "disabled with the value -1\n"
213  	      INDENT "(default 0; with --fence, --reboot, --unfence).",
214  	      "SECONDS" },
215  	    { "as-node-id", 'n', 0, G_OPTION_ARG_NONE, &options.as_nodeid,
216  	      "(Advanced) The supplied node is the corosync node ID\n"
217  	      INDENT "(with --last).",
218  	      NULL },
219  	    { "tolerance", 0, 0, G_OPTION_ARG_CALLBACK, add_tolerance,
220  	      "(Advanced) Do nothing if an equivalent --fence request\n"
221  	      INDENT "succeeded less than this many seconds earlier\n"
222  	      INDENT "(with --fence, --unfence, --reboot).",
223  	      "SECONDS" },
224  	
225  	    { NULL }
226  	};
227  	/* *INDENT-ON* */
228  	
229  	static pcmk__supported_format_t formats[] = {
230  	    PCMK__SUPPORTED_FORMAT_HTML,
231  	    PCMK__SUPPORTED_FORMAT_NONE,
232  	    PCMK__SUPPORTED_FORMAT_TEXT,
233  	    PCMK__SUPPORTED_FORMAT_XML,
234  	    { NULL, NULL, NULL }
235  	};
236  	
237  	static const int st_opts = st_opt_sync_call|st_opt_allow_self_fencing;
238  	
239  	static char *name = NULL;
240  	
241  	gboolean
242  	add_env_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
243  	    char *key = pcmk__assert_asprintf("OCF_RESKEY_%s", optarg);
244  	    const char *env = getenv(key);
245  	    gboolean retval = TRUE;
246  	
247  	    if (env == NULL) {
248  	        g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Invalid option: -e %s", optarg);
249  	        retval = FALSE;
250  	    } else {
251  	        pcmk__info("Got: '%s'='%s'", optarg, env);
252  	
253  	        if (options.params != NULL) {
254  	            options.params = pcmk__strkey_table(free, free);
255  	        }
256  	
257  	        pcmk__insert_dup(options.params, optarg, env);
258  	    }
259  	
260  	    free(key);
261  	    return retval;
262  	}
263  	
264  	gboolean
265  	add_fencing_device(const gchar *option_name, const gchar *optarg, gpointer data,
266  	                   GError **error)
267  	{
268  	    options.devices = g_list_append(options.devices, pcmk__str_copy(optarg));
269  	    return TRUE;
270  	}
271  	
272  	gboolean
273  	add_tolerance(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
274  	    // pcmk__request_fencing() expects an unsigned int
275  	    long long tolerance_ms = 0;
276  	
277  	    if ((pcmk__parse_ms(optarg, &tolerance_ms) != pcmk_rc_ok)
278  	        || (tolerance_ms < 0)) {
279  	
280  	        // @COMPAT Treat as an error and return FALSE?
281  	        pcmk__warn("Ignoring invalid tolerance '%s'", optarg);
282  	    } else {
283  	        options.tolerance_ms = (unsigned int) QB_MIN(tolerance_ms, UINT_MAX);
284  	    }
285  	    return TRUE;
286  	}
287  	
288  	gboolean
289  	add_fencing_params(const gchar *option_name, const gchar *optarg, gpointer data,
290  	                   GError **error)
291  	{
292  	    gchar *name = NULL;
293  	    gchar *value = NULL;
294  	    int rc = 0;
295  	    gboolean retval = TRUE;
296  	
297  	    pcmk__info("Scanning: -o %s", optarg);
298  	
299  	    rc = pcmk__scan_nvpair(optarg, &name, &value);
300  	
301  	    if (rc != pcmk_rc_ok) {
302  	        g_set_error(error, PCMK__RC_ERROR, rc, "Invalid option: -o %s: %s", optarg, pcmk_rc_str(rc));
303  	        retval = FALSE;
304  	    } else {
305  	        pcmk__info("Got: '%s'='%s'", name, value);
306  	
307  	        if (options.params == NULL) {
308  	            options.params = pcmk__strkey_table(free, free);
309  	        }
310  	
311  	        pcmk__insert_dup(options.params, name, value);
312  	    }
313  	
314  	    g_free(name);
315  	    g_free(value);
316  	    return retval;
317  	}
318  	
319  	gboolean
320  	set_tag(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
321  	    free(name);
322  	    name = pcmk__assert_asprintf("%s.%s", crm_system_name, optarg);
323  	    return TRUE;
324  	}
325  	
326  	static GOptionContext *
327  	build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
328  	    GOptionContext *context = NULL;
329  	
330  	    GOptionEntry extra_prog_entries[] = {
331  	        { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
332  	          "Be less descriptive in output.",
333  	          NULL },
334  	
335  	        { NULL }
336  	    };
337  	
338  	    context = pcmk__build_arg_context(args, "text (default), html, xml", group, NULL);
339  	
340  	    /* Add the -q option, which cannot be part of the globally supported options
341  	     * because some tools use that flag for something else.
342  	     */
343  	    pcmk__add_main_args(context, extra_prog_entries);
344  	
345  	    pcmk__add_arg_group(context, "definition", "Device Definition Commands:",
346  	                        "Show device definition help", defn_entries);
347  	    pcmk__add_arg_group(context, "queries", "Queries:",
348  	                        "Show query help", query_entries);
349  	    pcmk__add_arg_group(context, "fence", "Fencing Commands:",
350  	                        "Show fence help", fence_entries);
351  	    pcmk__add_arg_group(context, "additional", "Additional Options:",
352  	                        "Show additional options", addl_entries);
353  	    return context;
354  	}
355  	
356  	// \return Standard Pacemaker return code
357  	static int
358  	request_fencing(stonith_t *st, const char *target, const char *command,
359  	                GError **error)
360  	{
361  	    char *reason = NULL;
362  	    int rc = pcmk__request_fencing(st, target, command, name,
363  	                                   options.timeout * 1000,
364  	                                   options.tolerance_ms, options.delay,
365  	                                   &reason);
366  	
367  	    if (rc != pcmk_rc_ok) {
368  	        const char *rc_str = pcmk_rc_str(rc);
369  	        const char *what = "fence";
370  	
371  	        if (strcmp(command, PCMK_ACTION_ON) == 0) {
372  	            what = "unfence";
373  	        }
374  	
375  	        // If reason is identical to return code string, don't display it twice
376  	        if (pcmk__str_eq(rc_str, reason, pcmk__str_none)) {
377  	            g_clear_pointer(&reason, free);
378  	        }
379  	
380  	        g_set_error(error, PCMK__RC_ERROR, rc,
381  	                    "Couldn't %s %s: %s%s%s%s",
382  	                    what, target, rc_str,
383  	                    ((reason == NULL)? "" : " ("),
384  	                    ((reason == NULL)? "" : reason),
385  	                    ((reason == NULL)? "" : ")"));
386  	    }
387  	    free(reason);
388  	    return rc;
389  	}
390  	
391  	int
392  	main(int argc, char **argv)
393  	{
394  	    int rc = 0;
395  	    crm_exit_t exit_code = CRM_EX_OK;
396  	    bool no_connect = false;
397  	    bool required_agent = false;
398  	
399  	    char *target = NULL;
400  	    const char *device = NULL;
401  	    stonith_t *st = NULL;
402  	
403  	    GError *error = NULL;
404  	
405  	    pcmk__output_t *out = NULL;
406  	
407  	    GOptionGroup *output_group = NULL;
408  	    pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
409  	    gchar **processed_args = pcmk__cmdline_preproc(argv, "adehilorstvyBCDFHQRTU");
410  	    GOptionContext *context = build_arg_context(args, &output_group);
411  	
412  	    pcmk__register_formats(output_group, formats);
(1) Event path: Condition "!g_option_context_parse_strv(context, &processed_args, &error)", taking true branch.
413  	    if (!g_option_context_parse_strv(context, &processed_args, &error)) {
414  	        exit_code = CRM_EX_USAGE;
(2) Event path: Jumping to label "done".
415  	        goto done;
416  	    }
417  	
418  	    pcmk__cli_init_logging("stonith_admin", args->verbosity);
419  	
420  	    if (name == NULL) {
421  	        name = strdup(crm_system_name);
422  	    }
423  	
424  	    rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
425  	    if (rc != pcmk_rc_ok) {
426  	        exit_code = CRM_EX_ERROR;
427  	        g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
428  	                    args->output_ty, pcmk_rc_str(rc));
429  	        goto done;
430  	    }
431  	
432  	    pcmk__output_enable_list_element(out);
433  	
434  	    stonith__register_messages(out);
435  	
436  	    if (args->version) {
437  	        out->version(out);
438  	        goto done;
439  	    }
440  	
441  	    if (options.validate_cfg) {
442  	        required_agent = true;
443  	        no_connect = true;
444  	        action = 'K';
445  	    }
446  	
447  	    if (options.installed) {
448  	        no_connect = true;
449  	        action = 'I';
450  	    }
451  	
452  	    if (options.registered) {
453  	        action = 'L';
454  	    }
455  	
456  	    if (options.register_dev != NULL) {
457  	        required_agent = true;
458  	        action = 'R';
459  	        device = options.register_dev;
460  	    }
461  	
462  	    if (options.query != NULL) {
463  	        action = 'Q';
464  	        device = options.query;
465  	    }
466  	
467  	    if (options.unregister_dev != NULL) {
468  	        action = 'D';
469  	        device = options.unregister_dev;
470  	    }
471  	
472  	    if (options.targets != NULL) {
473  	        action = 's';
474  	        device = options.targets;
475  	    }
476  	
477  	    if (options.terminate != NULL) {
478  	        action = 'L';
479  	        target = options.terminate;
480  	    }
481  	
482  	    if (options.metadata) {
483  	        no_connect = true;
484  	        required_agent = true;
485  	        action = 'M';
486  	    }
487  	
488  	    if (options.reboot_host != NULL) {
489  	        no_connect = true;
490  	        action = 'B';
491  	        target = options.reboot_host;
492  	        crm_log_args(argc, argv);
493  	    }
494  	
495  	    if (options.fence_host != NULL) {
496  	        no_connect = true;
497  	        action = 'F';
498  	        target = options.fence_host;
499  	        crm_log_args(argc, argv);
500  	    }
501  	
502  	    if (options.unfence_host != NULL) {
503  	        no_connect = true;
504  	        action = 'U';
505  	        target = options.unfence_host;
506  	        crm_log_args(argc, argv);
507  	    }
508  	
509  	    if (options.confirm_host != NULL) {
510  	        action = 'C';
511  	        target = options.confirm_host;
512  	        crm_log_args(argc, argv);
513  	    }
514  	
515  	    if (options.last_fenced != NULL) {
516  	        action = 'h';
517  	        target = options.last_fenced;
518  	    }
519  	
520  	    if (options.history != NULL) {
521  	        action = 'H';
522  	        target = options.history;
523  	    }
524  	
525  	    if (options.register_level != NULL) {
526  	        action = 'r';
527  	        target = options.register_level;
528  	    }
529  	
530  	    if (options.unregister_level != NULL) {
531  	        action = 'd';
532  	        target = options.unregister_level;
533  	    }
534  	
535  	    if ((options.timeout > (UINT_MAX / 1000)) || (options.timeout < 0)) {
536  	        out->err(out, "Integer value \"%d\" for -t out of range", options.timeout);
537  	        exit_code = CRM_EX_USAGE;
538  	        goto done;
539  	    }
540  	
541  	    if (action == 0) {
542  	        char *help = g_option_context_get_help(context, TRUE, NULL);
543  	
544  	        out->err(out, "%s", help);
545  	        g_free(help);
546  	        exit_code = CRM_EX_USAGE;
547  	        goto done;
548  	    }
549  	
550  	    if (required_agent && options.agent == NULL) {
551  	        char *help = g_option_context_get_help(context, TRUE, NULL);
552  	
553  	        out->err(out, "Please specify an agent to query using -a,--agent [value]");
554  	        out->err(out, "%s", help);
555  	        g_free(help);
556  	        exit_code = CRM_EX_USAGE;
557  	        goto done;
558  	    }
559  	
560  	    out->quiet = args->quiet;
561  	
562  	    st = stonith__api_new();
563  	    if (st == NULL) {
564  	        rc = -ENOMEM;
565  	    } else if (!no_connect) {
566  	        rc = st->cmds->connect(st, name, NULL);
567  	    }
568  	    if (rc < 0) {
569  	        out->err(out, "Could not connect to fencer: %s", pcmk_strerror(rc));
570  	        exit_code = CRM_EX_DISCONNECT;
571  	        goto done;
572  	    }
573  	
574  	    switch (action) {
575  	        case 'I':
576  	            rc = pcmk__fence_installed(out, st);
577  	            if (rc != pcmk_rc_ok) {
578  	                out->err(out, "Failed to list installed devices: %s", pcmk_rc_str(rc));
579  	            }
580  	
581  	            break;
582  	
583  	        case 'L':
584  	            rc = pcmk__fence_registered(out, st, target, options.timeout*1000);
585  	            if (rc != pcmk_rc_ok) {
586  	                out->err(out, "Failed to list registered devices: %s", pcmk_rc_str(rc));
587  	            }
588  	
589  	            break;
590  	
591  	        case 'Q':
592  	            rc = st->cmds->monitor(st, st_opts, device, options.timeout);
593  	            if (rc != pcmk_rc_ok) {
594  	                rc = st->cmds->list(st, st_opts, device, NULL, options.timeout);
595  	            }
596  	            rc = pcmk_legacy2rc(rc);
597  	            break;
598  	
599  	        case 's':
600  	            rc = pcmk__fence_list_targets(out, st, device, options.timeout*1000);
601  	            if (rc != pcmk_rc_ok) {
602  	                out->err(out, "Couldn't list targets: %s", pcmk_rc_str(rc));
603  	            }
604  	
605  	            break;
606  	
607  	        case 'R': {
608  	            /* register_device wants a stonith_key_value_t instead of a GHashTable */
609  	            stonith_key_value_t *params = NULL;
610  	            GHashTableIter iter;
611  	            gpointer key, val;
612  	
613  	            if (options.params != NULL) {
614  	                g_hash_table_iter_init(&iter, options.params);
615  	                while (g_hash_table_iter_next(&iter, &key, &val)) {
616  	                    params = stonith__key_value_add(params, key, val);
617  	                }
618  	            }
619  	            rc = st->cmds->register_device(st, st_opts, device, NULL, options.agent,
620  	                                           params);
621  	            stonith__key_value_freeall(params, true, true);
622  	
623  	            rc = pcmk_legacy2rc(rc);
624  	            if (rc != pcmk_rc_ok) {
625  	                out->err(out, "Can't register device %s using agent %s: %s",
626  	                         device, options.agent, pcmk_rc_str(rc));
627  	            }
628  	            break;
629  	        }
630  	
631  	        case 'D':
632  	            rc = st->cmds->remove_device(st, st_opts, device);
633  	            rc = pcmk_legacy2rc(rc);
634  	            if (rc != pcmk_rc_ok) {
635  	                out->err(out, "Can't unregister device %s: %s",
636  	                         device, pcmk_rc_str(rc));
637  	            }
638  	            break;
639  	
640  	        case 'd':
641  	            rc = pcmk__fence_unregister_level(st, target, options.fence_level);
642  	            if (rc != pcmk_rc_ok) {
643  	                out->err(out, "Can't unregister topology level %d for %s: %s",
644  	                         options.fence_level, target, pcmk_rc_str(rc));
645  	            }
646  	            break;
647  	
648  	        case 'r':
649  	            rc = pcmk__fence_register_level(st, target, options.fence_level, options.devices);
650  	            if (rc != pcmk_rc_ok) {
651  	                out->err(out, "Can't register topology level %d for %s: %s",
652  	                         options.fence_level, target, pcmk_rc_str(rc));
653  	            }
654  	            break;
655  	
656  	        case 'M':
657  	            rc = pcmk__fence_metadata(out, st, options.agent, options.timeout*1000);
658  	            if (rc != pcmk_rc_ok) {
659  	                out->err(out, "Can't get fence agent meta-data: %s",
660  	                         pcmk_rc_str(rc));
661  	            }
662  	
663  	            break;
664  	
665  	        case 'C':
666  	            rc = st->cmds->confirm(st, st_opts, target);
667  	            rc = pcmk_legacy2rc(rc);
668  	            break;
669  	
670  	        case 'B':
671  	            rc = request_fencing(st, target, PCMK_ACTION_REBOOT, &error);
672  	            break;
673  	
674  	        case 'F':
675  	            rc = request_fencing(st, target, PCMK_ACTION_OFF, &error);
676  	            break;
677  	
678  	        case 'U':
679  	            rc = request_fencing(st, target, PCMK_ACTION_ON, &error);
680  	            break;
681  	
682  	        case 'h':
683  	            rc = pcmk__fence_last(out, target, options.as_nodeid);
684  	            break;
685  	
686  	        case 'H':
687  	            rc = pcmk__fence_history(out, st, target, options.timeout*1000, args->verbosity,
688  	                                     options.broadcast, options.cleanup);
689  	            break;
690  	
691  	        case 'K':
692  	            device = NULL;
693  	            if (options.devices != NULL) {
694  	                device = g_list_nth_data(options.devices, 0);
695  	            }
696  	
697  	            rc = pcmk__fence_validate(out, st, options.agent, device, options.params,
698  	                                        options.timeout*1000);
699  	            break;
700  	    }
701  	
702  	    pcmk__info("Command returned: %s (%d)", pcmk_rc_str(rc), rc);
703  	    exit_code = pcmk_rc2exitc(rc);
704  	
705  	  done:
706  	    g_strfreev(processed_args);
707  	    pcmk__free_arg_context(context);
708  	
709  	    pcmk__output_and_clear_error(&error, out);
710  	
(3) Event path: Condition "out != NULL", taking false branch.
711  	    if (out != NULL) {
712  	        out->finish(out, exit_code, true, NULL);
713  	        pcmk__output_free(out);
714  	    }
715  	    free(name);
716  	    g_list_free_full(options.devices, free);
717  	
CID (unavailable; MK=597af234f72efdc50948193ebb96fed0) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(4) Event assign_union_field: The union field "in" of "_pp" is written.
(5) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
718  	    g_clear_pointer(&options.params, g_hash_table_destroy);
719  	
720  	    if (st != NULL) {
721  	        st->cmds->disconnect(st);
722  	        stonith__api_free(st);
723  	    }
724  	
725  	    pcmk__unregister_formats();
726  	    return exit_code;
727  	}
728