root/lib/common/options.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcmk__cli_help
  2. pcmk__env_option
  3. pcmk__set_env_option
  4. pcmk__env_option_enabled
  5. pcmk__valid_interval_spec
  6. pcmk__valid_boolean
  7. pcmk__valid_number
  8. pcmk__valid_positive_number
  9. pcmk__valid_quorum
  10. pcmk__valid_script
  11. pcmk__valid_percentage
  12. cluster_option_value
  13. pcmk__cluster_option
  14. add_desc
  15. pcmk__format_option_metadata
  16. pcmk__validate_cluster_options

   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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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 
  36     crm_exit(CRM_EX_OK);
  37     while(1); // above does not return
  38 }
  39 
  40 
  41 /*
  42  * Environment variable option handling
  43  */
  44 
  45 /*!
  46  * \internal
  47  * \brief Get the value of a Pacemaker environment variable option
  48  *
  49  * If an environment variable option is set, with either a PCMK_ or (for
  50  * backward compatibility) HA_ prefix, log and return the value.
  51  *
  52  * \param[in] option  Environment variable name (without prefix)
  53  *
  54  * \return Value of environment variable option, or NULL in case of
  55  *         option name too long or value not found
  56  */
  57 const char *
  58 pcmk__env_option(const char *option)
     /* [previous][next][first][last][top][bottom][index][help] */
  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 
  91 /*!
  92  * \brief Set or unset a Pacemaker environment variable option
  93  *
  94  * Set an environment variable option with both a PCMK_ and (for
  95  * backward compatibility) HA_ prefix.
  96  *
  97  * \param[in] option  Environment variable name (without prefix)
  98  * \param[in] value   New value (or NULL to unset)
  99  */
 100 void
 101 pcmk__set_env_option(const char *option, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 138 /*!
 139  * \internal
 140  * \brief Check whether Pacemaker environment variable option is enabled
 141  *
 142  * Given a Pacemaker environment variable option that can either be boolean
 143  * or a list of daemon names, return true if the option is enabled for a given
 144  * daemon.
 145  *
 146  * \param[in] daemon   Daemon name (can be NULL)
 147  * \param[in] option   Pacemaker environment variable name
 148  *
 149  * \return true if variable is enabled for daemon, otherwise false
 150  */
 151 bool
 152 pcmk__env_option_enabled(const char *daemon, const char *option)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 168 {
 169     (void) crm_parse_interval_spec(value);
 170     return errno == 0;
 171 }
 172 
 173 bool
 174 pcmk__valid_boolean(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 249 /*!
 250  * \internal
 251  * \brief Check a table of configured options for a particular option
 252  *
 253  * \param[in,out] options    Name/value pairs for configured options
 254  * \param[in]     validate   If not NULL, validator function for option value
 255  * \param[in]     name       Option name to look for
 256  * \param[in]     old_name   Alternative option name to look for
 257  * \param[in]     def_value  Default to use if option not configured
 258  *
 259  * \return Option value (from supplied options table or default value)
 260  */
 261 static const char *
 262 cluster_option_value(GHashTable *options, bool (*validate)(const char *),
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 325 /*!
 326  * \internal
 327  * \brief Get the value of a cluster option
 328  *
 329  * \param[in,out] options      Name/value pairs for configured options
 330  * \param[in]     option_list  Possible cluster options
 331  * \param[in]     len          Length of \p option_list
 332  * \param[in]     name         (Primary) option name to look for
 333  *
 334  * \return Option value
 335  */
 336 const char *
 337 pcmk__cluster_option(GHashTable *options,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 356 /*!
 357  * \internal
 358  * \brief Add a description element to a meta-data string
 359  *
 360  * \param[in,out] s       Meta-data string to add to
 361  * \param[in]     tag     Name of element to add ("longdesc" or "shortdesc")
 362  * \param[in]     desc    Textual description to add
 363  * \param[in]     values  If not \p NULL, the allowed values for the parameter
 364  * \param[in]     spaces  If not \p NULL, spaces to insert at the beginning of
 365  *                        each line
 366  */
 367 static void
 368 add_desc(GString *s, const char *tag, const char *desc, const char *values,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

/* [previous][next][first][last][top][bottom][index][help] */