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