This source file includes following definitions.
- attr_value_cb
- command_cb
- delete_attr_cb
- get_attr_cb
- grant_standby_cb
- set_attr_cb
- ticket_grant_warning
- ticket_revoke_warning
- build_arg_context
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <sys/param.h>
13
14 #include <crm/crm.h>
15
16 #include <stdio.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <libgen.h>
24
25 #include <crm/common/xml.h>
26 #include <crm/common/ipc.h>
27 #include <crm/common/cmdline_internal.h>
28
29 #include <crm/cib.h>
30 #include <crm/cib/internal.h>
31 #include <crm/pengine/status.h>
32 #include <crm/pengine/internal.h>
33
34 #include <pacemaker-internal.h>
35
36 GError *error = NULL;
37
38 #define SUMMARY "Perform tasks related to cluster tickets\n\n" \
39 "Allows ticket attributes to be queried, modified and deleted."
40
41 struct {
42 gchar *attr_default;
43 gchar *attr_id;
44 char *attr_name;
45 char *attr_value;
46 gboolean force;
47 char *get_attr_name;
48 gboolean quiet;
49 gchar *set_name;
50 char ticket_cmd;
51 gchar *ticket_id;
52 gchar *xml_file;
53 } options = {
54 .ticket_cmd = 'S'
55 };
56
57 GList *attr_delete;
58 GHashTable *attr_set;
59 bool modified = false;
60 int cib_options = cib_sync_call;
61 static pcmk__output_t *out = NULL;
62
63 #define INDENT " "
64
65 static pcmk__supported_format_t formats[] = {
66 PCMK__SUPPORTED_FORMAT_NONE,
67 PCMK__SUPPORTED_FORMAT_TEXT,
68 PCMK__SUPPORTED_FORMAT_XML,
69 { NULL, NULL, NULL }
70 };
71
72 static gboolean
73 attr_value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
74 pcmk__str_update(&options.attr_value, optarg);
75
76 if (!options.attr_name || !options.attr_value) {
77 return TRUE;
78 }
79
80 pcmk__insert_dup(attr_set, options.attr_name, options.attr_value);
81 pcmk__str_update(&options.attr_name, NULL);
82 pcmk__str_update(&options.attr_value, NULL);
83
84 modified = true;
85
86 return TRUE;
87 }
88
89 static gboolean
90 command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
91 if (pcmk__str_any_of(option_name, "--info", "-l", NULL)) {
92 options.ticket_cmd = 'l';
93 } else if (pcmk__str_any_of(option_name, "--details", "-L", NULL)) {
94 options.ticket_cmd = 'L';
95 } else if (pcmk__str_any_of(option_name, "--raw", "-w", NULL)) {
96 options.ticket_cmd = 'w';
97 } else if (pcmk__str_any_of(option_name, "--query-xml", "-q", NULL)) {
98 options.ticket_cmd = 'q';
99 } else if (pcmk__str_any_of(option_name, "--constraints", "-c", NULL)) {
100 options.ticket_cmd = 'c';
101 } else if (pcmk__str_any_of(option_name, "--cleanup", "-C", NULL)) {
102 options.ticket_cmd = 'C';
103 }
104
105 return TRUE;
106 }
107
108 static gboolean
109 delete_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
110 attr_delete = g_list_append(attr_delete, strdup(optarg));
111 modified = true;
112 return TRUE;
113 }
114
115 static gboolean
116 get_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
117 pcmk__str_update(&options.get_attr_name, optarg);
118 options.ticket_cmd = 'G';
119 return TRUE;
120 }
121
122 static gboolean
123 grant_standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
124 if (pcmk__str_any_of(option_name, "--grant", "-g", NULL)) {
125 pcmk__insert_dup(attr_set, PCMK__XA_GRANTED, PCMK_VALUE_TRUE);
126 modified = true;
127 } else if (pcmk__str_any_of(option_name, "--revoke", "-r", NULL)) {
128 pcmk__insert_dup(attr_set, PCMK__XA_GRANTED, PCMK_VALUE_FALSE);
129 modified = true;
130 } else if (pcmk__str_any_of(option_name, "--standby", "-s", NULL)) {
131 pcmk__insert_dup(attr_set, PCMK_XA_STANDBY, PCMK_VALUE_TRUE);
132 modified = true;
133 } else if (pcmk__str_any_of(option_name, "--activate", "-a", NULL)) {
134 pcmk__insert_dup(attr_set, PCMK_XA_STANDBY, PCMK_VALUE_FALSE);
135 modified = true;
136 }
137
138 return TRUE;
139 }
140
141 static gboolean
142 set_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
143 pcmk__str_update(&options.attr_name, optarg);
144
145 if (!options.attr_name || !options.attr_value) {
146 return TRUE;
147 }
148
149 pcmk__insert_dup(attr_set, options.attr_name, options.attr_value);
150 pcmk__str_update(&options.attr_name, NULL);
151 pcmk__str_update(&options.attr_value, NULL);
152
153 modified = true;
154
155 return TRUE;
156 }
157
158 static GOptionEntry query_entries[] = {
159 { "info", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
160 "Display the information of ticket(s)",
161 NULL },
162
163 { "details", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
164 "Display the details of ticket(s)",
165 NULL },
166
167 { "raw", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
168 "Display the IDs of ticket(s)",
169 NULL },
170
171 { "query-xml", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
172 "Query the XML of ticket(s)",
173 NULL },
174
175 { "constraints", 'c', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
176 "Display the " PCMK_XE_RSC_TICKET " constraints that apply to ticket(s)",
177 NULL },
178
179 { NULL }
180 };
181
182 static GOptionEntry command_entries[] = {
183 { "grant", 'g', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
184 "Grant a ticket to this cluster site",
185 NULL },
186
187 { "revoke", 'r', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
188 "Revoke a ticket from this cluster site",
189 NULL },
190
191 { "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
192 "Tell this cluster site this ticket is standby",
193 NULL },
194
195 { "activate", 'a', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
196 "Tell this cluster site this ticket is active",
197 NULL },
198
199 { NULL }
200 };
201
202 static GOptionEntry advanced_entries[] = {
203 { "get-attr", 'G', 0, G_OPTION_ARG_CALLBACK, get_attr_cb,
204 "Display the named attribute for a ticket",
205 "ATTRIBUTE" },
206
207 { "set-attr", 'S', 0, G_OPTION_ARG_CALLBACK, set_attr_cb,
208 "Set the named attribute for a ticket",
209 "ATTRIBUTE" },
210
211 { "delete-attr", 'D', 0, G_OPTION_ARG_CALLBACK, delete_attr_cb,
212 "Delete the named attribute for a ticket",
213 "ATTRIBUTE" },
214
215 { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
216 "Delete all state of a ticket at this cluster site",
217 NULL },
218
219 { NULL}
220 };
221
222 static GOptionEntry addl_entries[] = {
223 { "attr-value", 'v', 0, G_OPTION_ARG_CALLBACK, attr_value_cb,
224 "Attribute value to use with -S",
225 "VALUE" },
226
227 { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default,
228 "(Advanced) Default attribute value to display if none is found\n"
229 INDENT "(for use with -G)",
230 "VALUE" },
231
232 { "force", 'f', 0, G_OPTION_ARG_NONE, &options.force,
233 "(Advanced) Force the action to be performed",
234 NULL },
235
236 { "ticket", 't', 0, G_OPTION_ARG_STRING, &options.ticket_id,
237 "Ticket ID",
238 "ID" },
239
240 { "xml-file", 'x', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.xml_file,
241 NULL,
242 NULL },
243
244 { NULL }
245 };
246
247 static GOptionEntry deprecated_entries[] = {
248 { "set-name", 'n', 0, G_OPTION_ARG_STRING, &options.set_name,
249 "(Advanced) ID of the " PCMK_XE_INSTANCE_ATTRIBUTES " object to change",
250 "ID" },
251
252 { "nvpair", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id,
253 "(Advanced) ID of the nvpair object to change/delete",
254 "ID" },
255
256 { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &options.quiet,
257 "Print only the value on stdout",
258 NULL },
259
260 { NULL }
261 };
262
263 static void
264 ticket_grant_warning(gchar *ticket_id)
265 {
266 out->err(out, "This command cannot help you verify whether '%s' has "
267 "been already granted elsewhere.\n"
268 "If you really want to grant '%s' to this site now, and "
269 "you know what you are doing,\n"
270 "please specify --force.",
271 ticket_id, ticket_id);
272 }
273
274 static void
275 ticket_revoke_warning(gchar *ticket_id)
276 {
277 out->err(out, "Revoking '%s' can trigger the specified '" PCMK_XA_LOSS_POLICY
278 "'(s) relating to '%s'.\n\n"
279 "You can check that with:\n"
280 "crm_ticket --ticket %s --constraints\n\n"
281 "Otherwise before revoking '%s', you may want to make '%s'"
282 "standby with:\n"
283 "crm_ticket --ticket %s --standby\n\n"
284 "If you really want to revoke '%s' from this site now, and "
285 "you know what you are doing,\n"
286 "please specify --force.",
287 ticket_id, ticket_id, ticket_id, ticket_id, ticket_id,
288 ticket_id, ticket_id);
289 }
290
291 static GOptionContext *
292 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
293 {
294 GOptionContext *context = NULL;
295
296 const char *description = "Examples:\n\n"
297 "Display the info of tickets:\n\n"
298 "\tcrm_ticket --info\n\n"
299 "Display the detailed info of tickets:\n\n"
300 "\tcrm_ticket --details\n\n"
301 "Display the XML of 'ticketA':\n\n"
302 "\tcrm_ticket --ticket ticketA --query-xml\n\n"
303 "Display the " PCMK_XE_RSC_TICKET " constraints that apply to 'ticketA':\n\n"
304 "\tcrm_ticket --ticket ticketA --constraints\n\n"
305 "Grant 'ticketA' to this cluster site:\n\n"
306 "\tcrm_ticket --ticket ticketA --grant\n\n"
307 "Revoke 'ticketA' from this cluster site:\n\n"
308 "\tcrm_ticket --ticket ticketA --revoke\n\n"
309 "Make 'ticketA' standby (the cluster site will treat a granted\n"
310 "'ticketA' as 'standby', and the dependent resources will be\n"
311 "stopped or demoted gracefully without triggering loss-policies):\n\n"
312 "\tcrm_ticket --ticket ticketA --standby\n\n"
313 "Activate 'ticketA' from being standby:\n\n"
314 "\tcrm_ticket --ticket ticketA --activate\n\n"
315 "Get the value of the 'granted' attribute for 'ticketA':\n\n"
316 "\tcrm_ticket --ticket ticketA --get-attr granted\n\n"
317 "Set the value of the 'standby' attribute for 'ticketA':\n\n"
318 "\tcrm_ticket --ticket ticketA --set-attr standby --attr-value true\n\n"
319 "Delete the 'granted' attribute for 'ticketA':\n\n"
320 "\tcrm_ticket --ticket ticketA --delete-attr granted\n\n"
321 "Erase the operation history of 'ticketA' at this cluster site,\n"
322 "causing the cluster site to 'forget' the existing ticket state:\n\n"
323 "\tcrm_ticket --ticket ticketA --cleanup\n\n";
324
325 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
326 g_option_context_set_description(context, description);
327
328 pcmk__add_arg_group(context, "queries", "Queries:",
329 "Show queries", query_entries);
330 pcmk__add_arg_group(context, "commands", "Commands:",
331 "Show command options", command_entries);
332 pcmk__add_arg_group(context, "advanced", "Advanced Options:",
333 "Show advanced options", advanced_entries);
334 pcmk__add_arg_group(context, "additional", "Additional Options:",
335 "Show additional options", addl_entries);
336 pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
337 "Show deprecated options", deprecated_entries);
338
339 return context;
340 }
341
342 int
343 main(int argc, char **argv)
344 {
345 pcmk_scheduler_t *scheduler = NULL;
346 xmlNode *cib_xml_copy = NULL;
347
348 cib_t *cib_conn = NULL;
349 crm_exit_t exit_code = CRM_EX_OK;
350 int rc = pcmk_rc_ok;
351
352 GOptionGroup *output_group = NULL;
353 pcmk__common_args_t *args = NULL;
354 GOptionContext *context = NULL;
355 gchar **processed_args = NULL;
356
357 attr_set = pcmk__strkey_table(free, free);
358 attr_delete = NULL;
359
360 args = pcmk__new_common_args(SUMMARY);
361 context = build_arg_context(args, &output_group);
362 processed_args = pcmk__cmdline_preproc(argv, "dintvxCDGS");
363
364 pcmk__register_formats(output_group, formats);
365 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
366 exit_code = CRM_EX_USAGE;
367 goto done;
368 }
369
370 pcmk__cli_init_logging("crm_ticket", args->verbosity);
371
372 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
373 if (rc != pcmk_rc_ok) {
374 exit_code = pcmk_rc2exitc(rc);
375 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
376 "Error creating output format %s: %s", args->output_ty,
377 pcmk_rc_str(rc));
378 goto done;
379 }
380
381 pe__register_messages(out);
382 pcmk__register_lib_messages(out);
383
384 if (args->version) {
385 out->version(out, false);
386 goto done;
387 }
388
389 scheduler = pcmk_new_scheduler();
390 if (scheduler == NULL) {
391 rc = errno;
392 exit_code = pcmk_rc2exitc(rc);
393 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
394 "Could not allocate scheduler data: %s", pcmk_rc_str(rc));
395 goto done;
396 }
397 pcmk__set_scheduler_flags(scheduler, pcmk__sched_no_counts);
398
399 cib_conn = cib_new();
400 if (cib_conn == NULL) {
401 exit_code = CRM_EX_DISCONNECT;
402 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not connect to the CIB manager");
403 goto done;
404 }
405
406 rc = cib__signon_attempts(cib_conn, cib_command, 5);
407 rc = pcmk_legacy2rc(rc);
408
409 if (rc != pcmk_rc_ok) {
410 exit_code = pcmk_rc2exitc(rc);
411 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not connect to the CIB: %s",
412 pcmk_rc_str(rc));
413 goto done;
414 }
415
416 if (options.xml_file != NULL) {
417 cib_xml_copy = pcmk__xml_read(options.xml_file);
418
419 } else {
420 rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml_copy,
421 cib_sync_call);
422 rc = pcmk_legacy2rc(rc);
423
424 if (rc != pcmk_rc_ok) {
425 exit_code = pcmk_rc2exitc(rc);
426 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not get local CIB: %s",
427 pcmk_rc_str(rc));
428 goto done;
429 }
430 }
431
432 rc = pcmk__update_configured_schema(&cib_xml_copy, false);
433 if (rc != pcmk_rc_ok) {
434 exit_code = pcmk_rc2exitc(rc);
435 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
436 "Could not update local CIB to latest schema version");
437 goto done;
438 }
439
440 scheduler->input = cib_xml_copy;
441 scheduler->priv->now = crm_time_new(NULL);
442
443 cluster_status(scheduler);
444
445
446
447
448 pcmk__unpack_constraints(scheduler);
449
450 if (options.ticket_cmd == 'l' || options.ticket_cmd == 'L' || options.ticket_cmd == 'w') {
451 bool raw = false;
452 bool details = false;
453
454 if (options.ticket_cmd == 'L') {
455 details = true;
456 } else if (options.ticket_cmd == 'w') {
457 raw = true;
458 }
459
460 rc = pcmk__ticket_info(out, scheduler, options.ticket_id, details, raw);
461 exit_code = pcmk_rc2exitc(rc);
462
463 if (rc == ENXIO) {
464 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
465 "No such ticket '%s'", options.ticket_id);
466 } else if (rc != pcmk_rc_ok) {
467 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
468 "Could not get ticket info: %s", pcmk_rc_str(rc));
469 }
470
471 } else if (options.ticket_cmd == 'q') {
472 rc = pcmk__ticket_state(out, cib_conn, options.ticket_id);
473
474 if (rc != pcmk_rc_ok && rc != pcmk_rc_duplicate_id) {
475 exit_code = pcmk_rc2exitc(rc);
476 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
477 "Could not query ticket XML: %s", pcmk_rc_str(rc));
478 } else {
479 exit_code = CRM_EX_OK;
480 }
481
482 } else if (options.ticket_cmd == 'c') {
483 rc = pcmk__ticket_constraints(out, cib_conn, options.ticket_id);
484 exit_code = pcmk_rc2exitc(rc);
485
486 if (rc != pcmk_rc_ok) {
487 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
488 "Could not show ticket constraints: %s", pcmk_rc_str(rc));
489 }
490
491 } else if (options.ticket_cmd == 'G') {
492 if (options.ticket_id == NULL) {
493 exit_code = CRM_EX_NOSUCH;
494 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
495 "Must supply ticket ID with -t");
496 goto done;
497 }
498
499 rc = pcmk__ticket_get_attr(out, scheduler, options.ticket_id,
500 options.get_attr_name, options.attr_default);
501 exit_code = pcmk_rc2exitc(rc);
502
503 } else if (options.ticket_cmd == 'C') {
504 if (options.ticket_id == NULL) {
505 exit_code = CRM_EX_USAGE;
506 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
507 "Must supply ticket ID with -t");
508 goto done;
509 }
510
511 rc = pcmk__ticket_delete(out, cib_conn, scheduler, options.ticket_id,
512 options.force);
513 exit_code = pcmk_rc2exitc(rc);
514
515 switch (rc) {
516 case ENXIO:
517 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
518 "No such ticket '%s'", options.ticket_id);
519 break;
520
521 case EACCES:
522 ticket_revoke_warning(options.ticket_id);
523 break;
524
525 case pcmk_rc_ok:
526 case pcmk_rc_duplicate_id:
527 break;
528
529 default:
530 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
531 "Could not clean up ticket: %s", pcmk_rc_str(rc));
532 break;
533 }
534
535 } else if (modified) {
536 if (options.ticket_id == NULL) {
537 exit_code = CRM_EX_USAGE;
538 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
539 "Must supply ticket ID with -t");
540 goto done;
541 }
542
543 if (options.attr_value
544 && (pcmk__str_empty(options.attr_name))) {
545 exit_code = CRM_EX_USAGE;
546 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
547 "Must supply attribute name with -S for -v %s", options.attr_value);
548 goto done;
549 }
550
551 if (options.attr_name
552 && (pcmk__str_empty(options.attr_value))) {
553 exit_code = CRM_EX_USAGE;
554 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
555 "Must supply attribute value with -v for -S %s", options.attr_value);
556 goto done;
557 }
558
559 if (attr_delete != NULL) {
560 rc = pcmk__ticket_remove_attr(out, cib_conn, scheduler, options.ticket_id,
561 attr_delete, options.force);
562
563 if (rc == EACCES) {
564 ticket_revoke_warning(options.ticket_id);
565 exit_code = CRM_EX_UNSAFE;
566 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
567 "Ticket modification not allowed without --force");
568 goto done;
569 }
570 } else {
571 rc = pcmk__ticket_set_attr(out, cib_conn, scheduler, options.ticket_id,
572 attr_set, options.force);
573
574 if (rc == EACCES) {
575 const char *value = NULL;
576
577 value = g_hash_table_lookup(attr_set, PCMK__XA_GRANTED);
578 if (crm_is_true(value)) {
579 ticket_grant_warning(options.ticket_id);
580 } else {
581 ticket_revoke_warning(options.ticket_id);
582 }
583
584 exit_code = CRM_EX_UNSAFE;
585 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
586 "Ticket modification not allowed without --force");
587 goto done;
588 }
589 }
590
591 exit_code = pcmk_rc2exitc(rc);
592
593 if (rc != pcmk_rc_ok && error == NULL) {
594 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
595 "Could not modify ticket: %s", pcmk_rc_str(rc));
596 }
597
598 } else if (options.ticket_cmd == 'S') {
599
600
601
602
603 if (pcmk__str_empty(options.attr_name)) {
604
605 exit_code = CRM_EX_USAGE;
606 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Must supply a command");
607 goto done;
608 }
609
610 if (options.ticket_id == NULL) {
611 exit_code = CRM_EX_USAGE;
612 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
613 "Must supply ticket ID with -t");
614 goto done;
615 }
616
617 if (pcmk__str_empty(options.attr_value)) {
618 exit_code = CRM_EX_USAGE;
619 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
620 "Must supply value with -v for -S %s", options.attr_name);
621 goto done;
622 }
623
624 } else {
625 exit_code = CRM_EX_USAGE;
626 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
627 "Unknown command: %c", options.ticket_cmd);
628 }
629
630 done:
631 if (attr_set) {
632 g_hash_table_destroy(attr_set);
633 }
634 attr_set = NULL;
635
636 if (attr_delete) {
637 g_list_free_full(attr_delete, free);
638 }
639 attr_delete = NULL;
640
641 pcmk_free_scheduler(scheduler);
642 scheduler = NULL;
643
644 cib__clean_up_connection(&cib_conn);
645
646 g_strfreev(processed_args);
647 pcmk__free_arg_context(context);
648 g_free(options.attr_default);
649 g_free(options.attr_id);
650 free(options.attr_name);
651 free(options.attr_value);
652 free(options.get_attr_name);
653 g_free(options.set_name);
654 g_free(options.ticket_id);
655 g_free(options.xml_file);
656
657 pcmk__output_and_clear_error(&error, out);
658
659 if (out != NULL) {
660 out->finish(out, exit_code, true, NULL);
661 pcmk__output_free(out);
662 }
663
664 pcmk__unregister_formats();
665 crm_exit(exit_code);
666 }