diff --git a/lib/common/options.c b/lib/common/options.c
index 8daea1b65e..4791528f51 100644
--- a/lib/common/options.c
+++ b/lib/common/options.c
@@ -1,726 +1,728 @@
 /*
  * Copyright 2004-2022 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #ifndef _GNU_SOURCE
 #  define _GNU_SOURCE
 #endif
 
 #include <crm_internal.h>
 
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
 #ifdef HAVE_GETOPT_H
 #  include <getopt.h>
 #endif
 
 #include <crm/crm.h>
 
 
 /*
  * Command-line option handling
  */
 
 static char *crm_short_options = NULL;
 static const pcmk__cli_option_t *crm_long_options = NULL;
 static const char *crm_app_description = NULL;
 static const char *crm_app_usage = NULL;
 
 void
 pcmk__cli_option_cleanup(void)
 {
     free(crm_short_options);
     crm_short_options = NULL;
 }
 
 static struct option *
 create_long_opts(const pcmk__cli_option_t *long_options)
 {
     struct option *long_opts = NULL;
 
 #ifdef HAVE_GETOPT_H
     int index = 0, lpc = 0;
 
     /*
      * A previous, possibly poor, choice of '?' as the short form of --help
      * means that getopt_long() returns '?' for both --help and for "unknown option"
      *
      * This dummy entry allows us to differentiate between the two in
      * pcmk__next_cli_option() and exit with the correct error code.
      */
     long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option));
     long_opts[index].name = "__dummmy__";
     long_opts[index].has_arg = 0;
     long_opts[index].flag = 0;
     long_opts[index].val = '_';
     index++;
 
     // cppcheck seems not to understand the abort-logic in pcmk__realloc
     // cppcheck-suppress memleak
     for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
         if (long_options[lpc].name[0] == '-') {
             continue;
         }
 
         long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option));
         /*fprintf(stderr, "Creating %d %s = %c\n", index,
          * long_options[lpc].name, long_options[lpc].val);      */
         long_opts[index].name = long_options[lpc].name;
         long_opts[index].has_arg = long_options[lpc].has_arg;
         long_opts[index].flag = long_options[lpc].flag;
         long_opts[index].val = long_options[lpc].val;
         index++;
     }
 
     /* Now create the list terminator */
     long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option));
     long_opts[index].name = NULL;
     long_opts[index].has_arg = 0;
     long_opts[index].flag = 0;
     long_opts[index].val = 0;
 #endif
 
     return long_opts;
 }
 
 /*!
  * \internal
  * \brief Define the command-line options a daemon or tool accepts
  *
  * \param[in] short_options  getopt(3)-style short option list
  * \param[in] app_usage      summary of how command is invoked (for help)
  * \param[in] long_options   definition of options accepted
  * \param[in] app_desc       brief command description (for help)
  */
 void
 pcmk__set_cli_options(const char *short_options, const char *app_usage,
                       const pcmk__cli_option_t *long_options,
                       const char *app_desc)
 {
     if (short_options) {
         crm_short_options = strdup(short_options);
 
     } else if (long_options) {
         int lpc = 0;
         int opt_string_len = 0;
         char *local_short_options = NULL;
 
         for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
             if (long_options[lpc].val && long_options[lpc].val != '-' && long_options[lpc].val < UCHAR_MAX) {
                 local_short_options = pcmk__realloc(local_short_options,
                                                     opt_string_len + 4);
                 local_short_options[opt_string_len++] = long_options[lpc].val;
                 /* getopt(3) says: Two colons mean an option takes an optional arg; */
                 if (long_options[lpc].has_arg == optional_argument) {
                     local_short_options[opt_string_len++] = ':';
                 }
                 if (long_options[lpc].has_arg >= required_argument) {
                     local_short_options[opt_string_len++] = ':';
                 }
                 local_short_options[opt_string_len] = 0;
             }
         }
         crm_short_options = local_short_options;
         crm_trace("Generated short option string: '%s'", local_short_options);
     }
 
     if (long_options) {
         crm_long_options = long_options;
     }
     if (app_desc) {
         crm_app_description = app_desc;
     }
     if (app_usage) {
         crm_app_usage = app_usage;
     }
 }
 
 int
 pcmk__next_cli_option(int argc, char **argv, int *index, const char **longname)
 {
 #ifdef HAVE_GETOPT_H
     static struct option *long_opts = NULL;
 
     if (long_opts == NULL && crm_long_options) {
         long_opts = create_long_opts(crm_long_options);
     }
 
     *index = 0;
     if (long_opts) {
         int flag = getopt_long(argc, argv, crm_short_options, long_opts, index);
 
         switch (flag) {
             case 0:
                 if (long_opts[*index].val) {
                     return long_opts[*index].val;
                 } else if (longname) {
                     *longname = long_opts[*index].name;
                 } else {
                     crm_notice("Unhandled option --%s", long_opts[*index].name);
                     return flag;
                 }
+                break;
+
             case -1:           /* End of option processing */
                 break;
             case ':':
                 crm_trace("Missing argument");
                 pcmk__cli_help('?', CRM_EX_USAGE);
                 break;
             case '?':
                 pcmk__cli_help('?', (*index? CRM_EX_OK : CRM_EX_USAGE));
                 break;
         }
         return flag;
     }
 #endif
 
     if (crm_short_options) {
         return getopt(argc, argv, crm_short_options);
     }
 
     return -1;
 }
 
 void
 pcmk__cli_help(char cmd, crm_exit_t exit_code)
 {
     int i = 0;
     FILE *stream = (exit_code ? stderr : stdout);
 
     if (cmd == 'v' || cmd == '$') {
         fprintf(stream, "Pacemaker %s\n", PACEMAKER_VERSION);
         fprintf(stream, "Written by Andrew Beekhof and "
                         "the Pacemaker project contributors\n");
         goto out;
     }
 
     if (cmd == '!') {
         fprintf(stream, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
         goto out;
     }
 
     fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description);
 
     if (crm_app_usage) {
         fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage);
     }
 
     if (crm_long_options) {
         fprintf(stream, "Options:\n");
         for (i = 0; crm_long_options[i].name != NULL; i++) {
             if (crm_long_options[i].flags & pcmk__option_hidden) {
 
             } else if (crm_long_options[i].flags & pcmk__option_paragraph) {
                 fprintf(stream, "%s\n\n", crm_long_options[i].desc);
 
             } else if (crm_long_options[i].flags & pcmk__option_example) {
                 fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc);
 
             } else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) {
                 fprintf(stream, "%s\n", crm_long_options[i].desc);
 
             } else {
                 /* is val printable as char ? */
                 if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) {
                     fprintf(stream, " -%c,", crm_long_options[i].val);
                 } else {
                     fputs("    ", stream);
                 }
                 fprintf(stream, " --%s%s\t%s\n", crm_long_options[i].name,
                         crm_long_options[i].has_arg == optional_argument ? "[=value]" :
                         crm_long_options[i].has_arg == required_argument ? "=value" : "",
                         crm_long_options[i].desc ? crm_long_options[i].desc : "");
             }
         }
 
     } else if (crm_short_options) {
         fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description);
         for (i = 0; crm_short_options[i] != 0; i++) {
             int has_arg = no_argument /* 0 */;
 
             if (crm_short_options[i + 1] == ':') {
                 if (crm_short_options[i + 2] == ':')
                     has_arg = optional_argument /* 2 */;
                 else
                     has_arg = required_argument /* 1 */;
             }
 
             fprintf(stream, " -%c %s\n", crm_short_options[i],
                     has_arg == optional_argument ? "[value]" :
                     has_arg == required_argument ? "{value}" : "");
             i += has_arg;
         }
     }
 
     fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT);
 
   out:
     crm_exit(exit_code);
     while(1); // above does not return
 }
 
 
 /*
  * Environment variable option handling
  */
 
 /*!
  * \internal
  * \brief Get the value of a Pacemaker environment variable option
  *
  * If an environment variable option is set, with either a PCMK_ or (for
  * backward compatibility) HA_ prefix, log and return the value.
  *
  * \param[in] option  Environment variable name (without prefix)
  *
  * \return Value of environment variable option, or NULL in case of
  *         option name too long or value not found
  */
 const char *
 pcmk__env_option(const char *option)
 {
     const char *const prefixes[] = {"PCMK_", "HA_"};
     char env_name[NAME_MAX];
     const char *value = NULL;
 
     CRM_CHECK(!pcmk__str_empty(option), return NULL);
 
     for (int i = 0; i < PCMK__NELEM(prefixes); i++) {
         int rv = snprintf(env_name, NAME_MAX, "%s%s", prefixes[i], option);
 
         if (rv < 0) {
             crm_err("Failed to write %s%s to buffer: %s", prefixes[i], option,
                     strerror(errno));
             return NULL;
         }
 
         if (rv >= sizeof(env_name)) {
             crm_trace("\"%s%s\" is too long", prefixes[i], option);
             continue;
         }
 
         value = getenv(env_name);
         if (value != NULL) {
             crm_trace("Found %s = %s", env_name, value);
             return value;
         }
     }
 
     crm_trace("Nothing found for %s", option);
     return NULL;
 }
 
 /*!
  * \brief Set or unset a Pacemaker environment variable option
  *
  * Set an environment variable option with both a PCMK_ and (for
  * backward compatibility) HA_ prefix.
  *
  * \param[in] option  Environment variable name (without prefix)
  * \param[in] value   New value (or NULL to unset)
  */
 void
 pcmk__set_env_option(const char *option, const char *value)
 {
     const char *const prefixes[] = {"PCMK_", "HA_"};
     char env_name[NAME_MAX];
 
     CRM_CHECK(!pcmk__str_empty(option) && (strchr(option, '=') == NULL),
               return);
 
     for (int i = 0; i < PCMK__NELEM(prefixes); i++) {
         int rv = snprintf(env_name, NAME_MAX, "%s%s", prefixes[i], option);
 
         if (rv < 0) {
             crm_err("Failed to write %s%s to buffer: %s", prefixes[i], option,
                     strerror(errno));
             return;
         }
 
         if (rv >= sizeof(env_name)) {
             crm_trace("\"%s%s\" is too long", prefixes[i], option);
             continue;
         }
 
         if (value != NULL) {
             crm_trace("Setting %s to %s", env_name, value);
             rv = setenv(env_name, value, 1);
         } else {
             crm_trace("Unsetting %s", env_name);
             rv = unsetenv(env_name);
         }
 
         if (rv < 0) {
             crm_err("Failed to %sset %s: %s", (value != NULL)? "" : "un",
                     env_name, strerror(errno));
         }
     }
 }
 
 /*!
  * \internal
  * \brief Check whether Pacemaker environment variable option is enabled
  *
  * Given a Pacemaker environment variable option that can either be boolean
  * or a list of daemon names, return true if the option is enabled for a given
  * daemon.
  *
  * \param[in] daemon   Daemon name (can be NULL)
  * \param[in] option   Pacemaker environment variable name
  *
  * \return true if variable is enabled for daemon, otherwise false
  */
 bool
 pcmk__env_option_enabled(const char *daemon, const char *option)
 {
     const char *value = pcmk__env_option(option);
 
     return (value != NULL)
         && (crm_is_true(value)
             || ((daemon != NULL) && (strstr(value, daemon) != NULL)));
 }
 
 
 /*
  * Cluster option handling
  */
 
 bool
 pcmk__valid_interval_spec(const char *value)
 {
     (void) crm_parse_interval_spec(value);
     return errno == 0;
 }
 
 bool
 pcmk__valid_boolean(const char *value)
 {
     int tmp;
 
     return crm_str_to_boolean(value, &tmp) == 1;
 }
 
 bool
 pcmk__valid_number(const char *value)
 {
     if (value == NULL) {
         return false;
 
     } else if (pcmk_str_is_minus_infinity(value) ||
                pcmk_str_is_infinity(value)) {
         return true;
     }
 
     return pcmk__scan_ll(value, NULL, 0LL) == pcmk_rc_ok;
 }
 
 bool
 pcmk__valid_positive_number(const char *value)
 {
     long long num = 0LL;
 
     return pcmk_str_is_infinity(value)
            || ((pcmk__scan_ll(value, &num, 0LL) == pcmk_rc_ok) && (num > 0));
 }
 
 bool
 pcmk__valid_quorum(const char *value)
 {
     return pcmk__strcase_any_of(value, "stop", "freeze", "ignore", "demote", "suicide", NULL);
 }
 
 bool
 pcmk__valid_script(const char *value)
 {
     struct stat st;
 
     if (pcmk__str_eq(value, "/dev/null", pcmk__str_casei)) {
         return true;
     }
 
     if (stat(value, &st) != 0) {
         crm_err("Script %s does not exist", value);
         return false;
     }
 
     if (S_ISREG(st.st_mode) == 0) {
         crm_err("Script %s is not a regular file", value);
         return false;
     }
 
     if ((st.st_mode & (S_IXUSR | S_IXGRP)) == 0) {
         crm_err("Script %s is not executable", value);
         return false;
     }
 
     return true;
 }
 
 bool
 pcmk__valid_percentage(const char *value)
 {
     char *end = NULL;
     long number = strtol(value, &end, 10);
 
     if (end && (end[0] != '%')) {
         return false;
     }
     return number >= 0;
 }
 
 /*!
  * \internal
  * \brief Check a table of configured options for a particular option
  *
  * \param[in,out] options    Name/value pairs for configured options
  * \param[in]     validate   If not NULL, validator function for option value
  * \param[in]     name       Option name to look for
  * \param[in]     old_name   Alternative option name to look for
  * \param[in]     def_value  Default to use if option not configured
  *
  * \return Option value (from supplied options table or default value)
  */
 static const char *
 cluster_option_value(GHashTable *options, bool (*validate)(const char *),
                      const char *name, const char *old_name,
                      const char *def_value)
 {
     const char *value = NULL;
     char *new_value = NULL;
 
     CRM_ASSERT(name != NULL);
 
     if (options) {
         value = g_hash_table_lookup(options, name);
 
         if ((value == NULL) && old_name) {
             value = g_hash_table_lookup(options, old_name);
             if (value != NULL) {
                 pcmk__config_warn("Support for legacy name '%s' for cluster "
                                   "option '%s' is deprecated and will be "
                                   "removed in a future release",
                                   old_name, name);
 
                 // Inserting copy with current name ensures we only warn once
                 new_value = strdup(value);
                 g_hash_table_insert(options, strdup(name), new_value);
                 value = new_value;
             }
         }
 
         if (value && validate && (validate(value) == FALSE)) {
             pcmk__config_err("Using default value for cluster option '%s' "
                              "because '%s' is invalid", name, value);
             value = NULL;
         }
 
         if (value) {
             return value;
         }
     }
 
     // No value found, use default
     value = def_value;
 
     if (value == NULL) {
         crm_trace("No value or default provided for cluster option '%s'",
                   name);
         return NULL;
     }
 
     if (validate) {
         CRM_CHECK(validate(value) != FALSE,
                   crm_err("Bug: default value for cluster option '%s' is invalid", name);
                   return NULL);
     }
 
     crm_trace("Using default value '%s' for cluster option '%s'",
               value, name);
     if (options) {
         new_value = strdup(value);
         g_hash_table_insert(options, strdup(name), new_value);
         value = new_value;
     }
     return value;
 }
 
 /*!
  * \internal
  * \brief Get the value of a cluster option
  *
  * \param[in,out] options      Name/value pairs for configured options
  * \param[in]     option_list  Possible cluster options
  * \param[in]     name         (Primary) option name to look for
  *
  * \return Option value
  */
 const char *
 pcmk__cluster_option(GHashTable *options,
                      const pcmk__cluster_option_t *option_list,
                      int len, const char *name)
 {
     const char *value = NULL;
 
     for (int lpc = 0; lpc < len; lpc++) {
         if (pcmk__str_eq(name, option_list[lpc].name, pcmk__str_casei)) {
             value = cluster_option_value(options, option_list[lpc].is_valid,
                                          option_list[lpc].name,
                                          option_list[lpc].alt_name,
                                          option_list[lpc].default_value);
             return value;
         }
     }
     CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name));
     return NULL;
 }
 
 /*!
  * \internal
  * \brief Add a description element to a meta-data string
  *
  * \param[in,out] s       Meta-data string to add to
  * \param[in]     tag     Name of element to add ("longdesc" or "shortdesc")
  * \param[in]     desc    Textual description to add
  * \param[in]     values  If not \p NULL, the allowed values for the parameter
  * \param[in]     spaces  If not \p NULL, spaces to insert at the beginning of
  *                        each line
  */
 static void
 add_desc(GString *s, const char *tag, const char *desc, const char *values,
          const char *spaces)
 {
     char *escaped_en = crm_xml_escape(desc);
 
     if (spaces != NULL) {
         g_string_append(s, spaces);
     }
     pcmk__g_strcat(s, "<", tag, " lang=\"en\">", escaped_en, NULL);
 
     if (values != NULL) {
         pcmk__g_strcat(s, "  Allowed values: ", values, NULL);
     }
     pcmk__g_strcat(s, "</", tag, ">\n", NULL);
 
 #ifdef ENABLE_NLS
     {
         static const char *locale = NULL;
 
         char *localized = crm_xml_escape(_(desc));
 
         if (strcmp(escaped_en, localized) != 0) {
             if (locale == NULL) {
                 locale = strtok(setlocale(LC_ALL, NULL), "_");
             }
 
             if (spaces != NULL) {
                 g_string_append(s, spaces);
             }
             pcmk__g_strcat(s, "<", tag, " lang=\"", locale, "\">", localized,
                            NULL);
 
             if (values != NULL) {
                 pcmk__g_strcat(s, _("  Allowed values: "), _(values), NULL);
             }
             pcmk__g_strcat(s, "</", tag, ">\n", NULL);
         }
         free(localized);
     }
 #endif
 
     free(escaped_en);
 }
 
 gchar *
 pcmk__format_option_metadata(const char *name, const char *desc_short,
                              const char *desc_long,
                              pcmk__cluster_option_t *option_list, int len)
 {
     /* big enough to hold "pacemaker-schedulerd metadata" output */
     GString *s = g_string_sized_new(13000);
 
     pcmk__g_strcat(s,
                    "<?xml version=\"1.0\"?>\n"
                    "<resource-agent name=\"", name, "\" "
                                    "version=\"" PACEMAKER_VERSION "\">\n"
                    "  <version>" PCMK_OCF_VERSION "</version>\n", NULL);
 
     add_desc(s, "longdesc", desc_long, NULL, "  ");
     add_desc(s, "shortdesc", desc_short, NULL, "  ");
 
     g_string_append(s, "  <parameters>\n");
 
     for (int lpc = 0; lpc < len; lpc++) {
         const char *opt_name = option_list[lpc].name;
         const char *opt_type = option_list[lpc].type;
         const char *opt_values = option_list[lpc].values;
         const char *opt_default = option_list[lpc].default_value;
         const char *opt_desc_short = option_list[lpc].description_short;
         const char *opt_desc_long = option_list[lpc].description_long;
 
         // The standard requires long and short parameter descriptions
         CRM_ASSERT((opt_desc_short != NULL) || (opt_desc_long != NULL));
 
         if (opt_desc_short == NULL) {
             opt_desc_short = opt_desc_long;
         } else if (opt_desc_long == NULL) {
             opt_desc_long = opt_desc_short;
         }
 
         // The standard requires a parameter type
         CRM_ASSERT(opt_type != NULL);
 
         pcmk__g_strcat(s, "    <parameter name=\"", opt_name, "\">\n", NULL);
 
         add_desc(s, "longdesc", opt_desc_long, opt_values, "      ");
         add_desc(s, "shortdesc", opt_desc_short, NULL, "      ");
 
         pcmk__g_strcat(s, "      <content type=\"", opt_type, "\"", NULL);
         if (opt_default != NULL) {
             pcmk__g_strcat(s, " default=\"", opt_default, "\"", NULL);
         }
 
         if ((opt_values != NULL) && (strcmp(opt_type, "select") == 0)) {
             char *str = strdup(opt_values);
             const char *delim = ", ";
             char *ptr = strtok(str, delim);
 
             g_string_append(s, ">\n");
 
             while (ptr != NULL) {
                 pcmk__g_strcat(s, "        <option value=\"", ptr, "\" />\n",
                                NULL);
                 ptr = strtok(NULL, delim);
             }
             g_string_append_printf(s, "      </content>\n");
             free(str);
 
         } else {
             g_string_append(s, "/>\n");
         }
 
         g_string_append(s, "    </parameter>\n");
     }
     g_string_append(s, "  </parameters>\n</resource-agent>\n");
 
     return g_string_free(s, FALSE);
 }
 
 void
 pcmk__validate_cluster_options(GHashTable *options,
                                pcmk__cluster_option_t *option_list, int len)
 {
     for (int lpc = 0; lpc < len; lpc++) {
         cluster_option_value(options, option_list[lpc].is_valid,
                              option_list[lpc].name,
                              option_list[lpc].alt_name,
                              option_list[lpc].default_value);
     }
 }