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
|
(1) Event path: |
Condition "rc != pcmk_rc_ok", taking true branch. |
367 if (rc != pcmk_rc_ok) {
368 const char *rc_str = pcmk_rc_str(rc);
369 const char *what = "fence";
370
|
(2) Event path: |
Condition "strcmp(command, "on") == 0", taking true branch. |
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
|
(3) Event path: |
Condition "pcmk__str_eq(rc_str, reason, pcmk__str_none)", taking true branch. |
376 if (pcmk__str_eq(rc_str, reason, pcmk__str_none)) {
|
CID (unavailable; MK=8fce5275529d1a7de757fa43c1c57f9d) (#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". |
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);
413 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
414 exit_code = CRM_EX_USAGE;
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
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
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