pacemaker  2.0.4-2deceaa3ae
Scalable High-Availability cluster resource manager
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, "demote")
411  || safe_str_eq(value, "suicide");
412 }
413 
414 bool
415 pcmk__valid_script(const char *value)
416 {
417  struct stat st;
418 
419  if (safe_str_eq(value, "/dev/null")) {
420  return true;
421  }
422 
423  if (stat(value, &st) != 0) {
424  crm_err("Script %s does not exist", value);
425  return false;
426  }
427 
428  if (S_ISREG(st.st_mode) == 0) {
429  crm_err("Script %s is not a regular file", value);
430  return false;
431  }
432 
433  if ((st.st_mode & (S_IXUSR | S_IXGRP)) == 0) {
434  crm_err("Script %s is not executable", value);
435  return false;
436  }
437 
438  return true;
439 }
440 
441 bool
442 pcmk__valid_utilization(const char *value)
443 {
444  char *end = NULL;
445  long number = strtol(value, &end, 10);
446 
447  if (end && (end[0] != '%')) {
448  return false;
449  }
450  return number >= 0;
451 }
452 
465 static const char *
466 cluster_option_value(GHashTable *options, bool (*validate)(const char *),
467  const char *name, const char *old_name,
468  const char *def_value)
469 {
470  const char *value = NULL;
471  char *new_value = NULL;
472 
473  CRM_ASSERT(name != NULL);
474 
475  if (options) {
476  value = g_hash_table_lookup(options, name);
477 
478  if ((value == NULL) && old_name) {
479  value = g_hash_table_lookup(options, old_name);
480  if (value != NULL) {
481  pcmk__config_warn("Support for legacy name '%s' for cluster "
482  "option '%s' is deprecated and will be "
483  "removed in a future release",
484  old_name, name);
485 
486  // Inserting copy with current name ensures we only warn once
487  new_value = strdup(value);
488  g_hash_table_insert(options, strdup(name), new_value);
489  value = new_value;
490  }
491  }
492 
493  if (value && validate && (validate(value) == FALSE)) {
494  pcmk__config_err("Using default value for cluster option '%s' "
495  "because '%s' is invalid", name, value);
496  value = NULL;
497  }
498 
499  if (value) {
500  return value;
501  }
502  }
503 
504  // No value found, use default
505  value = def_value;
506 
507  if (value == NULL) {
508  crm_trace("No value or default provided for cluster option '%s'",
509  name);
510  return NULL;
511  }
512 
513  if (validate) {
514  CRM_CHECK(validate(value) != FALSE,
515  crm_err("Bug: default value for cluster option '%s' is invalid", name);
516  return NULL);
517  }
518 
519  crm_trace("Using default value '%s' for cluster option '%s'",
520  value, name);
521  if (options) {
522  new_value = strdup(value);
523  g_hash_table_insert(options, strdup(name), new_value);
524  value = new_value;
525  }
526  return value;
527 }
528 
539 const char *
540 pcmk__cluster_option(GHashTable *options, pcmk__cluster_option_t *option_list,
541  int len, const char *name)
542 {
543  const char *value = NULL;
544 
545  for (int lpc = 0; lpc < len; lpc++) {
546  if (safe_str_eq(name, option_list[lpc].name)) {
547  value = cluster_option_value(options, option_list[lpc].is_valid,
548  option_list[lpc].name,
549  option_list[lpc].alt_name,
550  option_list[lpc].default_value);
551  return value;
552  }
553  }
554  CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name));
555  return NULL;
556 }
557 
558 void
559 pcmk__print_option_metadata(const char *name, const char *version,
560  const char *desc_short, const char *desc_long,
561  pcmk__cluster_option_t *option_list, int len)
562 {
563  int lpc = 0;
564 
565  fprintf(stdout, "<?xml version=\"1.0\"?>"
566  "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
567  "<resource-agent name=\"%s\">\n"
568  " <version>%s</version>\n"
569  " <longdesc lang=\"en\">%s</longdesc>\n"
570  " <shortdesc lang=\"en\">%s</shortdesc>\n"
571  " <parameters>\n", name, version, desc_long, desc_short);
572 
573  for (lpc = 0; lpc < len; lpc++) {
574  if ((option_list[lpc].description_long == NULL)
575  && (option_list[lpc].description_short == NULL)) {
576  continue;
577  }
578  fprintf(stdout, " <parameter name=\"%s\" unique=\"0\">\n"
579  " <shortdesc lang=\"en\">%s</shortdesc>\n"
580  " <content type=\"%s\" default=\"%s\"/>\n"
581  " <longdesc lang=\"en\">%s%s%s</longdesc>\n"
582  " </parameter>\n",
583  option_list[lpc].name,
584  option_list[lpc].description_short,
585  option_list[lpc].type,
586  option_list[lpc].default_value,
587  option_list[lpc].description_long?
588  option_list[lpc].description_long :
589  option_list[lpc].description_short,
590  (option_list[lpc].values? " Allowed values: " : ""),
591  (option_list[lpc].values? option_list[lpc].values : ""));
592  }
593  fprintf(stdout, " </parameters>\n</resource-agent>\n");
594 }
595 
596 void
597 pcmk__validate_cluster_options(GHashTable *options,
598  pcmk__cluster_option_t *option_list, int len)
599 {
600  for (int lpc = 0; lpc < len; lpc++) {
601  cluster_option_value(options, option_list[lpc].is_valid,
602  option_list[lpc].name,
603  option_list[lpc].alt_name,
604  option_list[lpc].default_value);
605  }
606 }
pcmk__cli_option_cleanup
void pcmk__cli_option_cleanup()
Definition: options.c:39
pcmk__set_env_option
void pcmk__set_env_option(const char *option, const char *value)
Set or unset a Pacemaker environment variable option.
Definition: options.c:317
CRM_EX_OK
@ CRM_EX_OK
Definition: results.h:173
pcmk__valid_boolean
bool pcmk__valid_boolean(const char *value)
Definition: options.c:374
pcmk__config_warn
#define pcmk__config_warn(fmt...)
Definition: internal.h:100
pcmk__cli_option_s
Definition: options_internal.h:37
st
stonith_t * st
Definition: pcmk_fence.c:27
pcmk__cli_help
void pcmk__cli_help(char cmd, crm_exit_t exit_code)
Definition: options.c:191
flags
uint64_t flags
Definition: remote.c:3
pcmk_str_is_minus_infinity
bool pcmk_str_is_minus_infinity(const char *s)
Definition: utils.c:625
BUILD_VERSION
#define BUILD_VERSION
Definition: config.h:8
crm_str_to_boolean
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:289
pcmk__cluster_option_s
Definition: options_internal.h:78
pcmk__valid_script
bool pcmk__valid_script(const char *value)
Definition: options.c:415
pcmk__valid_positive_number
bool pcmk__valid_positive_number(const char *value)
Definition: options.c:398
pcmk__valid_interval_spec
bool pcmk__valid_interval_spec(const char *value)
Definition: options.c:367
CRM_CHECK
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:233
crm_notice
#define crm_notice(fmt, args...)
Definition: logging.h:365
pcmk_str_is_infinity
bool pcmk_str_is_infinity(const char *s)
Definition: utils.c:620
type
enum crm_ais_msg_types type
Definition: internal.h:3
crm_err
#define crm_err(fmt, args...)
Definition: logging.h:363
pcmk__cli_option_s::name
const char * name
Definition: options_internal.h:40
crm_trace
#define crm_trace(fmt, args...)
Definition: logging.h:369
safe_str_eq
#define safe_str_eq(a, b)
Definition: util.h:65
setenv
int setenv(const char *name, const char *value, int why)
pcmk__set_cli_options
void pcmk__set_cli_options(const char *short_options, const char *app_usage, pcmk__cli_option_t *long_options, const char *app_desc)
Definition: options.c:105
crm_is_true
gboolean crm_is_true(const char *s)
Definition: strings.c:278
pcmk__cli_option_s::has_arg
int has_arg
Definition: options_internal.h:45
crm_parse_ll
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
pcmk__option_example
@ pcmk__option_example
Definition: options_internal.h:34
pcmk__valid_quorum
bool pcmk__valid_quorum(const char *value)
Definition: options.c:405
crm_system_name
char * crm_system_name
Definition: utils.c:52
CRM_FEATURES
#define CRM_FEATURES
Definition: config.h:35
pcmk__cli_option_s::flag
int * flag
Definition: options_internal.h:47
pcmk__config_err
#define pcmk__config_err(fmt...)
Definition: internal.h:95
PACKAGE_BUGREPORT
#define PACKAGE_BUGREPORT
Definition: config.h:517
pcmk__cli_option_s::val
int val
Definition: options_internal.h:49
pcmk__option_paragraph
@ pcmk__option_paragraph
Definition: options_internal.h:33
pcmk__next_cli_option
int pcmk__next_cli_option(int argc, char **argv, int *index, const char **longname)
Definition: options.c:146
pcmk__print_option_metadata
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:559
crm_parse_interval_spec
guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition: utils.c:309
daemon
int daemon(int nochdir, int noclose)
crm_exit_t
enum crm_exit_e crm_exit_t
pcmk__validate_cluster_options
void pcmk__validate_cluster_options(GHashTable *options, pcmk__cluster_option_t *option_list, int len)
Definition: options.c:597
CRM_ASSERT
#define CRM_ASSERT(expr)
Definition: results.h:42
pcmk__valid_number
bool pcmk__valid_number(const char *value)
Definition: options.c:382
version
uint32_t version
Definition: remote.c:1
pcmk__option_hidden
@ pcmk__option_hidden
Definition: options_internal.h:32
pcmk__valid_utilization
bool pcmk__valid_utilization(const char *value)
Definition: options.c:442
no_argument
#define no_argument
Definition: options_internal.h:26
pcmk__env_option_enabled
bool pcmk__env_option_enabled(const char *daemon, const char *option)
Definition: options.c:354
pcmk__cluster_option
const char * pcmk__cluster_option(GHashTable *options, pcmk__cluster_option_t *option_list, int len, const char *name)
Definition: options.c:540
crm_exit
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:751
NAME_MAX
#define NAME_MAX
Definition: logging.c:92
CRM_EX_USAGE
@ CRM_EX_USAGE
Definition: results.h:185
crm_internal.h
PACEMAKER_VERSION
#define PACEMAKER_VERSION
Definition: config.h:511
required_argument
#define required_argument
Definition: options_internal.h:27
name
char * name
Definition: pcmk_fence.c:30
crm.h
A dumping ground.
pcmk__env_option
const char * pcmk__env_option(const char *option)
Definition: options.c:284