pacemaker  2.0.4-2deceaa
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
options.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2020 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
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  * Command-line option handling
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
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  * A previous, possibly poor, choice of '?' as the short form of --help
55  * means that getopt_long() returns '?' for both --help and for "unknown option"
56  *
57  * This dummy entry allows us to differentiate between the two in
58  * pcmk__next_cli_option() and exit with the correct error code.
59  */
60  long_opts = realloc_safe(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  // cppcheck seems not to understand the abort-logic in realloc_safe
68  // cppcheck-suppress memleak
69  for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
70  if (long_options[lpc].name[0] == '-') {
71  continue;
72  }
73 
74  long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option));
75  /*fprintf(stderr, "Creating %d %s = %c\n", index,
76  * long_options[lpc].name, long_options[lpc].val); */
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  /* Now create the list terminator */
85  long_opts = realloc_safe(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 
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 = realloc_safe(local_short_options, opt_string_len + 4);
119  local_short_options[opt_string_len++] = long_options[lpc].val;
120  /* getopt(3) says: Two colons mean an option takes an optional arg; */
121  if (long_options[lpc].has_arg == optional_argument) {
122  local_short_options[opt_string_len++] = ':';
123  }
124  if (long_options[lpc].has_arg >= required_argument) {
125  local_short_options[opt_string_len++] = ':';
126  }
127  local_short_options[opt_string_len] = 0;
128  }
129  }
130  crm_short_options = local_short_options;
131  crm_trace("Generated short option string: '%s'", local_short_options);
132  }
133 
134  if (long_options) {
135  crm_long_options = long_options;
136  }
137  if (app_desc) {
138  crm_app_description = app_desc;
139  }
140  if (app_usage) {
141  crm_app_usage = app_usage;
142  }
143 }
144 
145 int
146 pcmk__next_cli_option(int argc, char **argv, int *index, const char **longname)
147 {
148 #ifdef HAVE_GETOPT_H
149  static struct option *long_opts = NULL;
150 
151  if (long_opts == NULL && crm_long_options) {
152  long_opts = create_long_opts(crm_long_options);
153  }
154 
155  *index = 0;
156  if (long_opts) {
157  int flag = getopt_long(argc, argv, crm_short_options, long_opts, index);
158 
159  switch (flag) {
160  case 0:
161  if (long_opts[*index].val) {
162  return long_opts[*index].val;
163  } else if (longname) {
164  *longname = long_opts[*index].name;
165  } else {
166  crm_notice("Unhandled option --%s", long_opts[*index].name);
167  return flag;
168  }
169  case -1: /* End of option processing */
170  break;
171  case ':':
172  crm_trace("Missing argument");
174  break;
175  case '?':
176  pcmk__cli_help('?', (*index? CRM_EX_OK : CRM_EX_USAGE));
177  break;
178  }
179  return flag;
180  }
181 #endif
182 
183  if (crm_short_options) {
184  return getopt(argc, argv, crm_short_options);
185  }
186 
187  return -1;
188 }
189 
190 void
191 pcmk__cli_help(char cmd, crm_exit_t exit_code)
192 {
193  int i = 0;
194  FILE *stream = (exit_code ? stderr : stdout);
195 
196  if (cmd == 'v' || cmd == '$') {
197  fprintf(stream, "Pacemaker %s\n", PACEMAKER_VERSION);
198  fprintf(stream, "Written by Andrew Beekhof\n");
199  goto out;
200  }
201 
202  if (cmd == '!') {
203  fprintf(stream, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
204  goto out;
205  }
206 
207  fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description);
208 
209  if (crm_app_usage) {
210  fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage);
211  }
212 
213  if (crm_long_options) {
214  fprintf(stream, "Options:\n");
215  for (i = 0; crm_long_options[i].name != NULL; i++) {
216  if (crm_long_options[i].flags & pcmk__option_hidden) {
217 
218  } else if (crm_long_options[i].flags & pcmk__option_paragraph) {
219  fprintf(stream, "%s\n\n", crm_long_options[i].desc);
220 
221  } else if (crm_long_options[i].flags & pcmk__option_example) {
222  fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc);
223 
224  } else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) {
225  fprintf(stream, "%s\n", crm_long_options[i].desc);
226 
227  } else {
228  /* is val printable as char ? */
229  if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) {
230  fprintf(stream, " -%c,", crm_long_options[i].val);
231  } else {
232  fputs(" ", stream);
233  }
234  fprintf(stream, " --%s%s\t%s\n", crm_long_options[i].name,
235  crm_long_options[i].has_arg == optional_argument ? "[=value]" :
236  crm_long_options[i].has_arg == required_argument ? "=value" : "",
237  crm_long_options[i].desc ? crm_long_options[i].desc : "");
238  }
239  }
240 
241  } else if (crm_short_options) {
242  fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description);
243  for (i = 0; crm_short_options[i] != 0; i++) {
244  int has_arg = no_argument /* 0 */;
245 
246  if (crm_short_options[i + 1] == ':') {
247  if (crm_short_options[i + 2] == ':')
248  has_arg = optional_argument /* 2 */;
249  else
250  has_arg = required_argument /* 1 */;
251  }
252 
253  fprintf(stream, " -%c %s\n", crm_short_options[i],
254  has_arg == optional_argument ? "[value]" :
255  has_arg == required_argument ? "{value}" : "");
256  i += has_arg;
257  }
258  }
259 
260  fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT);
261 
262  out:
263  crm_exit(exit_code);
264  while(1); // above does not return
265 }
266 
267 
268 /*
269  * Environment variable option handling
270  */
271 
283 const char *
284 pcmk__env_option(const char *option)
285 {
286  char env_name[NAME_MAX];
287  const char *value = NULL;
288 
289  snprintf(env_name, NAME_MAX, "PCMK_%s", option);
290  value = getenv(env_name);
291  if (value != NULL) {
292  crm_trace("Found %s = %s", env_name, value);
293  return value;
294  }
295 
296  snprintf(env_name, NAME_MAX, "HA_%s", option);
297  value = getenv(env_name);
298  if (value != NULL) {
299  crm_trace("Found %s = %s", env_name, value);
300  return value;
301  }
302 
303  crm_trace("Nothing found for %s", option);
304  return NULL;
305 }
306 
316 void
317 pcmk__set_env_option(const char *option, const char *value)
318 {
319  char env_name[NAME_MAX];
320 
321  snprintf(env_name, NAME_MAX, "PCMK_%s", option);
322  if (value) {
323  crm_trace("Setting %s to %s", env_name, value);
324  setenv(env_name, value, 1);
325  } else {
326  crm_trace("Unsetting %s", env_name);
327  unsetenv(env_name);
328  }
329 
330  snprintf(env_name, NAME_MAX, "HA_%s", option);
331  if (value) {
332  crm_trace("Setting %s to %s", env_name, value);
333  setenv(env_name, value, 1);
334  } else {
335  crm_trace("Unsetting %s", env_name);
336  unsetenv(env_name);
337  }
338 }
339 
353 bool
354 pcmk__env_option_enabled(const char *daemon, const char *option)
355 {
356  const char *value = pcmk__env_option(option);
357 
358  return (value != NULL) && (crm_is_true(value) || strstr(value, daemon));
359 }
360 
361 
362 /*
363  * Cluster option handling
364  */
365 
366 bool
367 pcmk__valid_interval_spec(const char *value)
368 {
369  (void) crm_parse_interval_spec(value);
370  return errno == 0;
371 }
372 
373 bool
374 pcmk__valid_boolean(const char *value)
375 {
376  int tmp;
377 
378  return crm_str_to_boolean(value, &tmp) == 1;
379 }
380 
381 bool
382 pcmk__valid_number(const char *value)
383 {
384  if (value == NULL) {
385  return false;
386 
387  } else if (pcmk_str_is_minus_infinity(value) ||
388  pcmk_str_is_infinity(value)) {
389  return true;
390  }
391 
392  errno = 0;
393  crm_parse_ll(value, NULL);
394  return errno == 0;
395 }
396 
397 bool
398 pcmk__valid_positive_number(const char *value)
399 {
400  return pcmk_str_is_infinity(value) ||
401  (crm_parse_ll(value, NULL) > 0);
402 }
403 
404 bool
405 pcmk__valid_quorum(const char *value)
406 {
407  return safe_str_eq(value, "stop")
408  || safe_str_eq(value, "freeze")
409  || safe_str_eq(value, "ignore")
410  || safe_str_eq(value, "suicide");
411 }
412 
413 bool
414 pcmk__valid_script(const char *value)
415 {
416  struct stat st;
417 
418  if (safe_str_eq(value, "/dev/null")) {
419  return true;
420  }
421 
422  if (stat(value, &st) != 0) {
423  crm_err("Script %s does not exist", value);
424  return false;
425  }
426 
427  if (S_ISREG(st.st_mode) == 0) {
428  crm_err("Script %s is not a regular file", value);
429  return false;
430  }
431 
432  if ((st.st_mode & (S_IXUSR | S_IXGRP)) == 0) {
433  crm_err("Script %s is not executable", value);
434  return false;
435  }
436 
437  return true;
438 }
439 
440 bool
441 pcmk__valid_utilization(const char *value)
442 {
443  char *end = NULL;
444  long number = strtol(value, &end, 10);
445 
446  if (end && (end[0] != '%')) {
447  return false;
448  }
449  return number >= 0;
450 }
451 
464 static const char *
465 cluster_option_value(GHashTable *options, bool (*validate)(const char *),
466  const char *name, const char *old_name,
467  const char *def_value)
468 {
469  const char *value = NULL;
470  char *new_value = NULL;
471 
472  CRM_ASSERT(name != NULL);
473 
474  if (options) {
475  value = g_hash_table_lookup(options, name);
476 
477  if ((value == NULL) && old_name) {
478  value = g_hash_table_lookup(options, old_name);
479  if (value != NULL) {
480  pcmk__config_warn("Support for legacy name '%s' for cluster "
481  "option '%s' is deprecated and will be "
482  "removed in a future release",
483  old_name, name);
484 
485  // Inserting copy with current name ensures we only warn once
486  new_value = strdup(value);
487  g_hash_table_insert(options, strdup(name), new_value);
488  value = new_value;
489  }
490  }
491 
492  if (value && validate && (validate(value) == FALSE)) {
493  pcmk__config_err("Using default value for cluster option '%s' "
494  "because '%s' is invalid", name, value);
495  value = NULL;
496  }
497 
498  if (value) {
499  return value;
500  }
501  }
502 
503  // No value found, use default
504  value = def_value;
505 
506  if (value == NULL) {
507  crm_trace("No value or default provided for cluster option '%s'",
508  name);
509  return NULL;
510  }
511 
512  if (validate) {
513  CRM_CHECK(validate(value) != FALSE,
514  crm_err("Bug: default value for cluster option '%s' is invalid", name);
515  return NULL);
516  }
517 
518  crm_trace("Using default value '%s' for cluster option '%s'",
519  value, name);
520  if (options) {
521  new_value = strdup(value);
522  g_hash_table_insert(options, strdup(name), new_value);
523  value = new_value;
524  }
525  return value;
526 }
527 
538 const char *
539 pcmk__cluster_option(GHashTable *options, pcmk__cluster_option_t *option_list,
540  int len, const char *name)
541 {
542  const char *value = NULL;
543 
544  for (int lpc = 0; lpc < len; lpc++) {
545  if (safe_str_eq(name, option_list[lpc].name)) {
546  value = cluster_option_value(options, option_list[lpc].is_valid,
547  option_list[lpc].name,
548  option_list[lpc].alt_name,
549  option_list[lpc].default_value);
550  return value;
551  }
552  }
553  CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name));
554  return NULL;
555 }
556 
557 void
558 pcmk__print_option_metadata(const char *name, const char *version,
559  const char *desc_short, const char *desc_long,
560  pcmk__cluster_option_t *option_list, int len)
561 {
562  int lpc = 0;
563 
564  fprintf(stdout, "<?xml version=\"1.0\"?>"
565  "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
566  "<resource-agent name=\"%s\">\n"
567  " <version>%s</version>\n"
568  " <longdesc lang=\"en\">%s</longdesc>\n"
569  " <shortdesc lang=\"en\">%s</shortdesc>\n"
570  " <parameters>\n", name, version, desc_long, desc_short);
571 
572  for (lpc = 0; lpc < len; lpc++) {
573  if ((option_list[lpc].description_long == NULL)
574  && (option_list[lpc].description_short == NULL)) {
575  continue;
576  }
577  fprintf(stdout, " <parameter name=\"%s\" unique=\"0\">\n"
578  " <shortdesc lang=\"en\">%s</shortdesc>\n"
579  " <content type=\"%s\" default=\"%s\"/>\n"
580  " <longdesc lang=\"en\">%s%s%s</longdesc>\n"
581  " </parameter>\n",
582  option_list[lpc].name,
583  option_list[lpc].description_short,
584  option_list[lpc].type,
585  option_list[lpc].default_value,
586  option_list[lpc].description_long?
587  option_list[lpc].description_long :
588  option_list[lpc].description_short,
589  (option_list[lpc].values? " Allowed values: " : ""),
590  (option_list[lpc].values? option_list[lpc].values : ""));
591  }
592  fprintf(stdout, " </parameters>\n</resource-agent>\n");
593 }
594 
595 void
596 pcmk__validate_cluster_options(GHashTable *options,
597  pcmk__cluster_option_t *option_list, int len)
598 {
599  for (int lpc = 0; lpc < len; lpc++) {
600  cluster_option_value(options, option_list[lpc].is_valid,
601  option_list[lpc].name,
602  option_list[lpc].alt_name,
603  option_list[lpc].default_value);
604  }
605 }
bool pcmk__valid_script(const char *value)
Definition: options.c:414
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:233
A dumping ground.
#define crm_notice(fmt, args...)
Definition: logging.h:365
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:751
#define pcmk__config_err(fmt...)
Definition: internal.h:95
char * crm_system_name
Definition: utils.c:52
#define required_argument
enum crm_exit_e crm_exit_t
void pcmk__set_env_option(const char *option, const char *value)
Set or unset a Pacemaker environment variable option.
Definition: options.c:317
guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition: utils.c:309
_Noreturn void pcmk__cli_help(char cmd, crm_exit_t exit_code)
Definition: options.c:191
const char * pcmk__env_option(const char *option)
Definition: options.c:284
#define PACEMAKER_VERSION
Definition: config.h:509
bool pcmk__valid_positive_number(const char *value)
Definition: options.c:398
bool pcmk__valid_quorum(const char *value)
Definition: options.c:405
bool pcmk__valid_interval_spec(const char *value)
Definition: options.c:367
int daemon(int nochdir, int noclose)
#define BUILD_VERSION
Definition: config.h:8
#define crm_trace(fmt, args...)
Definition: logging.h:369
int setenv(const char *name, const char *value, int why)
void pcmk__set_cli_options(const char *short_options, const char *usage, pcmk__cli_option_t *long_options, const char *app_desc)
Definition: options.c:105
const char * pcmk__cluster_option(GHashTable *options, pcmk__cluster_option_t *option_list, int len, const char *name)
Definition: options.c:539
bool pcmk_str_is_infinity(const char *s)
Definition: utils.c:620
void pcmk__print_option_metadata(const char *name, const char *version, const char *desc_short, const char *desc_long, pcmk__cluster_option_t *option_list, int len)
Definition: options.c:558
bool pcmk__valid_utilization(const char *value)
Definition: options.c:441
bool pcmk__valid_boolean(const char *value)
Definition: options.c:374
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:289
long long crm_parse_ll(const char *text, const char *default_text)
Parse a long long integer value from a string.
Definition: strings.c:100
void pcmk__cli_option_cleanup(void)
Definition: options.c:39
int pcmk__next_cli_option(int argc, char **argv, int *index, const char **longname)
Definition: options.c:146
#define crm_err(fmt, args...)
Definition: logging.h:363
#define CRM_ASSERT(expr)
Definition: results.h:42
bool pcmk_str_is_minus_infinity(const char *s)
Definition: utils.c:625
#define NAME_MAX
Definition: logging.c:92
bool pcmk__valid_number(const char *value)
Definition: options.c:382
#define no_argument
#define PACKAGE_BUGREPORT
Definition: config.h:515
void pcmk__validate_cluster_options(GHashTable *options, pcmk__cluster_option_t *option_list, int len)
Definition: options.c:596
gboolean crm_is_true(const char *s)
Definition: strings.c:278
#define safe_str_eq(a, b)
Definition: util.h:65
char * name
Definition: pcmk_fence.c:30
#define CRM_FEATURES
Definition: config.h:35
uint32_t version
Definition: remote.c:147
#define pcmk__config_warn(fmt...)
Definition: internal.h:100
uint64_t flags
Definition: remote.c:149
bool pcmk__env_option_enabled(const char *daemon, const char *option)
Definition: options.c:354
enum crm_ais_msg_types type
Definition: internal.h:83