diff --git a/include/crm/common/options_internal.h b/include/crm/common/options_internal.h
index f15b1fd1b8..d9c68f7433 100644
--- a/include/crm/common/options_internal.h
+++ b/include/crm/common/options_internal.h
@@ -1,158 +1,157 @@
 /*
  * Copyright 2006-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 PCMK__OPTIONS_INTERNAL__H
 #  define PCMK__OPTIONS_INTERNAL__H
 
 #  ifndef PCMK__CONFIG_H
 #    define PCMK__CONFIG_H
 #    include <config.h>   // HAVE_GETOPT, _Noreturn
 #  endif
 
 #  include <glib.h>     // GHashTable
 #  include <stdbool.h>  // bool
 
 /*
  * Command-line option handling
  *
  * This will all eventually go away as everything is converted to use GOption
  */
 
 #  ifdef HAVE_GETOPT_H
 #    include <getopt.h>
 #  else
 #    define no_argument 0
 #    define required_argument 1
 #  endif
 
 enum pcmk__cli_option_flags {
     pcmk__option_default    = (1 << 0),
     pcmk__option_hidden     = (1 << 1),
     pcmk__option_paragraph  = (1 << 2),
     pcmk__option_example    = (1 << 3),
 };
 
 typedef struct pcmk__cli_option_s {
     /* Fields from 'struct option' in getopt.h */
     /* name of long option */
     const char *name;
     /*
      * one of no_argument, required_argument, and optional_argument:
      * whether option takes an argument
      */
     int has_arg;
     /* if not NULL, set *flag to val when option found */
     int *flag;
     /* if flag not NULL, value to set *flag to; else return value */
     int val;
 
     /* Custom fields */
     const char *desc;
     long flags;
 } pcmk__cli_option_t;
 
 _Noreturn void pcmk__cli_help(char cmd, crm_exit_t exit_code);
-void pcmk__cli_option_cleanup(void);
 
 
 /*
  * Environment variable option handling
  */
 
 const char *pcmk__env_option(const char *option);
 void pcmk__set_env_option(const char *option, const char *value);
 bool pcmk__env_option_enabled(const char *daemon, const char *option);
 
 
 /*
  * Cluster option handling
  */
 
 typedef struct pcmk__cluster_option_s {
     const char *name;
     const char *alt_name;
     const char *type;
     const char *values;
     const char *default_value;
 
     bool (*is_valid)(const char *);
 
     const char *description_short;
     const char *description_long;
 
 } pcmk__cluster_option_t;
 
 const char *pcmk__cluster_option(GHashTable *options,
                                  const pcmk__cluster_option_t *option_list,
                                  int len, const char *name);
 
 gchar *pcmk__format_option_metadata(const char *name, const char *desc_short,
                                     const char *desc_long,
                                     pcmk__cluster_option_t *option_list,
                                     int len);
 
 void pcmk__validate_cluster_options(GHashTable *options,
                                     pcmk__cluster_option_t *option_list,
                                     int len);
 
 bool pcmk__valid_interval_spec(const char *value);
 bool pcmk__valid_boolean(const char *value);
 bool pcmk__valid_number(const char *value);
 bool pcmk__valid_positive_number(const char *value);
 bool pcmk__valid_quorum(const char *value);
 bool pcmk__valid_script(const char *value);
 bool pcmk__valid_percentage(const char *value);
 
 // from watchdog.c
 long pcmk__get_sbd_timeout(void);
 bool pcmk__get_sbd_sync_resource_startup(void);
 long pcmk__auto_watchdog_timeout(void);
 bool pcmk__valid_sbd_timeout(const char *value);
 
 // Constants for environment variable names
 #define PCMK__ENV_BLACKBOX                  "blackbox"
 #define PCMK__ENV_CLUSTER_TYPE              "cluster_type"
 #define PCMK__ENV_DEBUG                     "debug"
 #define PCMK__ENV_LOGFACILITY               "logfacility"
 #define PCMK__ENV_LOGFILE                   "logfile"
 #define PCMK__ENV_LOGPRIORITY               "logpriority"
 #define PCMK__ENV_MCP                       "mcp"
 #define PCMK__ENV_NODE_START_STATE          "node_start_state"
 #define PCMK__ENV_PHYSICAL_HOST             "physical_host"
 #define PCMK__ENV_QUORUM_TYPE               "quorum_type"
 #define PCMK__ENV_SHUTDOWN_DELAY            "shutdown_delay"
 #define PCMK__ENV_STDERR                    "stderr"
 
 // Constants for cluster option names
 #define PCMK__OPT_NODE_HEALTH_BASE          "node-health-base"
 #define PCMK__OPT_NODE_HEALTH_GREEN         "node-health-green"
 #define PCMK__OPT_NODE_HEALTH_RED           "node-health-red"
 #define PCMK__OPT_NODE_HEALTH_STRATEGY      "node-health-strategy"
 #define PCMK__OPT_NODE_HEALTH_YELLOW        "node-health-yellow"
 
 // Constants for meta-attribute names
 #define PCMK__META_ALLOW_UNHEALTHY_NODES    "allow-unhealthy-nodes"
 
 // Constants for enumerated values for various options
 #define PCMK__VALUE_CLUSTER                 "cluster"
 #define PCMK__VALUE_CUSTOM                  "custom"
 #define PCMK__VALUE_FENCING                 "fencing"
 #define PCMK__VALUE_GREEN                   "green"
 #define PCMK__VALUE_LOCAL                   "local"
 #define PCMK__VALUE_MIGRATE_ON_RED          "migrate-on-red"
 #define PCMK__VALUE_NONE                    "none"
 #define PCMK__VALUE_NOTHING                 "nothing"
 #define PCMK__VALUE_ONLY_GREEN              "only-green"
 #define PCMK__VALUE_PROGRESSIVE             "progressive"
 #define PCMK__VALUE_QUORUM                  "quorum"
 #define PCMK__VALUE_RED                     "red"
 #define PCMK__VALUE_UNFENCING               "unfencing"
 #define PCMK__VALUE_YELLOW                  "yellow"
 
 #endif // PCMK__OPTIONS_INTERNAL__H
diff --git a/lib/common/options.c b/lib/common/options.c
index 0aae9eab2e..4d61e715a5 100644
--- a/lib/common/options.c
+++ b/lib/common/options.c
@@ -1,517 +1,503 @@
 /*
  * 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;
-
-void
-pcmk__cli_option_cleanup(void)
-{
-    free(crm_short_options);
-    crm_short_options = NULL;
-}
-
 void
 pcmk__cli_help(char cmd, crm_exit_t exit_code)
 {
     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");
 
     } else if (cmd == '!') {
         fprintf(stream, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
     }
 
     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]     len          Length of \p option_list
  * \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);
     }
 }
diff --git a/lib/common/results.c b/lib/common/results.c
index c3e520cf6b..ca3d7dd3f0 100644
--- a/lib/common/results.c
+++ b/lib/common/results.c
@@ -1,1034 +1,1032 @@
 /*
  * 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.
  */
 
 #include <crm_internal.h>
 
 #ifndef _GNU_SOURCE
 #  define _GNU_SOURCE
 #endif
 
 #include <bzlib.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <qb/qbdefs.h>
 
 #include <crm/common/mainloop.h>
 #include <crm/common/xml.h>
 
 G_DEFINE_QUARK(pcmk-rc-error-quark, pcmk__rc_error)
 G_DEFINE_QUARK(pcmk-exitc-error-quark, pcmk__exitc_error)
 
 // General (all result code types)
 
 /*!
  * \brief Get the name and description of a given result code
  *
  * A result code can be interpreted as a member of any one of several families.
  *
  * \param[in]  code  The result code to look up
  * \param[in]  type  How \p code should be interpreted
  * \param[out] name  Where to store the result code's name
  * \param[out] desc  Where to store the result code's description
  *
  * \return Standard Pacemaker return code
  */
 int
 pcmk_result_get_strings(int code, enum pcmk_result_type type, const char **name,
                         const char **desc)
 {
     const char *code_name = NULL;
     const char *code_desc = NULL;
 
     switch (type) {
         case pcmk_result_legacy:
             code_name = pcmk_errorname(code);
             code_desc = pcmk_strerror(code);
             break;
         case pcmk_result_rc:
             code_name = pcmk_rc_name(code);
             code_desc = pcmk_rc_str(code);
             break;
         case pcmk_result_exitcode:
             code_name = crm_exit_name(code);
             code_desc = crm_exit_str((crm_exit_t) code);
             break;
         default:
             return pcmk_rc_undetermined;
     }
 
     if (name != NULL) {
         *name = code_name;
     }
     
     if (desc != NULL) {
         *desc = code_desc;
     }
     return pcmk_rc_ok;
 }
 
 /*!
  * \internal
  * \brief Get the lower and upper bounds of a result code family
  *
  * \param[in]   type    Type of result code
  * \param[out]  lower   Where to store the lower bound
  * \param[out]  upper   Where to store the upper bound
  *
  * \return Standard Pacemaker return code
  *
  * \note There is no true upper bound on standard Pacemaker return codes or
  *       legacy return codes. All system \p errno values are valid members of
  *       these result code families, and there is no global upper limit nor a
  *       constant by which to refer to the highest \p errno value on a given
  *       system.
  */
 int
 pcmk__result_bounds(enum pcmk_result_type type, int *lower, int *upper)
 {
     CRM_ASSERT((lower != NULL) && (upper != NULL));
 
     switch (type) {
         case pcmk_result_legacy:
             *lower = pcmk_ok;
             *upper = 256;   // should be enough for almost any system error code
             break;
         case pcmk_result_rc:
             *lower = pcmk_rc_error - pcmk__n_rc + 1;
             *upper = 256;
             break;
         case pcmk_result_exitcode:
             *lower = CRM_EX_OK;
             *upper = CRM_EX_MAX;
             break;
         default:
             *lower = 0;
             *upper = -1;
             return pcmk_rc_undetermined;
     }
     return pcmk_rc_ok;
 }
 
 // @COMPAT Legacy function return codes
 
 //! \deprecated Use standard return codes and pcmk_rc_name() instead
 const char *
 pcmk_errorname(int rc)
 {
     rc = abs(rc);
     switch (rc) {
         case pcmk_err_generic: return "pcmk_err_generic";
         case pcmk_err_no_quorum: return "pcmk_err_no_quorum";
         case pcmk_err_schema_validation: return "pcmk_err_schema_validation";
         case pcmk_err_transform_failed: return "pcmk_err_transform_failed";
         case pcmk_err_old_data: return "pcmk_err_old_data";
         case pcmk_err_diff_failed: return "pcmk_err_diff_failed";
         case pcmk_err_diff_resync: return "pcmk_err_diff_resync";
         case pcmk_err_cib_modified: return "pcmk_err_cib_modified";
         case pcmk_err_cib_backup: return "pcmk_err_cib_backup";
         case pcmk_err_cib_save: return "pcmk_err_cib_save";
         case pcmk_err_cib_corrupt: return "pcmk_err_cib_corrupt";
         case pcmk_err_multiple: return "pcmk_err_multiple";
         case pcmk_err_node_unknown: return "pcmk_err_node_unknown";
         case pcmk_err_already: return "pcmk_err_already";
         case pcmk_err_bad_nvpair: return "pcmk_err_bad_nvpair";
         case pcmk_err_unknown_format: return "pcmk_err_unknown_format";
         default: return pcmk_rc_name(rc); // system errno
     }
 }
 
 //! \deprecated Use standard return codes and pcmk_rc_str() instead
 const char *
 pcmk_strerror(int rc)
 {
     return pcmk_rc_str(pcmk_legacy2rc(rc));
 }
 
 // Standard Pacemaker API return codes
 
 /* This array is used only for nonzero values of pcmk_rc_e. Its values must be
  * kept in the exact reverse order of the enum value numbering (i.e. add new
  * values to the end of the array).
  */
 static const struct pcmk__rc_info {
     const char *name;
     const char *desc;
     int legacy_rc;
 } pcmk__rcs[] = {
     { "pcmk_rc_error",
       "Error",
       -pcmk_err_generic,
     },
     { "pcmk_rc_unknown_format",
       "Unknown output format",
       -pcmk_err_unknown_format,
     },
     { "pcmk_rc_bad_nvpair",
       "Bad name/value pair given",
       -pcmk_err_bad_nvpair,
     },
     { "pcmk_rc_already",
       "Already in requested state",
       -pcmk_err_already,
     },
     { "pcmk_rc_node_unknown",
       "Node not found",
       -pcmk_err_node_unknown,
     },
     { "pcmk_rc_multiple",
       "Resource active on multiple nodes",
       -pcmk_err_multiple,
     },
     { "pcmk_rc_cib_corrupt",
       "Could not parse on-disk configuration",
       -pcmk_err_cib_corrupt,
     },
     { "pcmk_rc_cib_save",
       "Could not save new configuration to disk",
       -pcmk_err_cib_save,
     },
     { "pcmk_rc_cib_backup",
       "Could not archive previous configuration",
       -pcmk_err_cib_backup,
     },
     { "pcmk_rc_cib_modified",
       "On-disk configuration was manually modified",
       -pcmk_err_cib_modified,
     },
     { "pcmk_rc_diff_resync",
       "Application of update diff failed, requesting full refresh",
       -pcmk_err_diff_resync,
     },
     { "pcmk_rc_diff_failed",
       "Application of update diff failed",
       -pcmk_err_diff_failed,
     },
     { "pcmk_rc_old_data",
       "Update was older than existing configuration",
       -pcmk_err_old_data,
     },
     { "pcmk_rc_transform_failed",
       "Schema transform failed",
       -pcmk_err_transform_failed,
     },
     { "pcmk_rc_schema_unchanged",
       "Schema is already the latest available",
       -pcmk_err_schema_unchanged,
     },
     { "pcmk_rc_schema_validation",
       "Update does not conform to the configured schema",
       -pcmk_err_schema_validation,
     },
     { "pcmk_rc_no_quorum",
       "Operation requires quorum",
       -pcmk_err_no_quorum,
     },
     { "pcmk_rc_ipc_unauthorized",
       "IPC server is blocked by unauthorized process",
       -pcmk_err_generic,
     },
     { "pcmk_rc_ipc_unresponsive",
       "IPC server is unresponsive",
       -pcmk_err_generic,
     },
     { "pcmk_rc_ipc_pid_only",
       "IPC server process is active but not accepting connections",
       -pcmk_err_generic,
     },
     { "pcmk_rc_op_unsatisfied",
       "Not applicable under current conditions",
       -pcmk_err_generic,
     },
     { "pcmk_rc_undetermined",
       "Result undetermined",
       -pcmk_err_generic,
     },
     { "pcmk_rc_before_range",
       "Result occurs before given range",
       -pcmk_err_generic,
     },
     { "pcmk_rc_within_range",
       "Result occurs within given range",
       -pcmk_err_generic,
     },
     { "pcmk_rc_after_range",
       "Result occurs after given range",
       -pcmk_err_generic,
     },
     { "pcmk_rc_no_output",
       "Output message produced no output",
       -pcmk_err_generic,
     },
     { "pcmk_rc_no_input",
       "Input file not available",
       -pcmk_err_generic,
     },
     { "pcmk_rc_underflow",
       "Value too small to be stored in data type",
       -pcmk_err_generic,
     },
     { "pcmk_rc_dot_error",
       "Error writing dot(1) file",
       -pcmk_err_generic,
     },
     { "pcmk_rc_graph_error",
       "Error writing graph file",
       -pcmk_err_generic,
     },
     { "pcmk_rc_invalid_transition",
       "Cluster simulation produced invalid transition",
       -pcmk_err_generic,
     },
     { "pcmk_rc_unpack_error",
       "Unable to parse CIB XML",
       -pcmk_err_generic,
     },
     { "pcmk_rc_duplicate_id",
       "Two or more XML elements have the same ID",
       -pcmk_err_generic,
     },
 };
 
 /*!
  * \internal
  * \brief The number of <tt>enum pcmk_rc_e</tt> values, excluding \c pcmk_rc_ok
  *
  * This constant stores the number of negative standard Pacemaker return codes.
  * These represent Pacemaker-custom error codes. The count does not include
  * positive system error numbers, nor does it include \c pcmk_rc_ok (success).
  */
 const size_t pcmk__n_rc = PCMK__NELEM(pcmk__rcs);
 
 /*!
  * \brief Get a return code constant name as a string
  *
  * \param[in] rc  Integer return code to convert
  *
  * \return String of constant name corresponding to rc
  */
 const char *
 pcmk_rc_name(int rc)
 {
     if ((rc <= pcmk_rc_error) && ((pcmk_rc_error - rc) < pcmk__n_rc)) {
         return pcmk__rcs[pcmk_rc_error - rc].name;
     }
     switch (rc) {
         case pcmk_rc_ok:        return "pcmk_rc_ok";
         case E2BIG:             return "E2BIG";
         case EACCES:            return "EACCES";
         case EADDRINUSE:        return "EADDRINUSE";
         case EADDRNOTAVAIL:     return "EADDRNOTAVAIL";
         case EAFNOSUPPORT:      return "EAFNOSUPPORT";
         case EAGAIN:            return "EAGAIN";
         case EALREADY:          return "EALREADY";
         case EBADF:             return "EBADF";
         case EBADMSG:           return "EBADMSG";
         case EBUSY:             return "EBUSY";
         case ECANCELED:         return "ECANCELED";
         case ECHILD:            return "ECHILD";
         case ECOMM:             return "ECOMM";
         case ECONNABORTED:      return "ECONNABORTED";
         case ECONNREFUSED:      return "ECONNREFUSED";
         case ECONNRESET:        return "ECONNRESET";
         /* case EDEADLK:        return "EDEADLK"; */
         case EDESTADDRREQ:      return "EDESTADDRREQ";
         case EDOM:              return "EDOM";
         case EDQUOT:            return "EDQUOT";
         case EEXIST:            return "EEXIST";
         case EFAULT:            return "EFAULT";
         case EFBIG:             return "EFBIG";
         case EHOSTDOWN:         return "EHOSTDOWN";
         case EHOSTUNREACH:      return "EHOSTUNREACH";
         case EIDRM:             return "EIDRM";
         case EILSEQ:            return "EILSEQ";
         case EINPROGRESS:       return "EINPROGRESS";
         case EINTR:             return "EINTR";
         case EINVAL:            return "EINVAL";
         case EIO:               return "EIO";
         case EISCONN:           return "EISCONN";
         case EISDIR:            return "EISDIR";
         case ELIBACC:           return "ELIBACC";
         case ELOOP:             return "ELOOP";
         case EMFILE:            return "EMFILE";
         case EMLINK:            return "EMLINK";
         case EMSGSIZE:          return "EMSGSIZE";
 #ifdef EMULTIHOP // Not available on OpenBSD
         case EMULTIHOP:         return "EMULTIHOP";
 #endif
         case ENAMETOOLONG:      return "ENAMETOOLONG";
         case ENETDOWN:          return "ENETDOWN";
         case ENETRESET:         return "ENETRESET";
         case ENETUNREACH:       return "ENETUNREACH";
         case ENFILE:            return "ENFILE";
         case ENOBUFS:           return "ENOBUFS";
         case ENODATA:           return "ENODATA";
         case ENODEV:            return "ENODEV";
         case ENOENT:            return "ENOENT";
         case ENOEXEC:           return "ENOEXEC";
         case ENOKEY:            return "ENOKEY";
         case ENOLCK:            return "ENOLCK";
 #ifdef ENOLINK // Not available on OpenBSD
         case ENOLINK:           return "ENOLINK";
 #endif
         case ENOMEM:            return "ENOMEM";
         case ENOMSG:            return "ENOMSG";
         case ENOPROTOOPT:       return "ENOPROTOOPT";
         case ENOSPC:            return "ENOSPC";
 #ifdef ENOSR
         case ENOSR:             return "ENOSR";
 #endif
 #ifdef ENOSTR
         case ENOSTR:            return "ENOSTR";
 #endif
         case ENOSYS:            return "ENOSYS";
         case ENOTBLK:           return "ENOTBLK";
         case ENOTCONN:          return "ENOTCONN";
         case ENOTDIR:           return "ENOTDIR";
         case ENOTEMPTY:         return "ENOTEMPTY";
         case ENOTSOCK:          return "ENOTSOCK";
 #if ENOTSUP != EOPNOTSUPP
         case ENOTSUP:           return "ENOTSUP";
 #endif
         case ENOTTY:            return "ENOTTY";
         case ENOTUNIQ:          return "ENOTUNIQ";
         case ENXIO:             return "ENXIO";
         case EOPNOTSUPP:        return "EOPNOTSUPP";
         case EOVERFLOW:         return "EOVERFLOW";
         case EPERM:             return "EPERM";
         case EPFNOSUPPORT:      return "EPFNOSUPPORT";
         case EPIPE:             return "EPIPE";
         case EPROTO:            return "EPROTO";
         case EPROTONOSUPPORT:   return "EPROTONOSUPPORT";
         case EPROTOTYPE:        return "EPROTOTYPE";
         case ERANGE:            return "ERANGE";
         case EREMOTE:           return "EREMOTE";
         case EREMOTEIO:         return "EREMOTEIO";
         case EROFS:             return "EROFS";
         case ESHUTDOWN:         return "ESHUTDOWN";
         case ESPIPE:            return "ESPIPE";
         case ESOCKTNOSUPPORT:   return "ESOCKTNOSUPPORT";
         case ESRCH:             return "ESRCH";
         case ESTALE:            return "ESTALE";
         case ETIME:             return "ETIME";
         case ETIMEDOUT:         return "ETIMEDOUT";
         case ETXTBSY:           return "ETXTBSY";
 #ifdef EUNATCH
         case EUNATCH:           return "EUNATCH";
 #endif
         case EUSERS:            return "EUSERS";
         /* case EWOULDBLOCK:    return "EWOULDBLOCK"; */
         case EXDEV:             return "EXDEV";
 
 #ifdef EBADE // Not available on OS X
         case EBADE:             return "EBADE";
         case EBADFD:            return "EBADFD";
         case EBADSLT:           return "EBADSLT";
         case EDEADLOCK:         return "EDEADLOCK";
         case EBADR:             return "EBADR";
         case EBADRQC:           return "EBADRQC";
         case ECHRNG:            return "ECHRNG";
 #ifdef EISNAM // Not available on OS X, Illumos, Solaris
         case EISNAM:            return "EISNAM";
         case EKEYEXPIRED:       return "EKEYEXPIRED";
         case EKEYREVOKED:       return "EKEYREVOKED";
 #endif
         case EKEYREJECTED:      return "EKEYREJECTED";
         case EL2HLT:            return "EL2HLT";
         case EL2NSYNC:          return "EL2NSYNC";
         case EL3HLT:            return "EL3HLT";
         case EL3RST:            return "EL3RST";
         case ELIBBAD:           return "ELIBBAD";
         case ELIBMAX:           return "ELIBMAX";
         case ELIBSCN:           return "ELIBSCN";
         case ELIBEXEC:          return "ELIBEXEC";
 #ifdef ENOMEDIUM // Not available on OS X, Illumos, Solaris
         case ENOMEDIUM:         return "ENOMEDIUM";
         case EMEDIUMTYPE:       return "EMEDIUMTYPE";
 #endif
         case ENONET:            return "ENONET";
         case ENOPKG:            return "ENOPKG";
         case EREMCHG:           return "EREMCHG";
         case ERESTART:          return "ERESTART";
         case ESTRPIPE:          return "ESTRPIPE";
 #ifdef EUCLEAN // Not available on OS X, Illumos, Solaris
         case EUCLEAN:           return "EUCLEAN";
 #endif
         case EXFULL:            return "EXFULL";
 #endif // EBADE
         default:                return "Unknown";
     }
 }
 
 /*!
  * \brief Get a user-friendly description of a return code
  *
  * \param[in] rc  Integer return code to convert
  *
  * \return String description of rc
  */
 const char *
 pcmk_rc_str(int rc)
 {
     if (rc == pcmk_rc_ok) {
         return "OK";
     }
     if ((rc <= pcmk_rc_error) && ((pcmk_rc_error - rc) < pcmk__n_rc)) {
         return pcmk__rcs[pcmk_rc_error - rc].desc;
     }
     if (rc < 0) {
         return "Error";
     }
 
     // Handle values that could be defined by system or by portability.h
     switch (rc) {
 #ifdef PCMK__ENOTUNIQ
         case ENOTUNIQ:      return "Name not unique on network";
 #endif
 #ifdef PCMK__ECOMM
         case ECOMM:         return "Communication error on send";
 #endif
 #ifdef PCMK__ELIBACC
         case ELIBACC:       return "Can not access a needed shared library";
 #endif
 #ifdef PCMK__EREMOTEIO
         case EREMOTEIO:     return "Remote I/O error";
 #endif
 #ifdef PCMK__ENOKEY
         case ENOKEY:        return "Required key not available";
 #endif
 #ifdef PCMK__ENODATA
         case ENODATA:       return "No data available";
 #endif
 #ifdef PCMK__ETIME
         case ETIME:         return "Timer expired";
 #endif
 #ifdef PCMK__EKEYREJECTED
         case EKEYREJECTED:  return "Key was rejected by service";
 #endif
         default:            return strerror(rc);
     }
 }
 
 // This returns negative values for errors
 //! \deprecated Use standard return codes instead
 int
 pcmk_rc2legacy(int rc)
 {
     if (rc >= 0) {
         return -rc; // OK or system errno
     }
     if ((rc <= pcmk_rc_error) && ((pcmk_rc_error - rc) < pcmk__n_rc)) {
         return pcmk__rcs[pcmk_rc_error - rc].legacy_rc;
     }
     return -pcmk_err_generic;
 }
 
 //! \deprecated Use standard return codes instead
 int
 pcmk_legacy2rc(int legacy_rc)
 {
     legacy_rc = abs(legacy_rc);
     switch (legacy_rc) {
         case pcmk_err_no_quorum:            return pcmk_rc_no_quorum;
         case pcmk_err_schema_validation:    return pcmk_rc_schema_validation;
         case pcmk_err_schema_unchanged:     return pcmk_rc_schema_unchanged;
         case pcmk_err_transform_failed:     return pcmk_rc_transform_failed;
         case pcmk_err_old_data:             return pcmk_rc_old_data;
         case pcmk_err_diff_failed:          return pcmk_rc_diff_failed;
         case pcmk_err_diff_resync:          return pcmk_rc_diff_resync;
         case pcmk_err_cib_modified:         return pcmk_rc_cib_modified;
         case pcmk_err_cib_backup:           return pcmk_rc_cib_backup;
         case pcmk_err_cib_save:             return pcmk_rc_cib_save;
         case pcmk_err_cib_corrupt:          return pcmk_rc_cib_corrupt;
         case pcmk_err_multiple:             return pcmk_rc_multiple;
         case pcmk_err_node_unknown:         return pcmk_rc_node_unknown;
         case pcmk_err_already:              return pcmk_rc_already;
         case pcmk_err_bad_nvpair:           return pcmk_rc_bad_nvpair;
         case pcmk_err_unknown_format:       return pcmk_rc_unknown_format;
         case pcmk_err_generic:              return pcmk_rc_error;
         case pcmk_ok:                       return pcmk_rc_ok;
         default:                            return legacy_rc; // system errno
     }
 }
 
 // Exit status codes
 
 const char *
 crm_exit_name(crm_exit_t exit_code)
 {
     switch (exit_code) {
         case CRM_EX_OK: return "CRM_EX_OK";
         case CRM_EX_ERROR: return "CRM_EX_ERROR";
         case CRM_EX_INVALID_PARAM: return "CRM_EX_INVALID_PARAM";
         case CRM_EX_UNIMPLEMENT_FEATURE: return "CRM_EX_UNIMPLEMENT_FEATURE";
         case CRM_EX_INSUFFICIENT_PRIV: return "CRM_EX_INSUFFICIENT_PRIV";
         case CRM_EX_NOT_INSTALLED: return "CRM_EX_NOT_INSTALLED";
         case CRM_EX_NOT_CONFIGURED: return "CRM_EX_NOT_CONFIGURED";
         case CRM_EX_NOT_RUNNING: return "CRM_EX_NOT_RUNNING";
         case CRM_EX_PROMOTED: return "CRM_EX_PROMOTED";
         case CRM_EX_FAILED_PROMOTED: return "CRM_EX_FAILED_PROMOTED";
         case CRM_EX_USAGE: return "CRM_EX_USAGE";
         case CRM_EX_DATAERR: return "CRM_EX_DATAERR";
         case CRM_EX_NOINPUT: return "CRM_EX_NOINPUT";
         case CRM_EX_NOUSER: return "CRM_EX_NOUSER";
         case CRM_EX_NOHOST: return "CRM_EX_NOHOST";
         case CRM_EX_UNAVAILABLE: return "CRM_EX_UNAVAILABLE";
         case CRM_EX_SOFTWARE: return "CRM_EX_SOFTWARE";
         case CRM_EX_OSERR: return "CRM_EX_OSERR";
         case CRM_EX_OSFILE: return "CRM_EX_OSFILE";
         case CRM_EX_CANTCREAT: return "CRM_EX_CANTCREAT";
         case CRM_EX_IOERR: return "CRM_EX_IOERR";
         case CRM_EX_TEMPFAIL: return "CRM_EX_TEMPFAIL";
         case CRM_EX_PROTOCOL: return "CRM_EX_PROTOCOL";
         case CRM_EX_NOPERM: return "CRM_EX_NOPERM";
         case CRM_EX_CONFIG: return "CRM_EX_CONFIG";
         case CRM_EX_FATAL: return "CRM_EX_FATAL";
         case CRM_EX_PANIC: return "CRM_EX_PANIC";
         case CRM_EX_DISCONNECT: return "CRM_EX_DISCONNECT";
         case CRM_EX_DIGEST: return "CRM_EX_DIGEST";
         case CRM_EX_NOSUCH: return "CRM_EX_NOSUCH";
         case CRM_EX_QUORUM: return "CRM_EX_QUORUM";
         case CRM_EX_UNSAFE: return "CRM_EX_UNSAFE";
         case CRM_EX_EXISTS: return "CRM_EX_EXISTS";
         case CRM_EX_MULTIPLE: return "CRM_EX_MULTIPLE";
         case CRM_EX_EXPIRED: return "CRM_EX_EXPIRED";
         case CRM_EX_NOT_YET_IN_EFFECT: return "CRM_EX_NOT_YET_IN_EFFECT";
         case CRM_EX_INDETERMINATE: return "CRM_EX_INDETERMINATE";
         case CRM_EX_UNSATISFIED: return "CRM_EX_UNSATISFIED";
         case CRM_EX_OLD: return "CRM_EX_OLD";
         case CRM_EX_TIMEOUT: return "CRM_EX_TIMEOUT";
         case CRM_EX_DEGRADED: return "CRM_EX_DEGRADED";
         case CRM_EX_DEGRADED_PROMOTED: return "CRM_EX_DEGRADED_PROMOTED";
         case CRM_EX_NONE: return "CRM_EX_NONE";
         case CRM_EX_MAX: return "CRM_EX_UNKNOWN";
     }
     return "CRM_EX_UNKNOWN";
 }
 
 const char *
 crm_exit_str(crm_exit_t exit_code)
 {
     switch (exit_code) {
         case CRM_EX_OK: return "OK";
         case CRM_EX_ERROR: return "Error occurred";
         case CRM_EX_INVALID_PARAM: return "Invalid parameter";
         case CRM_EX_UNIMPLEMENT_FEATURE: return "Unimplemented";
         case CRM_EX_INSUFFICIENT_PRIV: return "Insufficient privileges";
         case CRM_EX_NOT_INSTALLED: return "Not installed";
         case CRM_EX_NOT_CONFIGURED: return "Not configured";
         case CRM_EX_NOT_RUNNING: return "Not running";
         case CRM_EX_PROMOTED: return "Promoted";
         case CRM_EX_FAILED_PROMOTED: return "Failed in promoted role";
         case CRM_EX_USAGE: return "Incorrect usage";
         case CRM_EX_DATAERR: return "Invalid data given";
         case CRM_EX_NOINPUT: return "Input file not available";
         case CRM_EX_NOUSER: return "User does not exist";
         case CRM_EX_NOHOST: return "Host does not exist";
         case CRM_EX_UNAVAILABLE: return "Necessary service unavailable";
         case CRM_EX_SOFTWARE: return "Internal software bug";
         case CRM_EX_OSERR: return "Operating system error occurred";
         case CRM_EX_OSFILE: return "System file not available";
         case CRM_EX_CANTCREAT: return "Cannot create output file";
         case CRM_EX_IOERR: return "I/O error occurred";
         case CRM_EX_TEMPFAIL: return "Temporary failure, try again";
         case CRM_EX_PROTOCOL: return "Protocol violated";
         case CRM_EX_NOPERM: return "Insufficient privileges";
         case CRM_EX_CONFIG: return "Invalid configuration";
         case CRM_EX_FATAL: return "Fatal error occurred, will not respawn";
         case CRM_EX_PANIC: return "System panic required";
         case CRM_EX_DISCONNECT: return "Not connected";
         case CRM_EX_DIGEST: return "Digest mismatch";
         case CRM_EX_NOSUCH: return "No such object";
         case CRM_EX_QUORUM: return "Quorum required";
         case CRM_EX_UNSAFE: return "Operation not safe";
         case CRM_EX_EXISTS: return "Requested item already exists";
         case CRM_EX_MULTIPLE: return "Multiple items match request";
         case CRM_EX_EXPIRED: return "Requested item has expired";
         case CRM_EX_NOT_YET_IN_EFFECT: return "Requested item is not yet in effect";
         case CRM_EX_INDETERMINATE: return "Could not determine status";
         case CRM_EX_UNSATISFIED: return "Not applicable under current conditions";
         case CRM_EX_OLD: return "Update was older than existing configuration";
         case CRM_EX_TIMEOUT: return "Timeout occurred";
         case CRM_EX_DEGRADED: return "Service is active but might fail soon";
         case CRM_EX_DEGRADED_PROMOTED: return "Service is promoted but might fail soon";
         case CRM_EX_NONE: return "No exit status available";
         case CRM_EX_MAX: return "Error occurred";
     }
     if ((exit_code > 128) && (exit_code < CRM_EX_MAX)) {
         return "Interrupted by signal";
     }
     return "Unknown exit status";
 }
 
 /*!
  * \brief Map a function return code to the most similar exit code
  *
  * \param[in] rc  Function return code
  *
  * \return Most similar exit code
  */
 crm_exit_t
 pcmk_rc2exitc(int rc)
 {
     switch (rc) {
         case pcmk_rc_ok:
             return CRM_EX_OK;
 
         case pcmk_rc_no_quorum:
             return CRM_EX_QUORUM;
 
         case pcmk_rc_old_data:
             return CRM_EX_OLD;
 
         case pcmk_rc_schema_validation:
         case pcmk_rc_transform_failed:
         case pcmk_rc_unpack_error:
             return CRM_EX_CONFIG;
 
         case pcmk_rc_bad_nvpair:
             return CRM_EX_INVALID_PARAM;
 
         case EACCES:
             return CRM_EX_INSUFFICIENT_PRIV;
 
         case EBADF:
         case EINVAL:
         case EFAULT:
         case ENOSYS:
         case EOVERFLOW:
         case pcmk_rc_underflow:
             return CRM_EX_SOFTWARE;
 
         case EBADMSG:
         case EMSGSIZE:
         case ENOMSG:
         case ENOPROTOOPT:
         case EPROTO:
         case EPROTONOSUPPORT:
         case EPROTOTYPE:
             return CRM_EX_PROTOCOL;
 
         case ECOMM:
         case ENOMEM:
             return CRM_EX_OSERR;
 
         case ECONNABORTED:
         case ECONNREFUSED:
         case ECONNRESET:
         case ENOTCONN:
             return CRM_EX_DISCONNECT;
 
         case EEXIST:
         case pcmk_rc_already:
             return CRM_EX_EXISTS;
 
         case EIO:
         case pcmk_rc_no_output:
         case pcmk_rc_dot_error:
         case pcmk_rc_graph_error:
             return CRM_EX_IOERR;
 
         case ENOTSUP:
 #if EOPNOTSUPP != ENOTSUP
         case EOPNOTSUPP:
 #endif
             return CRM_EX_UNIMPLEMENT_FEATURE;
 
         case ENOTUNIQ:
         case pcmk_rc_multiple:
             return CRM_EX_MULTIPLE;
 
         case ENODEV:
         case ENOENT:
         case ENXIO:
         case pcmk_rc_unknown_format:
             return CRM_EX_NOSUCH;
 
         case pcmk_rc_node_unknown:
             return CRM_EX_NOHOST;
 
         case ETIME:
         case ETIMEDOUT:
             return CRM_EX_TIMEOUT;
 
         case EAGAIN:
         case EBUSY:
             return CRM_EX_UNSATISFIED;
 
         case pcmk_rc_before_range:
             return CRM_EX_NOT_YET_IN_EFFECT;
 
         case pcmk_rc_after_range:
             return CRM_EX_EXPIRED;
 
         case pcmk_rc_undetermined:
             return CRM_EX_INDETERMINATE;
 
         case pcmk_rc_op_unsatisfied:
             return CRM_EX_UNSATISFIED;
 
         case pcmk_rc_within_range:
             return CRM_EX_OK;
 
         case pcmk_rc_no_input:
             return CRM_EX_NOINPUT;
 
         case pcmk_rc_duplicate_id:
             return CRM_EX_MULTIPLE;
 
         default:
             return CRM_EX_ERROR;
     }
 }
 
 /*!
  * \brief Map a function return code to the most similar OCF exit code
  *
  * \param[in] rc  Function return code
  *
  * \return Most similar OCF exit code
  */
 enum ocf_exitcode
 pcmk_rc2ocf(int rc)
 {
     switch (rc) {
         case pcmk_rc_ok:
             return PCMK_OCF_OK;
 
         case pcmk_rc_bad_nvpair:
             return PCMK_OCF_INVALID_PARAM;
 
         case EACCES:
             return PCMK_OCF_INSUFFICIENT_PRIV;
 
         case ENOTSUP:
 #if EOPNOTSUPP != ENOTSUP
         case EOPNOTSUPP:
 #endif
             return PCMK_OCF_UNIMPLEMENT_FEATURE;
 
         default:
             return PCMK_OCF_UNKNOWN_ERROR;
     }
 }
 
 
 // Other functions
 
 const char *
 bz2_strerror(int rc)
 {
     // See ftp://sources.redhat.com/pub/bzip2/docs/manual_3.html#SEC17
     switch (rc) {
         case BZ_OK:
         case BZ_RUN_OK:
         case BZ_FLUSH_OK:
         case BZ_FINISH_OK:
         case BZ_STREAM_END:
             return "Ok";
         case BZ_CONFIG_ERROR:
             return "libbz2 has been improperly compiled on your platform";
         case BZ_SEQUENCE_ERROR:
             return "library functions called in the wrong order";
         case BZ_PARAM_ERROR:
             return "parameter is out of range or otherwise incorrect";
         case BZ_MEM_ERROR:
             return "memory allocation failed";
         case BZ_DATA_ERROR:
             return "data integrity error is detected during decompression";
         case BZ_DATA_ERROR_MAGIC:
             return "the compressed stream does not start with the correct magic bytes";
         case BZ_IO_ERROR:
             return "error reading or writing in the compressed file";
         case BZ_UNEXPECTED_EOF:
             return "compressed file finishes before the logical end of stream is detected";
         case BZ_OUTBUFF_FULL:
             return "output data will not fit into the buffer provided";
     }
     return "Data compression error";
 }
 
 crm_exit_t
 crm_exit(crm_exit_t rc)
 {
     /* A compiler could theoretically use any type for crm_exit_t, but an int
      * should always hold it, so cast to int to keep static analysis happy.
      */
     if ((((int) rc) < 0) || (((int) rc) > CRM_EX_MAX)) {
         rc = CRM_EX_ERROR;
     }
 
     mainloop_cleanup();
     crm_xml_cleanup();
 
-    pcmk__cli_option_cleanup();
-
     free(pcmk__our_nodename);
 
     if (crm_system_name) {
         crm_info("Exiting %s " CRM_XS " with status %d", crm_system_name, rc);
         free(crm_system_name);
     } else {
         crm_trace("Exiting with status %d", rc);
     }
     qb_log_fini(); // Don't log anything after this point
 
     exit(rc);
 }
 
 /*
  * External action results
  */
 
 /*!
  * \internal
  * \brief Set the result of an action
  *
  * \param[out] result        Where to set action result
  * \param[in]  exit_status   OCF exit status to set
  * \param[in]  exec_status   Execution status to set
  * \param[in]  exit_reason   Human-friendly description of event to set
  */
 void
 pcmk__set_result(pcmk__action_result_t *result, int exit_status,
                  enum pcmk_exec_status exec_status, const char *exit_reason)
 {
     if (result == NULL) {
         return;
     }
 
     result->exit_status = exit_status;
     result->execution_status = exec_status;
 
     if (!pcmk__str_eq(result->exit_reason, exit_reason, pcmk__str_none)) {
         free(result->exit_reason);
         result->exit_reason = (exit_reason == NULL)? NULL : strdup(exit_reason);
     }
 }
 
 
 /*!
  * \internal
  * \brief Set the result of an action, with a formatted exit reason
  *
  * \param[out] result        Where to set action result
  * \param[in]  exit_status   OCF exit status to set
  * \param[in]  exec_status   Execution status to set
  * \param[in]  format        printf-style format for a human-friendly
  *                           description of reason for result
  * \param[in]  ...           arguments for \p format
  */
 G_GNUC_PRINTF(4, 5)
 void
 pcmk__format_result(pcmk__action_result_t *result, int exit_status,
                     enum pcmk_exec_status exec_status,
                     const char *format, ...)
 {
     va_list ap;
     int len = 0;
     char *reason = NULL;
 
     if (result == NULL) {
         return;
     }
 
     result->exit_status = exit_status;
     result->execution_status = exec_status;
 
     if (format != NULL) {
         va_start(ap, format);
         len = vasprintf(&reason, format, ap);
         CRM_ASSERT(len > 0);
         va_end(ap);
     }
     free(result->exit_reason);
     result->exit_reason = reason;
 }
 
 /*!
  * \internal
  * \brief Set the output of an action
  *
  * \param[out] result         Action result to set output for
  * \param[in]  out            Action output to set (must be dynamically
  *                            allocated)
  * \param[in]  err            Action error output to set (must be dynamically
  *                            allocated)
  *
  * \note \p result will take ownership of \p out and \p err, so the caller
  *       should not free them.
  */
 void
 pcmk__set_result_output(pcmk__action_result_t *result, char *out, char *err)
 {
     if (result == NULL) {
         return;
     }
 
     free(result->action_stdout);
     result->action_stdout = out;
 
     free(result->action_stderr);
     result->action_stderr = err;
 }
 
 /*!
  * \internal
  * \brief Clear a result's exit reason, output, and error output
  *
  * \param[in,out] result  Result to reset
  */
 void
 pcmk__reset_result(pcmk__action_result_t *result)
 {
     if (result == NULL) {
         return;
     }
 
     free(result->exit_reason);
     result->exit_reason = NULL;
 
     free(result->action_stdout);
     result->action_stdout = NULL;
 
     free(result->action_stderr);
     result->action_stderr = NULL;
 }
 
 /*!
  * \internal
  * \brief Copy the result of an action
  *
  * \param[in]  src  Result to copy
  * \param[out] dst  Where to copy \p src to
  */
 void
 pcmk__copy_result(const pcmk__action_result_t *src, pcmk__action_result_t *dst)
 {
     CRM_CHECK((src != NULL) && (dst != NULL), return);
     dst->exit_status = src->exit_status;
     dst->execution_status = src->execution_status;
     pcmk__str_update(&dst->exit_reason, src->exit_reason);
     pcmk__str_update(&dst->action_stdout, src->action_stdout);
     pcmk__str_update(&dst->action_stderr, src->action_stderr);
 }
 
 // Deprecated functions kept only for backward API compatibility
 // LCOV_EXCL_START
 
 #include <crm/common/results_compat.h>
 
 crm_exit_t
 crm_errno2exit(int rc)
 {
     return pcmk_rc2exitc(pcmk_legacy2rc(rc));
 }
 
 // LCOV_EXCL_STOP
 // End deprecated API