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 a \c "PCMK_" prefix and optionally
  95  * an \c "HA_" prefix for backward compatibility.
  96  *
  97  * \param[in] option  Environment variable name (without prefix)
  98  * \param[in] value   New value (or NULL to unset)
  99  * \param[in] compat  If false and \p value is not \c NULL, set only
 100  *                    \c "PCMK_<option>"; otherwise, set (or unset) both
 101  *                    \c "PCMK_<option>" and \c "HA_<option>"
 102  *
 103  * \note \p compat is ignored when \p value is \c NULL. A \c NULL \p value
 104  *       means we're unsetting \p option. \c pcmk__get_env_option() checks for
 105  *       both prefixes, so we want to clear them both.
 106  */
 107 void
 108 pcmk__set_env_option(const char *option, const char *value, bool compat)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 151 /*!
 152  * \internal
 153  * \brief Check whether Pacemaker environment variable option is enabled
 154  *
 155  * Given a Pacemaker environment variable option that can either be boolean
 156  * or a list of daemon names, return true if the option is enabled for a given
 157  * daemon.
 158  *
 159  * \param[in] daemon   Daemon name (can be NULL)
 160  * \param[in] option   Pacemaker environment variable name
 161  *
 162  * \return true if variable is enabled for daemon, otherwise false
 163  */
 164 bool
 165 pcmk__env_option_enabled(const char *daemon, const char *option)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 181 {
 182     (void) crm_parse_interval_spec(value);
 183     return errno == 0;
 184 }
 185 
 186 bool
 187 pcmk__valid_boolean(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 262 /*!
 263  * \internal
 264  * \brief Check a table of configured options for a particular option
 265  *
 266  * \param[in,out] options    Name/value pairs for configured options
 267  * \param[in]     validate   If not NULL, validator function for option value
 268  * \param[in]     name       Option name to look for
 269  * \param[in]     old_name   Alternative option name to look for
 270  * \param[in]     def_value  Default to use if option not configured
 271  *
 272  * \return Option value (from supplied options table or default value)
 273  */
 274 static const char *
 275 cluster_option_value(GHashTable *options, bool (*validate)(const char *),
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 338 /*!
 339  * \internal
 340  * \brief Get the value of a cluster option
 341  *
 342  * \param[in,out] options      Name/value pairs for configured options
 343  * \param[in]     option_list  Possible cluster options
 344  * \param[in]     len          Length of \p option_list
 345  * \param[in]     name         (Primary) option name to look for
 346  *
 347  * \return Option value
 348  */
 349 const char *
 350 pcmk__cluster_option(GHashTable *options,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 369 /*!
 370  * \internal
 371  * \brief Add a description element to a meta-data string
 372  *
 373  * \param[in,out] s       Meta-data string to add to
 374  * \param[in]     tag     Name of element to add ("longdesc" or "shortdesc")
 375  * \param[in]     desc    Textual description to add
 376  * \param[in]     values  If not \p NULL, the allowed values for the parameter
 377  * \param[in]     spaces  If not \p NULL, spaces to insert at the beginning of
 378  *                        each line
 379  */
 380 static void
 381 add_desc(GString *s, const char *tag, const char *desc, const char *values,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

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