This source file includes following definitions.
- find_ticket
- print_date
- print_ticket
- print_ticket_list
- find_ticket_state
- find_ticket_constraints
- dump_ticket_xml
- dump_constraints
- get_ticket_state_attr
- ticket_warning
- allow_modification
- modify_ticket_state
- delete_ticket_state
- 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/msg_xml.h>
26 #include <crm/common/xml.h>
27 #include <crm/common/ipc.h>
28
29 #include <crm/cib.h>
30 #include <crm/pengine/rules.h>
31 #include <crm/pengine/status.h>
32
33 #include <pacemaker-internal.h>
34
35 gboolean do_force = FALSE;
36 gboolean BE_QUIET = FALSE;
37 const char *ticket_id = NULL;
38 const char *get_attr_name = NULL;
39 const char *attr_name = NULL;
40 const char *attr_value = NULL;
41 const char *attr_id = NULL;
42 const char *set_name = NULL;
43 const char *attr_default = NULL;
44 const char *xml_file = NULL;
45 char ticket_cmd = 'S';
46 int cib_options = cib_sync_call;
47
48 static pe_ticket_t *
49 find_ticket(const char *ticket_id, pe_working_set_t * data_set)
50 {
51 pe_ticket_t *ticket = NULL;
52
53 ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
54
55 return ticket;
56 }
57
58 static void
59 print_date(time_t time)
60 {
61 int lpc = 0;
62 char date_str[26];
63
64 asctime_r(localtime(&time), date_str);
65 for (; lpc < 26; lpc++) {
66 if (date_str[lpc] == '\n') {
67 date_str[lpc] = 0;
68 }
69 }
70 fprintf(stdout, "'%s'", date_str);
71 }
72
73 static int
74 print_ticket(pe_ticket_t * ticket, gboolean raw, gboolean details)
75 {
76 if (raw) {
77 fprintf(stdout, "%s\n", ticket->id);
78 return pcmk_ok;
79 }
80
81 fprintf(stdout, "%s\t%s %s",
82 ticket->id, ticket->granted ? "granted" : "revoked",
83 ticket->standby ? "[standby]" : " ");
84
85 if (details && g_hash_table_size(ticket->state) > 0) {
86 GHashTableIter iter;
87 const char *name = NULL;
88 const char *value = NULL;
89 int lpc = 0;
90
91 fprintf(stdout, " (");
92
93 g_hash_table_iter_init(&iter, ticket->state);
94 while (g_hash_table_iter_next(&iter, (void **)&name, (void **)&value)) {
95 if (lpc > 0) {
96 fprintf(stdout, ", ");
97 }
98 fprintf(stdout, "%s=", name);
99 if (pcmk__str_any_of(name, "last-granted", "expires", NULL)) {
100 long long time_ll;
101
102 pcmk__scan_ll(value, &time_ll, 0);
103 print_date((time_t) time_ll);
104 } else {
105 fprintf(stdout, "%s", value);
106 }
107 lpc++;
108 }
109
110 fprintf(stdout, ")\n");
111
112 } else {
113 if (ticket->last_granted > -1) {
114 fprintf(stdout, " last-granted=");
115 print_date(ticket->last_granted);
116 }
117 fprintf(stdout, "\n");
118 }
119
120 return pcmk_ok;
121 }
122
123 static int
124 print_ticket_list(pe_working_set_t * data_set, gboolean raw, gboolean details)
125 {
126 GHashTableIter iter;
127 pe_ticket_t *ticket = NULL;
128
129 g_hash_table_iter_init(&iter, data_set->tickets);
130
131 while (g_hash_table_iter_next(&iter, NULL, (void **)&ticket)) {
132 print_ticket(ticket, raw, details);
133 }
134
135 return pcmk_ok;
136 }
137
138 #define XPATH_MAX 1024
139
140 static int
141 find_ticket_state(cib_t * the_cib, const char *ticket_id, xmlNode ** ticket_state_xml)
142 {
143 int offset = 0;
144 int rc = pcmk_ok;
145 xmlNode *xml_search = NULL;
146
147 char *xpath_string = NULL;
148
149 CRM_ASSERT(ticket_state_xml != NULL);
150 *ticket_state_xml = NULL;
151
152 xpath_string = calloc(1, XPATH_MAX);
153 offset += snprintf(xpath_string + offset, XPATH_MAX - offset, "%s", "/cib/status/tickets");
154
155 if (ticket_id) {
156 offset += snprintf(xpath_string + offset, XPATH_MAX - offset, "/%s[@id=\"%s\"]",
157 XML_CIB_TAG_TICKET_STATE, ticket_id);
158 }
159
160 CRM_LOG_ASSERT(offset > 0);
161 rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search,
162 cib_sync_call | cib_scope_local | cib_xpath);
163
164 if (rc != pcmk_ok) {
165 goto done;
166 }
167
168 crm_log_xml_debug(xml_search, "Match");
169 if (xml_has_children(xml_search)) {
170 if (ticket_id) {
171 fprintf(stdout, "Multiple ticket_states match ticket_id=%s\n", ticket_id);
172 }
173 *ticket_state_xml = xml_search;
174 } else {
175 *ticket_state_xml = xml_search;
176 }
177
178 done:
179 free(xpath_string);
180 return rc;
181 }
182
183 static int
184 find_ticket_constraints(cib_t * the_cib, const char *ticket_id, xmlNode ** ticket_cons_xml)
185 {
186 int offset = 0;
187 int rc = pcmk_ok;
188 xmlNode *xml_search = NULL;
189
190 char *xpath_string = NULL;
191
192 CRM_ASSERT(ticket_cons_xml != NULL);
193 *ticket_cons_xml = NULL;
194
195 xpath_string = calloc(1, XPATH_MAX);
196 offset +=
197 snprintf(xpath_string + offset, XPATH_MAX - offset, "%s/%s",
198 get_object_path(XML_CIB_TAG_CONSTRAINTS), XML_CONS_TAG_RSC_TICKET);
199
200 if (ticket_id) {
201 offset += snprintf(xpath_string + offset, XPATH_MAX - offset, "[@ticket=\"%s\"]",
202 ticket_id);
203 }
204
205 CRM_LOG_ASSERT(offset > 0);
206 rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search,
207 cib_sync_call | cib_scope_local | cib_xpath);
208
209 if (rc != pcmk_ok) {
210 goto done;
211 }
212
213 crm_log_xml_debug(xml_search, "Match");
214 *ticket_cons_xml = xml_search;
215
216 done:
217 free(xpath_string);
218 return rc;
219 }
220
221 static int
222 dump_ticket_xml(cib_t * the_cib, const char *ticket_id)
223 {
224 int rc = pcmk_ok;
225 xmlNode *state_xml = NULL;
226
227 rc = find_ticket_state(the_cib, ticket_id, &state_xml);
228
229 if (state_xml == NULL) {
230 return rc;
231 }
232
233 fprintf(stdout, "State XML:\n");
234 if (state_xml) {
235 char *state_xml_str = NULL;
236
237 state_xml_str = dump_xml_formatted(state_xml);
238 fprintf(stdout, "\n%s\n", crm_str(state_xml_str));
239 free_xml(state_xml);
240 free(state_xml_str);
241 }
242
243 return pcmk_ok;
244 }
245
246 static int
247 dump_constraints(cib_t * the_cib, const char *ticket_id)
248 {
249 int rc = pcmk_ok;
250 xmlNode *cons_xml = NULL;
251 char *cons_xml_str = NULL;
252
253 rc = find_ticket_constraints(the_cib, ticket_id, &cons_xml);
254
255 if (cons_xml == NULL) {
256 return rc;
257 }
258
259 cons_xml_str = dump_xml_formatted(cons_xml);
260 fprintf(stdout, "Constraints XML:\n\n%s\n", crm_str(cons_xml_str));
261 free_xml(cons_xml);
262 free(cons_xml_str);
263
264 return pcmk_ok;
265 }
266
267 static int
268 get_ticket_state_attr(const char *ticket_id, const char *attr_name, const char **attr_value,
269 pe_working_set_t * data_set)
270 {
271 pe_ticket_t *ticket = NULL;
272
273 CRM_ASSERT(attr_value != NULL);
274 *attr_value = NULL;
275
276 ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
277 if (ticket == NULL) {
278 return -ENXIO;
279 }
280
281 *attr_value = g_hash_table_lookup(ticket->state, attr_name);
282 if (*attr_value == NULL) {
283 return -ENXIO;
284 }
285
286 return pcmk_ok;
287 }
288
289 static gboolean
290 ticket_warning(const char *ticket_id, const char *action)
291 {
292 gboolean rc = FALSE;
293 int offset = 0;
294 static int text_max = 1024;
295
296 char *warning = NULL;
297 const char *word = NULL;
298
299 warning = calloc(1, text_max);
300 if (pcmk__str_eq(action, "grant", pcmk__str_casei)) {
301 offset += snprintf(warning + offset, text_max - offset,
302 "This command cannot help you verify whether '%s' has been already granted elsewhere.\n",
303 ticket_id);
304 word = "to";
305
306 } else {
307 offset += snprintf(warning + offset, text_max - offset,
308 "Revoking '%s' can trigger the specified 'loss-policy'(s) relating to '%s'.\n\n",
309 ticket_id, ticket_id);
310
311 offset += snprintf(warning + offset, text_max - offset,
312 "You can check that with:\ncrm_ticket --ticket %s --constraints\n\n",
313 ticket_id);
314
315 offset += snprintf(warning + offset, text_max - offset,
316 "Otherwise before revoking '%s', you may want to make '%s' standby with:\ncrm_ticket --ticket %s --standby\n\n",
317 ticket_id, ticket_id, ticket_id);
318 word = "from";
319 }
320
321 offset += snprintf(warning + offset, text_max - offset,
322 "If you really want to %s '%s' %s this site now, and you know what you are doing,\n",
323 action, ticket_id, word);
324
325 offset += snprintf(warning + offset, text_max - offset,
326 "please specify --force.");
327
328 CRM_LOG_ASSERT(offset > 0);
329 fprintf(stdout, "%s\n", warning);
330
331 free(warning);
332 return rc;
333 }
334
335 static gboolean
336 allow_modification(const char *ticket_id, GList *attr_delete,
337 GHashTable *attr_set)
338 {
339 const char *value = NULL;
340 GList *list_iter = NULL;
341
342 if (do_force) {
343 return TRUE;
344 }
345
346 if (g_hash_table_lookup_extended(attr_set, "granted", NULL, (gpointer *) & value)) {
347 if (crm_is_true(value)) {
348 ticket_warning(ticket_id, "grant");
349 return FALSE;
350
351 } else {
352 ticket_warning(ticket_id, "revoke");
353 return FALSE;
354 }
355 }
356
357 for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) {
358 const char *key = (const char *)list_iter->data;
359
360 if (pcmk__str_eq(key, "granted", pcmk__str_casei)) {
361 ticket_warning(ticket_id, "revoke");
362 return FALSE;
363 }
364 }
365
366 return TRUE;
367 }
368
369 static int
370 modify_ticket_state(const char * ticket_id, GList *attr_delete, GHashTable * attr_set,
371 cib_t * cib, pe_working_set_t * data_set)
372 {
373 int rc = pcmk_ok;
374 xmlNode *xml_top = NULL;
375 xmlNode *ticket_state_xml = NULL;
376 gboolean found = FALSE;
377
378 GList *list_iter = NULL;
379 GHashTableIter hash_iter;
380
381 char *key = NULL;
382 char *value = NULL;
383
384 pe_ticket_t *ticket = NULL;
385
386 rc = find_ticket_state(cib, ticket_id, &ticket_state_xml);
387 if (rc == pcmk_ok) {
388 crm_debug("Found a match state for ticket: id=%s", ticket_id);
389 xml_top = ticket_state_xml;
390 found = TRUE;
391
392 } else if (rc != -ENXIO) {
393 return rc;
394
395 } else if (g_hash_table_size(attr_set) == 0){
396 return pcmk_ok;
397
398 } else {
399 xmlNode *xml_obj = NULL;
400
401 xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
402 xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS);
403 ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE);
404 crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id);
405 }
406
407 for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) {
408 const char *key = (const char *)list_iter->data;
409 xml_remove_prop(ticket_state_xml, key);
410 }
411
412 ticket = find_ticket(ticket_id, data_set);
413
414 g_hash_table_iter_init(&hash_iter, attr_set);
415 while (g_hash_table_iter_next(&hash_iter, (gpointer *) & key, (gpointer *) & value)) {
416 crm_xml_add(ticket_state_xml, key, value);
417
418 if (pcmk__str_eq(key, "granted", pcmk__str_casei)
419 && (ticket == NULL || ticket->granted == FALSE)
420 && crm_is_true(value)) {
421
422 char *now = pcmk__ttoa(time(NULL));
423
424 crm_xml_add(ticket_state_xml, "last-granted", now);
425 free(now);
426 }
427 }
428
429 if (found && (attr_delete != NULL)) {
430 crm_log_xml_debug(xml_top, "Replace");
431 rc = cib->cmds->replace(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options);
432
433 } else {
434 crm_log_xml_debug(xml_top, "Update");
435 rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, cib_options);
436 }
437
438 free_xml(xml_top);
439 return rc;
440 }
441
442 static int
443 delete_ticket_state(const char *ticket_id, cib_t * cib)
444 {
445 xmlNode *ticket_state_xml = NULL;
446
447 int rc = pcmk_ok;
448
449 rc = find_ticket_state(cib, ticket_id, &ticket_state_xml);
450
451 if (rc == -ENXIO) {
452 return pcmk_ok;
453
454 } else if (rc != pcmk_ok) {
455 return rc;
456 }
457
458 crm_log_xml_debug(ticket_state_xml, "Delete");
459
460 rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options);
461
462 if (rc == pcmk_ok) {
463 fprintf(stdout, "Cleaned up %s\n", ticket_id);
464 }
465
466 free_xml(ticket_state_xml);
467 return rc;
468 }
469
470 static pcmk__cli_option_t long_options[] = {
471
472 {
473 "help", no_argument, NULL, '?',
474 "\t\tThis text", pcmk__option_default
475 },
476 {
477 "version", no_argument, NULL, '$',
478 "\t\tVersion information", pcmk__option_default
479 },
480 {
481 "verbose", no_argument, NULL, 'V',
482 "\t\tIncrease debug output", pcmk__option_default
483 },
484 {
485 "quiet", no_argument, NULL, 'Q',
486 "\t\tPrint only the value on stdout\n", pcmk__option_default
487 },
488 {
489 "ticket", required_argument, NULL, 't',
490 "\tTicket ID", pcmk__option_default
491 },
492 {
493 "-spacer-", no_argument, NULL, '-',
494 "\nQueries:", pcmk__option_default
495 },
496 {
497 "info", no_argument, NULL, 'l',
498 "\t\tDisplay the information of ticket(s)", pcmk__option_default
499 },
500 {
501 "details", no_argument, NULL, 'L',
502 "\t\tDisplay the details of ticket(s)", pcmk__option_default
503 },
504 {
505 "raw", no_argument, NULL, 'w',
506 "\t\tDisplay the IDs of ticket(s)", pcmk__option_default
507 },
508 {
509 "query-xml", no_argument, NULL, 'q',
510 "\tQuery the XML of ticket(s)", pcmk__option_default
511 },
512 {
513 "constraints", no_argument, NULL, 'c',
514 "\tDisplay the rsc_ticket constraints that apply to ticket(s)",
515 pcmk__option_default
516 },
517 {
518 "-spacer-", no_argument, NULL, '-',
519 "\nCommands:", pcmk__option_default
520 },
521 {
522 "grant", no_argument, NULL, 'g',
523 "\t\tGrant a ticket to this cluster site", pcmk__option_default
524 },
525 {
526 "revoke", no_argument, NULL, 'r',
527 "\t\tRevoke a ticket from this cluster site", pcmk__option_default
528 },
529 {
530 "standby", no_argument, NULL, 's',
531 "\t\tTell this cluster site this ticket is standby",
532 pcmk__option_default
533 },
534 {
535 "activate", no_argument, NULL, 'a',
536 "\tTell this cluster site this ticket is active", pcmk__option_default
537 },
538 {
539 "-spacer-", no_argument, NULL, '-',
540 "\nAdvanced Commands:", pcmk__option_default
541 },
542 {
543 "get-attr", required_argument, NULL, 'G',
544 "\tDisplay the named attribute for a ticket", pcmk__option_default
545 },
546 {
547 "set-attr", required_argument, NULL, 'S',
548 "\tSet the named attribute for a ticket", pcmk__option_default
549 },
550 {
551 "delete-attr", required_argument, NULL, 'D',
552 "\tDelete the named attribute for a ticket", pcmk__option_default
553 },
554 {
555 "cleanup", no_argument, NULL, 'C',
556 "\t\tDelete all state of a ticket at this cluster site",
557 pcmk__option_default
558 },
559 {
560 "-spacer-", no_argument, NULL, '-',
561 "\nAdditional Options:", pcmk__option_default
562 },
563 {
564 "attr-value", required_argument, NULL, 'v',
565 "\tAttribute value to use with -S", pcmk__option_default
566 },
567 {
568 "default", required_argument, NULL, 'd',
569 "\t(Advanced) Default attribute value to display if none is found "
570 "(for use with -G)",
571 pcmk__option_default
572 },
573 {
574 "force", no_argument, NULL, 'f',
575 "\t\t(Advanced) Force the action to be performed", pcmk__option_default
576 },
577 {
578 "xml-file", required_argument, NULL, 'x',
579 NULL, pcmk__option_hidden
580 },
581
582
583 {
584 "set-name", required_argument, NULL, 'n',
585 "\t(Advanced) ID of the instance_attributes object to change",
586 pcmk__option_default
587 },
588 {
589 "nvpair", required_argument, NULL, 'i',
590 "\t(Advanced) ID of the nvpair object to change/delete",
591 pcmk__option_default
592 },
593 {
594 "-spacer-", no_argument, NULL, '-',
595 "\nExamples:", pcmk__option_paragraph
596 },
597 {
598 "-spacer-", no_argument, NULL, '-',
599 "Display the info of tickets:", pcmk__option_paragraph
600 },
601 {
602 "-spacer-", no_argument, NULL, '-',
603 " crm_ticket --info", pcmk__option_example
604 },
605 {
606 "-spacer-", no_argument, NULL, '-',
607 "Display the detailed info of tickets:", pcmk__option_paragraph
608 },
609 {
610 "-spacer-", no_argument, NULL, '-',
611 " crm_ticket --details", pcmk__option_example
612 },
613 {
614 "-spacer-", no_argument, NULL, '-',
615 "Display the XML of 'ticketA':", pcmk__option_paragraph
616 },
617 {
618 "-spacer-", no_argument, NULL, '-',
619 " crm_ticket --ticket ticketA --query-xml", pcmk__option_example
620 },
621 {
622 "-spacer-", no_argument, NULL, '-',
623 "Display the rsc_ticket constraints that apply to 'ticketA':",
624 pcmk__option_paragraph
625 },
626 {
627 "-spacer-", no_argument, NULL, '-',
628 " crm_ticket --ticket ticketA --constraints", pcmk__option_example
629 },
630 {
631 "-spacer-", no_argument, NULL, '-',
632 "Grant 'ticketA' to this cluster site:", pcmk__option_paragraph
633 },
634 {
635 "-spacer-", no_argument, NULL, '-',
636 " crm_ticket --ticket ticketA --grant", pcmk__option_example
637 },
638 {
639 "-spacer-", no_argument, NULL, '-',
640 "Revoke 'ticketA' from this cluster site:", pcmk__option_paragraph
641 },
642 {
643 "-spacer-", no_argument, NULL, '-',
644 " crm_ticket --ticket ticketA --revoke", pcmk__option_example
645 },
646 {
647 "-spacer-", no_argument, NULL, '-',
648 "Make 'ticketA' standby (the cluster site will treat a granted "
649 "'ticketA' as 'standby', and the dependent resources will be "
650 "stopped or demoted gracefully without triggering loss-policies):",
651 pcmk__option_paragraph
652 },
653 {
654 "-spacer-", no_argument, NULL, '-',
655 " crm_ticket --ticket ticketA --standby", pcmk__option_example
656 },
657 {
658 "-spacer-", no_argument, NULL, '-',
659 "Activate 'ticketA' from being standby:", pcmk__option_paragraph
660 },
661 {
662 "-spacer-", no_argument, NULL, '-',
663 " crm_ticket --ticket ticketA --activate", pcmk__option_example
664 },
665 {
666 "-spacer-", no_argument, NULL, '-',
667 "Get the value of the 'granted' attribute for 'ticketA':",
668 pcmk__option_paragraph
669 },
670 {
671 "-spacer-", no_argument, NULL, '-',
672 " crm_ticket --ticket ticketA --get-attr granted", pcmk__option_example
673 },
674 {
675 "-spacer-", no_argument, NULL, '-',
676 "Set the value of the 'standby' attribute for 'ticketA':",
677 pcmk__option_paragraph
678 },
679 {
680 "-spacer-", no_argument, NULL, '-',
681 " crm_ticket --ticket ticketA --set-attr standby --attr-value true",
682 pcmk__option_example
683 },
684 {
685 "-spacer-", no_argument, NULL, '-',
686 "Delete the 'granted' attribute for 'ticketA':", pcmk__option_paragraph
687 },
688 {
689 "-spacer-", no_argument, NULL, '-',
690 " crm_ticket --ticket ticketA --delete-attr granted",
691 pcmk__option_example
692 },
693 {
694 "-spacer-", no_argument, NULL, '-',
695 "Erase the operation history of 'ticketA' at this cluster site:",
696 pcmk__option_paragraph
697 },
698 {
699 "-spacer-", no_argument, NULL, '-',
700 "The cluster site will 'forget' the existing ticket state.",
701 pcmk__option_paragraph
702 },
703 {
704 "-spacer-", no_argument, NULL, '-',
705 " crm_ticket --ticket ticketA --cleanup", pcmk__option_example
706 },
707 { 0, 0, 0, 0 }
708 };
709
710 int
711 main(int argc, char **argv)
712 {
713 pe_working_set_t *data_set = NULL;
714 xmlNode *cib_xml_copy = NULL;
715 xmlNode *cib_constraints = NULL;
716
717 cib_t *cib_conn = NULL;
718 crm_exit_t exit_code = CRM_EX_OK;
719 int rc = pcmk_ok;
720
721 int option_index = 0;
722 int argerr = 0;
723 int flag;
724 guint modified = 0;
725
726 GList *attr_delete = NULL;
727 GHashTable *attr_set = pcmk__strkey_table(free, free);
728
729 crm_log_init(NULL, LOG_CRIT, FALSE, FALSE, argc, argv, FALSE);
730 pcmk__set_cli_options(NULL, "<query>|<command> [options]", long_options,
731 "perform tasks related to cluster tickets\n\n"
732 "Allows ticket attributes to be queried, modified "
733 "and deleted.\n");
734
735 if (argc < 2) {
736 pcmk__cli_help('?', CRM_EX_USAGE);
737 }
738
739 while (1) {
740 flag = pcmk__next_cli_option(argc, argv, &option_index, NULL);
741 if (flag == -1)
742 break;
743
744 switch (flag) {
745 case 'V':
746 crm_bump_log_level(argc, argv);
747 break;
748 case '$':
749 case '?':
750 pcmk__cli_help(flag, CRM_EX_OK);
751 break;
752 case 'Q':
753 BE_QUIET = TRUE;
754 break;
755 case 't':
756 ticket_id = optarg;
757 break;
758 case 'l':
759 case 'L':
760 case 'w':
761 case 'q':
762 case 'c':
763 ticket_cmd = flag;
764 break;
765 case 'g':
766 g_hash_table_insert(attr_set, strdup("granted"), strdup("true"));
767 modified++;
768 break;
769 case 'r':
770 g_hash_table_insert(attr_set, strdup("granted"), strdup("false"));
771 modified++;
772 break;
773 case 's':
774 g_hash_table_insert(attr_set, strdup("standby"), strdup("true"));
775 modified++;
776 break;
777 case 'a':
778 g_hash_table_insert(attr_set, strdup("standby"), strdup("false"));
779 modified++;
780 break;
781 case 'G':
782 get_attr_name = optarg;
783 ticket_cmd = flag;
784 break;
785 case 'S':
786 attr_name = optarg;
787 if (attr_name && attr_value) {
788 g_hash_table_insert(attr_set, strdup(attr_name), strdup(attr_value));
789 attr_name = NULL;
790 attr_value = NULL;
791 modified++;
792 }
793 break;
794 case 'D':
795 attr_delete = g_list_append(attr_delete, optarg);
796 modified++;
797 break;
798 case 'C':
799 ticket_cmd = flag;
800 break;
801 case 'v':
802 attr_value = optarg;
803 if (attr_name && attr_value) {
804 g_hash_table_insert(attr_set, strdup(attr_name), strdup(attr_value));
805 attr_name = NULL;
806 attr_value = NULL;
807 modified++;
808 }
809 break;
810 case 'd':
811 attr_default = optarg;
812 break;
813 case 'f':
814 do_force = TRUE;
815 break;
816 case 'x':
817 xml_file = optarg;
818 break;
819 case 'n':
820 set_name = optarg;
821 break;
822 case 'i':
823 attr_id = optarg;
824 break;
825
826 default:
827 CMD_ERR("Argument code 0%o (%c) is not (?yet?) supported", flag, flag);
828 ++argerr;
829 break;
830 }
831 }
832
833 if (optind < argc && argv[optind] != NULL) {
834 CMD_ERR("non-option ARGV-elements:");
835 while (optind < argc && argv[optind] != NULL) {
836 CMD_ERR("%s", argv[optind++]);
837 ++argerr;
838 }
839 }
840
841 if (optind > argc) {
842 ++argerr;
843 }
844
845 if (argerr) {
846 pcmk__cli_help('?', CRM_EX_USAGE);
847 }
848
849 data_set = pe_new_working_set();
850 if (data_set == NULL) {
851 crm_perror(LOG_CRIT, "Could not allocate working set");
852 exit_code = CRM_EX_OSERR;
853 goto done;
854 }
855 pe__set_working_set_flags(data_set, pe_flag_no_counts|pe_flag_no_compat);
856
857 cib_conn = cib_new();
858 if (cib_conn == NULL) {
859 CMD_ERR("Could not connect to the CIB manager");
860 exit_code = CRM_EX_DISCONNECT;
861 goto done;
862 }
863
864 rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
865 if (rc != pcmk_ok) {
866 CMD_ERR("Could not connect to CIB: %s", pcmk_strerror(rc));
867 exit_code = crm_errno2exit(rc);
868 goto done;
869 }
870
871 if (xml_file != NULL) {
872 cib_xml_copy = filename2xml(xml_file);
873
874 } else {
875 rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml_copy, cib_scope_local | cib_sync_call);
876 if (rc != pcmk_ok) {
877 CMD_ERR("Could not get local CIB: %s", pcmk_strerror(rc));
878 exit_code = crm_errno2exit(rc);
879 goto done;
880 }
881 }
882
883 if (cli_config_update(&cib_xml_copy, NULL, FALSE) == FALSE) {
884 CMD_ERR("Could not update local CIB to latest schema version");
885 exit_code = CRM_EX_CONFIG;
886 goto done;
887 }
888
889 data_set->input = cib_xml_copy;
890 data_set->now = crm_time_new(NULL);
891
892 cluster_status(data_set);
893
894
895
896 cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set->input);
897 unpack_constraints(cib_constraints, data_set);
898
899 if (ticket_cmd == 'l' || ticket_cmd == 'L' || ticket_cmd == 'w') {
900 gboolean raw = FALSE;
901 gboolean details = FALSE;
902
903 if (ticket_cmd == 'L') {
904 details = TRUE;
905 } else if (ticket_cmd == 'w') {
906 raw = TRUE;
907 }
908
909 if (ticket_id) {
910 pe_ticket_t *ticket = find_ticket(ticket_id, data_set);
911
912 if (ticket == NULL) {
913 CMD_ERR("No such ticket '%s'", ticket_id);
914 exit_code = CRM_EX_NOSUCH;
915 goto done;
916 }
917 rc = print_ticket(ticket, raw, details);
918
919 } else {
920 rc = print_ticket_list(data_set, raw, details);
921 }
922 if (rc != pcmk_ok) {
923 CMD_ERR("Could not print ticket: %s", pcmk_strerror(rc));
924 }
925 exit_code = crm_errno2exit(rc);
926
927 } else if (ticket_cmd == 'q') {
928 rc = dump_ticket_xml(cib_conn, ticket_id);
929 if (rc != pcmk_ok) {
930 CMD_ERR("Could not query ticket XML: %s", pcmk_strerror(rc));
931 }
932 exit_code = crm_errno2exit(rc);
933
934 } else if (ticket_cmd == 'c') {
935 rc = dump_constraints(cib_conn, ticket_id);
936 if (rc != pcmk_ok) {
937 CMD_ERR("Could not show ticket constraints: %s", pcmk_strerror(rc));
938 }
939 exit_code = crm_errno2exit(rc);
940
941 } else if (ticket_cmd == 'G') {
942 const char *value = NULL;
943
944 if (ticket_id == NULL) {
945 CMD_ERR("Must supply ticket ID with -t");
946 exit_code = CRM_EX_NOSUCH;
947 goto done;
948 }
949
950 rc = get_ticket_state_attr(ticket_id, get_attr_name, &value, data_set);
951 if (rc == pcmk_ok) {
952 fprintf(stdout, "%s\n", value);
953 } else if (rc == -ENXIO && attr_default) {
954 fprintf(stdout, "%s\n", attr_default);
955 rc = pcmk_ok;
956 }
957 exit_code = crm_errno2exit(rc);
958
959 } else if (ticket_cmd == 'C') {
960 if (ticket_id == NULL) {
961 CMD_ERR("Must supply ticket ID with -t");
962 exit_code = CRM_EX_USAGE;
963 goto done;
964 }
965
966 if (do_force == FALSE) {
967 pe_ticket_t *ticket = NULL;
968
969 ticket = find_ticket(ticket_id, data_set);
970 if (ticket == NULL) {
971 CMD_ERR("No such ticket '%s'", ticket_id);
972 exit_code = CRM_EX_NOSUCH;
973 goto done;
974 }
975
976 if (ticket->granted) {
977 ticket_warning(ticket_id, "revoke");
978 exit_code = CRM_EX_INSUFFICIENT_PRIV;
979 goto done;
980 }
981 }
982
983 rc = delete_ticket_state(ticket_id, cib_conn);
984 if (rc != pcmk_ok) {
985 CMD_ERR("Could not clean up ticket: %s", pcmk_strerror(rc));
986 }
987 exit_code = crm_errno2exit(rc);
988
989 } else if (modified) {
990 if (ticket_id == NULL) {
991 CMD_ERR("Must supply ticket ID with -t");
992 exit_code = CRM_EX_USAGE;
993 goto done;
994 }
995
996 if (attr_value
997 && (pcmk__str_empty(attr_name))) {
998 CMD_ERR("Must supply attribute name with -S for -v %s", attr_value);
999 exit_code = CRM_EX_USAGE;
1000 goto done;
1001 }
1002
1003 if (attr_name
1004 && (pcmk__str_empty(attr_value))) {
1005 CMD_ERR("Must supply attribute value with -v for -S %s", attr_name);
1006 exit_code = CRM_EX_USAGE;
1007 goto done;
1008 }
1009
1010 if (allow_modification(ticket_id, attr_delete, attr_set) == FALSE) {
1011 CMD_ERR("Ticket modification not allowed");
1012 exit_code = CRM_EX_INSUFFICIENT_PRIV;
1013 goto done;
1014 }
1015
1016 rc = modify_ticket_state(ticket_id, attr_delete, attr_set, cib_conn, data_set);
1017 if (rc != pcmk_ok) {
1018 CMD_ERR("Could not modify ticket: %s", pcmk_strerror(rc));
1019 }
1020 exit_code = crm_errno2exit(rc);
1021
1022 } else if (ticket_cmd == 'S') {
1023
1024
1025
1026
1027 if (pcmk__str_empty(attr_name)) {
1028
1029 CMD_ERR("Must supply a command");
1030 exit_code = CRM_EX_USAGE;
1031 goto done;
1032 }
1033
1034 if (ticket_id == NULL) {
1035 CMD_ERR("Must supply ticket ID with -t");
1036 exit_code = CRM_EX_USAGE;
1037 goto done;
1038 }
1039
1040 if (pcmk__str_empty(attr_value)) {
1041 CMD_ERR("Must supply value with -v for -S %s", attr_name);
1042 exit_code = CRM_EX_USAGE;
1043 goto done;
1044 }
1045
1046 } else {
1047 CMD_ERR("Unknown command: %c", ticket_cmd);
1048 exit_code = CRM_EX_USAGE;
1049 }
1050
1051 done:
1052 if (attr_set) {
1053 g_hash_table_destroy(attr_set);
1054 }
1055 attr_set = NULL;
1056
1057 if (attr_delete) {
1058 g_list_free(attr_delete);
1059 }
1060 attr_delete = NULL;
1061
1062 pe_free_working_set(data_set);
1063 data_set = NULL;
1064
1065 if (cib_conn != NULL) {
1066 cib_conn->cmds->signoff(cib_conn);
1067 cib_delete(cib_conn);
1068 }
1069
1070 if (rc == -pcmk_err_no_quorum) {
1071 CMD_ERR("Use --force to ignore quorum");
1072 }
1073
1074 crm_exit(exit_code);
1075 }