pacemaker  2.1.5-b7adf64e51
Scalable High-Availability cluster resource manager
options.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2022 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 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
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  * 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 = 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  // cppcheck seems not to understand the abort-logic in pcmk__realloc
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 = pcmk__realloc(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 = 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 
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  /* getopt(3) says: Two colons mean an option takes an optional arg; */
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: /* End of option processing */
174  break;
175  case ':':
176  crm_trace("Missing argument");
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  /* is val printable as char ? */
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 /* 0 */;
250 
251  if (crm_short_options[i + 1] == ':') {
252  if (crm_short_options[i + 2] == ':')
253  has_arg = optional_argument /* 2 */;
254  else
255  has_arg = required_argument /* 1 */;
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); // above does not return
270 }
271 
272 
273 /*
274  * Environment variable option handling
275  */
276 
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 
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 
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  * Cluster option handling
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 
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  // Inserting copy with current name ensures we only warn once
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  // No value found, use default
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 
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 
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  /* big enough to hold "pacemaker-schedulerd metadata" output */
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  // The standard requires long and short parameter descriptions
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  // The standard requires a parameter type
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 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
A dumping ground.
#define crm_notice(fmt, args...)
Definition: logging.h:361
void pcmk__set_env_option(const char *option, const char *value)
Set or unset a Pacemaker environment variable option.
Definition: options.c:333
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:856
void pcmk__cli_help(char cmd, crm_exit_t exit_code)
Definition: options.c:195
const char * name
Definition: cib.c:24
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:928
bool pcmk__valid_script(const char *value)
Definition: options.c:443
#define pcmk__config_warn(fmt...)
bool pcmk__valid_interval_spec(const char *value)
Definition: options.c:399
#define pcmk__config_err(fmt...)
bool pcmk__valid_positive_number(const char *value)
Definition: options.c:428
char * crm_system_name
Definition: utils.c:51
#define required_argument
enum crm_exit_e crm_exit_t
Command line usage error.
Definition: results.h:248
char * strerror(int errnum)
#define PACEMAKER_VERSION
Definition: config.h:505
bool pcmk__valid_percentage(const char *value)
Definition: options.c:470
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition: strings.c:97
int daemon(int nochdir, int noclose)
stonith_t * st
Definition: pcmk_fence.c:28
const char * pcmk__cluster_option(GHashTable *options, const pcmk__cluster_option_t *option_list, int len, const char *name)
Definition: options.c:568
#define BUILD_VERSION
Definition: config.h:8
bool pcmk__valid_quorum(const char *value)
Definition: options.c:437
#define crm_trace(fmt, args...)
Definition: logging.h:365
int setenv(const char *name, const char *value, int why)
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1214
#define PCMK__NELEM(a)
Definition: internal.h:41
#define _(String)
Definition: crm_internal.h:53
Success.
Definition: results.h:234
bool pcmk_str_is_infinity(const char *s)
Definition: utils.c:543
void pcmk__set_cli_options(const char *short_options, const char *app_usage, const pcmk__cli_option_t *long_options, const char *app_desc)
Definition: options.c:105
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:427
void pcmk__cli_option_cleanup(void)
Definition: options.c:39
#define crm_err(fmt, args...)
Definition: logging.h:359
#define CRM_ASSERT(expr)
Definition: results.h:42
bool pcmk_str_is_minus_infinity(const char *s)
Definition: utils.c:548
char guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition: utils.c:271
int pcmk__next_cli_option(int argc, char **argv, int *index, const char **longname)
Definition: options.c:148
#define NAME_MAX
Definition: logging.c:103
#define no_argument
const char * description_short
#define PCMK_OCF_VERSION
Definition: agents.h:52
void pcmk__validate_cluster_options(GHashTable *options, pcmk__cluster_option_t *option_list, int len)
Definition: options.c:719
#define PACKAGE_BUGREPORT
Definition: config.h:511
bool pcmk__env_option_enabled(const char *daemon, const char *option)
Definition: options.c:384
gboolean crm_is_true(const char *s)
Definition: strings.c:416
const char * pcmk__env_option(const char *option)
Definition: options.c:290
bool pcmk__valid_number(const char *value)
Definition: options.c:414
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition: xml.c:1406
#define CRM_FEATURES
Definition: config.h:33
bool pcmk__valid_boolean(const char *value)
Definition: options.c:406
gchar * pcmk__format_option_metadata(const char *name, const char *desc_short, const char *desc_long, pcmk__cluster_option_t *option_list, int len)
Definition: options.c:644
uint64_t flags
Definition: remote.c:215
const char * description_long