pacemaker 2.1.6-6fdc9deea29
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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
24void
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
57const char *
58pcmk__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
100void
101pcmk__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
151bool
152pcmk__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
166bool
168{
169 (void) crm_parse_interval_spec(value);
170 return errno == 0;
171}
172
173bool
174pcmk__valid_boolean(const char *value)
175{
176 int tmp;
177
178 return crm_str_to_boolean(value, &tmp) == 1;
179}
180
181bool
182pcmk__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
195bool
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
204bool
205pcmk__valid_quorum(const char *value)
206{
207 return pcmk__strcase_any_of(value, "stop", "freeze", "ignore", "demote", "suicide", NULL);
208}
209
210bool
211pcmk__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
237bool
238pcmk__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
261static const char *
262cluster_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
336const char *
337pcmk__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
367static void
368add_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
412gchar *
413pcmk__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
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
487void
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 PCMK_OCF_VERSION
Definition agents.h:54
const char * name
Definition cib.c:24
#define PCMK__NELEM(a)
Definition internal.h:44
bool pcmk_str_is_infinity(const char *s)
Definition utils.c:543
char guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition utils.c:271
gboolean crm_is_true(const char *s)
Definition strings.c:416
int crm_str_to_boolean(const char *s, int *ret)
Definition strings.c:427
bool pcmk_str_is_minus_infinity(const char *s)
Definition utils.c:548
#define PACEMAKER_VERSION
Definition config.h:499
#define CRM_FEATURES
Definition config.h:33
#define BUILD_VERSION
Definition config.h:8
A dumping ground.
#define _(String)
#define NAME_MAX
Definition logging.c:104
#define CRM_CHECK(expr, failure_action)
Definition logging.h:235
#define crm_err(fmt, args...)
Definition logging.h:375
#define crm_trace(fmt, args...)
Definition logging.h:381
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
void pcmk__cli_help(char cmd)
Definition options.c:25
bool pcmk__valid_interval_spec(const char *value)
Definition options.c:167
void pcmk__set_env_option(const char *option, const char *value)
Set or unset a Pacemaker environment variable option.
Definition options.c:101
bool pcmk__valid_boolean(const char *value)
Definition options.c:174
bool pcmk__valid_number(const char *value)
Definition options.c:182
bool pcmk__env_option_enabled(const char *daemon, const char *option)
Definition options.c:152
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
bool pcmk__valid_quorum(const char *value)
Definition options.c:205
bool pcmk__valid_percentage(const char *value)
Definition options.c:238
const char * pcmk__cluster_option(GHashTable *options, const pcmk__cluster_option_t *option_list, int len, const char *name)
Definition options.c:337
const char * pcmk__env_option(const char *option)
Definition options.c:58
void pcmk__validate_cluster_options(GHashTable *options, pcmk__cluster_option_t *option_list, int len)
Definition options.c:488
bool pcmk__valid_positive_number(const char *value)
Definition options.c:196
bool pcmk__valid_script(const char *value)
Definition options.c:211
stonith_t * st
Definition pcmk_fence.c:28
char * strerror(int errnum)
int setenv(const char *name, const char *value, int why)
int daemon(int nochdir, int noclose)
#define CRM_ASSERT(expr)
Definition results.h:42
@ CRM_EX_OK
Success.
Definition results.h:237
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition results.c:874
@ pcmk_rc_ok
Definition results.h:151
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition strings.c:97
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:933
@ pcmk__str_casei
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1217
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition xml.c:1310