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 no_children;
54 gboolean score_update;
55 gboolean sync_call;
56
57
58
59
60 gboolean extended_version;
61
62
63 gboolean local;
64 } options;
65
66 int do_init(void);
67 static int do_work(xmlNode *input, xmlNode **output);
68 void cibadmin_op_callback(xmlNode *msg, int call_id, int rc, xmlNode *output,
69 void *user_data);
70
71 static void
72 print_xml_output(xmlNode * xml)
73 {
74 if (!xml) {
75 return;
76 } else if (xml->type != XML_ELEMENT_NODE) {
77 return;
78 }
79
80 if (pcmk_is_set(options.cmd_options, cib_xpath_address)) {
81 const char *id = crm_element_value(xml, PCMK_XA_ID);
82
83 if (pcmk__xe_is(xml, PCMK__XE_XPATH_QUERY)) {
84 xmlNode *child = NULL;
85
86 for (child = xml->children; child; child = child->next) {
87 print_xml_output(child);
88 }
89
90 } else if (id) {
91 printf("%s\n", id);
92 }
93
94 } else {
95 GString *buf = g_string_sized_new(1024);
96
97 pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buf, 0);
98
99 fprintf(stdout, "%s", buf->str);
100 g_string_free(buf, TRUE);
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.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 PCMK_XE_CONFIGURATION,
144 PCMK_XE_NODES,
145 PCMK_XE_RESOURCES,
146 PCMK_XE_CONSTRAINTS,
147 PCMK_XE_CRM_CONFIG,
148 PCMK_XE_RSC_DEFAULTS,
149 PCMK_XE_OP_DEFAULTS,
150 PCMK_XE_ACLS,
151 PCMK_XE_FENCING_TOPOLOGY,
152 PCMK_XE_TAGS,
153 PCMK_XE_ALERTS,
154 PCMK_XE_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 "<" PCMK_XE_OP " " PCMK_XA_ID "=\"rsc1_op1\" "
289 PCMK_XA_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 " PCMK_XA_VALIDATE_WITH " 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 { "scope", 'o', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, section_cb,
351 "Limit scope of operation to specific section of CIB\n"
352 INDENT "Valid values: " PCMK_XE_CONFIGURATION ", " PCMK_XE_NODES
353 ", " PCMK_XE_RESOURCES ", " PCMK_XE_CONSTRAINTS
354 ", " PCMK_XE_CRM_CONFIG ", " PCMK_XE_RSC_DEFAULTS ",\n"
355 INDENT " " PCMK_XE_OP_DEFAULTS ", " PCMK_XE_ACLS
356 ", " PCMK_XE_FENCING_TOPOLOGY ", " PCMK_XE_TAGS ", " PCMK_XE_ALERTS
357 ", " PCMK_XE_STATUS "\n"
358 INDENT "If both --scope/-o and --xpath/-a are specified, the last one to "
359 "appear takes effect",
360 "value" },
361
362 { "xpath", 'A', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, section_cb,
363 "A valid XPath to use instead of --scope/-o\n"
364 INDENT "If both --scope/-o and --xpath/-a are specified, the last one to "
365 "appear takes effect",
366 "value" },
367
368 { "node-path", 'e', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
369 &options.get_node_path,
370 "When performing XPath queries, return paths of any matches found\n"
371 INDENT "(for example, "
372 "\"/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION
373 "/" PCMK_XE_RESOURCES "/" PCMK_XE_CLONE
374 "[@" PCMK_XA_ID "='dummy-clone']"
375 "/" PCMK_XE_PRIMITIVE "[@" PCMK_XA_ID "='dummy']\")",
376 NULL },
377
378 { "show-access", 'S', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
379 show_access_cb,
380 "Whether to use syntax highlighting for ACLs (with -Q/--query and "
381 "-U/--user)\n"
382 INDENT "Allowed values: 'color' (default for terminal), 'text' (plain text, "
383 "default for non-terminal),\n"
384 INDENT " 'namespace', or 'auto' (use default value)\n"
385 INDENT "Default value: 'auto'",
386 "[value]" },
387
388 { "score", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.score_update,
389 "Treat new attribute values as atomic score updates where possible "
390 "(with --modify/-M).\n"
391
392 INDENT "This currently happens by default and cannot be disabled, but\n"
393 INDENT "this default behavior is deprecated and will be removed in a\n"
394 INDENT "future release. Set this flag if this behavior is desired.\n"
395
396 INDENT "This option takes effect when updating XML attributes. For an\n"
397 INDENT "attribute named \"name\", if the new value is \"name++\" or\n"
398 INDENT "\"name+=X\" for some score X, the new value is set as follows:\n"
399 INDENT "If attribute \"name\" is not already set to some value in\n"
400 INDENT "the element being updated, the new value is set as a literal\n"
401 INDENT "string.\n"
402 INDENT "If the new value is \"name++\", then the attribute is set to \n"
403 INDENT "its existing value (parsed as a score) plus 1.\n"
404 INDENT "If the new value is \"name+=X\" for some score X, then the\n"
405 INDENT "attribute is set to its existing value plus X, where the\n"
406 INDENT "existing value and X are parsed and added as scores.\n"
407
408 INDENT "Scores are integer values capped at INFINITY and -INFINITY.\n"
409 INDENT "Refer to Pacemaker Explained for more details on scores,\n"
410 INDENT "including how they are parsed and added.",
411 NULL },
412
413 { "allow-create", 'c', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
414 &options.allow_create,
415 "(Advanced) Allow target of --modify/-M to be created if it does not "
416 "exist",
417 NULL },
418
419 { "no-children", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
420 &options.no_children,
421 "(Advanced) When querying an object, do not include its children in the "
422 "result",
423 NULL },
424
425 { "node", 'N', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.dest_node,
426 "(Advanced) Send command to the specified host", "value" },
427
428
429 { "local", 'l', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.local,
430 "(deprecated)", NULL },
431
432 { NULL }
433 };
434
435 static GOptionContext *
436 build_arg_context(pcmk__common_args_t *args)
437 {
438 const char *desc = NULL;
439 GOptionContext *context = NULL;
440
441 GOptionEntry extra_prog_entries[] = {
442
443 { "extended-version", '!', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
444 &options.extended_version, "deprecated", NULL },
445
446 { NULL }
447 };
448
449 desc = "Examples:\n\n"
450 "Query the configuration:\n\n"
451 "\t# cibadmin --query\n\n"
452 "Query just the cluster options configuration:\n\n"
453 "\t# cibadmin --query --scope " PCMK_XE_CRM_CONFIG "\n\n"
454 "Query all '" PCMK_META_TARGET_ROLE "' settings:\n\n"
455 "\t# cibadmin --query --xpath "
456 "\"//" PCMK_XE_NVPAIR
457 "[@" PCMK_XA_NAME "='" PCMK_META_TARGET_ROLE"']\"\n\n"
458 "Remove all '" PCMK_META_IS_MANAGED "' settings:\n\n"
459 "\t# cibadmin --delete-all --xpath "
460 "\"//" PCMK_XE_NVPAIR
461 "[@" PCMK_XA_NAME "='" PCMK_META_IS_MANAGED "']\"\n\n"
462 "Remove the resource named 'old':\n\n"
463 "\t# cibadmin --delete --xml-text "
464 "'<" PCMK_XE_PRIMITIVE " " PCMK_XA_ID "=\"old\"/>'\n\n"
465 "Remove all resources from the configuration:\n\n"
466 "\t# cibadmin --replace --scope " PCMK_XE_RESOURCES
467 " --xml-text '<" PCMK_XE_RESOURCES "/>'\n\n"
468 "Replace complete configuration with contents of "
469 "$HOME/pacemaker.xml:\n\n"
470 "\t# cibadmin --replace --xml-file $HOME/pacemaker.xml\n\n"
471 "Replace " PCMK_XE_CONSTRAINTS " section of configuration with "
472 "contents of $HOME/constraints.xml:\n\n"
473 "\t# cibadmin --replace --scope " PCMK_XE_CONSTRAINTS
474 " --xml-file $HOME/constraints.xml\n\n"
475 "Increase configuration version to prevent old configurations from "
476 "being loaded accidentally:\n\n"
477 "\t# cibadmin --modify --score --xml-text "
478 "'<" PCMK_XE_CIB " " PCMK_XA_ADMIN_EPOCH
479 "=\"" PCMK_XA_ADMIN_EPOCH "++\"/>'\n\n"
480 "Edit the configuration with your favorite $EDITOR:\n\n"
481 "\t# cibadmin --query > $HOME/local.xml\n\n"
482 "\t# $EDITOR $HOME/local.xml\n\n"
483 "\t# cibadmin --replace --xml-file $HOME/local.xml\n\n"
484 "Assuming terminal, render configuration in color (green for "
485 "writable, blue for readable, red for\n"
486 "denied) to visualize permissions for user tony:\n\n"
487 "\t# cibadmin --show-access=color --query --user tony | less -r\n\n"
488 "SEE ALSO:\n"
489 " crm(8), pcs(8), crm_shadow(8), crm_diff(8)\n";
490
491 context = pcmk__build_arg_context(args, NULL, NULL, "<command>");
492 g_option_context_set_description(context, desc);
493
494 pcmk__add_main_args(context, extra_prog_entries);
495
496 pcmk__add_arg_group(context, "commands", "Commands:", "Show command help",
497 command_entries);
498 pcmk__add_arg_group(context, "data", "Data:", "Show data help",
499 data_entries);
500 pcmk__add_arg_group(context, "additional", "Additional Options:",
501 "Show additional options", addl_entries);
502 return context;
503 }
504
505 int
506 main(int argc, char **argv)
507 {
508 int rc = pcmk_rc_ok;
509 const char *source = NULL;
510 xmlNode *output = NULL;
511 xmlNode *input = NULL;
512 gchar *acl_cred = NULL;
513
514 GError *error = NULL;
515
516 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
517 gchar **processed_args = pcmk__cmdline_preproc(argv, "ANSUXhotx");
518 GOptionContext *context = build_arg_context(args);
519
520 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
521 exit_code = CRM_EX_USAGE;
522 goto done;
523 }
524
525 if (g_strv_length(processed_args) > 1) {
526 gchar *help = g_option_context_get_help(context, TRUE, NULL);
527 GString *extra = g_string_sized_new(128);
528
529 for (int lpc = 1; processed_args[lpc] != NULL; lpc++) {
530 if (extra->len > 0) {
531 g_string_append_c(extra, ' ');
532 }
533 g_string_append(extra, processed_args[lpc]);
534 }
535
536 exit_code = CRM_EX_USAGE;
537 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
538 "non-option ARGV-elements: %s\n\n%s", extra->str, help);
539 g_free(help);
540 g_string_free(extra, TRUE);
541 goto done;
542 }
543
544 if (args->version || options.extended_version) {
545 g_strfreev(processed_args);
546 pcmk__free_arg_context(context);
547
548
549
550
551
552
553
554 pcmk__cli_help(options.extended_version? '!' : 'v');
555 }
556
557
558
559
560
561
562
563
564 pcmk__cli_init_logging("cibadmin", 0);
565 set_crm_log_level(LOG_CRIT);
566
567 if (args->verbosity > 0) {
568 cib__set_call_options(options.cmd_options, crm_system_name,
569 cib_verbose);
570
571 for (int i = 0; i < args->verbosity; i++) {
572 crm_bump_log_level(argc, argv);
573 }
574 }
575
576 if (options.cib_action == NULL) {
577
578 gchar *help = g_option_context_get_help(context, TRUE, NULL);
579
580 exit_code = CRM_EX_USAGE;
581 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
582 "Must specify a command option\n\n%s", help);
583 g_free(help);
584 goto done;
585 }
586
587 if (strcmp(options.cib_action, "empty") == 0) {
588
589 GString *buf = g_string_sized_new(1024);
590
591 output = createEmptyCib(1);
592 crm_xml_add(output, PCMK_XA_VALIDATE_WITH, options.validate_with);
593
594 pcmk__xml_string(output, pcmk__xml_fmt_pretty, buf, 0);
595 fprintf(stdout, "%s", buf->str);
596 g_string_free(buf, TRUE);
597 goto done;
598 }
599
600 if (cib_action_is_dangerous() && !options.force) {
601 exit_code = CRM_EX_UNSAFE;
602 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
603 "The supplied command is considered dangerous. To prevent "
604 "accidental destruction of the cluster, the --force flag "
605 "is required in order to proceed.");
606 goto done;
607 }
608
609 if (options.message_timeout_sec < 1) {
610
611 options.message_timeout_sec = 30;
612 }
613
614 if (options.section_type == cibadmin_section_xpath) {
615
616 cib__set_call_options(options.cmd_options, crm_system_name,
617 cib_xpath);
618
619 } else if (options.section_type == cibadmin_section_scope) {
620 if (!scope_is_valid(options.cib_section)) {
621
622 fprintf(stderr,
623 "Invalid value '%s' for '--scope'. Operation will apply "
624 "to the entire CIB.\n", options.cib_section);
625 }
626 }
627
628 if (options.allow_create) {
629
630 cib__set_call_options(options.cmd_options, crm_system_name,
631 cib_can_create);
632 }
633
634 if (options.delete_all) {
635
636 cib__set_call_options(options.cmd_options, crm_system_name,
637 cib_multiple);
638 }
639
640 if (options.get_node_path) {
641
642
643
644 cib__set_call_options(options.cmd_options, crm_system_name,
645 cib_xpath_address);
646 }
647
648 if (options.no_children) {
649
650 cib__set_call_options(options.cmd_options, crm_system_name,
651 cib_no_children);
652 }
653
654 if (options.sync_call
655 || (options.acl_render_mode != pcmk__acl_render_none)) {
656
657
658
659
660
661
662
663 cib__set_call_options(options.cmd_options, crm_system_name,
664 cib_sync_call);
665 }
666
667 if (options.input_file != NULL) {
668 input = pcmk__xml_read(options.input_file);
669 source = options.input_file;
670
671 } else if (options.input_xml != NULL) {
672 input = pcmk__xml_parse(options.input_xml);
673 source = "input string";
674
675 } else if (options.input_stdin) {
676 input = pcmk__xml_read(NULL);
677 source = "STDIN";
678
679 } else if (options.acl_render_mode != pcmk__acl_render_none) {
680 char *username = pcmk__uid2username(geteuid());
681 bool required = pcmk_acl_required(username);
682
683 free(username);
684
685 if (required) {
686 if (options.force) {
687 fprintf(stderr, "The supplied command can provide skewed"
688 " result since it is run under user that also"
689 " gets guarded per ACLs on their own right."
690 " Continuing since --force flag was"
691 " provided.\n");
692
693 } else {
694 exit_code = CRM_EX_UNSAFE;
695 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
696 "The supplied command can provide skewed result "
697 "since it is run under user that also gets guarded "
698 "per ACLs in their own right. To accept the risk "
699 "of such a possible distortion (without even "
700 "knowing it at this time), use the --force flag.");
701 goto done;
702 }
703 }
704
705 if (options.cib_user == NULL) {
706 exit_code = CRM_EX_USAGE;
707 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
708 "The supplied command requires -U user specified.");
709 goto done;
710 }
711
712
713
714
715
716
717
718
719
720 acl_cred = options.cib_user;
721 options.cib_user = NULL;
722 }
723
724 if (input != NULL) {
725 crm_log_xml_debug(input, "[admin input]");
726
727 } else if (source != NULL) {
728 exit_code = CRM_EX_CONFIG;
729 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
730 "Couldn't parse input from %s.", source);
731 goto done;
732 }
733
734 if (pcmk__str_eq(options.cib_action, "md5-sum", pcmk__str_casei)) {
735 char *digest = NULL;
736
737 if (input == NULL) {
738 exit_code = CRM_EX_USAGE;
739 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
740 "Please supply XML to process with -X, -x, or -p");
741 goto done;
742 }
743
744 digest = pcmk__digest_on_disk_cib(input);
745 fprintf(stderr, "Digest: ");
746 fprintf(stdout, "%s\n", pcmk__s(digest, "<null>"));
747 free(digest);
748 goto done;
749
750 } else if (strcmp(options.cib_action, "md5-sum-versioned") == 0) {
751 char *digest = NULL;
752
753 if (input == NULL) {
754 exit_code = CRM_EX_USAGE;
755 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
756 "Please supply XML to process with -X, -x, or -p");
757 goto done;
758 }
759
760 digest = pcmk__digest_xml(input, true);
761 fprintf(stdout, "%s\n", pcmk__s(digest, "<null>"));
762 free(digest);
763 goto done;
764
765 } else if (pcmk__str_eq(options.cib_action, PCMK__CIB_REQUEST_MODIFY,
766 pcmk__str_none)) {
767
768
769
770 cib__set_call_options(options.cmd_options, crm_system_name,
771 cib_score_update);
772 }
773
774 rc = do_init();
775 if (rc != pcmk_ok) {
776 rc = pcmk_legacy2rc(rc);
777 exit_code = pcmk_rc2exitc(rc);
778
779 crm_err("Init failed, could not perform requested operations: %s",
780 pcmk_rc_str(rc));
781 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
782 "Init failed, could not perform requested operations: %s",
783 pcmk_rc_str(rc));
784 goto done;
785 }
786
787 rc = do_work(input, &output);
788 if (!pcmk_is_set(options.cmd_options, cib_sync_call)
789 && (the_cib->variant != cib_file)
790 && (rc >= 0)) {
791
792
793
794
795
796 request_id = rc;
797
798 the_cib->cmds->register_callback(the_cib, request_id,
799 options.message_timeout_sec, FALSE,
800 NULL, "cibadmin_op_callback",
801 cibadmin_op_callback);
802
803 mainloop = g_main_loop_new(NULL, FALSE);
804
805 crm_trace("%s waiting for reply from the local CIB", crm_system_name);
806
807 crm_info("Starting mainloop");
808 g_main_loop_run(mainloop);
809
810 } else {
811 rc = pcmk_legacy2rc(rc);
812
813 if ((rc == pcmk_rc_schema_unchanged)
814 && (strcmp(options.cib_action, PCMK__CIB_REQUEST_UPGRADE) == 0)) {
815
816 report_schema_unchanged();
817
818 } else if (rc != pcmk_rc_ok) {
819 crm_err("Call failed: %s", pcmk_rc_str(rc));
820 fprintf(stderr, "Call failed: %s\n", pcmk_rc_str(rc));
821 exit_code = pcmk_rc2exitc(rc);
822
823 if (rc == pcmk_rc_schema_validation) {
824 if (strcmp(options.cib_action,
825 PCMK__CIB_REQUEST_UPGRADE) == 0) {
826 xmlNode *obj = NULL;
827
828 if (the_cib->cmds->query(the_cib, NULL, &obj,
829 options.cmd_options) == pcmk_ok) {
830 pcmk__update_schema(&obj, NULL, true, false);
831 }
832 pcmk__xml_free(obj);
833
834 } else if (output != NULL) {
835
836 pcmk__validate_xml(output, NULL, NULL, NULL);
837 }
838 }
839 }
840 }
841
842 if ((output != NULL)
843 && (options.acl_render_mode != pcmk__acl_render_none)) {
844
845 xmlDoc *acl_evaled_doc;
846 rc = pcmk__acl_annotate_permissions(acl_cred, output->doc, &acl_evaled_doc);
847 if (rc == pcmk_rc_ok) {
848 xmlChar *rendered = NULL;
849
850 rc = pcmk__acl_evaled_render(acl_evaled_doc,
851 options.acl_render_mode, &rendered);
852 if (rc != pcmk_rc_ok) {
853 exit_code = CRM_EX_CONFIG;
854 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
855 "Could not render evaluated access: %s",
856 pcmk_rc_str(rc));
857 goto done;
858 }
859 printf("%s\n", (char *) rendered);
860 free(rendered);
861
862 } else {
863 exit_code = CRM_EX_CONFIG;
864 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
865 "Could not evaluate access per request (%s, error: %s)",
866 acl_cred, pcmk_rc_str(rc));
867 goto done;
868 }
869
870 } else if (output != NULL) {
871 print_xml_output(output);
872 }
873
874 crm_trace("%s exiting normally", crm_system_name);
875
876 done:
877 g_strfreev(processed_args);
878 pcmk__free_arg_context(context);
879
880 g_free(options.cib_user);
881 g_free(options.dest_node);
882 g_free(options.input_file);
883 g_free(options.input_xml);
884 free(options.cib_section);
885 free(options.validate_with);
886
887 g_free(acl_cred);
888 pcmk__xml_free(input);
889 pcmk__xml_free(output);
890
891 rc = cib__clean_up_connection(&the_cib);
892 if (exit_code == CRM_EX_OK) {
893 exit_code = pcmk_rc2exitc(rc);
894 }
895
896 pcmk__output_and_clear_error(&error, NULL);
897 crm_exit(exit_code);
898 }
899
900 static int
901 do_work(xmlNode *input, xmlNode **output)
902 {
903
904 the_cib->call_timeout = options.message_timeout_sec;
905 if ((strcmp(options.cib_action, PCMK__CIB_REQUEST_REPLACE) == 0)
906 && pcmk__xe_is(input, PCMK_XE_CIB)) {
907 xmlNode *status = pcmk_find_cib_element(input, PCMK_XE_STATUS);
908
909 if (status == NULL) {
910 pcmk__xe_create(input, PCMK_XE_STATUS);
911 }
912 }
913
914 crm_trace("Passing \"%s\" to variant_op...", options.cib_action);
915 return cib_internal_op(the_cib, options.cib_action, options.dest_node,
916 options.cib_section, input, output,
917 options.cmd_options, options.cib_user);
918 }
919
920 int
921 do_init(void)
922 {
923 int rc = pcmk_ok;
924
925 the_cib = cib_new();
926 rc = cib__signon_attempts(the_cib, cib_command, 5);
927 if (rc != pcmk_ok) {
928 crm_err("Could not connect to the CIB: %s", pcmk_strerror(rc));
929 fprintf(stderr, "Could not connect to the CIB: %s\n",
930 pcmk_strerror(rc));
931 }
932
933 return rc;
934 }
935
936 void
937 cibadmin_op_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
938 {
939 rc = pcmk_legacy2rc(rc);
940 exit_code = pcmk_rc2exitc(rc);
941
942 if (rc == pcmk_rc_schema_unchanged) {
943 report_schema_unchanged();
944
945 } else if (rc != pcmk_rc_ok) {
946 crm_warn("Call %s failed: %s " QB_XS " rc=%d",
947 options.cib_action, pcmk_rc_str(rc), rc);
948 fprintf(stderr, "Call %s failed: %s\n",
949 options.cib_action, pcmk_rc_str(rc));
950 print_xml_output(output);
951
952 } else if ((strcmp(options.cib_action, PCMK__CIB_REQUEST_QUERY) == 0)
953 && (output == NULL)) {
954 crm_err("Query returned no output");
955 crm_log_xml_err(msg, "no output");
956
957 } else if (output == NULL) {
958 crm_info("Call passed");
959
960 } else {
961 crm_info("Call passed");
962 print_xml_output(output);
963 }
964
965 if (call_id == request_id) {
966 g_main_loop_quit(mainloop);
967
968 } else {
969 crm_info("Message was not the response we were looking for (%d vs. %d)",
970 call_id, request_id);
971 }
972 }