This source file includes following definitions.
- pcmk__cli_option_cleanup
- create_long_opts
- pcmk__set_cli_options
- pcmk__next_cli_option
- pcmk__cli_help
- pcmk__env_option
- pcmk__set_env_option
- pcmk__env_option_enabled
- pcmk__valid_interval_spec
- pcmk__valid_boolean
- pcmk__valid_number
- pcmk__valid_positive_number
- pcmk__valid_quorum
- pcmk__valid_script
- pcmk__valid_percentage
- cluster_option_value
- pcmk__cluster_option
- add_desc
- pcmk__format_option_metadata
- pcmk__validate_cluster_options
1
2
3
4
5
6
7
8
9
10 #ifndef _GNU_SOURCE
11 # define _GNU_SOURCE
12 #endif
13
14 #include <crm_internal.h>
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21
22 #ifdef HAVE_GETOPT_H
23 # include <getopt.h>
24 #endif
25
26 #include <crm/crm.h>
27
28
29
30
31
32
33 static char *crm_short_options = NULL;
34 static const pcmk__cli_option_t *crm_long_options = NULL;
35 static const char *crm_app_description = NULL;
36 static const char *crm_app_usage = NULL;
37
38 void
39 pcmk__cli_option_cleanup(void)
40 {
41 free(crm_short_options);
42 crm_short_options = NULL;
43 }
44
45 static struct option *
46 create_long_opts(const pcmk__cli_option_t *long_options)
47 {
48 struct option *long_opts = NULL;
49
50 #ifdef HAVE_GETOPT_H
51 int index = 0, lpc = 0;
52
53
54
55
56
57
58
59
60 long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option));
61 long_opts[index].name = "__dummmy__";
62 long_opts[index].has_arg = 0;
63 long_opts[index].flag = 0;
64 long_opts[index].val = '_';
65 index++;
66
67
68
69 for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
70 if (long_options[lpc].name[0] == '-') {
71 continue;
72 }
73
74 long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option));
75
76
77 long_opts[index].name = long_options[lpc].name;
78 long_opts[index].has_arg = long_options[lpc].has_arg;
79 long_opts[index].flag = long_options[lpc].flag;
80 long_opts[index].val = long_options[lpc].val;
81 index++;
82 }
83
84
85 long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option));
86 long_opts[index].name = NULL;
87 long_opts[index].has_arg = 0;
88 long_opts[index].flag = 0;
89 long_opts[index].val = 0;
90 #endif
91
92 return long_opts;
93 }
94
95
96
97
98
99
100
101
102
103
104 void
105 pcmk__set_cli_options(const char *short_options, const char *app_usage,
106 const pcmk__cli_option_t *long_options,
107 const char *app_desc)
108 {
109 if (short_options) {
110 crm_short_options = strdup(short_options);
111
112 } else if (long_options) {
113 int lpc = 0;
114 int opt_string_len = 0;
115 char *local_short_options = NULL;
116
117 for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
118 if (long_options[lpc].val && long_options[lpc].val != '-' && long_options[lpc].val < UCHAR_MAX) {
119 local_short_options = pcmk__realloc(local_short_options,
120 opt_string_len + 4);
121 local_short_options[opt_string_len++] = long_options[lpc].val;
122
123 if (long_options[lpc].has_arg == optional_argument) {
124 local_short_options[opt_string_len++] = ':';
125 }
126 if (long_options[lpc].has_arg >= required_argument) {
127 local_short_options[opt_string_len++] = ':';
128 }
129 local_short_options[opt_string_len] = 0;
130 }
131 }
132 crm_short_options = local_short_options;
133 crm_trace("Generated short option string: '%s'", local_short_options);
134 }
135
136 if (long_options) {
137 crm_long_options = long_options;
138 }
139 if (app_desc) {
140 crm_app_description = app_desc;
141 }
142 if (app_usage) {
143 crm_app_usage = app_usage;
144 }
145 }
146
147 int
148 pcmk__next_cli_option(int argc, char **argv, int *index, const char **longname)
149 {
150 #ifdef HAVE_GETOPT_H
151 static struct option *long_opts = NULL;
152
153 if (long_opts == NULL && crm_long_options) {
154 long_opts = create_long_opts(crm_long_options);
155 }
156
157 *index = 0;
158 if (long_opts) {
159 int flag = getopt_long(argc, argv, crm_short_options, long_opts, index);
160
161 switch (flag) {
162 case 0:
163 if (long_opts[*index].val) {
164 return long_opts[*index].val;
165 } else if (longname) {
166 *longname = long_opts[*index].name;
167 } else {
168 crm_notice("Unhandled option --%s", long_opts[*index].name);
169 return flag;
170 }
171 break;
172
173 case -1:
174 break;
175 case ':':
176 crm_trace("Missing argument");
177 pcmk__cli_help('?', CRM_EX_USAGE);
178 break;
179 case '?':
180 pcmk__cli_help('?', (*index? CRM_EX_OK : CRM_EX_USAGE));
181 break;
182 }
183 return flag;
184 }
185 #endif
186
187 if (crm_short_options) {
188 return getopt(argc, argv, crm_short_options);
189 }
190
191 return -1;
192 }
193
194 void
195 pcmk__cli_help(char cmd, crm_exit_t exit_code)
196 {
197 int i = 0;
198 FILE *stream = (exit_code ? stderr : stdout);
199
200 if (cmd == 'v' || cmd == '$') {
201 fprintf(stream, "Pacemaker %s\n", PACEMAKER_VERSION);
202 fprintf(stream, "Written by Andrew Beekhof and "
203 "the Pacemaker project contributors\n");
204 goto out;
205 }
206
207 if (cmd == '!') {
208 fprintf(stream, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
209 goto out;
210 }
211
212 fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description);
213
214 if (crm_app_usage) {
215 fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage);
216 }
217
218 if (crm_long_options) {
219 fprintf(stream, "Options:\n");
220 for (i = 0; crm_long_options[i].name != NULL; i++) {
221 if (crm_long_options[i].flags & pcmk__option_hidden) {
222
223 } else if (crm_long_options[i].flags & pcmk__option_paragraph) {
224 fprintf(stream, "%s\n\n", crm_long_options[i].desc);
225
226 } else if (crm_long_options[i].flags & pcmk__option_example) {
227 fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc);
228
229 } else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) {
230 fprintf(stream, "%s\n", crm_long_options[i].desc);
231
232 } else {
233
234 if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) {
235 fprintf(stream, " -%c,", crm_long_options[i].val);
236 } else {
237 fputs(" ", stream);
238 }
239 fprintf(stream, " --%s%s\t%s\n", crm_long_options[i].name,
240 crm_long_options[i].has_arg == optional_argument ? "[=value]" :
241 crm_long_options[i].has_arg == required_argument ? "=value" : "",
242 crm_long_options[i].desc ? crm_long_options[i].desc : "");
243 }
244 }
245
246 } else if (crm_short_options) {
247 fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description);
248 for (i = 0; crm_short_options[i] != 0; i++) {
249 int has_arg = no_argument ;
250
251 if (crm_short_options[i + 1] == ':') {
252 if (crm_short_options[i + 2] == ':')
253 has_arg = optional_argument ;
254 else
255 has_arg = required_argument ;
256 }
257
258 fprintf(stream, " -%c %s\n", crm_short_options[i],
259 has_arg == optional_argument ? "[value]" :
260 has_arg == required_argument ? "{value}" : "");
261 i += has_arg;
262 }
263 }
264
265 fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT);
266
267 out:
268 crm_exit(exit_code);
269 while(1);
270 }
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289 const char *
290 pcmk__env_option(const char *option)
291 {
292 const char *const prefixes[] = {"PCMK_", "HA_"};
293 char env_name[NAME_MAX];
294 const char *value = NULL;
295
296 CRM_CHECK(!pcmk__str_empty(option), return NULL);
297
298 for (int i = 0; i < PCMK__NELEM(prefixes); i++) {
299 int rv = snprintf(env_name, NAME_MAX, "%s%s", prefixes[i], option);
300
301 if (rv < 0) {
302 crm_err("Failed to write %s%s to buffer: %s", prefixes[i], option,
303 strerror(errno));
304 return NULL;
305 }
306
307 if (rv >= sizeof(env_name)) {
308 crm_trace("\"%s%s\" is too long", prefixes[i], option);
309 continue;
310 }
311
312 value = getenv(env_name);
313 if (value != NULL) {
314 crm_trace("Found %s = %s", env_name, value);
315 return value;
316 }
317 }
318
319 crm_trace("Nothing found for %s", option);
320 return NULL;
321 }
322
323
324
325
326
327
328
329
330
331
332 void
333 pcmk__set_env_option(const char *option, const char *value)
334 {
335 const char *const prefixes[] = {"PCMK_", "HA_"};
336 char env_name[NAME_MAX];
337
338 CRM_CHECK(!pcmk__str_empty(option) && (strchr(option, '=') == NULL),
339 return);
340
341 for (int i = 0; i < PCMK__NELEM(prefixes); i++) {
342 int rv = snprintf(env_name, NAME_MAX, "%s%s", prefixes[i], option);
343
344 if (rv < 0) {
345 crm_err("Failed to write %s%s to buffer: %s", prefixes[i], option,
346 strerror(errno));
347 return;
348 }
349
350 if (rv >= sizeof(env_name)) {
351 crm_trace("\"%s%s\" is too long", prefixes[i], option);
352 continue;
353 }
354
355 if (value != NULL) {
356 crm_trace("Setting %s to %s", env_name, value);
357 rv = setenv(env_name, value, 1);
358 } else {
359 crm_trace("Unsetting %s", env_name);
360 rv = unsetenv(env_name);
361 }
362
363 if (rv < 0) {
364 crm_err("Failed to %sset %s: %s", (value != NULL)? "" : "un",
365 env_name, strerror(errno));
366 }
367 }
368 }
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383 bool
384 pcmk__env_option_enabled(const char *daemon, const char *option)
385 {
386 const char *value = pcmk__env_option(option);
387
388 return (value != NULL)
389 && (crm_is_true(value)
390 || ((daemon != NULL) && (strstr(value, daemon) != NULL)));
391 }
392
393
394
395
396
397
398 bool
399 pcmk__valid_interval_spec(const char *value)
400 {
401 (void) crm_parse_interval_spec(value);
402 return errno == 0;
403 }
404
405 bool
406 pcmk__valid_boolean(const char *value)
407 {
408 int tmp;
409
410 return crm_str_to_boolean(value, &tmp) == 1;
411 }
412
413 bool
414 pcmk__valid_number(const char *value)
415 {
416 if (value == NULL) {
417 return false;
418
419 } else if (pcmk_str_is_minus_infinity(value) ||
420 pcmk_str_is_infinity(value)) {
421 return true;
422 }
423
424 return pcmk__scan_ll(value, NULL, 0LL) == pcmk_rc_ok;
425 }
426
427 bool
428 pcmk__valid_positive_number(const char *value)
429 {
430 long long num = 0LL;
431
432 return pcmk_str_is_infinity(value)
433 || ((pcmk__scan_ll(value, &num, 0LL) == pcmk_rc_ok) && (num > 0));
434 }
435
436 bool
437 pcmk__valid_quorum(const char *value)
438 {
439 return pcmk__strcase_any_of(value, "stop", "freeze", "ignore", "demote", "suicide", NULL);
440 }
441
442 bool
443 pcmk__valid_script(const char *value)
444 {
445 struct stat st;
446
447 if (pcmk__str_eq(value, "/dev/null", pcmk__str_casei)) {
448 return true;
449 }
450
451 if (stat(value, &st) != 0) {
452 crm_err("Script %s does not exist", value);
453 return false;
454 }
455
456 if (S_ISREG(st.st_mode) == 0) {
457 crm_err("Script %s is not a regular file", value);
458 return false;
459 }
460
461 if ((st.st_mode & (S_IXUSR | S_IXGRP)) == 0) {
462 crm_err("Script %s is not executable", value);
463 return false;
464 }
465
466 return true;
467 }
468
469 bool
470 pcmk__valid_percentage(const char *value)
471 {
472 char *end = NULL;
473 long number = strtol(value, &end, 10);
474
475 if (end && (end[0] != '%')) {
476 return false;
477 }
478 return number >= 0;
479 }
480
481
482
483
484
485
486
487
488
489
490
491
492
493 static const char *
494 cluster_option_value(GHashTable *options, bool (*validate)(const char *),
495 const char *name, const char *old_name,
496 const char *def_value)
497 {
498 const char *value = NULL;
499 char *new_value = NULL;
500
501 CRM_ASSERT(name != NULL);
502
503 if (options) {
504 value = g_hash_table_lookup(options, name);
505
506 if ((value == NULL) && old_name) {
507 value = g_hash_table_lookup(options, old_name);
508 if (value != NULL) {
509 pcmk__config_warn("Support for legacy name '%s' for cluster "
510 "option '%s' is deprecated and will be "
511 "removed in a future release",
512 old_name, name);
513
514
515 new_value = strdup(value);
516 g_hash_table_insert(options, strdup(name), new_value);
517 value = new_value;
518 }
519 }
520
521 if (value && validate && (validate(value) == FALSE)) {
522 pcmk__config_err("Using default value for cluster option '%s' "
523 "because '%s' is invalid", name, value);
524 value = NULL;
525 }
526
527 if (value) {
528 return value;
529 }
530 }
531
532
533 value = def_value;
534
535 if (value == NULL) {
536 crm_trace("No value or default provided for cluster option '%s'",
537 name);
538 return NULL;
539 }
540
541 if (validate) {
542 CRM_CHECK(validate(value) != FALSE,
543 crm_err("Bug: default value for cluster option '%s' is invalid", name);
544 return NULL);
545 }
546
547 crm_trace("Using default value '%s' for cluster option '%s'",
548 value, name);
549 if (options) {
550 new_value = strdup(value);
551 g_hash_table_insert(options, strdup(name), new_value);
552 value = new_value;
553 }
554 return value;
555 }
556
557
558
559
560
561
562
563
564
565
566
567 const char *
568 pcmk__cluster_option(GHashTable *options,
569 const pcmk__cluster_option_t *option_list,
570 int len, const char *name)
571 {
572 const char *value = NULL;
573
574 for (int lpc = 0; lpc < len; lpc++) {
575 if (pcmk__str_eq(name, option_list[lpc].name, pcmk__str_casei)) {
576 value = cluster_option_value(options, option_list[lpc].is_valid,
577 option_list[lpc].name,
578 option_list[lpc].alt_name,
579 option_list[lpc].default_value);
580 return value;
581 }
582 }
583 CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name));
584 return NULL;
585 }
586
587
588
589
590
591
592
593
594
595
596
597
598 static void
599 add_desc(GString *s, const char *tag, const char *desc, const char *values,
600 const char *spaces)
601 {
602 char *escaped_en = crm_xml_escape(desc);
603
604 if (spaces != NULL) {
605 g_string_append(s, spaces);
606 }
607 pcmk__g_strcat(s, "<", tag, " lang=\"en\">", escaped_en, NULL);
608
609 if (values != NULL) {
610 pcmk__g_strcat(s, " Allowed values: ", values, NULL);
611 }
612 pcmk__g_strcat(s, "</", tag, ">\n", NULL);
613
614 #ifdef ENABLE_NLS
615 {
616 static const char *locale = NULL;
617
618 char *localized = crm_xml_escape(_(desc));
619
620 if (strcmp(escaped_en, localized) != 0) {
621 if (locale == NULL) {
622 locale = strtok(setlocale(LC_ALL, NULL), "_");
623 }
624
625 if (spaces != NULL) {
626 g_string_append(s, spaces);
627 }
628 pcmk__g_strcat(s, "<", tag, " lang=\"", locale, "\">", localized,
629 NULL);
630
631 if (values != NULL) {
632 pcmk__g_strcat(s, _(" Allowed values: "), _(values), NULL);
633 }
634 pcmk__g_strcat(s, "</", tag, ">\n", NULL);
635 }
636 free(localized);
637 }
638 #endif
639
640 free(escaped_en);
641 }
642
643 gchar *
644 pcmk__format_option_metadata(const char *name, const char *desc_short,
645 const char *desc_long,
646 pcmk__cluster_option_t *option_list, int len)
647 {
648
649 GString *s = g_string_sized_new(13000);
650
651 pcmk__g_strcat(s,
652 "<?xml version=\"1.0\"?>\n"
653 "<resource-agent name=\"", name, "\" "
654 "version=\"" PACEMAKER_VERSION "\">\n"
655 " <version>" PCMK_OCF_VERSION "</version>\n", NULL);
656
657 add_desc(s, "longdesc", desc_long, NULL, " ");
658 add_desc(s, "shortdesc", desc_short, NULL, " ");
659
660 g_string_append(s, " <parameters>\n");
661
662 for (int lpc = 0; lpc < len; lpc++) {
663 const char *opt_name = option_list[lpc].name;
664 const char *opt_type = option_list[lpc].type;
665 const char *opt_values = option_list[lpc].values;
666 const char *opt_default = option_list[lpc].default_value;
667 const char *opt_desc_short = option_list[lpc].description_short;
668 const char *opt_desc_long = option_list[lpc].description_long;
669
670
671 CRM_ASSERT((opt_desc_short != NULL) || (opt_desc_long != NULL));
672
673 if (opt_desc_short == NULL) {
674 opt_desc_short = opt_desc_long;
675 } else if (opt_desc_long == NULL) {
676 opt_desc_long = opt_desc_short;
677 }
678
679
680 CRM_ASSERT(opt_type != NULL);
681
682 pcmk__g_strcat(s, " <parameter name=\"", opt_name, "\">\n", NULL);
683
684 add_desc(s, "longdesc", opt_desc_long, opt_values, " ");
685 add_desc(s, "shortdesc", opt_desc_short, NULL, " ");
686
687 pcmk__g_strcat(s, " <content type=\"", opt_type, "\"", NULL);
688 if (opt_default != NULL) {
689 pcmk__g_strcat(s, " default=\"", opt_default, "\"", NULL);
690 }
691
692 if ((opt_values != NULL) && (strcmp(opt_type, "select") == 0)) {
693 char *str = strdup(opt_values);
694 const char *delim = ", ";
695 char *ptr = strtok(str, delim);
696
697 g_string_append(s, ">\n");
698
699 while (ptr != NULL) {
700 pcmk__g_strcat(s, " <option value=\"", ptr, "\" />\n",
701 NULL);
702 ptr = strtok(NULL, delim);
703 }
704 g_string_append_printf(s, " </content>\n");
705 free(str);
706
707 } else {
708 g_string_append(s, "/>\n");
709 }
710
711 g_string_append(s, " </parameter>\n");
712 }
713 g_string_append(s, " </parameters>\n</resource-agent>\n");
714
715 return g_string_free(s, FALSE);
716 }
717
718 void
719 pcmk__validate_cluster_options(GHashTable *options,
720 pcmk__cluster_option_t *option_list, int len)
721 {
722 for (int lpc = 0; lpc < len; lpc++) {
723 cluster_option_value(options, option_list[lpc].is_valid,
724 option_list[lpc].name,
725 option_list[lpc].alt_name,
726 option_list[lpc].default_value);
727 }
728 }