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_utilization
- cluster_option_value
- pcmk__cluster_option
- pcmk__print_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 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()
40 {
41 free(crm_short_options);
42 crm_short_options = NULL;
43 }
44
45 static struct option *
46 create_long_opts(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 pcmk__cli_option_t *long_options, const char *app_desc)
107 {
108 if (short_options) {
109 crm_short_options = strdup(short_options);
110
111 } else if (long_options) {
112 int lpc = 0;
113 int opt_string_len = 0;
114 char *local_short_options = NULL;
115
116 for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
117 if (long_options[lpc].val && long_options[lpc].val != '-' && long_options[lpc].val < UCHAR_MAX) {
118 local_short_options = pcmk__realloc(local_short_options,
119 opt_string_len + 4);
120 local_short_options[opt_string_len++] = long_options[lpc].val;
121
122 if (long_options[lpc].has_arg == optional_argument) {
123 local_short_options[opt_string_len++] = ':';
124 }
125 if (long_options[lpc].has_arg >= required_argument) {
126 local_short_options[opt_string_len++] = ':';
127 }
128 local_short_options[opt_string_len] = 0;
129 }
130 }
131 crm_short_options = local_short_options;
132 crm_trace("Generated short option string: '%s'", local_short_options);
133 }
134
135 if (long_options) {
136 crm_long_options = long_options;
137 }
138 if (app_desc) {
139 crm_app_description = app_desc;
140 }
141 if (app_usage) {
142 crm_app_usage = app_usage;
143 }
144 }
145
146 int
147 pcmk__next_cli_option(int argc, char **argv, int *index, const char **longname)
148 {
149 #ifdef HAVE_GETOPT_H
150 static struct option *long_opts = NULL;
151
152 if (long_opts == NULL && crm_long_options) {
153 long_opts = create_long_opts(crm_long_options);
154 }
155
156 *index = 0;
157 if (long_opts) {
158 int flag = getopt_long(argc, argv, crm_short_options, long_opts, index);
159
160 switch (flag) {
161 case 0:
162 if (long_opts[*index].val) {
163 return long_opts[*index].val;
164 } else if (longname) {
165 *longname = long_opts[*index].name;
166 } else {
167 crm_notice("Unhandled option --%s", long_opts[*index].name);
168 return flag;
169 }
170 case -1:
171 break;
172 case ':':
173 crm_trace("Missing argument");
174 pcmk__cli_help('?', CRM_EX_USAGE);
175 break;
176 case '?':
177 pcmk__cli_help('?', (*index? CRM_EX_OK : CRM_EX_USAGE));
178 break;
179 }
180 return flag;
181 }
182 #endif
183
184 if (crm_short_options) {
185 return getopt(argc, argv, crm_short_options);
186 }
187
188 return -1;
189 }
190
191 void
192 pcmk__cli_help(char cmd, crm_exit_t exit_code)
193 {
194 int i = 0;
195 FILE *stream = (exit_code ? stderr : stdout);
196
197 if (cmd == 'v' || cmd == '$') {
198 fprintf(stream, "Pacemaker %s\n", PACEMAKER_VERSION);
199 fprintf(stream, "Written by Andrew Beekhof\n");
200 goto out;
201 }
202
203 if (cmd == '!') {
204 fprintf(stream, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
205 goto out;
206 }
207
208 fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description);
209
210 if (crm_app_usage) {
211 fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage);
212 }
213
214 if (crm_long_options) {
215 fprintf(stream, "Options:\n");
216 for (i = 0; crm_long_options[i].name != NULL; i++) {
217 if (crm_long_options[i].flags & pcmk__option_hidden) {
218
219 } else if (crm_long_options[i].flags & pcmk__option_paragraph) {
220 fprintf(stream, "%s\n\n", crm_long_options[i].desc);
221
222 } else if (crm_long_options[i].flags & pcmk__option_example) {
223 fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc);
224
225 } else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) {
226 fprintf(stream, "%s\n", crm_long_options[i].desc);
227
228 } else {
229
230 if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) {
231 fprintf(stream, " -%c,", crm_long_options[i].val);
232 } else {
233 fputs(" ", stream);
234 }
235 fprintf(stream, " --%s%s\t%s\n", crm_long_options[i].name,
236 crm_long_options[i].has_arg == optional_argument ? "[=value]" :
237 crm_long_options[i].has_arg == required_argument ? "=value" : "",
238 crm_long_options[i].desc ? crm_long_options[i].desc : "");
239 }
240 }
241
242 } else if (crm_short_options) {
243 fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description);
244 for (i = 0; crm_short_options[i] != 0; i++) {
245 int has_arg = no_argument ;
246
247 if (crm_short_options[i + 1] == ':') {
248 if (crm_short_options[i + 2] == ':')
249 has_arg = optional_argument ;
250 else
251 has_arg = required_argument ;
252 }
253
254 fprintf(stream, " -%c %s\n", crm_short_options[i],
255 has_arg == optional_argument ? "[value]" :
256 has_arg == required_argument ? "{value}" : "");
257 i += has_arg;
258 }
259 }
260
261 fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT);
262
263 out:
264 crm_exit(exit_code);
265 while(1);
266 }
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284 const char *
285 pcmk__env_option(const char *option)
286 {
287 char env_name[NAME_MAX];
288 const char *value = NULL;
289
290 snprintf(env_name, NAME_MAX, "PCMK_%s", option);
291 value = getenv(env_name);
292 if (value != NULL) {
293 crm_trace("Found %s = %s", env_name, value);
294 return value;
295 }
296
297 snprintf(env_name, NAME_MAX, "HA_%s", option);
298 value = getenv(env_name);
299 if (value != NULL) {
300 crm_trace("Found %s = %s", env_name, value);
301 return value;
302 }
303
304 crm_trace("Nothing found for %s", option);
305 return NULL;
306 }
307
308
309
310
311
312
313
314
315
316
317 void
318 pcmk__set_env_option(const char *option, const char *value)
319 {
320 char env_name[NAME_MAX];
321
322 snprintf(env_name, NAME_MAX, "PCMK_%s", option);
323 if (value) {
324 crm_trace("Setting %s to %s", env_name, value);
325 setenv(env_name, value, 1);
326 } else {
327 crm_trace("Unsetting %s", env_name);
328 unsetenv(env_name);
329 }
330
331 snprintf(env_name, NAME_MAX, "HA_%s", option);
332 if (value) {
333 crm_trace("Setting %s to %s", env_name, value);
334 setenv(env_name, value, 1);
335 } else {
336 crm_trace("Unsetting %s", env_name);
337 unsetenv(env_name);
338 }
339 }
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354 bool
355 pcmk__env_option_enabled(const char *daemon, const char *option)
356 {
357 const char *value = pcmk__env_option(option);
358
359 return (value != NULL) && (crm_is_true(value) || strstr(value, daemon));
360 }
361
362
363
364
365
366
367 bool
368 pcmk__valid_interval_spec(const char *value)
369 {
370 (void) crm_parse_interval_spec(value);
371 return errno == 0;
372 }
373
374 bool
375 pcmk__valid_boolean(const char *value)
376 {
377 int tmp;
378
379 return crm_str_to_boolean(value, &tmp) == 1;
380 }
381
382 bool
383 pcmk__valid_number(const char *value)
384 {
385 if (value == NULL) {
386 return false;
387
388 } else if (pcmk_str_is_minus_infinity(value) ||
389 pcmk_str_is_infinity(value)) {
390 return true;
391 }
392
393 return pcmk__scan_ll(value, NULL, 0LL) == pcmk_rc_ok;
394 }
395
396 bool
397 pcmk__valid_positive_number(const char *value)
398 {
399 long long num = 0LL;
400
401 return pcmk_str_is_infinity(value)
402 || ((pcmk__scan_ll(value, &num, 0LL) == pcmk_rc_ok) && (num > 0));
403 }
404
405 bool
406 pcmk__valid_quorum(const char *value)
407 {
408 return pcmk__strcase_any_of(value, "stop", "freeze", "ignore", "demote", "suicide", NULL);
409 }
410
411 bool
412 pcmk__valid_script(const char *value)
413 {
414 struct stat st;
415
416 if (pcmk__str_eq(value, "/dev/null", pcmk__str_casei)) {
417 return true;
418 }
419
420 if (stat(value, &st) != 0) {
421 crm_err("Script %s does not exist", value);
422 return false;
423 }
424
425 if (S_ISREG(st.st_mode) == 0) {
426 crm_err("Script %s is not a regular file", value);
427 return false;
428 }
429
430 if ((st.st_mode & (S_IXUSR | S_IXGRP)) == 0) {
431 crm_err("Script %s is not executable", value);
432 return false;
433 }
434
435 return true;
436 }
437
438 bool
439 pcmk__valid_utilization(const char *value)
440 {
441 char *end = NULL;
442 long number = strtol(value, &end, 10);
443
444 if (end && (end[0] != '%')) {
445 return false;
446 }
447 return number >= 0;
448 }
449
450
451
452
453
454
455
456
457
458
459
460
461
462 static const char *
463 cluster_option_value(GHashTable *options, bool (*validate)(const char *),
464 const char *name, const char *old_name,
465 const char *def_value)
466 {
467 const char *value = NULL;
468 char *new_value = NULL;
469
470 CRM_ASSERT(name != NULL);
471
472 if (options) {
473 value = g_hash_table_lookup(options, name);
474
475 if ((value == NULL) && old_name) {
476 value = g_hash_table_lookup(options, old_name);
477 if (value != NULL) {
478 pcmk__config_warn("Support for legacy name '%s' for cluster "
479 "option '%s' is deprecated and will be "
480 "removed in a future release",
481 old_name, name);
482
483
484 new_value = strdup(value);
485 g_hash_table_insert(options, strdup(name), new_value);
486 value = new_value;
487 }
488 }
489
490 if (value && validate && (validate(value) == FALSE)) {
491 pcmk__config_err("Using default value for cluster option '%s' "
492 "because '%s' is invalid", name, value);
493 value = NULL;
494 }
495
496 if (value) {
497 return value;
498 }
499 }
500
501
502 value = def_value;
503
504 if (value == NULL) {
505 crm_trace("No value or default provided for cluster option '%s'",
506 name);
507 return NULL;
508 }
509
510 if (validate) {
511 CRM_CHECK(validate(value) != FALSE,
512 crm_err("Bug: default value for cluster option '%s' is invalid", name);
513 return NULL);
514 }
515
516 crm_trace("Using default value '%s' for cluster option '%s'",
517 value, name);
518 if (options) {
519 new_value = strdup(value);
520 g_hash_table_insert(options, strdup(name), new_value);
521 value = new_value;
522 }
523 return value;
524 }
525
526
527
528
529
530
531
532
533
534
535
536 const char *
537 pcmk__cluster_option(GHashTable *options, pcmk__cluster_option_t *option_list,
538 int len, const char *name)
539 {
540 const char *value = NULL;
541
542 for (int lpc = 0; lpc < len; lpc++) {
543 if (pcmk__str_eq(name, option_list[lpc].name, pcmk__str_casei)) {
544 value = cluster_option_value(options, option_list[lpc].is_valid,
545 option_list[lpc].name,
546 option_list[lpc].alt_name,
547 option_list[lpc].default_value);
548 return value;
549 }
550 }
551 CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name));
552 return NULL;
553 }
554
555 void
556 pcmk__print_option_metadata(const char *name, const char *version,
557 const char *desc_short, const char *desc_long,
558 pcmk__cluster_option_t *option_list, int len)
559 {
560 int lpc = 0;
561
562 fprintf(stdout, "<?xml version=\"1.0\"?>"
563 "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
564 "<resource-agent name=\"%s\">\n"
565 " <version>%s</version>\n"
566 " <longdesc lang=\"en\">%s</longdesc>\n"
567 " <shortdesc lang=\"en\">%s</shortdesc>\n"
568 " <parameters>\n", name, version, desc_long, desc_short);
569
570 for (lpc = 0; lpc < len; lpc++) {
571 if ((option_list[lpc].description_long == NULL)
572 && (option_list[lpc].description_short == NULL)) {
573 continue;
574 }
575 fprintf(stdout, " <parameter name=\"%s\" unique=\"0\">\n"
576 " <shortdesc lang=\"en\">%s</shortdesc>\n"
577 " <content type=\"%s\" default=\"%s\"/>\n"
578 " <longdesc lang=\"en\">%s%s%s</longdesc>\n"
579 " </parameter>\n",
580 option_list[lpc].name,
581 option_list[lpc].description_short,
582 option_list[lpc].type,
583 option_list[lpc].default_value,
584 option_list[lpc].description_long?
585 option_list[lpc].description_long :
586 option_list[lpc].description_short,
587 (option_list[lpc].values? " Allowed values: " : ""),
588 (option_list[lpc].values? option_list[lpc].values : ""));
589 }
590 fprintf(stdout, " </parameters>\n</resource-agent>\n");
591 }
592
593 void
594 pcmk__validate_cluster_options(GHashTable *options,
595 pcmk__cluster_option_t *option_list, int len)
596 {
597 for (int lpc = 0; lpc < len; lpc++) {
598 cluster_option_value(options, option_list[lpc].is_valid,
599 option_list[lpc].name,
600 option_list[lpc].alt_name,
601 option_list[lpc].default_value);
602 }
603 }