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