pacemaker  2.1.6-802a72226b
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 #include <crm/crm.h>
23 
24 void
25 pcmk__cli_help(char cmd)
26 {
27  if (cmd == 'v' || cmd == '$') {
28  printf("Pacemaker %s\n", PACEMAKER_VERSION);
29  printf("Written by Andrew Beekhof and "
30  "the Pacemaker project contributors\n");
31 
32  } else if (cmd == '!') {
33  printf("Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
34  }
35 
37  while(1); // above does not return
38 }
39 
40 
41 /*
42  * Environment variable option handling
43  */
44 
57 const char *
58 pcmk__env_option(const char *option)
59 {
60  const char *const prefixes[] = {"PCMK_", "HA_"};
61  char env_name[NAME_MAX];
62  const char *value = NULL;
63 
64  CRM_CHECK(!pcmk__str_empty(option), return NULL);
65 
66  for (int i = 0; i < PCMK__NELEM(prefixes); i++) {
67  int rv = snprintf(env_name, NAME_MAX, "%s%s", prefixes[i], option);
68 
69  if (rv < 0) {
70  crm_err("Failed to write %s%s to buffer: %s", prefixes[i], option,
71  strerror(errno));
72  return NULL;
73  }
74 
75  if (rv >= sizeof(env_name)) {
76  crm_trace("\"%s%s\" is too long", prefixes[i], option);
77  continue;
78  }
79 
80  value = getenv(env_name);
81  if (value != NULL) {
82  crm_trace("Found %s = %s", env_name, value);
83  return value;
84  }
85  }
86 
87  crm_trace("Nothing found for %s", option);
88  return NULL;
89 }
90 
100 void
101 pcmk__set_env_option(const char *option, const char *value)
102 {
103  const char *const prefixes[] = {"PCMK_", "HA_"};
104  char env_name[NAME_MAX];
105 
106  CRM_CHECK(!pcmk__str_empty(option) && (strchr(option, '=') == NULL),
107  return);
108 
109  for (int i = 0; i < PCMK__NELEM(prefixes); i++) {
110  int rv = snprintf(env_name, NAME_MAX, "%s%s", prefixes[i], option);
111 
112  if (rv < 0) {
113  crm_err("Failed to write %s%s to buffer: %s", prefixes[i], option,
114  strerror(errno));
115  return;
116  }
117 
118  if (rv >= sizeof(env_name)) {
119  crm_trace("\"%s%s\" is too long", prefixes[i], option);
120  continue;
121  }
122 
123  if (value != NULL) {
124  crm_trace("Setting %s to %s", env_name, value);
125  rv = setenv(env_name, value, 1);
126  } else {
127  crm_trace("Unsetting %s", env_name);
128  rv = unsetenv(env_name);
129  }
130 
131  if (rv < 0) {
132  crm_err("Failed to %sset %s: %s", (value != NULL)? "" : "un",
133  env_name, strerror(errno));
134  }
135  }
136 }
137 
151 bool
152 pcmk__env_option_enabled(const char *daemon, const char *option)
153 {
154  const char *value = pcmk__env_option(option);
155 
156  return (value != NULL)
157  && (crm_is_true(value)
158  || ((daemon != NULL) && (strstr(value, daemon) != NULL)));
159 }
160 
161 
162 /*
163  * Cluster option handling
164  */
165 
166 bool
167 pcmk__valid_interval_spec(const char *value)
168 {
169  (void) crm_parse_interval_spec(value);
170  return errno == 0;
171 }
172 
173 bool
174 pcmk__valid_boolean(const char *value)
175 {
176  int tmp;
177 
178  return crm_str_to_boolean(value, &tmp) == 1;
179 }
180 
181 bool
182 pcmk__valid_number(const char *value)
183 {
184  if (value == NULL) {
185  return false;
186 
187  } else if (pcmk_str_is_minus_infinity(value) ||
188  pcmk_str_is_infinity(value)) {
189  return true;
190  }
191 
192  return pcmk__scan_ll(value, NULL, 0LL) == pcmk_rc_ok;
193 }
194 
195 bool
196 pcmk__valid_positive_number(const char *value)
197 {
198  long long num = 0LL;
199 
200  return pcmk_str_is_infinity(value)
201  || ((pcmk__scan_ll(value, &num, 0LL) == pcmk_rc_ok) && (num > 0));
202 }
203 
204 bool
205 pcmk__valid_quorum(const char *value)
206 {
207  return pcmk__strcase_any_of(value, "stop", "freeze", "ignore", "demote", "suicide", NULL);
208 }
209 
210 bool
211 pcmk__valid_script(const char *value)
212 {
213  struct stat st;
214 
215  if (pcmk__str_eq(value, "/dev/null", pcmk__str_casei)) {
216  return true;
217  }
218 
219  if (stat(value, &st) != 0) {
220  crm_err("Script %s does not exist", value);
221  return false;
222  }
223 
224  if (S_ISREG(st.st_mode) == 0) {
225  crm_err("Script %s is not a regular file", value);
226  return false;
227  }
228 
229  if ((st.st_mode & (S_IXUSR | S_IXGRP)) == 0) {
230  crm_err("Script %s is not executable", value);
231  return false;
232  }
233 
234  return true;
235 }
236 
237 bool
238 pcmk__valid_percentage(const char *value)
239 {
240  char *end = NULL;
241  long number = strtol(value, &end, 10);
242 
243  if (end && (end[0] != '%')) {
244  return false;
245  }
246  return number >= 0;
247 }
248 
261 static const char *
262 cluster_option_value(GHashTable *options, bool (*validate)(const char *),
263  const char *name, const char *old_name,
264  const char *def_value)
265 {
266  const char *value = NULL;
267  char *new_value = NULL;
268 
269  CRM_ASSERT(name != NULL);
270 
271  if (options) {
272  value = g_hash_table_lookup(options, name);
273 
274  if ((value == NULL) && old_name) {
275  value = g_hash_table_lookup(options, old_name);
276  if (value != NULL) {
277  pcmk__config_warn("Support for legacy name '%s' for cluster "
278  "option '%s' is deprecated and will be "
279  "removed in a future release",
280  old_name, name);
281 
282  // Inserting copy with current name ensures we only warn once
283  new_value = strdup(value);
284  g_hash_table_insert(options, strdup(name), new_value);
285  value = new_value;
286  }
287  }
288 
289  if (value && validate && (validate(value) == FALSE)) {
290  pcmk__config_err("Using default value for cluster option '%s' "
291  "because '%s' is invalid", name, value);
292  value = NULL;
293  }
294 
295  if (value) {
296  return value;
297  }
298  }
299 
300  // No value found, use default
301  value = def_value;
302 
303  if (value == NULL) {
304  crm_trace("No value or default provided for cluster option '%s'",
305  name);
306  return NULL;
307  }
308 
309  if (validate) {
310  CRM_CHECK(validate(value) != FALSE,
311  crm_err("Bug: default value for cluster option '%s' is invalid", name);
312  return NULL);
313  }
314 
315  crm_trace("Using default value '%s' for cluster option '%s'",
316  value, name);
317  if (options) {
318  new_value = strdup(value);
319  g_hash_table_insert(options, strdup(name), new_value);
320  value = new_value;
321  }
322  return value;
323 }
324 
336 const char *
337 pcmk__cluster_option(GHashTable *options,
338  const pcmk__cluster_option_t *option_list,
339  int len, const char *name)
340 {
341  const char *value = NULL;
342 
343  for (int lpc = 0; lpc < len; lpc++) {
344  if (pcmk__str_eq(name, option_list[lpc].name, pcmk__str_casei)) {
345  value = cluster_option_value(options, option_list[lpc].is_valid,
346  option_list[lpc].name,
347  option_list[lpc].alt_name,
348  option_list[lpc].default_value);
349  return value;
350  }
351  }
352  CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name));
353  return NULL;
354 }
355 
367 static void
368 add_desc(GString *s, const char *tag, const char *desc, const char *values,
369  const char *spaces)
370 {
371  char *escaped_en = crm_xml_escape(desc);
372 
373  if (spaces != NULL) {
374  g_string_append(s, spaces);
375  }
376  pcmk__g_strcat(s, "<", tag, " lang=\"en\">", escaped_en, NULL);
377 
378  if (values != NULL) {
379  pcmk__g_strcat(s, " Allowed values: ", values, NULL);
380  }
381  pcmk__g_strcat(s, "</", tag, ">\n", NULL);
382 
383 #ifdef ENABLE_NLS
384  {
385  static const char *locale = NULL;
386 
387  char *localized = crm_xml_escape(_(desc));
388 
389  if (strcmp(escaped_en, localized) != 0) {
390  if (locale == NULL) {
391  locale = strtok(setlocale(LC_ALL, NULL), "_");
392  }
393 
394  if (spaces != NULL) {
395  g_string_append(s, spaces);
396  }
397  pcmk__g_strcat(s, "<", tag, " lang=\"", locale, "\">", localized,
398  NULL);
399 
400  if (values != NULL) {
401  pcmk__g_strcat(s, _(" Allowed values: "), _(values), NULL);
402  }
403  pcmk__g_strcat(s, "</", tag, ">\n", NULL);
404  }
405  free(localized);
406  }
407 #endif
408 
409  free(escaped_en);
410 }
411 
412 gchar *
413 pcmk__format_option_metadata(const char *name, const char *desc_short,
414  const char *desc_long,
415  pcmk__cluster_option_t *option_list, int len)
416 {
417  /* big enough to hold "pacemaker-schedulerd metadata" output */
418  GString *s = g_string_sized_new(13000);
419 
420  pcmk__g_strcat(s,
421  "<?xml version=\"1.0\"?>\n"
422  "<resource-agent name=\"", name, "\" "
423  "version=\"" PACEMAKER_VERSION "\">\n"
424  " <version>" PCMK_OCF_VERSION "</version>\n", NULL);
425 
426  add_desc(s, "longdesc", desc_long, NULL, " ");
427  add_desc(s, "shortdesc", desc_short, NULL, " ");
428 
429  g_string_append(s, " <parameters>\n");
430 
431  for (int lpc = 0; lpc < len; lpc++) {
432  const char *opt_name = option_list[lpc].name;
433  const char *opt_type = option_list[lpc].type;
434  const char *opt_values = option_list[lpc].values;
435  const char *opt_default = option_list[lpc].default_value;
436  const char *opt_desc_short = option_list[lpc].description_short;
437  const char *opt_desc_long = option_list[lpc].description_long;
438 
439  // The standard requires long and short parameter descriptions
440  CRM_ASSERT((opt_desc_short != NULL) || (opt_desc_long != NULL));
441 
442  if (opt_desc_short == NULL) {
443  opt_desc_short = opt_desc_long;
444  } else if (opt_desc_long == NULL) {
445  opt_desc_long = opt_desc_short;
446  }
447 
448  // The standard requires a parameter type
449  CRM_ASSERT(opt_type != NULL);
450 
451  pcmk__g_strcat(s, " <parameter name=\"", opt_name, "\">\n", NULL);
452 
453  add_desc(s, "longdesc", opt_desc_long, opt_values, " ");
454  add_desc(s, "shortdesc", opt_desc_short, NULL, " ");
455 
456  pcmk__g_strcat(s, " <content type=\"", opt_type, "\"", NULL);
457  if (opt_default != NULL) {
458  pcmk__g_strcat(s, " default=\"", opt_default, "\"", NULL);
459  }
460 
461  if ((opt_values != NULL) && (strcmp(opt_type, "select") == 0)) {
462  char *str = strdup(opt_values);
463  const char *delim = ", ";
464  char *ptr = strtok(str, delim);
465 
466  g_string_append(s, ">\n");
467 
468  while (ptr != NULL) {
469  pcmk__g_strcat(s, " <option value=\"", ptr, "\" />\n",
470  NULL);
471  ptr = strtok(NULL, delim);
472  }
473  g_string_append_printf(s, " </content>\n");
474  free(str);
475 
476  } else {
477  g_string_append(s, "/>\n");
478  }
479 
480  g_string_append(s, " </parameter>\n");
481  }
482  g_string_append(s, " </parameters>\n</resource-agent>\n");
483 
484  return g_string_free(s, FALSE);
485 }
486 
487 void
488 pcmk__validate_cluster_options(GHashTable *options,
489  pcmk__cluster_option_t *option_list, int len)
490 {
491  for (int lpc = 0; lpc < len; lpc++) {
492  cluster_option_value(options, option_list[lpc].is_valid,
493  option_list[lpc].name,
494  option_list[lpc].alt_name,
495  option_list[lpc].default_value);
496  }
497 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:235
A dumping ground.
void pcmk__set_env_option(const char *option, const char *value)
Set or unset a Pacemaker environment variable option.
Definition: options.c:101
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:874
const char * name
Definition: cib.c:24
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:933
bool pcmk__valid_script(const char *value)
Definition: options.c:211
#define pcmk__config_warn(fmt...)
bool pcmk__valid_interval_spec(const char *value)
Definition: options.c:167
#define pcmk__config_err(fmt...)
bool pcmk__valid_positive_number(const char *value)
Definition: options.c:196
char * strerror(int errnum)
#define PACEMAKER_VERSION
Definition: config.h:502
bool pcmk__valid_percentage(const char *value)
Definition: options.c:238
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:337
#define BUILD_VERSION
Definition: config.h:8
bool pcmk__valid_quorum(const char *value)
Definition: options.c:205
#define crm_trace(fmt, args...)
Definition: logging.h:383
int setenv(const char *name, const char *value, int why)
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1217
#define PCMK__NELEM(a)
Definition: internal.h:44
#define _(String)
Definition: crm_internal.h:53
Success.
Definition: results.h:237
bool pcmk_str_is_infinity(const char *s)
Definition: utils.c:543
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:427
#define crm_err(fmt, args...)
Definition: logging.h:377
#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
#define NAME_MAX
Definition: logging.c:104
const char * description_short
#define PCMK_OCF_VERSION
Definition: agents.h:54
void pcmk__validate_cluster_options(GHashTable *options, pcmk__cluster_option_t *option_list, int len)
Definition: options.c:488
void pcmk__cli_help(char cmd)
Definition: options.c:25
bool pcmk__env_option_enabled(const char *daemon, const char *option)
Definition: options.c:152
gboolean crm_is_true(const char *s)
Definition: strings.c:416
const char * pcmk__env_option(const char *option)
Definition: options.c:58
bool pcmk__valid_number(const char *value)
Definition: options.c:182
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition: xml.c:1310
#define CRM_FEATURES
Definition: config.h:33
bool pcmk__valid_boolean(const char *value)
Definition: options.c:174
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:413
const char * description_long