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