root/lib/common/options.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk__cli_option_cleanup
  2. create_long_opts
  3. pcmk__set_cli_options
  4. pcmk__next_cli_option
  5. pcmk__cli_help
  6. pcmk__env_option
  7. pcmk__set_env_option
  8. pcmk__env_option_enabled
  9. pcmk__valid_interval_spec
  10. pcmk__valid_boolean
  11. pcmk__valid_number
  12. pcmk__valid_positive_number
  13. pcmk__valid_quorum
  14. pcmk__valid_script
  15. pcmk__valid_percentage
  16. cluster_option_value
  17. pcmk__cluster_option
  18. add_desc
  19. pcmk__format_option_metadata
  20. 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 #ifdef HAVE_GETOPT_H
  23 #  include <getopt.h>
  24 #endif
  25 
  26 #include <crm/crm.h>
  27 
  28 
  29 /*
  30  * Command-line option handling
  31  */
  32 
  33 static char *crm_short_options = NULL;
  34 static pcmk__cli_option_t *crm_long_options = NULL;
  35 static const char *crm_app_description = NULL;
  36 static const char *crm_app_usage = NULL;
  37 
  38 void
  39 pcmk__cli_option_cleanup()
     /* [previous][next][first][last][top][bottom][index][help] */
  40 {
  41     free(crm_short_options);
  42     crm_short_options = NULL;
  43 }
  44 
  45 static struct option *
  46 create_long_opts(pcmk__cli_option_t *long_options)
     /* [previous][next][first][last][top][bottom][index][help] */
  47 {
  48     struct option *long_opts = NULL;
  49 
  50 #ifdef HAVE_GETOPT_H
  51     int index = 0, lpc = 0;
  52 
  53     /*
  54      * A previous, possibly poor, choice of '?' as the short form of --help
  55      * means that getopt_long() returns '?' for both --help and for "unknown option"
  56      *
  57      * This dummy entry allows us to differentiate between the two in
  58      * pcmk__next_cli_option() and exit with the correct error code.
  59      */
  60     long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option));
  61     long_opts[index].name = "__dummmy__";
  62     long_opts[index].has_arg = 0;
  63     long_opts[index].flag = 0;
  64     long_opts[index].val = '_';
  65     index++;
  66 
  67     // cppcheck seems not to understand the abort-logic in pcmk__realloc
  68     // cppcheck-suppress memleak
  69     for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
  70         if (long_options[lpc].name[0] == '-') {
  71             continue;
  72         }
  73 
  74         long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option));
  75         /*fprintf(stderr, "Creating %d %s = %c\n", index,
  76          * long_options[lpc].name, long_options[lpc].val);      */
  77         long_opts[index].name = long_options[lpc].name;
  78         long_opts[index].has_arg = long_options[lpc].has_arg;
  79         long_opts[index].flag = long_options[lpc].flag;
  80         long_opts[index].val = long_options[lpc].val;
  81         index++;
  82     }
  83 
  84     /* Now create the list terminator */
  85     long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option));
  86     long_opts[index].name = NULL;
  87     long_opts[index].has_arg = 0;
  88     long_opts[index].flag = 0;
  89     long_opts[index].val = 0;
  90 #endif
  91 
  92     return long_opts;
  93 }
  94 
  95 /*!
  96  * \internal
  97  * \brief Define the command-line options a daemon or tool accepts
  98  *
  99  * \param[in] short_options  getopt(3)-style short option list
 100  * \param[in] app_usage      summary of how command is invoked (for help)
 101  * \param[in] long_options   definition of options accepted
 102  * \param[in] app_desc       brief command description (for help)
 103  */
 104 void
 105 pcmk__set_cli_options(const char *short_options, const char *app_usage,
     /* [previous][next][first][last][top][bottom][index][help] */
 106                       pcmk__cli_option_t *long_options, const char *app_desc)
 107 {
 108     if (short_options) {
 109         crm_short_options = strdup(short_options);
 110 
 111     } else if (long_options) {
 112         int lpc = 0;
 113         int opt_string_len = 0;
 114         char *local_short_options = NULL;
 115 
 116         for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
 117             if (long_options[lpc].val && long_options[lpc].val != '-' && long_options[lpc].val < UCHAR_MAX) {
 118                 local_short_options = pcmk__realloc(local_short_options,
 119                                                     opt_string_len + 4);
 120                 local_short_options[opt_string_len++] = long_options[lpc].val;
 121                 /* getopt(3) says: Two colons mean an option takes an optional arg; */
 122                 if (long_options[lpc].has_arg == optional_argument) {
 123                     local_short_options[opt_string_len++] = ':';
 124                 }
 125                 if (long_options[lpc].has_arg >= required_argument) {
 126                     local_short_options[opt_string_len++] = ':';
 127                 }
 128                 local_short_options[opt_string_len] = 0;
 129             }
 130         }
 131         crm_short_options = local_short_options;
 132         crm_trace("Generated short option string: '%s'", local_short_options);
 133     }
 134 
 135     if (long_options) {
 136         crm_long_options = long_options;
 137     }
 138     if (app_desc) {
 139         crm_app_description = app_desc;
 140     }
 141     if (app_usage) {
 142         crm_app_usage = app_usage;
 143     }
 144 }
 145 
 146 int
 147 pcmk__next_cli_option(int argc, char **argv, int *index, const char **longname)
     /* [previous][next][first][last][top][bottom][index][help] */
 148 {
 149 #ifdef HAVE_GETOPT_H
 150     static struct option *long_opts = NULL;
 151 
 152     if (long_opts == NULL && crm_long_options) {
 153         long_opts = create_long_opts(crm_long_options);
 154     }
 155 
 156     *index = 0;
 157     if (long_opts) {
 158         int flag = getopt_long(argc, argv, crm_short_options, long_opts, index);
 159 
 160         switch (flag) {
 161             case 0:
 162                 if (long_opts[*index].val) {
 163                     return long_opts[*index].val;
 164                 } else if (longname) {
 165                     *longname = long_opts[*index].name;
 166                 } else {
 167                     crm_notice("Unhandled option --%s", long_opts[*index].name);
 168                     return flag;
 169                 }
 170             case -1:           /* End of option processing */
 171                 break;
 172             case ':':
 173                 crm_trace("Missing argument");
 174                 pcmk__cli_help('?', CRM_EX_USAGE);
 175                 break;
 176             case '?':
 177                 pcmk__cli_help('?', (*index? CRM_EX_OK : CRM_EX_USAGE));
 178                 break;
 179         }
 180         return flag;
 181     }
 182 #endif
 183 
 184     if (crm_short_options) {
 185         return getopt(argc, argv, crm_short_options);
 186     }
 187 
 188     return -1;
 189 }
 190 
 191 void
 192 pcmk__cli_help(char cmd, crm_exit_t exit_code)
     /* [previous][next][first][last][top][bottom][index][help] */
 193 {
 194     int i = 0;
 195     FILE *stream = (exit_code ? stderr : stdout);
 196 
 197     if (cmd == 'v' || cmd == '$') {
 198         fprintf(stream, "Pacemaker %s\n", PACEMAKER_VERSION);
 199         fprintf(stream, "Written by Andrew Beekhof and "
 200                         "the Pacemaker project contributors\n");
 201         goto out;
 202     }
 203 
 204     if (cmd == '!') {
 205         fprintf(stream, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
 206         goto out;
 207     }
 208 
 209     fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description);
 210 
 211     if (crm_app_usage) {
 212         fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage);
 213     }
 214 
 215     if (crm_long_options) {
 216         fprintf(stream, "Options:\n");
 217         for (i = 0; crm_long_options[i].name != NULL; i++) {
 218             if (crm_long_options[i].flags & pcmk__option_hidden) {
 219 
 220             } else if (crm_long_options[i].flags & pcmk__option_paragraph) {
 221                 fprintf(stream, "%s\n\n", crm_long_options[i].desc);
 222 
 223             } else if (crm_long_options[i].flags & pcmk__option_example) {
 224                 fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc);
 225 
 226             } else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) {
 227                 fprintf(stream, "%s\n", crm_long_options[i].desc);
 228 
 229             } else {
 230                 /* is val printable as char ? */
 231                 if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) {
 232                     fprintf(stream, " -%c,", crm_long_options[i].val);
 233                 } else {
 234                     fputs("    ", stream);
 235                 }
 236                 fprintf(stream, " --%s%s\t%s\n", crm_long_options[i].name,
 237                         crm_long_options[i].has_arg == optional_argument ? "[=value]" :
 238                         crm_long_options[i].has_arg == required_argument ? "=value" : "",
 239                         crm_long_options[i].desc ? crm_long_options[i].desc : "");
 240             }
 241         }
 242 
 243     } else if (crm_short_options) {
 244         fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description);
 245         for (i = 0; crm_short_options[i] != 0; i++) {
 246             int has_arg = no_argument /* 0 */;
 247 
 248             if (crm_short_options[i + 1] == ':') {
 249                 if (crm_short_options[i + 2] == ':')
 250                     has_arg = optional_argument /* 2 */;
 251                 else
 252                     has_arg = required_argument /* 1 */;
 253             }
 254 
 255             fprintf(stream, " -%c %s\n", crm_short_options[i],
 256                     has_arg == optional_argument ? "[value]" :
 257                     has_arg == required_argument ? "{value}" : "");
 258             i += has_arg;
 259         }
 260     }
 261 
 262     fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT);
 263 
 264   out:
 265     crm_exit(exit_code);
 266     while(1); // above does not return
 267 }
 268 
 269 
 270 /*
 271  * Environment variable option handling
 272  */
 273 
 274 /*!
 275  * \internal
 276  * \brief Get the value of a Pacemaker environment variable option
 277  *
 278  * If an environment variable option is set, with either a PCMK_ or (for
 279  * backward compatibility) HA_ prefix, log and return the value.
 280  *
 281  * \param[in] option  Environment variable name (without prefix)
 282  *
 283  * \return Value of environment variable option
 284  */
 285 const char *
 286 pcmk__env_option(const char *option)
     /* [previous][next][first][last][top][bottom][index][help] */
 287 {
 288     char env_name[NAME_MAX];
 289     const char *value = NULL;
 290 
 291     snprintf(env_name, NAME_MAX, "PCMK_%s", option);
 292     value = getenv(env_name);
 293     if (value != NULL) {
 294         crm_trace("Found %s = %s", env_name, value);
 295         return value;
 296     }
 297 
 298     snprintf(env_name, NAME_MAX, "HA_%s", option);
 299     value = getenv(env_name);
 300     if (value != NULL) {
 301         crm_trace("Found %s = %s", env_name, value);
 302         return value;
 303     }
 304 
 305     crm_trace("Nothing found for %s", option);
 306     return NULL;
 307 }
 308 
 309 /*!
 310  * \brief Set or unset a Pacemaker environment variable option
 311  *
 312  * Set an environment variable option with both a PCMK_ and (for
 313  * backward compatibility) HA_ prefix.
 314  *
 315  * \param[in] option  Environment variable name (without prefix)
 316  * \param[in] value   New value (or NULL to unset)
 317  */
 318 void
 319 pcmk__set_env_option(const char *option, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 320 {
 321     char env_name[NAME_MAX];
 322 
 323     snprintf(env_name, NAME_MAX, "PCMK_%s", option);
 324     if (value) {
 325         crm_trace("Setting %s to %s", env_name, value);
 326         setenv(env_name, value, 1);
 327     } else {
 328         crm_trace("Unsetting %s", env_name);
 329         unsetenv(env_name);
 330     }
 331 
 332     snprintf(env_name, NAME_MAX, "HA_%s", option);
 333     if (value) {
 334         crm_trace("Setting %s to %s", env_name, value);
 335         setenv(env_name, value, 1);
 336     } else {
 337         crm_trace("Unsetting %s", env_name);
 338         unsetenv(env_name);
 339     }
 340 }
 341 
 342 /*!
 343  * \internal
 344  * \brief Check whether Pacemaker environment variable option is enabled
 345  *
 346  * Given a Pacemaker environment variable option that can either be boolean
 347  * or a list of daemon names, return true if the option is enabled for a given
 348  * daemon.
 349  *
 350  * \param[in] daemon   Daemon name
 351  * \param[in] option   Pacemaker environment variable name
 352  *
 353  * \return true if variable is enabled for daemon, otherwise false
 354  */
 355 bool
 356 pcmk__env_option_enabled(const char *daemon, const char *option)
     /* [previous][next][first][last][top][bottom][index][help] */
 357 {
 358     const char *value = pcmk__env_option(option);
 359 
 360     return (value != NULL) && (crm_is_true(value) || strstr(value, daemon));
 361 }
 362 
 363 
 364 /*
 365  * Cluster option handling
 366  */
 367 
 368 bool
 369 pcmk__valid_interval_spec(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 370 {
 371     (void) crm_parse_interval_spec(value);
 372     return errno == 0;
 373 }
 374 
 375 bool
 376 pcmk__valid_boolean(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 377 {
 378     int tmp;
 379 
 380     return crm_str_to_boolean(value, &tmp) == 1;
 381 }
 382 
 383 bool
 384 pcmk__valid_number(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 385 {
 386     if (value == NULL) {
 387         return false;
 388 
 389     } else if (pcmk_str_is_minus_infinity(value) ||
 390                pcmk_str_is_infinity(value)) {
 391         return true;
 392     }
 393 
 394     return pcmk__scan_ll(value, NULL, 0LL) == pcmk_rc_ok;
 395 }
 396 
 397 bool
 398 pcmk__valid_positive_number(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 399 {
 400     long long num = 0LL;
 401 
 402     return pcmk_str_is_infinity(value)
 403            || ((pcmk__scan_ll(value, &num, 0LL) == pcmk_rc_ok) && (num > 0));
 404 }
 405 
 406 bool
 407 pcmk__valid_quorum(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 408 {
 409     return pcmk__strcase_any_of(value, "stop", "freeze", "ignore", "demote", "suicide", NULL);
 410 }
 411 
 412 bool
 413 pcmk__valid_script(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 414 {
 415     struct stat st;
 416 
 417     if (pcmk__str_eq(value, "/dev/null", pcmk__str_casei)) {
 418         return true;
 419     }
 420 
 421     if (stat(value, &st) != 0) {
 422         crm_err("Script %s does not exist", value);
 423         return false;
 424     }
 425 
 426     if (S_ISREG(st.st_mode) == 0) {
 427         crm_err("Script %s is not a regular file", value);
 428         return false;
 429     }
 430 
 431     if ((st.st_mode & (S_IXUSR | S_IXGRP)) == 0) {
 432         crm_err("Script %s is not executable", value);
 433         return false;
 434     }
 435 
 436     return true;
 437 }
 438 
 439 bool
 440 pcmk__valid_percentage(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 441 {
 442     char *end = NULL;
 443     long number = strtol(value, &end, 10);
 444 
 445     if (end && (end[0] != '%')) {
 446         return false;
 447     }
 448     return number >= 0;
 449 }
 450 
 451 /*!
 452  * \internal
 453  * \brief Check a table of configured options for a particular option
 454  *
 455  * \param[in] options    Name/value pairs for configured options
 456  * \param[in] validate   If not NULL, validator function for option value
 457  * \param[in] name       Option name to look for
 458  * \param[in] old_name   Alternative option name to look for
 459  * \param[in] def_value  Default to use if option not configured
 460  *
 461  * \return Option value (from supplied options table or default value)
 462  */
 463 static const char *
 464 cluster_option_value(GHashTable *options, bool (*validate)(const char *),
     /* [previous][next][first][last][top][bottom][index][help] */
 465                      const char *name, const char *old_name,
 466                      const char *def_value)
 467 {
 468     const char *value = NULL;
 469     char *new_value = NULL;
 470 
 471     CRM_ASSERT(name != NULL);
 472 
 473     if (options) {
 474         value = g_hash_table_lookup(options, name);
 475 
 476         if ((value == NULL) && old_name) {
 477             value = g_hash_table_lookup(options, old_name);
 478             if (value != NULL) {
 479                 pcmk__config_warn("Support for legacy name '%s' for cluster "
 480                                   "option '%s' is deprecated and will be "
 481                                   "removed in a future release",
 482                                   old_name, name);
 483 
 484                 // Inserting copy with current name ensures we only warn once
 485                 new_value = strdup(value);
 486                 g_hash_table_insert(options, strdup(name), new_value);
 487                 value = new_value;
 488             }
 489         }
 490 
 491         if (value && validate && (validate(value) == FALSE)) {
 492             pcmk__config_err("Using default value for cluster option '%s' "
 493                              "because '%s' is invalid", name, value);
 494             value = NULL;
 495         }
 496 
 497         if (value) {
 498             return value;
 499         }
 500     }
 501 
 502     // No value found, use default
 503     value = def_value;
 504 
 505     if (value == NULL) {
 506         crm_trace("No value or default provided for cluster option '%s'",
 507                   name);
 508         return NULL;
 509     }
 510 
 511     if (validate) {
 512         CRM_CHECK(validate(value) != FALSE,
 513                   crm_err("Bug: default value for cluster option '%s' is invalid", name);
 514                   return NULL);
 515     }
 516 
 517     crm_trace("Using default value '%s' for cluster option '%s'",
 518               value, name);
 519     if (options) {
 520         new_value = strdup(value);
 521         g_hash_table_insert(options, strdup(name), new_value);
 522         value = new_value;
 523     }
 524     return value;
 525 }
 526 
 527 /*!
 528  * \internal
 529  * \brief Get the value of a cluster option
 530  *
 531  * \param[in] options      Name/value pairs for configured options
 532  * \param[in] option_list  Possible cluster options
 533  * \param[in] name         (Primary) option name to look for
 534  *
 535  * \return Option value
 536  */
 537 const char *
 538 pcmk__cluster_option(GHashTable *options, pcmk__cluster_option_t *option_list,
     /* [previous][next][first][last][top][bottom][index][help] */
 539                      int len, const char *name)
 540 {
 541     const char *value = NULL;
 542 
 543     for (int lpc = 0; lpc < len; lpc++) {
 544         if (pcmk__str_eq(name, option_list[lpc].name, pcmk__str_casei)) {
 545             value = cluster_option_value(options, option_list[lpc].is_valid,
 546                                          option_list[lpc].name,
 547                                          option_list[lpc].alt_name,
 548                                          option_list[lpc].default_value);
 549             return value;
 550         }
 551     }
 552     CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name));
 553     return NULL;
 554 }
 555 
 556 /*!
 557  * \internal
 558  * \brief Add a description element to a meta-data string
 559  *
 560  * \param[in] s       Meta-data string to add to
 561  * \param[in] tag     Name of element to add ("longdesc" or "shortdesc")
 562  * \param[in] desc    Textual description to add
 563  * \param[in] values  If not NULL, the allowed values for the parameter
 564  */
 565 static void
 566 add_desc(GString *s, const char *tag, const char *desc, const char *values, const char *spaces)
     /* [previous][next][first][last][top][bottom][index][help] */
 567 {
 568     char *escaped_en = crm_xml_escape(desc);
 569 
 570     g_string_append_printf(s, "<%s lang=\"en\">%s",
 571                            tag, escaped_en);
 572     if (values != NULL) {
 573         g_string_append_printf(s, "  Allowed values: %s", values);
 574     }
 575     g_string_append_printf(s, "</%s>\n", tag);
 576 
 577 #ifdef ENABLE_NLS
 578     {
 579         static const char *locale = NULL;
 580 
 581         char *localized = crm_xml_escape(_(desc));
 582 
 583         if (strcmp(escaped_en, localized) != 0) {
 584             if (locale == NULL) {
 585                 locale = strtok(setlocale(LC_ALL, NULL), "_");
 586             }
 587 
 588             if (spaces != NULL) {
 589                 g_string_append_printf(s, "%s", spaces);
 590             }
 591             g_string_append_printf(s, "<%s lang=\"%s\">%s",
 592                                    tag, locale, localized);
 593             if (values != NULL) {
 594                 g_string_append(s, _("  Allowed values: "));
 595                 g_string_append_printf(s, "%s", _(values));
 596             }
 597             g_string_append_printf(s, "</%s>\n", tag);
 598         }
 599         free(localized);
 600     }
 601 #endif
 602 
 603     free(escaped_en);
 604 }
 605 
 606 char *
 607 pcmk__format_option_metadata(const char *name, const char *desc_short,
     /* [previous][next][first][last][top][bottom][index][help] */
 608                              const char *desc_long,
 609                              pcmk__cluster_option_t *option_list, int len)
 610 {
 611     char *retval;
 612     /* big enough to hold "pacemaker-schedulerd metadata" output */
 613     GString *s = g_string_sized_new(13000);
 614     int lpc = 0;
 615 
 616     g_string_append_printf(s, "<?xml version=\"1.0\"?>"
 617                               "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
 618                               "<resource-agent name=\"%s\">\n"
 619                               "  <version>%s</version>\n",
 620                               name, PCMK_OCF_VERSION);
 621 
 622     g_string_append(s, "  ");
 623     add_desc(s, "longdesc", desc_long, NULL, "  ");
 624 
 625     g_string_append(s, "  ");
 626     add_desc(s, "shortdesc", desc_short, NULL, "  ");
 627 
 628     g_string_append(s, "  <parameters>\n");
 629 
 630     for (lpc = 0; lpc < len; lpc++) {
 631         const char *long_desc = option_list[lpc].description_long;
 632 
 633         if (long_desc == NULL) {
 634             long_desc = option_list[lpc].description_short;
 635             if (long_desc == NULL) {
 636                 continue; // The standard requires a parameter description
 637             }
 638         }
 639 
 640         g_string_append_printf(s, "    <parameter name=\"%s\">\n",
 641                                option_list[lpc].name);
 642 
 643         g_string_append(s, "      ");
 644         add_desc(s, "longdesc", long_desc, option_list[lpc].values, "      ");
 645 
 646         g_string_append(s, "      ");
 647         add_desc(s, "shortdesc", option_list[lpc].description_short, NULL, "      ");
 648 
 649         if (option_list[lpc].values && !strcmp(option_list[lpc].type, "select")) {
 650             char *str = strdup(option_list[lpc].values);
 651             char delim[] = ", ";
 652             char *ptr = strtok(str, delim);
 653 
 654             g_string_append_printf(s, "      <content type=\"%s\" default=\"%s\">\n",
 655                                    option_list[lpc].type,
 656                                    option_list[lpc].default_value);
 657 
 658             while (ptr != NULL) {
 659                 g_string_append_printf(s, "        <option value=\"%s\" />\n", ptr);
 660                 ptr = strtok(NULL, delim);
 661             }
 662 
 663             g_string_append_printf(s, "      </content>\n");
 664             free(str);
 665 
 666         } else {
 667             g_string_append_printf(s, "      <content type=\"%s\" default=\"%s\"/>\n",
 668                                    option_list[lpc].type,
 669                                    option_list[lpc].default_value
 670             );
 671         }
 672 
 673         g_string_append_printf(s, "    </parameter>\n");
 674     }
 675     g_string_append_printf(s, "  </parameters>\n</resource-agent>\n");
 676 
 677     retval = s->str;
 678     g_string_free(s, FALSE);
 679     return retval;
 680 }
 681 
 682 void
 683 pcmk__validate_cluster_options(GHashTable *options,
     /* [previous][next][first][last][top][bottom][index][help] */
 684                                pcmk__cluster_option_t *option_list, int len)
 685 {
 686     for (int lpc = 0; lpc < len; lpc++) {
 687         cluster_option_value(options, option_list[lpc].is_valid,
 688                              option_list[lpc].name,
 689                              option_list[lpc].alt_name,
 690                              option_list[lpc].default_value);
 691     }
 692 }

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