This source file includes following definitions.
- PCMK__OUTPUT_ARGS
- delete_cb
- promotion_cb
- update_cb
- utilization_cb
- value_cb
- controller_event_cb
- get_node_name_from_local
- get_node_name_from_controller
- send_attrd_update
- delete_attr_on_node
- command_delete
- update_attr_on_node
- command_update
- output_one_attribute
- command_query
- set_type
- use_attrd
- try_ipc_update
- pattern_used_correctly
- delete_used_correctly
- build_arg_context
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <libgen.h>
19 #include <time.h>
20
21 #include <sys/param.h>
22 #include <sys/types.h>
23
24 #include <crm/crm.h>
25 #include <crm/msg_xml.h>
26 #include <crm/common/xml.h>
27 #include <crm/common/ipc.h>
28 #include <crm/common/util.h>
29 #include <crm/cluster.h>
30
31 #include <crm/cib.h>
32 #include <crm/cib/internal.h>
33 #include <crm/common/attrd_internal.h>
34 #include <crm/common/cmdline_internal.h>
35 #include <crm/common/ipc_attrd_internal.h>
36 #include <crm/common/ipc_controld.h>
37 #include <crm/common/output_internal.h>
38 #include <sys/utsname.h>
39
40 #include <pcmki/pcmki_output.h>
41
42 #define SUMMARY "crm_attribute - query and update Pacemaker cluster options and node attributes"
43
44 GError *error = NULL;
45 crm_exit_t exit_code = CRM_EX_OK;
46 uint64_t cib_opts = cib_sync_call;
47
48 PCMK__OUTPUT_ARGS("attribute", "const char *", "const char *", "const char *",
49 "const char *", "const char *")
50 static int
51 attribute_text(pcmk__output_t *out, va_list args)
52 {
53 const char *scope = va_arg(args, const char *);
54 const char *instance = va_arg(args, const char *);
55 const char *name = va_arg(args, const char *);
56 const char *value = va_arg(args, const char *);
57 const char *host G_GNUC_UNUSED = va_arg(args, const char *);
58
59 if (out->quiet) {
60 if (value != NULL) {
61 pcmk__formatted_printf(out, "%s\n", value);
62 }
63 } else {
64 out->info(out, "%s%s %s%s %s%s value=%s",
65 scope ? "scope=" : "", scope ? scope : "",
66 instance ? "id=" : "", instance ? instance : "",
67 name ? "name=" : "", name ? name : "",
68 value ? value : "(null)");
69 }
70
71 return pcmk_rc_ok;
72 }
73
74 static pcmk__supported_format_t formats[] = {
75 PCMK__SUPPORTED_FORMAT_NONE,
76 PCMK__SUPPORTED_FORMAT_TEXT,
77 PCMK__SUPPORTED_FORMAT_XML,
78 { NULL, NULL, NULL }
79 };
80
81 static pcmk__message_entry_t fmt_functions[] = {
82 { "attribute", "text", attribute_text },
83
84 { NULL, NULL, NULL }
85 };
86
87 struct {
88 char command;
89 gchar *attr_default;
90 gchar *attr_id;
91 gchar *attr_name;
92 gchar *attr_pattern;
93 char *attr_value;
94 char *dest_node;
95 gchar *dest_uname;
96 gboolean inhibit;
97 gchar *set_name;
98 char *set_type;
99 gchar *type;
100 gboolean promotion_score;
101 } options = {
102 .command = 'G',
103 .promotion_score = FALSE
104 };
105
106 #define INDENT " "
107
108 static gboolean
109 delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
110 options.command = 'D';
111 pcmk__str_update(&options.attr_value, NULL);
112 return TRUE;
113 }
114
115 static gboolean
116 promotion_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
117 char *score_name = NULL;
118
119 options.promotion_score = TRUE;
120
121 if (options.attr_name) {
122 g_free(options.attr_name);
123 }
124
125 score_name = pcmk_promotion_score_name(optarg);
126 if (score_name != NULL) {
127 options.attr_name = g_strdup(score_name);
128 free(score_name);
129 } else {
130 options.attr_name = NULL;
131 }
132
133 return TRUE;
134 }
135
136 static gboolean
137 update_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
138 options.command = 'u';
139 pcmk__str_update(&options.attr_value, optarg);
140 return TRUE;
141 }
142
143 static gboolean
144 utilization_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
145 if (options.type) {
146 g_free(options.type);
147 }
148
149 options.type = g_strdup(XML_CIB_TAG_NODES);
150 pcmk__str_update(&options.set_type, XML_TAG_UTILIZATION);
151 return TRUE;
152 }
153
154 static gboolean
155 value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
156 options.command = 'G';
157 pcmk__str_update(&options.attr_value, NULL);
158 return TRUE;
159 }
160
161 static GOptionEntry selecting_entries[] = {
162 { "id", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id,
163 "(Advanced) Operate on instance of specified attribute with this\n"
164 INDENT "XML ID",
165 "XML_ID"
166 },
167
168 { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name,
169 "Operate on attribute or option with this name. For queries, this\n"
170 INDENT "is optional, in which case all matching attributes will be\n"
171 INDENT "returned.",
172 "NAME"
173 },
174
175 { "pattern", 'P', 0, G_OPTION_ARG_STRING, &options.attr_pattern,
176 "Operate on all attributes matching this pattern\n"
177 INDENT "(with -G, or with -v/-D and -l reboot)",
178 "PATTERN"
179 },
180
181 { "promotion", 'p', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, promotion_cb,
182 "Operate on node attribute used as promotion score for specified\n"
183 INDENT "resource, or resource given in OCF_RESOURCE_INSTANCE environment\n"
184 INDENT "variable if none is specified; this also defaults -l/--lifetime\n"
185 INDENT "to reboot (normally invoked from an OCF resource agent)",
186 "RESOURCE"
187 },
188
189 { "set-name", 's', 0, G_OPTION_ARG_STRING, &options.set_name,
190 "(Advanced) Operate on instance of specified attribute that is\n"
191 INDENT "within set with this XML ID",
192 "NAME"
193 },
194
195 { NULL }
196 };
197
198 static GOptionEntry command_entries[] = {
199 { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, delete_cb,
200 "Delete the attribute/option",
201 NULL
202 },
203
204 { "query", 'G', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
205 "Query the current value of the attribute/option.\n"
206 INDENT "See also: -n, -P",
207 NULL
208 },
209
210 { "update", 'v', 0, G_OPTION_ARG_CALLBACK, update_cb,
211 "Update the value of the attribute/option",
212 "VALUE"
213 },
214
215 { NULL }
216 };
217
218 static GOptionEntry addl_entries[] = {
219 { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default,
220 "(Advanced) Default value to display if none is found in configuration",
221 "VALUE"
222 },
223
224 { "lifetime", 'l', 0, G_OPTION_ARG_STRING, &options.type,
225 "Lifetime of the node attribute.\n"
226 INDENT "Valid values: reboot, forever",
227 "LIFETIME"
228 },
229
230 { "node", 'N', 0, G_OPTION_ARG_STRING, &options.dest_uname,
231 "Set a node attribute for named node (instead of a cluster option).\n"
232 INDENT "See also: -l",
233 "NODE"
234 },
235
236 { "type", 't', 0, G_OPTION_ARG_STRING, &options.type,
237 "Which part of the configuration to update/delete/query the option in.\n"
238 INDENT "Valid values: crm_config, rsc_defaults, op_defaults, tickets",
239 "SECTION"
240 },
241
242 { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, utilization_cb,
243 "Set an utilization attribute for the node.",
244 NULL
245 },
246
247 { "inhibit-policy-engine", '!', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.inhibit,
248 NULL, NULL
249 },
250
251 { NULL }
252 };
253
254 static GOptionEntry deprecated_entries[] = {
255 { "attr-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_id,
256 NULL, NULL
257 },
258
259 { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_name,
260 NULL, NULL
261 },
262
263 { "attr-value", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, update_cb,
264 NULL, NULL
265 },
266
267 { "delete-attr", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, delete_cb,
268 NULL, NULL
269 },
270
271 { "get-value", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
272 NULL, NULL
273 },
274
275 { "node-uname", 'U', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.dest_uname,
276 NULL, NULL
277 },
278
279 { NULL }
280 };
281
282 static void
283 controller_event_cb(pcmk_ipc_api_t *controld_api,
284 enum pcmk_ipc_event event_type, crm_exit_t status,
285 void *event_data, void *user_data)
286 {
287 pcmk_controld_api_reply_t *reply = event_data;
288
289 if (event_type != pcmk_ipc_event_reply) {
290 return;
291 }
292
293 if (status != CRM_EX_OK) {
294 exit_code = status;
295 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
296 "Bad reply from controller: %s", crm_exit_str(exit_code));
297 return;
298 }
299
300 if (reply->reply_type != pcmk_controld_reply_info) {
301 exit_code = CRM_EX_PROTOCOL;
302 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
303 "Unknown reply type %d from controller", reply->reply_type);
304 return;
305 }
306
307 if (reply->data.node_info.uname == NULL) {
308 exit_code = CRM_EX_NOHOST;
309 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
310 "Node is not known to cluster");
311 }
312
313 exit_code = CRM_EX_OK;
314 pcmk__str_update(&options.dest_uname, reply->data.node_info.uname);
315 }
316
317 static void
318 get_node_name_from_local(void)
319 {
320 char *hostname = pcmk_hostname();
321
322 g_free(options.dest_uname);
323
324
325
326
327
328 options.dest_uname = g_strdup(hostname);
329 free(hostname);
330 }
331
332 static int
333 get_node_name_from_controller(void)
334 {
335 int rc = pcmk_rc_ok;
336 pcmk_ipc_api_t *controld_api = NULL;
337
338 rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
339 if (controld_api == NULL) {
340 g_set_error(&error, PCMK__RC_ERROR, rc, "Could not connect to controller: %s",
341 pcmk_rc_str(rc));
342 return rc;
343 }
344
345 pcmk_register_ipc_callback(controld_api, controller_event_cb, NULL);
346
347 rc = pcmk_connect_ipc(controld_api, pcmk_ipc_dispatch_sync);
348 if (rc != pcmk_rc_ok) {
349 g_set_error(&error, PCMK__RC_ERROR, rc, "Could not connect to controller: %s",
350 pcmk_rc_str(rc));
351 pcmk_free_ipc_api(controld_api);
352 return rc;
353 }
354
355 rc = pcmk_controld_api_node_info(controld_api, 0);
356
357 if (rc != pcmk_rc_ok) {
358 g_set_error(&error, PCMK__RC_ERROR, rc, "Could not ping controller: %s",
359 pcmk_rc_str(rc));
360 }
361
362
363
364
365
366
367 if (exit_code != CRM_EX_OK) {
368 rc = pcmk_rc_error;
369 }
370
371 pcmk_disconnect_ipc(controld_api);
372 pcmk_free_ipc_api(controld_api);
373
374 return rc;
375 }
376
377 static int
378 send_attrd_update(char command, const char *attr_node, const char *attr_name,
379 const char *attr_value, const char *attr_set,
380 const char *attr_dampen, uint32_t attr_options)
381 {
382 int rc = pcmk_rc_ok;
383 uint32_t opts = attr_options;
384
385 if (options.attr_pattern) {
386 opts |= pcmk__node_attr_pattern;
387 }
388
389 switch (command) {
390 case 'D':
391 rc = pcmk__attrd_api_delete(NULL, attr_node, attr_name, opts);
392 break;
393
394 case 'u':
395 rc = pcmk__attrd_api_update(NULL, attr_node, attr_name,
396 attr_value, NULL, attr_set, NULL,
397 opts | pcmk__node_attr_value);
398 break;
399 }
400
401 if (rc != pcmk_rc_ok) {
402 g_set_error(&error, PCMK__RC_ERROR, rc, "Could not update %s=%s: %s (%d)",
403 attr_name, attr_value, pcmk_rc_str(rc), rc);
404 }
405
406 return rc;
407 }
408
409 struct delete_data_s {
410 pcmk__output_t *out;
411 cib_t *cib;
412 };
413
414 static int
415 delete_attr_on_node(xmlNode *child, void *userdata)
416 {
417 struct delete_data_s *dd = (struct delete_data_s *) userdata;
418
419 const char *attr_name = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
420 int rc = pcmk_rc_ok;
421
422 if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) {
423 return pcmk_rc_ok;
424 }
425
426 rc = cib__delete_node_attr(dd->out, dd->cib, cib_opts, options.type,
427 options.dest_node, options.set_type,
428 options.set_name, options.attr_id,
429 attr_name, options.attr_value, NULL);
430
431 if (rc == ENXIO) {
432 rc = pcmk_rc_ok;
433 }
434
435 return rc;
436 }
437
438 static int
439 command_delete(pcmk__output_t *out, cib_t *cib)
440 {
441 int rc = pcmk_rc_ok;
442
443 xmlNode *result = NULL;
444 bool use_pattern = options.attr_pattern != NULL;
445
446
447 if (use_pattern) {
448 struct delete_data_s dd = { out, cib };
449
450 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
451 options.set_type, options.set_name, NULL, NULL,
452 NULL, &result);
453
454 if (rc != pcmk_rc_ok) {
455 goto done_deleting;
456 }
457
458 rc = pcmk__xe_foreach_child(result, NULL, delete_attr_on_node, &dd);
459
460 if (rc != pcmk_rc_ok) {
461 goto done_deleting;
462 }
463
464 } else {
465 rc = cib__delete_node_attr(out, cib, cib_opts, options.type, options.dest_node,
466 options.set_type, options.set_name, options.attr_id,
467 options.attr_name, options.attr_value, NULL);
468 }
469
470 done_deleting:
471 free_xml(result);
472
473 if (rc == ENXIO) {
474
475
476
477
478 rc = pcmk_rc_ok;
479 }
480
481 return rc;
482 }
483
484 struct update_data_s {
485 pcmk__output_t *out;
486 cib_t *cib;
487 int is_remote_node;
488 };
489
490 static int
491 update_attr_on_node(xmlNode *child, void *userdata)
492 {
493 struct update_data_s *ud = (struct update_data_s *) userdata;
494
495 const char *attr_name = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
496
497 if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) {
498 return pcmk_rc_ok;
499 }
500
501 return cib__update_node_attr(ud->out, ud->cib, cib_opts, options.type,
502 options.dest_node, options.set_type,
503 options.set_name, options.attr_id,
504 attr_name, options.attr_value, NULL,
505 ud->is_remote_node ? "remote" : NULL);
506 }
507
508 static int
509 command_update(pcmk__output_t *out, cib_t *cib, int is_remote_node)
510 {
511 int rc = pcmk_rc_ok;
512
513 xmlNode *result = NULL;
514 bool use_pattern = options.attr_pattern != NULL;
515
516 CRM_LOG_ASSERT(options.type != NULL);
517 CRM_LOG_ASSERT(options.attr_name != NULL);
518 CRM_LOG_ASSERT(options.attr_value != NULL);
519
520
521 if (use_pattern) {
522 struct update_data_s ud = { out, cib, is_remote_node };
523
524 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
525 options.set_type, options.set_name, NULL, NULL,
526 NULL, &result);
527
528 if (rc != pcmk_rc_ok) {
529 goto done_updating;
530 }
531
532 rc = pcmk__xe_foreach_child(result, NULL, update_attr_on_node, &ud);
533
534 if (rc != pcmk_rc_ok) {
535 goto done_updating;
536 }
537
538 } else {
539 rc = cib__update_node_attr(out, cib, cib_opts, options.type,
540 options.dest_node, options.set_type,
541 options.set_name, options.attr_id,
542 options.attr_name, options.attr_value,
543 NULL, is_remote_node ? "remote" : NULL);
544 }
545
546 done_updating:
547 free_xml(result);
548 return rc;
549 }
550
551 struct output_data_s {
552 pcmk__output_t *out;
553 bool use_pattern;
554 bool did_output;
555 };
556
557 static int
558 output_one_attribute(xmlNode *node, void *userdata)
559 {
560 struct output_data_s *od = (struct output_data_s *) userdata;
561
562 const char *name = crm_element_value(node, XML_NVPAIR_ATTR_NAME);
563 const char *value = crm_element_value(node, XML_NVPAIR_ATTR_VALUE);
564 const char *host = crm_element_value(node, PCMK__XA_ATTR_NODE_NAME);
565
566 const char *type = options.type;
567 const char *attr_id = options.attr_id;
568
569 if (od->use_pattern && !pcmk__str_eq(name, options.attr_pattern, pcmk__str_regex)) {
570 return pcmk_rc_ok;
571 }
572
573 od->out->message(od->out, "attribute", type, attr_id, name, value, host);
574 od->did_output = true;
575 crm_info("Read %s='%s' %s%s",
576 pcmk__s(name, "<null>"), pcmk__s(value, ""),
577 options.set_name ? "in " : "", options.set_name ? options.set_name : "");
578
579 return pcmk_rc_ok;
580 }
581
582 static int
583 command_query(pcmk__output_t *out, cib_t *cib)
584 {
585 int rc = pcmk_rc_ok;
586
587 xmlNode *result = NULL;
588 bool use_pattern = options.attr_pattern != NULL;
589
590
591
592
593
594
595 if (use_pattern) {
596 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
597 options.set_type, options.set_name, NULL,
598 NULL, NULL, &result);
599 } else {
600 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
601 options.set_type, options.set_name, options.attr_id,
602 options.attr_name, NULL, &result);
603 }
604
605 if (rc == ENXIO && options.attr_default) {
606
607 const char *type = options.type;
608 const char *attr_id = options.attr_id;
609 const char *attr_name = options.attr_name;
610 const char *attr_default = options.attr_default;
611 const char *dest_uname = options.dest_uname;
612
613 out->message(out, "attribute", type, attr_id, attr_name, attr_default,
614 dest_uname);
615 rc = pcmk_rc_ok;
616
617 } else if (rc != pcmk_rc_ok) {
618
619
620 } else if (xml_has_children(result)) {
621 struct output_data_s od = { out, use_pattern, false };
622
623 pcmk__xe_foreach_child(result, NULL, output_one_attribute, &od);
624
625 if (!od.did_output) {
626 rc = ENXIO;
627 }
628
629 } else {
630 struct output_data_s od = { out, use_pattern, false };
631 output_one_attribute(result, &od);
632 }
633
634 free_xml(result);
635 return rc;
636 }
637
638 static void
639 set_type(void)
640 {
641 if (options.type == NULL) {
642 if (options.promotion_score) {
643
644 options.type = g_strdup(XML_CIB_TAG_STATUS);
645
646 } else if (options.dest_uname != NULL) {
647
648 options.type = g_strdup(XML_CIB_TAG_NODES);
649
650 } else {
651
652 options.type = g_strdup(XML_CIB_TAG_CRMCONFIG);
653 }
654
655 } else if (pcmk__str_eq(options.type, "reboot", pcmk__str_casei)) {
656 options.type = g_strdup(XML_CIB_TAG_STATUS);
657
658 } else if (pcmk__str_eq(options.type, "forever", pcmk__str_casei)) {
659 options.type = g_strdup(XML_CIB_TAG_NODES);
660 }
661 }
662
663 static bool
664 use_attrd(void)
665 {
666
667
668
669 return pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei) &&
670 getenv("CIB_file") == NULL && getenv("CIB_shadow") == NULL;
671 }
672
673 static bool
674 try_ipc_update(void)
675 {
676 return use_attrd() && (options.command == 'D' || options.command == 'u');
677 }
678
679 static bool
680 pattern_used_correctly(void)
681 {
682
683
684
685
686 return options.command == 'G' ||
687 ((options.command == 'u' || options.command == 'D') &&
688 pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei));
689 }
690
691 static bool
692 delete_used_correctly(void)
693 {
694 return options.command != 'D' || options.attr_name != NULL || options.attr_pattern != NULL;
695 }
696
697 static GOptionContext *
698 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
699 GOptionContext *context = NULL;
700
701 GOptionEntry extra_prog_entries[] = {
702 { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
703 "Print only the value on stdout",
704 NULL },
705
706 { "quiet", 'Q', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &(args->quiet),
707 NULL, NULL
708 },
709
710 { NULL }
711 };
712
713 const char *description = "Examples:\n\n"
714 "Add new node attribute called 'location' with the value of 'office' for host 'myhost':\n\n"
715 "\tcrm_attribute --node myhost --name location --update office\n\n"
716 "Query the value of the 'location' node attribute for host 'myhost':\n\n"
717 "\tcrm_attribute --node myhost --name location --query\n\n"
718 "Change the value of the 'location' node attribute for host 'myhost':\n\n"
719 "\tcrm_attribute --node myhost --name location --update backoffice\n\n"
720 "Delete the 'location' node attribute for host 'myhost':\n\n"
721 "\tcrm_attribute --node myhost --name location --delete\n\n"
722 "Query the value of the 'cluster-delay' cluster option:\n\n"
723 "\tcrm_attribute --type crm_config --name cluster-delay --query\n\n"
724 "Query value of the 'cluster-delay' cluster option and print only the value:\n\n"
725 "\tcrm_attribute --type crm_config --name cluster-delay --query --quiet\n\n";
726
727 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
728 pcmk__add_main_args(context, extra_prog_entries);
729 g_option_context_set_description(context, description);
730
731 pcmk__add_arg_group(context, "selections", "Selecting attributes:",
732 "Show selecting options", selecting_entries);
733 pcmk__add_arg_group(context, "command", "Commands:",
734 "Show command options", command_entries);
735 pcmk__add_arg_group(context, "additional", "Additional options:",
736 "Show additional options", addl_entries);
737 pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
738 "Show deprecated options", deprecated_entries);
739
740 return context;
741 }
742
743 int
744 main(int argc, char **argv)
745 {
746 cib_t *the_cib = NULL;
747 int is_remote_node = 0;
748 int attrd_opts = pcmk__node_attr_none;
749
750 int rc = pcmk_rc_ok;
751
752 pcmk__output_t *out = NULL;
753
754 GOptionGroup *output_group = NULL;
755 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
756 gchar **processed_args = pcmk__cmdline_preproc(argv, "NPUdilnpstv");
757 GOptionContext *context = build_arg_context(args, &output_group);
758
759 pcmk__register_formats(output_group, formats);
760 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
761 exit_code = CRM_EX_USAGE;
762 goto done;
763 }
764
765 pcmk__cli_init_logging("crm_attribute", args->verbosity);
766
767 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
768 if (rc != pcmk_rc_ok) {
769 exit_code = CRM_EX_ERROR;
770 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
771 args->output_ty, pcmk_rc_str(rc));
772 goto done;
773 }
774
775 pcmk__register_lib_messages(out);
776 pcmk__register_messages(out, fmt_functions);
777
778 if (args->version) {
779 out->version(out, false);
780 goto done;
781 }
782
783 out->quiet = args->quiet;
784
785 if (options.promotion_score && options.attr_name == NULL) {
786 exit_code = CRM_EX_USAGE;
787 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
788 "-p/--promotion must be called from an OCF resource agent "
789 "or with a resource ID specified");
790 goto done;
791 }
792
793 if (options.inhibit) {
794 crm_warn("Inhibiting notifications for this update");
795 cib__set_call_options(cib_opts, crm_system_name, cib_inhibit_notify);
796 }
797
798 the_cib = cib_new();
799 rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
800 rc = pcmk_legacy2rc(rc);
801
802 if (rc != pcmk_rc_ok) {
803 exit_code = pcmk_rc2exitc(rc);
804 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
805 "Could not connect to the CIB: %s", pcmk_rc_str(rc));
806 goto done;
807 }
808
809 set_type();
810
811
812 if (!pcmk__strcase_any_of(options.type, XML_CIB_TAG_CRMCONFIG, XML_CIB_TAG_TICKETS,
813 NULL)) {
814
815
816
817
818 const char *target = pcmk__node_attr_target(options.dest_uname);
819
820 if (target != NULL) {
821 g_free(options.dest_uname);
822 options.dest_uname = g_strdup(target);
823 } else if (getenv("CIB_file") != NULL && options.dest_uname == NULL) {
824 get_node_name_from_local();
825 }
826
827 if (options.dest_uname == NULL) {
828 rc = get_node_name_from_controller();
829
830 if (rc == pcmk_rc_error) {
831
832
833
834 goto done;
835 } else if (rc != pcmk_rc_ok) {
836
837
838
839 exit_code = pcmk_rc2exitc(rc);
840 goto done;
841 }
842 }
843
844 rc = query_node_uuid(the_cib, options.dest_uname, &options.dest_node, &is_remote_node);
845 rc = pcmk_legacy2rc(rc);
846
847 if (rc != pcmk_rc_ok) {
848 exit_code = pcmk_rc2exitc(rc);
849 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
850 "Could not map name=%s to a UUID", options.dest_uname);
851 goto done;
852 }
853 }
854
855 if (!delete_used_correctly()) {
856 exit_code = CRM_EX_USAGE;
857 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
858 "Error: must specify attribute name or pattern to delete");
859 goto done;
860 }
861
862 if (options.attr_pattern) {
863 if (!pattern_used_correctly()) {
864 exit_code = CRM_EX_USAGE;
865 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
866 "Error: pattern can only be used with query, or with "
867 "till-reboot update or delete");
868 goto done;
869 }
870
871 g_free(options.attr_name);
872 options.attr_name = options.attr_pattern;
873 }
874
875 if (is_remote_node) {
876 attrd_opts = pcmk__node_attr_remote;
877 }
878
879 if (try_ipc_update() &&
880 (send_attrd_update(options.command, options.dest_uname, options.attr_name,
881 options.attr_value, options.set_name, NULL, attrd_opts) == pcmk_rc_ok)) {
882 crm_info("Update %s=%s sent via pacemaker-attrd",
883 options.attr_name, ((options.command == 'D')? "<none>" : options.attr_value));
884
885 } else if (options.command == 'D') {
886 rc = command_delete(out, the_cib);
887
888 } else if (options.command == 'u') {
889 rc = command_update(out, the_cib, is_remote_node);
890
891 } else {
892 rc = command_query(out, the_cib);
893 }
894
895 if (rc == ENOTUNIQ) {
896 exit_code = pcmk_rc2exitc(rc);
897 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
898 "Please choose from one of the matches below and supply the 'id' with --attr-id");
899
900 } else if (rc != pcmk_rc_ok) {
901 exit_code = pcmk_rc2exitc(rc);
902 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
903 "Error performing operation: %s", pcmk_strerror(rc));
904 }
905
906 done:
907 g_strfreev(processed_args);
908 pcmk__free_arg_context(context);
909
910 free(options.attr_default);
911 g_free(options.attr_id);
912 g_free(options.attr_name);
913 free(options.attr_value);
914 free(options.dest_node);
915 g_free(options.dest_uname);
916 g_free(options.set_name);
917 free(options.set_type);
918 g_free(options.type);
919
920 cib__clean_up_connection(&the_cib);
921
922 pcmk__output_and_clear_error(error, out);
923
924 if (out != NULL) {
925 out->finish(out, exit_code, true, NULL);
926 pcmk__output_free(out);
927 }
928
929 return crm_exit(exit_code);
930 }