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