pacemaker 2.1.7-2.1.7
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
107void
108pcmk__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
164bool
165pcmk__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
179bool
181{
182 (void) crm_parse_interval_spec(value);
183 return errno == 0;
184}
185
186bool
187pcmk__valid_boolean(const char *value)
188{
189 int tmp;
190
191 return crm_str_to_boolean(value, &tmp) == 1;
192}
193
194bool
195pcmk__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
208bool
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
217bool
218pcmk__valid_quorum(const char *value)
219{
220 return pcmk__strcase_any_of(value, "stop", "freeze", "ignore", "demote", "suicide", NULL);
221}
222
223bool
224pcmk__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
250bool
251pcmk__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
274static const char *
275cluster_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
349const char *
350pcmk__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
380static void
381add_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
425gchar *
426pcmk__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
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
500void
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 PCMK_OCF_VERSION
Definition agents.h:54
const char * name
Definition cib.c:26
#define PCMK__NELEM(a)
Definition internal.h:46
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:424
bool pcmk_str_is_minus_infinity(const char *s)
Definition utils.c:548
#define PACEMAKER_VERSION
Definition config.h:514
#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:109
#define CRM_CHECK(expr, failure_action)
Definition logging.h:238
#define crm_err(fmt, args...)
Definition logging.h:379
#define crm_trace(fmt, args...)
Definition logging.h:385
#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:180
bool pcmk__valid_boolean(const char *value)
Definition options.c:187
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
bool pcmk__valid_number(const char *value)
Definition options.c:195
bool pcmk__env_option_enabled(const char *daemon, const char *option)
Definition options.c:165
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
bool pcmk__valid_quorum(const char *value)
Definition options.c:218
bool pcmk__valid_percentage(const char *value)
Definition options.c:251
const char * pcmk__cluster_option(GHashTable *options, const pcmk__cluster_option_t *option_list, int len, const char *name)
Definition options.c:350
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:501
bool pcmk__valid_positive_number(const char *value)
Definition options.c:209
bool pcmk__valid_script(const char *value)
Definition options.c:224
stonith_t * st
Definition pcmk_fence.c:28
#define CRM_ASSERT(expr)
Definition results.h:42
@ CRM_EX_OK
Success.
Definition results.h:240
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition results.c:936
@ pcmk_rc_ok
Definition results.h:154
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:1329