pacemaker  2.1.7-0f7f88312f
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 
107 void
108 pcmk__set_env_option(const char *option, const char *value, bool compat)
109 {
110  // @COMPAT Drop support for "HA_" options eventually
111  const char *const prefixes[] = {"PCMK_", "HA_"};
112  char env_name[NAME_MAX];
113 
114  CRM_CHECK(!pcmk__str_empty(option) && (strchr(option, '=') == NULL),
115  return);
116 
117  for (int i = 0; i < PCMK__NELEM(prefixes); i++) {
118  int rv = snprintf(env_name, NAME_MAX, "%s%s", prefixes[i], option);
119 
120  if (rv < 0) {
121  crm_err("Failed to write %s%s to buffer: %s", prefixes[i], option,
122  strerror(errno));
123  return;
124  }
125 
126  if (rv >= sizeof(env_name)) {
127  crm_trace("\"%s%s\" is too long", prefixes[i], option);
128  continue;
129  }
130 
131  if (value != NULL) {
132  crm_trace("Setting %s to %s", env_name, value);
133  rv = setenv(env_name, value, 1);
134  } else {
135  crm_trace("Unsetting %s", env_name);
136  rv = unsetenv(env_name);
137  }
138 
139  if (rv < 0) {
140  crm_err("Failed to %sset %s: %s", (value != NULL)? "" : "un",
141  env_name, strerror(errno));
142  }
143 
144  if (!compat && (value != NULL)) {
145  // For set, don't proceed to HA_<option> unless compat is enabled
146  break;
147  }
148  }
149 }
150 
164 bool
165 pcmk__env_option_enabled(const char *daemon, const char *option)
166 {
167  const char *value = pcmk__env_option(option);
168 
169  return (value != NULL)
170  && (crm_is_true(value)
171  || ((daemon != NULL) && (strstr(value, daemon) != NULL)));
172 }
173 
174 
175 /*
176  * Cluster option handling
177  */
178 
179 bool
180 pcmk__valid_interval_spec(const char *value)
181 {
182  (void) crm_parse_interval_spec(value);
183  return errno == 0;
184 }
185 
186 bool
187 pcmk__valid_boolean(const char *value)
188 {
189  int tmp;
190 
191  return crm_str_to_boolean(value, &tmp) == 1;
192 }
193 
194 bool
195 pcmk__valid_number(const char *value)
196 {
197  if (value == NULL) {
198  return false;
199 
200  } else if (pcmk_str_is_minus_infinity(value) ||
201  pcmk_str_is_infinity(value)) {
202  return true;
203  }
204 
205  return pcmk__scan_ll(value, NULL, 0LL) == pcmk_rc_ok;
206 }
207 
208 bool
209 pcmk__valid_positive_number(const char *value)
210 {
211  long long num = 0LL;
212 
213  return pcmk_str_is_infinity(value)
214  || ((pcmk__scan_ll(value, &num, 0LL) == pcmk_rc_ok) && (num > 0));
215 }
216 
217 bool
218 pcmk__valid_quorum(const char *value)
219 {
220  return pcmk__strcase_any_of(value, "stop", "freeze", "ignore", "demote", "suicide", NULL);
221 }
222 
223 bool
224 pcmk__valid_script(const char *value)
225 {
226  struct stat st;
227 
228  if (pcmk__str_eq(value, "/dev/null", pcmk__str_casei)) {
229  return true;
230  }
231 
232  if (stat(value, &st) != 0) {
233  crm_err("Script %s does not exist", value);
234  return false;
235  }
236 
237  if (S_ISREG(st.st_mode) == 0) {
238  crm_err("Script %s is not a regular file", value);
239  return false;
240  }
241 
242  if ((st.st_mode & (S_IXUSR | S_IXGRP)) == 0) {
243  crm_err("Script %s is not executable", value);
244  return false;
245  }
246 
247  return true;
248 }
249 
250 bool
251 pcmk__valid_percentage(const char *value)
252 {
253  char *end = NULL;
254  long number = strtol(value, &end, 10);
255 
256  if (end && (end[0] != '%')) {
257  return false;
258  }
259  return number >= 0;
260 }
261 
274 static const char *
275 cluster_option_value(GHashTable *options, bool (*validate)(const char *),
276  const char *name, const char *old_name,
277  const char *def_value)
278 {
279  const char *value = NULL;
280  char *new_value = NULL;
281 
282  CRM_ASSERT(name != NULL);
283 
284  if (options) {
285  value = g_hash_table_lookup(options, name);
286 
287  if ((value == NULL) && old_name) {
288  value = g_hash_table_lookup(options, old_name);
289  if (value != NULL) {
290  pcmk__config_warn("Support for legacy name '%s' for cluster "
291  "option '%s' is deprecated and will be "
292  "removed in a future release",
293  old_name, name);
294 
295  // Inserting copy with current name ensures we only warn once
296  new_value = strdup(value);
297  g_hash_table_insert(options, strdup(name), new_value);
298  value = new_value;
299  }
300  }
301 
302  if (value && validate && (validate(value) == FALSE)) {
303  pcmk__config_err("Using default value for cluster option '%s' "
304  "because '%s' is invalid", name, value);
305  value = NULL;
306  }
307 
308  if (value) {
309  return value;
310  }
311  }
312 
313  // No value found, use default
314  value = def_value;
315 
316  if (value == NULL) {
317  crm_trace("No value or default provided for cluster option '%s'",
318  name);
319  return NULL;
320  }
321 
322  if (validate) {
323  CRM_CHECK(validate(value) != FALSE,
324  crm_err("Bug: default value for cluster option '%s' is invalid", name);
325  return NULL);
326  }
327 
328  crm_trace("Using default value '%s' for cluster option '%s'",
329  value, name);
330  if (options) {
331  new_value = strdup(value);
332  g_hash_table_insert(options, strdup(name), new_value);
333  value = new_value;
334  }
335  return value;
336 }
337 
349 const char *
350 pcmk__cluster_option(GHashTable *options,
351  const pcmk__cluster_option_t *option_list,
352  int len, const char *name)
353 {
354  const char *value = NULL;
355 
356  for (int lpc = 0; lpc < len; lpc++) {
357  if (pcmk__str_eq(name, option_list[lpc].name, pcmk__str_casei)) {
358  value = cluster_option_value(options, option_list[lpc].is_valid,
359  option_list[lpc].name,
360  option_list[lpc].alt_name,
361  option_list[lpc].default_value);
362  return value;
363  }
364  }
365  CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name));
366  return NULL;
367 }
368 
380 static void
381 add_desc(GString *s, const char *tag, const char *desc, const char *values,
382  const char *spaces)
383 {
384  char *escaped_en = crm_xml_escape(desc);
385 
386  if (spaces != NULL) {
387  g_string_append(s, spaces);
388  }
389  pcmk__g_strcat(s, "<", tag, " lang=\"en\">", escaped_en, NULL);
390 
391  if (values != NULL) {
392  pcmk__g_strcat(s, " Allowed values: ", values, NULL);
393  }
394  pcmk__g_strcat(s, "</", tag, ">\n", NULL);
395 
396 #ifdef ENABLE_NLS
397  {
398  static const char *locale = NULL;
399 
400  char *localized = crm_xml_escape(_(desc));
401 
402  if (strcmp(escaped_en, localized) != 0) {
403  if (locale == NULL) {
404  locale = strtok(setlocale(LC_ALL, NULL), "_");
405  }
406 
407  if (spaces != NULL) {
408  g_string_append(s, spaces);
409  }
410  pcmk__g_strcat(s, "<", tag, " lang=\"", locale, "\">", localized,
411  NULL);
412 
413  if (values != NULL) {
414  pcmk__g_strcat(s, _(" Allowed values: "), _(values), NULL);
415  }
416  pcmk__g_strcat(s, "</", tag, ">\n", NULL);
417  }
418  free(localized);
419  }
420 #endif
421 
422  free(escaped_en);
423 }
424 
425 gchar *
426 pcmk__format_option_metadata(const char *name, const char *desc_short,
427  const char *desc_long,
428  pcmk__cluster_option_t *option_list, int len)
429 {
430  /* big enough to hold "pacemaker-schedulerd metadata" output */
431  GString *s = g_string_sized_new(13000);
432 
433  pcmk__g_strcat(s,
434  "<?xml version=\"1.0\"?>\n"
435  "<resource-agent name=\"", name, "\" "
436  "version=\"" PACEMAKER_VERSION "\">\n"
437  " <version>" PCMK_OCF_VERSION "</version>\n", NULL);
438 
439  add_desc(s, "longdesc", desc_long, NULL, " ");
440  add_desc(s, "shortdesc", desc_short, NULL, " ");
441 
442  g_string_append(s, " <parameters>\n");
443 
444  for (int lpc = 0; lpc < len; lpc++) {
445  const char *opt_name = option_list[lpc].name;
446  const char *opt_type = option_list[lpc].type;
447  const char *opt_values = option_list[lpc].values;
448  const char *opt_default = option_list[lpc].default_value;
449  const char *opt_desc_short = option_list[lpc].description_short;
450  const char *opt_desc_long = option_list[lpc].description_long;
451 
452  // The standard requires long and short parameter descriptions
453  CRM_ASSERT((opt_desc_short != NULL) || (opt_desc_long != NULL));
454 
455  if (opt_desc_short == NULL) {
456  opt_desc_short = opt_desc_long;
457  } else if (opt_desc_long == NULL) {
458  opt_desc_long = opt_desc_short;
459  }
460 
461  // The standard requires a parameter type
462  CRM_ASSERT(opt_type != NULL);
463 
464  pcmk__g_strcat(s, " <parameter name=\"", opt_name, "\">\n", NULL);
465 
466  add_desc(s, "longdesc", opt_desc_long, opt_values, " ");
467  add_desc(s, "shortdesc", opt_desc_short, NULL, " ");
468 
469  pcmk__g_strcat(s, " <content type=\"", opt_type, "\"", NULL);
470  if (opt_default != NULL) {
471  pcmk__g_strcat(s, " default=\"", opt_default, "\"", NULL);
472  }
473 
474  if ((opt_values != NULL) && (strcmp(opt_type, "select") == 0)) {
475  char *str = strdup(opt_values);
476  const char *delim = ", ";
477  char *ptr = strtok(str, delim);
478 
479  g_string_append(s, ">\n");
480 
481  while (ptr != NULL) {
482  pcmk__g_strcat(s, " <option value=\"", ptr, "\" />\n",
483  NULL);
484  ptr = strtok(NULL, delim);
485  }
486  g_string_append_printf(s, " </content>\n");
487  free(str);
488 
489  } else {
490  g_string_append(s, "/>\n");
491  }
492 
493  g_string_append(s, " </parameter>\n");
494  }
495  g_string_append(s, " </parameters>\n</resource-agent>\n");
496 
497  return g_string_free(s, FALSE);
498 }
499 
500 void
501 pcmk__validate_cluster_options(GHashTable *options,
502  pcmk__cluster_option_t *option_list, int len)
503 {
504  for (int lpc = 0; lpc < len; lpc++) {
505  cluster_option_value(options, option_list[lpc].is_valid,
506  option_list[lpc].name,
507  option_list[lpc].alt_name,
508  option_list[lpc].default_value);
509  }
510 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
A dumping ground.
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:936
const char * name
Definition: cib.c:26
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:224
#define pcmk__config_warn(fmt...)
bool pcmk__valid_interval_spec(const char *value)
Definition: options.c:180
#define pcmk__config_err(fmt...)
bool pcmk__valid_positive_number(const char *value)
Definition: options.c:209
#define PACEMAKER_VERSION
Definition: config.h:517
bool pcmk__valid_percentage(const char *value)
Definition: options.c:251
void pcmk__set_env_option(const char *option, const char *value, bool compat)
Set or unset a Pacemaker environment variable option.
Definition: options.c:108
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition: strings.c:97
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:350
#define BUILD_VERSION
Definition: config.h:8
bool pcmk__valid_quorum(const char *value)
Definition: options.c:218
#define crm_trace(fmt, args...)
Definition: logging.h:387
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1217
#define PCMK__NELEM(a)
Definition: internal.h:46
#define _(String)
Definition: crm_internal.h:53
Success.
Definition: results.h:240
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:424
#define crm_err(fmt, args...)
Definition: logging.h:381
#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:109
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:501
void pcmk__cli_help(char cmd)
Definition: options.c:25
bool pcmk__env_option_enabled(const char *daemon, const char *option)
Definition: options.c:165
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:195
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition: xml.c:1329
#define CRM_FEATURES
Definition: config.h:33
bool pcmk__valid_boolean(const char *value)
Definition: options.c:187
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:426
const char * description_long