This source file includes following definitions.
- print_xml_output
- report_schema_unchanged
- cib_action_is_dangerous
- scope_is_valid
- command_cb
- show_access_cb
- section_cb
- build_arg_context
- main
- do_work
- do_init
- cibadmin_op_callback
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11 #include <stdio.h>
12 #include <crm/crm.h>
13 #include <crm/msg_xml.h>
14 #include <crm/common/cmdline_internal.h>
15 #include <crm/common/ipc.h>
16 #include <crm/common/xml.h>
17 #include <crm/cib/internal.h>
18
19 #include <pacemaker-internal.h>
20
21 #define SUMMARY "query and edit the Pacemaker configuration"
22
23 #define INDENT " "
24
25 enum cibadmin_section_type {
26 cibadmin_section_all = 0,
27 cibadmin_section_scope,
28 cibadmin_section_xpath,
29 };
30
31 static int request_id = 0;
32
33 static cib_t *the_cib = NULL;
34 static GMainLoop *mainloop = NULL;
35 static crm_exit_t exit_code = CRM_EX_OK;
36
37 static struct {
38 const char *cib_action;
39 int cmd_options;
40 enum cibadmin_section_type section_type;
41 char *cib_section;
42 char *validate_with;
43 gint message_timeout_sec;
44 enum pcmk__acl_render_how acl_render_mode;
45 gchar *cib_user;
46 gchar *dest_node;
47 gchar *input_file;
48 gchar *input_xml;
49 gboolean input_stdin;
50 bool delete_all;
51 gboolean allow_create;
52 gboolean force;
53 gboolean get_node_path;
54 gboolean local;
55 gboolean no_children;
56 gboolean sync_call;
57
58
59
60
61 gboolean extended_version;
62
63
64 gboolean no_bcast;
65 } options;
66
67 int do_init(void);
68 static int do_work(xmlNode *input, xmlNode **output);
69 void cibadmin_op_callback(xmlNode *msg, int call_id, int rc, xmlNode *output,
70 void *user_data);
71
72 static void
73 print_xml_output(xmlNode * xml)
74 {
75 char *buffer;
76
77 if (!xml) {
78 return;
79 } else if (xml->type != XML_ELEMENT_NODE) {
80 return;
81 }
82
83 if (pcmk_is_set(options.cmd_options, cib_xpath_address)) {
84 const char *id = crm_element_value(xml, XML_ATTR_ID);
85
86 if (pcmk__str_eq((const char *)xml->name, "xpath-query", pcmk__str_casei)) {
87 xmlNode *child = NULL;
88
89 for (child = xml->children; child; child = child->next) {
90 print_xml_output(child);
91 }
92
93 } else if (id) {
94 printf("%s\n", id);
95 }
96
97 } else {
98 buffer = dump_xml_formatted(xml);
99 fprintf(stdout, "%s", pcmk__s(buffer, "<null>\n"));
100 free(buffer);
101 }
102 }
103
104
105 static void
106 report_schema_unchanged(void)
107 {
108 const char *err = pcmk_rc_str(pcmk_rc_schema_unchanged);
109
110 crm_info("Upgrade unnecessary: %s\n", err);
111 printf("Upgrade unnecessary: %s\n", err);
112 exit_code = CRM_EX_OK;
113 }
114
115
116
117
118
119
120 static inline bool
121 cib_action_is_dangerous(void)
122 {
123 return options.no_bcast || options.delete_all
124 || pcmk__str_any_of(options.cib_action,
125 PCMK__CIB_REQUEST_UPGRADE,
126 PCMK__CIB_REQUEST_ERASE,
127 NULL);
128 }
129
130
131
132
133
134
135
136
137
138
139 static inline bool
140 scope_is_valid(const char *scope)
141 {
142 return pcmk__str_any_of(scope,
143 XML_CIB_TAG_CONFIGURATION,
144 XML_CIB_TAG_NODES,
145 XML_CIB_TAG_RESOURCES,
146 XML_CIB_TAG_CONSTRAINTS,
147 XML_CIB_TAG_CRMCONFIG,
148 XML_CIB_TAG_RSCCONFIG,
149 XML_CIB_TAG_OPCONFIG,
150 XML_CIB_TAG_ACLS,
151 XML_TAG_FENCING_TOPOLOGY,
152 XML_CIB_TAG_TAGS,
153 XML_CIB_TAG_ALERTS,
154 XML_CIB_TAG_STATUS,
155 NULL);
156 }
157
158 static gboolean
159 command_cb(const gchar *option_name, const gchar *optarg, gpointer data,
160 GError **error)
161 {
162 options.delete_all = false;
163
164 if (pcmk__str_any_of(option_name, "-u", "--upgrade", NULL)) {
165 options.cib_action = PCMK__CIB_REQUEST_UPGRADE;
166
167 } else if (pcmk__str_any_of(option_name, "-Q", "--query", NULL)) {
168 options.cib_action = PCMK__CIB_REQUEST_QUERY;
169
170 } else if (pcmk__str_any_of(option_name, "-E", "--erase", NULL)) {
171 options.cib_action = PCMK__CIB_REQUEST_ERASE;
172
173 } else if (pcmk__str_any_of(option_name, "-B", "--bump", NULL)) {
174 options.cib_action = PCMK__CIB_REQUEST_BUMP;
175
176 } else if (pcmk__str_any_of(option_name, "-C", "--create", NULL)) {
177 options.cib_action = PCMK__CIB_REQUEST_CREATE;
178
179 } else if (pcmk__str_any_of(option_name, "-M", "--modify", NULL)) {
180 options.cib_action = PCMK__CIB_REQUEST_MODIFY;
181
182 } else if (pcmk__str_any_of(option_name, "-P", "--patch", NULL)) {
183 options.cib_action = PCMK__CIB_REQUEST_APPLY_PATCH;
184
185 } else if (pcmk__str_any_of(option_name, "-R", "--replace", NULL)) {
186 options.cib_action = PCMK__CIB_REQUEST_REPLACE;
187
188 } else if (pcmk__str_any_of(option_name, "-D", "--delete", NULL)) {
189 options.cib_action = PCMK__CIB_REQUEST_DELETE;
190
191 } else if (pcmk__str_any_of(option_name, "-d", "--delete-all", NULL)) {
192 options.cib_action = PCMK__CIB_REQUEST_DELETE;
193 options.delete_all = true;
194
195 } else if (pcmk__str_any_of(option_name, "-a", "--empty", NULL)) {
196 options.cib_action = "empty";
197 pcmk__str_update(&options.validate_with, optarg);
198
199 } else if (pcmk__str_any_of(option_name, "-5", "--md5-sum", NULL)) {
200 options.cib_action = "md5-sum";
201
202 } else if (pcmk__str_any_of(option_name, "-6", "--md5-sum-versioned",
203 NULL)) {
204 options.cib_action = "md5-sum-versioned";
205
206 } else {
207
208 return FALSE;
209 }
210
211 return TRUE;
212 }
213
214 static gboolean
215 show_access_cb(const gchar *option_name, const gchar *optarg, gpointer data,
216 GError **error)
217 {
218 if (pcmk__str_eq(optarg, "auto", pcmk__str_null_matches)) {
219 options.acl_render_mode = pcmk__acl_render_default;
220
221 } else if (g_strcmp0(optarg, "namespace") == 0) {
222 options.acl_render_mode = pcmk__acl_render_namespace;
223
224 } else if (g_strcmp0(optarg, "text") == 0) {
225 options.acl_render_mode = pcmk__acl_render_text;
226
227 } else if (g_strcmp0(optarg, "color") == 0) {
228 options.acl_render_mode = pcmk__acl_render_color;
229
230 } else {
231 g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
232 "Invalid value '%s' for option '%s'",
233 optarg, option_name);
234 return FALSE;
235 }
236 return TRUE;
237 }
238
239 static gboolean
240 section_cb(const gchar *option_name, const gchar *optarg, gpointer data,
241 GError **error)
242 {
243 if (pcmk__str_any_of(option_name, "-o", "--scope", NULL)) {
244 options.section_type = cibadmin_section_scope;
245
246 } else if (pcmk__str_any_of(option_name, "-A", "--xpath", NULL)) {
247 options.section_type = cibadmin_section_xpath;
248
249 } else {
250
251 return FALSE;
252 }
253
254 pcmk__str_update(&options.cib_section, optarg);
255 return TRUE;
256 }
257
258 static GOptionEntry command_entries[] = {
259 { "upgrade", 'u', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
260 "Upgrade the configuration to the latest syntax", NULL },
261
262 { "query", 'Q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
263 "Query the contents of the CIB", NULL },
264
265 { "erase", 'E', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
266 "Erase the contents of the whole CIB", NULL },
267
268 { "bump", 'B', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
269 "Increase the CIB's epoch value by 1", NULL },
270
271 { "create", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
272 "Create an object in the CIB (will fail if object already exists)",
273 NULL },
274
275 { "modify", 'M', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
276 "Find object somewhere in CIB's XML tree and update it (fails if object "
277 "does not exist unless -c is also specified)",
278 NULL },
279
280 { "patch", 'P', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
281 "Supply an update in the form of an XML diff (see crm_diff(8))", NULL },
282
283 { "replace", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
284 "Recursively replace an object in the CIB", NULL },
285
286 { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
287 "Delete first object matching supplied criteria (for example, "
288 "<" XML_ATTR_OP " " XML_ATTR_ID "=\"rsc1_op1\" "
289 XML_ATTR_NAME "=\"monitor\"/>).\n"
290 INDENT "The XML element name and all attributes must match in order for "
291 "the element to be deleted.",
292 NULL },
293
294 { "delete-all", 'd', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
295 command_cb,
296 "When used with --xpath, remove all matching objects in the "
297 "configuration instead of just the first one",
298 NULL },
299
300 { "empty", 'a', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
301 command_cb,
302 "Output an empty CIB. Accepts an optional schema name argument to use as "
303 "the " XML_ATTR_VALIDATION " value.\n"
304 INDENT "If no schema is given, the latest will be used.",
305 "[schema]" },
306
307 { "md5-sum", '5', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
308 "Calculate the on-disk CIB digest", NULL },
309
310 { "md5-sum-versioned", '6', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
311 command_cb, "Calculate an on-the-wire versioned CIB digest", NULL },
312
313 { NULL }
314 };
315
316 static GOptionEntry data_entries[] = {
317
318
319
320
321
322 { "xml-text", 'X', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING,
323 &options.input_xml, "Retrieve XML from the supplied string", "value" },
324
325 { "xml-file", 'x', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME,
326 &options.input_file, "Retrieve XML from the named file", "value" },
327
328 { "xml-pipe", 'p', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
329 &options.input_stdin, "Retrieve XML from stdin", NULL },
330
331 { NULL }
332 };
333
334 static GOptionEntry addl_entries[] = {
335 { "force", 'f', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.force,
336 "Force the action to be performed", NULL },
337
338 { "timeout", 't', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT,
339 &options.message_timeout_sec,
340 "Time (in seconds) to wait before declaring the operation failed",
341 "value" },
342
343 { "user", 'U', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.cib_user,
344 "Run the command with permissions of the named user (valid only for the "
345 "root and " CRM_DAEMON_USER " accounts)", "value" },
346
347 { "sync-call", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
348 &options.sync_call, "Wait for call to complete before returning", NULL },
349
350 { "local", 'l', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.local,
351 "Command takes effect locally (should be used only for queries)", NULL },
352
353 { "scope", 'o', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, section_cb,
354 "Limit scope of operation to specific section of CIB\n"
355 INDENT "Valid values: " XML_CIB_TAG_CONFIGURATION ", " XML_CIB_TAG_NODES
356 ", " XML_CIB_TAG_RESOURCES ", " XML_CIB_TAG_CONSTRAINTS
357 ", " XML_CIB_TAG_CRMCONFIG ", " XML_CIB_TAG_RSCCONFIG ",\n"
358 INDENT " " XML_CIB_TAG_OPCONFIG ", " XML_CIB_TAG_ACLS
359 ", " XML_TAG_FENCING_TOPOLOGY ", " XML_CIB_TAG_TAGS
360 ", " XML_CIB_TAG_ALERTS ", " XML_CIB_TAG_STATUS "\n"
361 INDENT "If both --scope/-o and --xpath/-a are specified, the last one to "
362 "appear takes effect",
363 "value" },
364
365 { "xpath", 'A', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, section_cb,
366 "A valid XPath to use instead of --scope/-o\n"
367 INDENT "If both --scope/-o and --xpath/-a are specified, the last one to "
368 "appear takes effect",
369 "value" },
370
371 { "node-path", 'e', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
372 &options.get_node_path,
373 "When performing XPath queries, return paths of any matches found\n"
374 INDENT "(for example, "
375 "\"/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION
376 "/" XML_CIB_TAG_RESOURCES "/" XML_CIB_TAG_INCARNATION
377 "[@" XML_ATTR_ID "='dummy-clone']"
378 "/" XML_CIB_TAG_RESOURCE "[@" XML_ATTR_ID "='dummy']\")",
379 NULL },
380
381 { "show-access", 'S', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
382 show_access_cb,
383 "Whether to use syntax highlighting for ACLs (with -Q/--query and "
384 "-U/--user)\n"
385 INDENT "Allowed values: 'color' (default for terminal), 'text' (plain text, "
386 "default for non-terminal),\n"
387 INDENT " 'namespace', or 'auto' (use default value)\n"
388 INDENT "Default value: 'auto'",
389 "[value]" },
390
391 { "allow-create", 'c', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
392 &options.allow_create,
393 "(Advanced) Allow target of --modify/-M to be created if it does not "
394 "exist",
395 NULL },
396
397 { "no-children", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
398 &options.no_children,
399 "(Advanced) When querying an object, do not include its children in the "
400 "result",
401 NULL },
402
403 { "node", 'N', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.dest_node,
404 "(Advanced) Send command to the specified host", "value" },
405
406
407 { "no-bcast", 'b', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
408 &options.no_bcast, "deprecated", NULL },
409
410
411 { "host", 'h', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING,
412 &options.dest_node, "deprecated", NULL },
413
414 { NULL }
415 };
416
417 static GOptionContext *
418 build_arg_context(pcmk__common_args_t *args)
419 {
420 const char *desc = NULL;
421 GOptionContext *context = NULL;
422
423 GOptionEntry extra_prog_entries[] = {
424
425 { "extended-version", '!', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
426 &options.extended_version, "deprecated", NULL },
427
428 { NULL }
429 };
430
431 desc = "Examples:\n\n"
432 "Query the configuration from the local node:\n\n"
433 "\t# cibadmin --query --local\n\n"
434 "Query just the cluster options configuration:\n\n"
435 "\t# cibadmin --query --scope " XML_CIB_TAG_CRMCONFIG "\n\n"
436 "Query all '" XML_RSC_ATTR_TARGET_ROLE "' settings:\n\n"
437 "\t# cibadmin --query --xpath "
438 "\"//" XML_CIB_TAG_NVPAIR
439 "[@" XML_NVPAIR_ATTR_NAME "='" XML_RSC_ATTR_TARGET_ROLE"']\""
440 "\n\n"
441 "Remove all '" XML_RSC_ATTR_MANAGED "' settings:\n\n"
442 "\t# cibadmin --delete-all --xpath "
443 "\"//" XML_CIB_TAG_NVPAIR
444 "[@" XML_NVPAIR_ATTR_NAME "='" XML_RSC_ATTR_MANAGED "']\"\n\n"
445 "Remove the resource named 'old':\n\n"
446 "\t# cibadmin --delete --xml-text "
447 "'<" XML_CIB_TAG_RESOURCE " " XML_ATTR_ID "=\"old\"/>'\n\n"
448 "Remove all resources from the configuration:\n\n"
449 "\t# cibadmin --replace --scope " XML_CIB_TAG_RESOURCES
450 " --xml-text '<" XML_CIB_TAG_RESOURCES "/>'\n\n"
451 "Replace complete configuration with contents of "
452 "$HOME/pacemaker.xml:\n\n"
453 "\t# cibadmin --replace --xml-file $HOME/pacemaker.xml\n\n"
454 "Replace " XML_CIB_TAG_CONSTRAINTS " section of configuration with "
455 "contents of $HOME/constraints.xml:\n\n"
456 "\t# cibadmin --replace --scope " XML_CIB_TAG_CONSTRAINTS
457 " --xml-file $HOME/constraints.xml\n\n"
458 "Increase configuration version to prevent old configurations from "
459 "being loaded accidentally:\n\n"
460 "\t# cibadmin --modify --xml-text "
461 "'<" XML_TAG_CIB " " XML_ATTR_GENERATION_ADMIN
462 "=\"" XML_ATTR_GENERATION_ADMIN "++\"/>'\n\n"
463 "Edit the configuration with your favorite $EDITOR:\n\n"
464 "\t# cibadmin --query > $HOME/local.xml\n\n"
465 "\t# $EDITOR $HOME/local.xml\n\n"
466 "\t# cibadmin --replace --xml-file $HOME/local.xml\n\n"
467 "Assuming terminal, render configuration in color (green for "
468 "writable, blue for readable, red for\n"
469 "denied) to visualize permissions for user tony:\n\n"
470 "\t# cibadmin --show-access=color --query --user tony | less -r\n\n"
471 "SEE ALSO:\n"
472 " crm(8), pcs(8), crm_shadow(8), crm_diff(8)\n";
473
474 context = pcmk__build_arg_context(args, NULL, NULL, "<command>");
475 g_option_context_set_description(context, desc);
476
477 pcmk__add_main_args(context, extra_prog_entries);
478
479 pcmk__add_arg_group(context, "commands", "Commands:", "Show command help",
480 command_entries);
481 pcmk__add_arg_group(context, "data", "Data:", "Show data help",
482 data_entries);
483 pcmk__add_arg_group(context, "additional", "Additional Options:",
484 "Show additional options", addl_entries);
485 return context;
486 }
487
488 int
489 main(int argc, char **argv)
490 {
491 int rc = pcmk_rc_ok;
492 const char *source = NULL;
493 xmlNode *output = NULL;
494 xmlNode *input = NULL;
495 gchar *acl_cred = NULL;
496
497 GError *error = NULL;
498
499 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
500 gchar **processed_args = pcmk__cmdline_preproc(argv, "ANSUXhotx");
501 GOptionContext *context = build_arg_context(args);
502
503 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
504 exit_code = CRM_EX_USAGE;
505 goto done;
506 }
507
508 if (g_strv_length(processed_args) > 1) {
509 gchar *help = g_option_context_get_help(context, TRUE, NULL);
510 GString *extra = g_string_sized_new(128);
511
512 for (int lpc = 1; processed_args[lpc] != NULL; lpc++) {
513 if (extra->len > 0) {
514 g_string_append_c(extra, ' ');
515 }
516 g_string_append(extra, processed_args[lpc]);
517 }
518
519 exit_code = CRM_EX_USAGE;
520 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
521 "non-option ARGV-elements: %s\n\n%s", extra->str, help);
522 g_free(help);
523 g_string_free(extra, TRUE);
524 goto done;
525 }
526
527 if (args->version || options.extended_version) {
528 g_strfreev(processed_args);
529 pcmk__free_arg_context(context);
530
531
532
533
534
535
536
537 pcmk__cli_help(options.extended_version? '!' : 'v');
538 }
539
540
541
542
543
544
545
546
547 pcmk__cli_init_logging("cibadmin", 0);
548 set_crm_log_level(LOG_CRIT);
549
550 if (args->verbosity > 0) {
551 cib__set_call_options(options.cmd_options, crm_system_name,
552 cib_verbose);
553
554 for (int i = 0; i < args->verbosity; i++) {
555 crm_bump_log_level(argc, argv);
556 }
557 }
558
559 if (options.cib_action == NULL) {
560
561 gchar *help = g_option_context_get_help(context, TRUE, NULL);
562
563 exit_code = CRM_EX_USAGE;
564 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
565 "Must specify a command option\n\n%s", help);
566 g_free(help);
567 goto done;
568 }
569
570 if (strcmp(options.cib_action, "empty") == 0) {
571
572 char *buf = NULL;
573
574 output = createEmptyCib(1);
575 crm_xml_add(output, XML_ATTR_VALIDATION, options.validate_with);
576 buf = dump_xml_formatted(output);
577 fprintf(stdout, "%s", pcmk__s(buf, "<null>\n"));
578 free(buf);
579 goto done;
580 }
581
582 if (cib_action_is_dangerous() && !options.force) {
583 exit_code = CRM_EX_UNSAFE;
584 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
585 "The supplied command is considered dangerous. To prevent "
586 "accidental destruction of the cluster, the --force flag "
587 "is required in order to proceed.");
588 goto done;
589 }
590
591 if (options.message_timeout_sec < 1) {
592
593 options.message_timeout_sec = 30;
594 }
595
596 if (options.section_type == cibadmin_section_xpath) {
597
598 cib__set_call_options(options.cmd_options, crm_system_name,
599 cib_xpath);
600
601 } else if (options.section_type == cibadmin_section_scope) {
602 if (!scope_is_valid(options.cib_section)) {
603
604 fprintf(stderr,
605 "Invalid value '%s' for '--scope'. Operation will apply "
606 "to the entire CIB.\n", options.cib_section);
607 }
608 }
609
610 if (options.allow_create) {
611
612 cib__set_call_options(options.cmd_options, crm_system_name,
613 cib_can_create);
614 }
615
616 if (options.delete_all) {
617
618 cib__set_call_options(options.cmd_options, crm_system_name,
619 cib_multiple);
620 }
621
622 if (options.get_node_path) {
623
624
625
626 cib__set_call_options(options.cmd_options, crm_system_name,
627 cib_xpath_address);
628 }
629
630 if (options.local) {
631
632 cib__set_call_options(options.cmd_options, crm_system_name,
633 cib_scope_local);
634 }
635
636
637 if (options.no_bcast) {
638
639 cib__set_call_options(options.cmd_options, crm_system_name,
640 cib_inhibit_bcast|cib_scope_local);
641 }
642
643 if (options.no_children) {
644
645 cib__set_call_options(options.cmd_options, crm_system_name,
646 cib_no_children);
647 }
648
649 if (options.sync_call
650 || (options.acl_render_mode != pcmk__acl_render_none)) {
651
652
653
654
655
656
657
658 cib__set_call_options(options.cmd_options, crm_system_name,
659 cib_sync_call);
660 }
661
662 if (options.input_file != NULL) {
663 input = filename2xml(options.input_file);
664 source = options.input_file;
665
666 } else if (options.input_xml != NULL) {
667 input = string2xml(options.input_xml);
668 source = "input string";
669
670 } else if (options.input_stdin) {
671 source = "STDIN";
672 input = stdin2xml();
673
674 } else if (options.acl_render_mode != pcmk__acl_render_none) {
675 char *username = pcmk__uid2username(geteuid());
676 bool required = pcmk_acl_required(username);
677
678 free(username);
679
680 if (required) {
681 if (options.force) {
682 fprintf(stderr, "The supplied command can provide skewed"
683 " result since it is run under user that also"
684 " gets guarded per ACLs on their own right."
685 " Continuing since --force flag was"
686 " provided.\n");
687
688 } else {
689 exit_code = CRM_EX_UNSAFE;
690 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
691 "The supplied command can provide skewed result "
692 "since it is run under user that also gets guarded "
693 "per ACLs in their own right. To accept the risk "
694 "of such a possible distortion (without even "
695 "knowing it at this time), use the --force flag.");
696 goto done;
697 }
698 }
699
700 if (options.cib_user == NULL) {
701 exit_code = CRM_EX_USAGE;
702 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
703 "The supplied command requires -U user specified.");
704 goto done;
705 }
706
707
708
709
710
711
712
713
714
715 acl_cred = options.cib_user;
716 options.cib_user = NULL;
717 }
718
719 if (input != NULL) {
720 crm_log_xml_debug(input, "[admin input]");
721
722 } else if (source != NULL) {
723 exit_code = CRM_EX_CONFIG;
724 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
725 "Couldn't parse input from %s.", source);
726 goto done;
727 }
728
729 if (strcmp(options.cib_action, "md5-sum") == 0) {
730 char *digest = NULL;
731
732 if (input == NULL) {
733 exit_code = CRM_EX_USAGE;
734 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
735 "Please supply XML to process with -X, -x, or -p");
736 goto done;
737 }
738
739 digest = calculate_on_disk_digest(input);
740 fprintf(stderr, "Digest: ");
741 fprintf(stdout, "%s\n", pcmk__s(digest, "<null>"));
742 free(digest);
743 goto done;
744
745 } else if (strcmp(options.cib_action, "md5-sum-versioned") == 0) {
746 char *digest = NULL;
747 const char *version = NULL;
748
749 if (input == NULL) {
750 exit_code = CRM_EX_USAGE;
751 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
752 "Please supply XML to process with -X, -x, or -p");
753 goto done;
754 }
755
756 version = crm_element_value(input, XML_ATTR_CRM_VERSION);
757 digest = calculate_xml_versioned_digest(input, FALSE, TRUE, version);
758 fprintf(stderr, "Versioned (%s) digest: ", version);
759 fprintf(stdout, "%s\n", pcmk__s(digest, "<null>"));
760 free(digest);
761 goto done;
762 }
763
764 rc = do_init();
765 if (rc != pcmk_ok) {
766 rc = pcmk_legacy2rc(rc);
767 exit_code = pcmk_rc2exitc(rc);
768
769 crm_err("Init failed, could not perform requested operations: %s",
770 pcmk_rc_str(rc));
771 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
772 "Init failed, could not perform requested operations: %s",
773 pcmk_rc_str(rc));
774 goto done;
775 }
776
777 rc = do_work(input, &output);
778 if (rc > 0) {
779
780
781
782 request_id = rc;
783
784 the_cib->cmds->register_callback(the_cib, request_id,
785 options.message_timeout_sec, FALSE,
786 NULL, "cibadmin_op_callback",
787 cibadmin_op_callback);
788
789 mainloop = g_main_loop_new(NULL, FALSE);
790
791 crm_trace("%s waiting for reply from the local CIB", crm_system_name);
792
793 crm_info("Starting mainloop");
794 g_main_loop_run(mainloop);
795
796 } else if ((rc == -pcmk_err_schema_unchanged)
797 && (strcmp(options.cib_action,
798 PCMK__CIB_REQUEST_UPGRADE) == 0)) {
799 report_schema_unchanged();
800
801 } else if (rc < 0) {
802 rc = pcmk_legacy2rc(rc);
803 crm_err("Call failed: %s", pcmk_rc_str(rc));
804 fprintf(stderr, "Call failed: %s\n", pcmk_rc_str(rc));
805
806 if (rc == pcmk_rc_schema_validation) {
807 if (strcmp(options.cib_action, PCMK__CIB_REQUEST_UPGRADE) == 0) {
808 xmlNode *obj = NULL;
809 int version = 0;
810
811 if (the_cib->cmds->query(the_cib, NULL, &obj,
812 options.cmd_options) == pcmk_ok) {
813 update_validation(&obj, &version, 0, TRUE, FALSE);
814 }
815 free_xml(obj);
816
817 } else if (output) {
818 validate_xml_verbose(output);
819 }
820 }
821 exit_code = pcmk_rc2exitc(rc);
822 }
823
824 if ((output != NULL)
825 && (options.acl_render_mode != pcmk__acl_render_none)) {
826
827 xmlDoc *acl_evaled_doc;
828 rc = pcmk__acl_annotate_permissions(acl_cred, output->doc, &acl_evaled_doc);
829 if (rc == pcmk_rc_ok) {
830 xmlChar *rendered = NULL;
831
832 rc = pcmk__acl_evaled_render(acl_evaled_doc,
833 options.acl_render_mode, &rendered);
834 if (rc != pcmk_rc_ok) {
835 exit_code = CRM_EX_CONFIG;
836 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
837 "Could not render evaluated access: %s",
838 pcmk_rc_str(rc));
839 goto done;
840 }
841 printf("%s\n", (char *) rendered);
842 free(rendered);
843
844 } else {
845 exit_code = CRM_EX_CONFIG;
846 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
847 "Could not evaluate access per request (%s, error: %s)",
848 acl_cred, pcmk_rc_str(rc));
849 goto done;
850 }
851
852 } else if (output != NULL) {
853 print_xml_output(output);
854 }
855
856 crm_trace("%s exiting normally", crm_system_name);
857
858 done:
859 g_strfreev(processed_args);
860 pcmk__free_arg_context(context);
861
862 g_free(options.cib_user);
863 g_free(options.dest_node);
864 g_free(options.input_file);
865 g_free(options.input_xml);
866 free(options.cib_section);
867 free(options.validate_with);
868
869 g_free(acl_cred);
870 free_xml(input);
871 free_xml(output);
872
873 rc = cib__clean_up_connection(&the_cib);
874 if (exit_code == CRM_EX_OK) {
875 exit_code = pcmk_rc2exitc(rc);
876 }
877
878 pcmk__output_and_clear_error(&error, NULL);
879 crm_exit(exit_code);
880 }
881
882 static int
883 do_work(xmlNode *input, xmlNode **output)
884 {
885
886 the_cib->call_timeout = options.message_timeout_sec;
887 if ((strcmp(options.cib_action, PCMK__CIB_REQUEST_REPLACE) == 0)
888 && pcmk__str_eq(crm_element_name(input), XML_TAG_CIB, pcmk__str_casei)) {
889 xmlNode *status = pcmk_find_cib_element(input, XML_CIB_TAG_STATUS);
890
891 if (status == NULL) {
892 create_xml_node(input, XML_CIB_TAG_STATUS);
893 }
894 }
895
896 crm_trace("Passing \"%s\" to variant_op...", options.cib_action);
897 return cib_internal_op(the_cib, options.cib_action, options.dest_node,
898 options.cib_section, input, output,
899 options.cmd_options, options.cib_user);
900 }
901
902 int
903 do_init(void)
904 {
905 int rc = pcmk_ok;
906
907 the_cib = cib_new();
908 rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
909 if (rc != pcmk_ok) {
910 crm_err("Could not connect to the CIB: %s", pcmk_strerror(rc));
911 fprintf(stderr, "Could not connect to the CIB: %s\n",
912 pcmk_strerror(rc));
913 }
914
915 return rc;
916 }
917
918 void
919 cibadmin_op_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
920 {
921 rc = pcmk_legacy2rc(rc);
922 exit_code = pcmk_rc2exitc(rc);
923
924 if (rc == pcmk_rc_schema_unchanged) {
925 report_schema_unchanged();
926
927 } else if (rc != pcmk_rc_ok) {
928 crm_warn("Call %s failed: %s " CRM_XS " rc=%d",
929 options.cib_action, pcmk_rc_str(rc), rc);
930 fprintf(stderr, "Call %s failed: %s\n",
931 options.cib_action, pcmk_rc_str(rc));
932 print_xml_output(output);
933
934 } else if ((strcmp(options.cib_action, PCMK__CIB_REQUEST_QUERY) == 0)
935 && (output == NULL)) {
936 crm_err("Query returned no output");
937 crm_log_xml_err(msg, "no output");
938
939 } else if (output == NULL) {
940 crm_info("Call passed");
941
942 } else {
943 crm_info("Call passed");
944 print_xml_output(output);
945 }
946
947 if (call_id == request_id) {
948 g_main_loop_quit(mainloop);
949
950 } else {
951 crm_info("Message was not the response we were looking for (%d vs. %d)",
952 call_id, request_id);
953 }
954 }