diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c
index 6dbd049c62..3c59192a68 100644
--- a/daemons/controld/controld_control.c
+++ b/daemons/controld/controld_control.c
@@ -1,836 +1,836 @@
 /*
  * Copyright 2004-2021 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU General Public License version 2
  * or later (GPLv2+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/pengine/rules.h>
 #include <crm/cluster/internal.h>
 #include <crm/cluster/election_internal.h>
 #include <crm/common/ipc_internal.h>
 
 #include <pacemaker-controld.h>
 
 qb_ipcs_service_t *ipcs = NULL;
 
 #if SUPPORT_COROSYNC
 extern gboolean crm_connect_corosync(crm_cluster_t * cluster);
 #endif
 
 void crm_shutdown(int nsig);
 gboolean crm_read_options(gpointer user_data);
 
 gboolean fsa_has_quorum = FALSE;
 crm_trigger_t *fsa_source = NULL;
 crm_trigger_t *config_read = NULL;
 bool no_quorum_suicide_escalation = FALSE;
 bool controld_shutdown_lock_enabled = false;
 
 /*	 A_HA_CONNECT	*/
 void
 do_ha_control(long long action,
               enum crmd_fsa_cause cause,
               enum crmd_fsa_state cur_state,
               enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     gboolean registered = FALSE;
     static crm_cluster_t *cluster = NULL;
 
     if (cluster == NULL) {
         cluster = calloc(1, sizeof(crm_cluster_t));
     }
 
     if (action & A_HA_DISCONNECT) {
         crm_cluster_disconnect(cluster);
         crm_info("Disconnected from the cluster");
 
         controld_set_fsa_input_flags(R_HA_DISCONNECTED);
     }
 
     if (action & A_HA_CONNECT) {
         crm_set_status_callback(&peer_update_callback);
         crm_set_autoreap(FALSE);
 
         if (is_corosync_cluster()) {
 #if SUPPORT_COROSYNC
             registered = crm_connect_corosync(cluster);
 #endif
         }
 
         if (registered == TRUE) {
             controld_election_init(cluster->uname);
             fsa_our_uname = cluster->uname;
             fsa_our_uuid = cluster->uuid;
             if(cluster->uuid == NULL) {
                 crm_err("Could not obtain local uuid");
                 registered = FALSE;
             }
         }
 
         if (registered == FALSE) {
             controld_set_fsa_input_flags(R_HA_DISCONNECTED);
             register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
             return;
         }
 
         populate_cib_nodes(node_update_none, __func__);
         controld_clear_fsa_input_flags(R_HA_DISCONNECTED);
         crm_info("Connected to the cluster");
     }
 
     if (action & ~(A_HA_CONNECT | A_HA_DISCONNECT)) {
         crm_err("Unexpected action %s in %s", fsa_action2string(action),
                 __func__);
     }
 }
 
 /*	 A_SHUTDOWN	*/
 void
 do_shutdown(long long action,
             enum crmd_fsa_cause cause,
             enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     /* just in case */
     controld_set_fsa_input_flags(R_SHUTDOWN);
     controld_disconnect_fencer(FALSE);
 }
 
 /*	 A_SHUTDOWN_REQ	*/
 void
 do_shutdown_req(long long action,
                 enum crmd_fsa_cause cause,
                 enum crmd_fsa_state cur_state,
                 enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     xmlNode *msg = NULL;
 
     controld_set_fsa_input_flags(R_SHUTDOWN);
     //controld_set_fsa_input_flags(R_STAYDOWN);
     crm_info("Sending shutdown request to all peers (DC is %s)",
              (fsa_our_dc? fsa_our_dc : "not set"));
     msg = create_request(CRM_OP_SHUTDOWN_REQ, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL);
 
     if (send_cluster_message(NULL, crm_msg_crmd, msg, TRUE) == FALSE) {
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
     }
     free_xml(msg);
 }
 
 extern char *max_generation_from;
 extern xmlNode *max_generation_xml;
 extern GHashTable *resource_history;
 extern GHashTable *voted;
 
 void
 crmd_fast_exit(crm_exit_t exit_code)
 {
     if (pcmk_is_set(fsa_input_register, R_STAYDOWN)) {
         crm_warn("Inhibiting respawn "CRM_XS" remapping exit code %d to %d",
                  exit_code, CRM_EX_FATAL);
         exit_code = CRM_EX_FATAL;
 
     } else if ((exit_code == CRM_EX_OK)
                && pcmk_is_set(fsa_input_register, R_IN_RECOVERY)) {
         crm_err("Could not recover from internal error");
         exit_code = CRM_EX_ERROR;
     }
     crm_exit(exit_code);
 }
 
 crm_exit_t
 crmd_exit(crm_exit_t exit_code)
 {
     GList *gIter = NULL;
     GMainLoop *mloop = crmd_mainloop;
 
     static bool in_progress = FALSE;
 
     if (in_progress && (exit_code == CRM_EX_OK)) {
         crm_debug("Exit is already in progress");
         return exit_code;
 
     } else if(in_progress) {
         crm_notice("Error during shutdown process, exiting now with status %d (%s)",
                    exit_code, crm_exit_str(exit_code));
         crm_write_blackbox(SIGTRAP, NULL);
         crmd_fast_exit(exit_code);
     }
 
     in_progress = TRUE;
     crm_trace("Preparing to exit with status %d (%s)",
               exit_code, crm_exit_str(exit_code));
 
     /* Suppress secondary errors resulting from us disconnecting everything */
     controld_set_fsa_input_flags(R_HA_DISCONNECTED);
 
 /* Close all IPC servers and clients to ensure any and all shared memory files are cleaned up */
 
     if(ipcs) {
         crm_trace("Closing IPC server");
         mainloop_del_ipc_server(ipcs);
         ipcs = NULL;
     }
 
     controld_close_attrd_ipc();
     pe_subsystem_free();
     controld_disconnect_fencer(TRUE);
 
     if ((exit_code == CRM_EX_OK) && (crmd_mainloop == NULL)) {
         crm_debug("No mainloop detected");
         exit_code = CRM_EX_ERROR;
     }
 
     /* On an error, just get out.
      *
      * Otherwise, make the effort to have mainloop exit gracefully so
      * that it (mostly) cleans up after itself and valgrind has less
      * to report on - allowing real errors stand out
      */
     if (exit_code != CRM_EX_OK) {
         crm_notice("Forcing immediate exit with status %d (%s)",
                    exit_code, crm_exit_str(exit_code));
         crm_write_blackbox(SIGTRAP, NULL);
         crmd_fast_exit(exit_code);
     }
 
 /* Clean up as much memory as possible for valgrind */
 
     for (gIter = fsa_message_queue; gIter != NULL; gIter = gIter->next) {
         fsa_data_t *fsa_data = gIter->data;
 
         crm_info("Dropping %s: [ state=%s cause=%s origin=%s ]",
                  fsa_input2string(fsa_data->fsa_input),
                  fsa_state2string(fsa_state),
                  fsa_cause2string(fsa_data->fsa_cause), fsa_data->origin);
         delete_fsa_input(fsa_data);
     }
 
     controld_clear_fsa_input_flags(R_MEMBERSHIP);
     g_list_free(fsa_message_queue); fsa_message_queue = NULL;
 
     metadata_cache_fini();
     controld_election_fini();
 
     /* Tear down the CIB manager connection, but don't free it yet -- it could
      * be used when we drain the mainloop later.
      */
 
     controld_disconnect_cib_manager();
 
     verify_stopped(fsa_state, LOG_WARNING);
     controld_clear_fsa_input_flags(R_LRM_CONNECTED);
     lrm_state_destroy_all();
 
     /* This basically will not work, since mainloop has a reference to it */
     mainloop_destroy_trigger(fsa_source); fsa_source = NULL;
 
     mainloop_destroy_trigger(config_read); config_read = NULL;
     mainloop_destroy_trigger(transition_trigger); transition_trigger = NULL;
 
     pcmk__client_cleanup();
     crm_peer_destroy();
 
     controld_free_fsa_timers();
     te_cleanup_stonith_history_sync(NULL, TRUE);
     controld_free_sched_timer();
 
     free(fsa_our_dc_version); fsa_our_dc_version = NULL;
     free(fsa_our_uname); fsa_our_uname = NULL;
     free(fsa_our_uuid); fsa_our_uuid = NULL;
     free(fsa_our_dc); fsa_our_dc = NULL;
 
     free(fsa_cluster_name); fsa_cluster_name = NULL;
 
     free(te_uuid); te_uuid = NULL;
     free(failed_stop_offset); failed_stop_offset = NULL;
     free(failed_start_offset); failed_start_offset = NULL;
 
     free(max_generation_from); max_generation_from = NULL;
     free_xml(max_generation_xml); max_generation_xml = NULL;
 
     mainloop_destroy_signal(SIGPIPE);
     mainloop_destroy_signal(SIGUSR1);
     mainloop_destroy_signal(SIGTERM);
     mainloop_destroy_signal(SIGTRAP);
     /* leave SIGCHLD engaged as we might still want to drain some service-actions */
 
     if (mloop) {
         GMainContext *ctx = g_main_loop_get_context(crmd_mainloop);
 
         /* Don't re-enter this block */
         crmd_mainloop = NULL;
 
         /* no signals on final draining anymore */
         mainloop_destroy_signal(SIGCHLD);
 
         crm_trace("Draining mainloop %d %d", g_main_loop_is_running(mloop), g_main_context_pending(ctx));
 
         {
             int lpc = 0;
 
             while((g_main_context_pending(ctx) && lpc < 10)) {
                 lpc++;
                 crm_trace("Iteration %d", lpc);
                 g_main_context_dispatch(ctx);
             }
         }
 
         crm_trace("Closing mainloop %d %d", g_main_loop_is_running(mloop), g_main_context_pending(ctx));
         g_main_loop_quit(mloop);
 
         /* Won't do anything yet, since we're inside it now */
         g_main_loop_unref(mloop);
     } else {
         mainloop_destroy_signal(SIGCHLD);
     }
 
     cib_delete(fsa_cib_conn);
     fsa_cib_conn = NULL;
 
     throttle_fini();
 
     /* Graceful */
     crm_trace("Done preparing for exit with status %d (%s)",
               exit_code, crm_exit_str(exit_code));
     return exit_code;
 }
 
 /*	 A_EXIT_0, A_EXIT_1	*/
 void
 do_exit(long long action,
         enum crmd_fsa_cause cause,
         enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     crm_exit_t exit_code = CRM_EX_OK;
     int log_level = LOG_INFO;
     const char *exit_type = "gracefully";
 
     if (action & A_EXIT_1) {
         log_level = LOG_ERR;
         exit_type = "forcefully";
         exit_code = CRM_EX_ERROR;
     }
 
     verify_stopped(cur_state, LOG_ERR);
     do_crm_log(log_level, "Performing %s - %s exiting the controller",
                fsa_action2string(action), exit_type);
 
     crm_info("[%s] stopped (%d)", crm_system_name, exit_code);
     crmd_exit(exit_code);
 }
 
 static void sigpipe_ignore(int nsig) { return; }
 
 /*	 A_STARTUP	*/
 void
 do_startup(long long action,
            enum crmd_fsa_cause cause,
            enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     crm_debug("Registering Signal Handlers");
     mainloop_add_signal(SIGTERM, crm_shutdown);
     mainloop_add_signal(SIGPIPE, sigpipe_ignore);
 
     fsa_source = mainloop_add_trigger(G_PRIORITY_HIGH, crm_fsa_trigger, NULL);
     config_read = mainloop_add_trigger(G_PRIORITY_HIGH, crm_read_options, NULL);
     transition_trigger = mainloop_add_trigger(G_PRIORITY_LOW, te_graph_trigger, NULL);
 
     crm_debug("Creating CIB manager and executor objects");
     fsa_cib_conn = cib_new();
 
     lrm_state_init_local();
     if (controld_init_fsa_timers() == FALSE) {
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
     }
 }
 
 // \return libqb error code (0 on success, -errno on error)
 static int32_t
 accept_controller_client(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
 {
     crm_trace("Accepting new IPC client connection");
     if (pcmk__new_client(c, uid, gid) == NULL) {
         return -EIO;
     }
     return 0;
 }
 
 // \return libqb error code (0 on success, -errno on error)
 static int32_t
 dispatch_controller_ipc(qb_ipcs_connection_t * c, void *data, size_t size)
 {
     uint32_t id = 0;
     uint32_t flags = 0;
     pcmk__client_t *client = pcmk__find_client(c);
 
     xmlNode *msg = pcmk__client_data2xml(client, data, &id, &flags);
 
     if (msg == NULL) {
         pcmk__ipc_send_ack(client, id, flags, "ack", CRM_EX_PROTOCOL);
         return 0;
     }
     pcmk__ipc_send_ack(client, id, flags, "ack", CRM_EX_INDETERMINATE);
 
     CRM_ASSERT(client->user != NULL);
     pcmk__update_acl_user(msg, F_CRM_USER, client->user);
 
     crm_xml_add(msg, F_CRM_SYS_FROM, client->id);
     if (controld_authorize_ipc_message(msg, client, NULL)) {
         crm_trace("Processing IPC message from client %s",
                   pcmk__client_name(client));
         route_message(C_IPC_MESSAGE, msg);
     }
 
     trigger_fsa();
     free_xml(msg);
     return 0;
 }
 
 static int32_t
 crmd_ipc_closed(qb_ipcs_connection_t * c)
 {
     pcmk__client_t *client = pcmk__find_client(c);
 
     if (client) {
         crm_trace("Disconnecting %sregistered client %s (%p/%p)",
                   (client->userdata? "" : "un"), pcmk__client_name(client),
                   c, client);
         free(client->userdata);
         pcmk__free_client(client);
         trigger_fsa();
     }
     return 0;
 }
 
 static void
 crmd_ipc_destroy(qb_ipcs_connection_t * c)
 {
     crm_trace("Connection %p", c);
     crmd_ipc_closed(c);
 }
 
 /*	 A_STOP	*/
 void
 do_stop(long long action,
         enum crmd_fsa_cause cause,
         enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     crm_trace("Closing IPC server");
     mainloop_del_ipc_server(ipcs); ipcs = NULL;
     register_fsa_input(C_FSA_INTERNAL, I_TERMINATE, NULL);
 }
 
 /*	 A_STARTED	*/
 void
 do_started(long long action,
            enum crmd_fsa_cause cause,
            enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     static struct qb_ipcs_service_handlers crmd_callbacks = {
         .connection_accept = accept_controller_client,
         .connection_created = NULL,
         .msg_process = dispatch_controller_ipc,
         .connection_closed = crmd_ipc_closed,
         .connection_destroyed = crmd_ipc_destroy
     };
 
     if (cur_state != S_STARTING) {
         crm_err("Start cancelled... %s", fsa_state2string(cur_state));
         return;
 
     } else if (!pcmk_is_set(fsa_input_register, R_MEMBERSHIP)) {
         crm_info("Delaying start, no membership data (%.16llx)", R_MEMBERSHIP);
 
         crmd_fsa_stall(TRUE);
         return;
 
     } else if (!pcmk_is_set(fsa_input_register, R_LRM_CONNECTED)) {
         crm_info("Delaying start, not connected to executor (%.16llx)", R_LRM_CONNECTED);
 
         crmd_fsa_stall(TRUE);
         return;
 
     } else if (!pcmk_is_set(fsa_input_register, R_CIB_CONNECTED)) {
         crm_info("Delaying start, CIB not connected (%.16llx)", R_CIB_CONNECTED);
 
         crmd_fsa_stall(TRUE);
         return;
 
     } else if (!pcmk_is_set(fsa_input_register, R_READ_CONFIG)) {
         crm_info("Delaying start, Config not read (%.16llx)", R_READ_CONFIG);
 
         crmd_fsa_stall(TRUE);
         return;
 
     } else if (!pcmk_is_set(fsa_input_register, R_PEER_DATA)) {
 
         crm_info("Delaying start, No peer data (%.16llx)", R_PEER_DATA);
         crmd_fsa_stall(TRUE);
         return;
     }
 
     crm_debug("Init server comms");
     ipcs = pcmk__serve_controld_ipc(&crmd_callbacks);
     if (ipcs == NULL) {
         crm_err("Failed to create IPC server: shutting down and inhibiting respawn");
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
     } else {
         crm_notice("Pacemaker controller successfully started and accepting connections");
     }
     controld_trigger_fencer_connect();
 
     controld_clear_fsa_input_flags(R_STARTING);
     register_fsa_input(msg_data->fsa_cause, I_PENDING, NULL);
 }
 
 /*	 A_RECOVER	*/
 void
 do_recover(long long action,
            enum crmd_fsa_cause cause,
            enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     controld_set_fsa_input_flags(R_IN_RECOVERY);
     crm_warn("Fast-tracking shutdown in response to errors");
 
     register_fsa_input(C_FSA_INTERNAL, I_TERMINATE, NULL);
 }
 
 static pcmk__cluster_option_t crmd_opts[] = {
     /* name, old name, type, allowed values,
      * default value, validator,
      * short description,
      * long description
      */
     {
         "dc-version", NULL, "string", NULL, "none", NULL,
         "Pacemaker version on cluster node elected Designated Controller (DC)",
         "Includes a hash which identifies the exact changeset the code was "
             "built from. Used for diagnostic purposes."
     },
     {
         "cluster-infrastructure", NULL, "string", NULL, "corosync", NULL,
         "The messaging stack on which Pacemaker is currently running",
         "Used for informational and diagnostic purposes."
     },
     {
         "cluster-name", NULL, "string", NULL, NULL, NULL,
         "An arbitrary name for the cluster",
         "This optional value is mostly for users' convenience as desired "
             "in administration, but may also be used in Pacemaker "
             "configuration rules via the #cluster-name node attribute, and "
             "by higher-level tools and resource agents."
     },
     {
         XML_CONFIG_ATTR_DC_DEADTIME, NULL, "time",
         NULL, "20s", pcmk__valid_interval_spec,
         "How long to wait for a response from other nodes during start-up",
         "The optimal value will depend on the speed and load of your network "
             "and the type of switches used."
     },
     {
         XML_CONFIG_ATTR_RECHECK, NULL, "time",
         "Zero disables polling, while positive values are an interval in seconds"
             "(unless other units are specified, for example \"5min\")",
         "15min", pcmk__valid_interval_spec,
         "Polling interval to recheck cluster state and evaluate rules "
             "with date specifications",
         "Pacemaker is primarily event-driven, and looks ahead to know when to "
             "recheck cluster state for failure timeouts and most time-based "
             "rules. However, it will also recheck the cluster after this "
             "amount of inactivity, to evaluate rules with date specifications "
             "and serve as a fail-safe for certain types of scheduler bugs."
     },
     {
         "load-threshold", NULL, "percentage", NULL,
         "80%", pcmk__valid_utilization,
         "Maximum amount of system load that should be used by cluster nodes",
         "The cluster will slow down its recovery process when the amount of "
             "system resources used (currently CPU) approaches this limit",
     },
     {
         "node-action-limit", NULL, "integer", NULL,
         "0", pcmk__valid_number,
         "Maximum number of jobs that can be scheduled per node "
             "(defaults to 2x cores)"
     },
     { XML_CONFIG_ATTR_FENCE_REACTION, NULL, "string", NULL, "stop", NULL,
         "How a cluster node should react if notified of its own fencing",
         "A cluster node may receive notification of its own fencing if fencing "
         "is misconfigured, or if fabric fencing is in use that doesn't cut "
         "cluster communication. Allowed values are \"stop\" to attempt to "
         "immediately stop Pacemaker and stay stopped, or \"panic\" to attempt "
         "to immediately reboot the local node, falling back to stop on failure."
     },
     {
         XML_CONFIG_ATTR_ELECTION_FAIL, NULL, "time", NULL,
         "2min", pcmk__valid_interval_spec,
         "*** Advanced Use Only ***",
         "Declare an election failed if it is not decided within this much "
             "time. If you need to adjust this value, it probably indicates "
             "the presence of a bug."
     },
     {
         XML_CONFIG_ATTR_FORCE_QUIT, NULL, "time", NULL,
         "20min", pcmk__valid_interval_spec,
         "*** Advanced Use Only ***",
         "Exit immediately if shutdown does not complete within this much "
             "time. If you need to adjust this value, it probably indicates "
             "the presence of a bug."
     },
     {
         "join-integration-timeout", "crmd-integration-timeout", "time", NULL,
         "3min", pcmk__valid_interval_spec,
         "*** Advanced Use Only ***",
         "If you need to adjust this value, it probably indicates "
             "the presence of a bug."
     },
     {
         "join-finalization-timeout", "crmd-finalization-timeout", "time", NULL,
         "30min", pcmk__valid_interval_spec,
         "*** Advanced Use Only ***",
         "If you need to adjust this value, it probably indicates "
             "the presence of a bug."
     },
     {
         "transition-delay", "crmd-transition-delay", "time", NULL,
         "0s", pcmk__valid_interval_spec,
         "*** Advanced Use Only *** Enabling this option will slow down "
             "cluster recovery under all conditions",
         "Delay cluster recovery for this much time to allow for additional "
             "events to occur. Useful if your configuration is sensitive to "
             "the order in which ping updates arrive."
     },
     {
         "stonith-watchdog-timeout", NULL, "time", NULL,
         "0", controld_verify_stonith_watchdog_timeout,
         "How long to wait before we can assume nodes are safely down "
             "when watchdog-based self-fencing via SBD is in use",
         "If nonzero, along with `have-watchdog=true` automatically set by the "
             "cluster, when fencing is required, watchdog-based self-fencing "
             "will be performed via SBD without requiring a fencing resource "
             "explicitly configured. "
             "If `stonith-watchdog-timeout` is set to a positive value, unseen "
             "nodes are assumed to self-fence within this much time. +WARNING:+ "
             "It must be ensured that this value is larger than the "
             "`SBD_WATCHDOG_TIMEOUT` environment variable on all nodes. "
             "Pacemaker verifies the settings individually on all nodes and "
             "prevents startup or shuts down if configured wrongly on the fly. "
             "It's strongly recommended that `SBD_WATCHDOG_TIMEOUT` is set to "
             "the same value on all nodes. "
             "If `stonith-watchdog-timeout` is set to a negative value, and "
             "`SBD_WATCHDOG_TIMEOUT` is set, twice that value will be used. "
             "+WARNING:+ In this case, it's essential (currently not verified by "
             "Pacemaker) that `SBD_WATCHDOG_TIMEOUT` is set to the same value on "
             "all nodes."
     },
     {
         "stonith-max-attempts", NULL, "integer", NULL,
         "10", pcmk__valid_positive_number,
         "How many times fencing can fail before it will no longer be "
             "immediately re-attempted on a target"
     },
 
     // Already documented in libpe_status (other values must be kept identical)
     {
-        "no-quorum-policy", NULL, "enum", "stop, freeze, ignore, demote, suicide",
+        "no-quorum-policy", NULL, "select", "stop, freeze, ignore, demote, suicide",
         "stop", pcmk__valid_quorum, NULL, NULL
     },
     {
         XML_CONFIG_ATTR_SHUTDOWN_LOCK, NULL, "boolean", NULL,
         "false", pcmk__valid_boolean, NULL, NULL
     },
 };
 
 void
 crmd_metadata(void)
 {
-    pcmk__print_option_metadata("pacemaker-controld", "1.0",
+    pcmk__print_option_metadata("pacemaker-controld",
                                 "Pacemaker controller options",
                                 "Cluster options used by Pacemaker's "
                                     "controller (formerly called crmd)",
                                 crmd_opts, PCMK__NELEM(crmd_opts));
 }
 
 static void
 verify_crmd_options(GHashTable * options)
 {
     pcmk__validate_cluster_options(options, crmd_opts, PCMK__NELEM(crmd_opts));
 }
 
 static const char *
 crmd_pref(GHashTable * options, const char *name)
 {
     return pcmk__cluster_option(options, crmd_opts, PCMK__NELEM(crmd_opts),
                                 name);
 }
 
 static void
 config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
 {
     const char *value = NULL;
     GHashTable *config_hash = NULL;
     crm_time_t *now = crm_time_new(NULL);
     xmlNode *crmconfig = NULL;
     xmlNode *alerts = NULL;
 
     if (rc != pcmk_ok) {
         fsa_data_t *msg_data = NULL;
 
         crm_err("Local CIB query resulted in an error: %s", pcmk_strerror(rc));
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
 
         if (rc == -EACCES || rc == -pcmk_err_schema_validation) {
             crm_err("The cluster is mis-configured - shutting down and staying down");
             controld_set_fsa_input_flags(R_STAYDOWN);
         }
         goto bail;
     }
 
     crmconfig = output;
     if ((crmconfig) &&
         (crm_element_name(crmconfig)) &&
         (strcmp(crm_element_name(crmconfig), XML_CIB_TAG_CRMCONFIG) != 0)) {
         crmconfig = first_named_child(crmconfig, XML_CIB_TAG_CRMCONFIG);
     }
     if (!crmconfig) {
         fsa_data_t *msg_data = NULL;
 
         crm_err("Local CIB query for " XML_CIB_TAG_CRMCONFIG " section failed");
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
         goto bail;
     }
 
     crm_debug("Call %d : Parsing CIB options", call_id);
     config_hash = pcmk__strkey_table(free, free);
     pe_unpack_nvpairs(crmconfig, crmconfig, XML_CIB_TAG_PROPSET, NULL,
                       config_hash, CIB_OPTIONS_FIRST, FALSE, now, NULL);
 
     verify_crmd_options(config_hash);
 
     value = crmd_pref(config_hash, XML_CONFIG_ATTR_DC_DEADTIME);
     election_trigger->period_ms = crm_parse_interval_spec(value);
 
     value = crmd_pref(config_hash, "node-action-limit"); /* Also checks migration-limit */
     throttle_update_job_max(value);
 
     value = crmd_pref(config_hash, "load-threshold");
     if(value) {
         throttle_set_load_target(strtof(value, NULL) / 100.0);
     }
 
     value = crmd_pref(config_hash, "no-quorum-policy");
     if (pcmk__str_eq(value, "suicide", pcmk__str_casei) && pcmk__locate_sbd()) {
         no_quorum_suicide_escalation = TRUE;
     }
 
     set_fence_reaction(crmd_pref(config_hash, XML_CONFIG_ATTR_FENCE_REACTION));
 
     value = crmd_pref(config_hash,"stonith-max-attempts");
     update_stonith_max_attempts(value);
 
     value = crmd_pref(config_hash, XML_CONFIG_ATTR_FORCE_QUIT);
     shutdown_escalation_timer->period_ms = crm_parse_interval_spec(value);
     crm_debug("Shutdown escalation occurs if DC has not responded to request in %ums",
               shutdown_escalation_timer->period_ms);
 
     value = crmd_pref(config_hash, XML_CONFIG_ATTR_ELECTION_FAIL);
     controld_set_election_period(value);
 
     value = crmd_pref(config_hash, XML_CONFIG_ATTR_RECHECK);
     recheck_interval_ms = crm_parse_interval_spec(value);
     crm_debug("Re-run scheduler after %dms of inactivity", recheck_interval_ms);
 
     value = crmd_pref(config_hash, "transition-delay");
     transition_timer->period_ms = crm_parse_interval_spec(value);
 
     value = crmd_pref(config_hash, "join-integration-timeout");
     integration_timer->period_ms = crm_parse_interval_spec(value);
 
     value = crmd_pref(config_hash, "join-finalization-timeout");
     finalization_timer->period_ms = crm_parse_interval_spec(value);
 
     value = crmd_pref(config_hash, XML_CONFIG_ATTR_SHUTDOWN_LOCK);
     controld_shutdown_lock_enabled = crm_is_true(value);
 
     free(fsa_cluster_name);
     fsa_cluster_name = NULL;
 
     value = g_hash_table_lookup(config_hash, "cluster-name");
     if (value) {
         fsa_cluster_name = strdup(value);
     }
 
     alerts = first_named_child(output, XML_CIB_TAG_ALERTS);
     crmd_unpack_alerts(alerts);
 
     controld_set_fsa_input_flags(R_READ_CONFIG);
     crm_trace("Triggering FSA: %s", __func__);
     mainloop_set_trigger(fsa_source);
 
     g_hash_table_destroy(config_hash);
   bail:
     crm_time_free(now);
 }
 
 gboolean
 crm_read_options(gpointer user_data)
 {
     int call_id =
         fsa_cib_conn->cmds->query(fsa_cib_conn,
             "//" XML_CIB_TAG_CRMCONFIG " | //" XML_CIB_TAG_ALERTS,
             NULL, cib_xpath | cib_scope_local);
 
     fsa_register_cib_callback(call_id, FALSE, NULL, config_query_callback);
     crm_trace("Querying the CIB... call %d", call_id);
     return TRUE;
 }
 
 /*	 A_READCONFIG	*/
 void
 do_read_config(long long action,
                enum crmd_fsa_cause cause,
                enum crmd_fsa_state cur_state,
                enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 {
     throttle_init();
     mainloop_set_trigger(config_read);
 }
 
 void
 crm_shutdown(int nsig)
 {
     if ((crmd_mainloop == NULL) || !g_main_loop_is_running(crmd_mainloop)) {
         crmd_exit(CRM_EX_OK);
         return;
     }
 
     if (pcmk_is_set(fsa_input_register, R_SHUTDOWN)) {
         crm_err("Escalating shutdown");
         register_fsa_input_before(C_SHUTDOWN, I_ERROR, NULL);
         return;
     }
 
     controld_set_fsa_input_flags(R_SHUTDOWN);
     register_fsa_input(C_SHUTDOWN, I_SHUTDOWN, NULL);
 
     if (shutdown_escalation_timer->period_ms == 0) {
         const char *value = crmd_pref(NULL, XML_CONFIG_ATTR_FORCE_QUIT);
 
         shutdown_escalation_timer->period_ms = crm_parse_interval_spec(value);
     }
 
     crm_notice("Initiating controller shutdown sequence " CRM_XS
                " limit=%ums", shutdown_escalation_timer->period_ms);
     controld_start_timer(shutdown_escalation_timer);
 }
diff --git a/include/crm/common/options_internal.h b/include/crm/common/options_internal.h
index a18680d2aa..23906bebdf 100644
--- a/include/crm/common/options_internal.h
+++ b/include/crm/common/options_internal.h
@@ -1,135 +1,135 @@
 /*
  * Copyright 2006-2021 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;
 
 void pcmk__set_cli_options(const char *short_options, const char *usage,
                            pcmk__cli_option_t *long_options,
                            const char *app_desc);
 int pcmk__next_cli_option(int argc, char **argv, int *index,
                           const char **longname);
 _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,
                                  pcmk__cluster_option_t *option_list, int len,
                                  const char *name);
 
-void pcmk__print_option_metadata(const char *name, const char *version,
-                                 const char *desc_short, const char *desc_long,
+void pcmk__print_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_utilization(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_CLUSTER_TYPE "cluster_type"
 #define PCMK__ENV_QUORUM_TYPE "quorum_type"
 #define PCMK__ENV_DEBUG "debug"
 #define PCMK__ENV_LOGFILE "logfile"
 #define PCMK__ENV_LOGFACILITY "logfacility"
 #define PCMK__ENV_SHUTDOWN_DELAY "shutdown_delay"
 #define PCMK__ENV_NODE_START_STATE "node_start_state"
 #define PCMK__ENV_MCP "mcp"
 #define PCMK__ENV_LOGPRIORITY "logpriority"
 #define PCMK__ENV_STDERR "stderr"
 #define PCMK__ENV_BLACKBOX "blackbox"
 
 #endif // PCMK__OPTIONS_INTERNAL__H
diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c
index 3dd68e3b4d..800c068976 100644
--- a/lib/cib/cib_utils.c
+++ b/lib/cib/cib_utils.c
@@ -1,826 +1,826 @@
 /*
  * Original copyright 2004 International Business Machines
  * Later changes copyright 2008-2021 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>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 #include <sys/utsname.h>
 
 #include <glib.h>
 
 #include <crm/crm.h>
 #include <crm/cib/internal.h>
 #include <crm/msg_xml.h>
 #include <crm/common/iso8601_internal.h>
 #include <crm/common/xml.h>
 #include <crm/common/xml_internal.h>
 #include <crm/pengine/rules.h>
 
 struct config_root_s {
     const char *name;
     const char *parent;
     const char *path;
 };
 
  /*
   * "//crm_config" will also work in place of "/cib/configuration/crm_config"
   * The / prefix means find starting from the root, whereas the // prefix means
   * find anywhere and risks multiple matches
   */
 /* *INDENT-OFF* */
 static struct config_root_s known_paths[] = {
     { NULL,                         NULL,                 "//cib" },
     { XML_TAG_CIB,                  NULL,                 "//cib" },
     { XML_CIB_TAG_STATUS,           "/cib",               "//cib/status" },
     { XML_CIB_TAG_CONFIGURATION,    "/cib",               "//cib/configuration" },
     { XML_CIB_TAG_CRMCONFIG,        "/cib/configuration", "//cib/configuration/crm_config" },
     { XML_CIB_TAG_NODES,            "/cib/configuration", "//cib/configuration/nodes" },
     { XML_CIB_TAG_RESOURCES,        "/cib/configuration", "//cib/configuration/resources" },
     { XML_CIB_TAG_CONSTRAINTS,      "/cib/configuration", "//cib/configuration/constraints" },
     { XML_CIB_TAG_OPCONFIG,         "/cib/configuration", "//cib/configuration/op_defaults" },
     { XML_CIB_TAG_RSCCONFIG,        "/cib/configuration", "//cib/configuration/rsc_defaults" },
     { XML_CIB_TAG_ACLS,             "/cib/configuration", "//cib/configuration/acls" },
     { XML_TAG_FENCING_TOPOLOGY,     "/cib/configuration", "//cib/configuration/fencing-topology" },
     { XML_CIB_TAG_TAGS,             "/cib/configuration", "//cib/configuration/tags" },
     { XML_CIB_TAG_ALERTS,           "/cib/configuration", "//cib/configuration/alerts" },
     { XML_CIB_TAG_SECTION_ALL,      NULL,                 "//cib" },
 };
 /* *INDENT-ON* */
 
 xmlNode *
 cib_get_generation(cib_t * cib)
 {
     xmlNode *the_cib = NULL;
     xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);
 
     cib->cmds->query(cib, NULL, &the_cib, cib_scope_local | cib_sync_call);
     if (the_cib != NULL) {
         copy_in_properties(generation, the_cib);
         free_xml(the_cib);
     }
 
     return generation;
 }
 
 gboolean
 cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
 {
     *epoch = -1;
     *updates = -1;
     *admin_epoch = -1;
 
     if (cib == NULL) {
         return FALSE;
 
     } else {
         crm_element_value_int(cib, XML_ATTR_GENERATION, epoch);
         crm_element_value_int(cib, XML_ATTR_NUMUPDATES, updates);
         crm_element_value_int(cib, XML_ATTR_GENERATION_ADMIN, admin_epoch);
     }
     return TRUE;
 }
 
 gboolean
 cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
                          int *_admin_epoch, int *_epoch, int *_updates)
 {
     int add[] = { 0, 0, 0 };
     int del[] = { 0, 0, 0 };
 
     xml_patch_versions(diff, add, del);
 
     *admin_epoch = add[0];
     *epoch = add[1];
     *updates = add[2];
 
     *_admin_epoch = del[0];
     *_epoch = del[1];
     *_updates = del[2];
 
     return TRUE;
 }
 
 /*
  * The caller should never free the return value
  */
 
 const char *
 get_object_path(const char *object_type)
 {
     int lpc = 0;
     int max = PCMK__NELEM(known_paths);
 
     for (; lpc < max; lpc++) {
         if ((object_type == NULL && known_paths[lpc].name == NULL)
             || pcmk__str_eq(object_type, known_paths[lpc].name, pcmk__str_casei)) {
             return known_paths[lpc].path;
         }
     }
     return NULL;
 }
 
 const char *
 get_object_parent(const char *object_type)
 {
     int lpc = 0;
     int max = PCMK__NELEM(known_paths);
 
     for (; lpc < max; lpc++) {
         if (pcmk__str_eq(object_type, known_paths[lpc].name, pcmk__str_casei)) {
             return known_paths[lpc].parent;
         }
     }
     return NULL;
 }
 
 xmlNode *
 get_object_root(const char *object_type, xmlNode * the_root)
 {
     const char *xpath = get_object_path(object_type);
 
     if (xpath == NULL) {
         return the_root;        /* or return NULL? */
     }
 
     return get_xpath_object(xpath, the_root, LOG_TRACE);
 }
 
 /*!
  * \brief Create XML for a new (empty) CIB
  *
  * \param[in] cib_epoch   What to use as "epoch" CIB property
  *
  * \return Newly created XML for empty CIB
  * \note It is the caller's responsibility to free the result with free_xml().
  */
 xmlNode *
 createEmptyCib(int cib_epoch)
 {
     xmlNode *cib_root = NULL, *config = NULL;
 
     cib_root = create_xml_node(NULL, XML_TAG_CIB);
     crm_xml_add(cib_root, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
     crm_xml_add(cib_root, XML_ATTR_VALIDATION, xml_latest_schema());
 
     crm_xml_add_int(cib_root, XML_ATTR_GENERATION, cib_epoch);
     crm_xml_add_int(cib_root, XML_ATTR_NUMUPDATES, 0);
     crm_xml_add_int(cib_root, XML_ATTR_GENERATION_ADMIN, 0);
 
     config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION);
     create_xml_node(cib_root, XML_CIB_TAG_STATUS);
 
     create_xml_node(config, XML_CIB_TAG_CRMCONFIG);
     create_xml_node(config, XML_CIB_TAG_NODES);
     create_xml_node(config, XML_CIB_TAG_RESOURCES);
     create_xml_node(config, XML_CIB_TAG_CONSTRAINTS);
 
 #if PCMK__RESOURCE_STICKINESS_DEFAULT != 0
     {
         xmlNode *rsc_defaults = create_xml_node(config, XML_CIB_TAG_RSCCONFIG);
         xmlNode *meta = create_xml_node(rsc_defaults, XML_TAG_META_SETS);
         xmlNode *nvpair = create_xml_node(meta, XML_CIB_TAG_NVPAIR);
 
         crm_xml_add(meta, XML_ATTR_ID, "build-resource-defaults");
         crm_xml_add(nvpair, XML_ATTR_ID, "build-" XML_RSC_ATTR_STICKINESS);
         crm_xml_add(nvpair, XML_NVPAIR_ATTR_NAME, XML_RSC_ATTR_STICKINESS);
         crm_xml_add_int(nvpair, XML_NVPAIR_ATTR_VALUE,
                         PCMK__RESOURCE_STICKINESS_DEFAULT);
     }
 #endif
     return cib_root;
 }
 
 static bool
 cib_acl_enabled(xmlNode *xml, const char *user)
 {
     bool rc = FALSE;
 
     if(pcmk_acl_required(user)) {
         const char *value = NULL;
         GHashTable *options = pcmk__strkey_table(free, free);
 
         cib_read_config(options, xml);
         value = cib_pref(options, "enable-acl");
         rc = crm_is_true(value);
         g_hash_table_destroy(options);
     }
 
     crm_trace("CIB ACL is %s", rc ? "enabled" : "disabled");
     return rc;
 }
 
 int
 cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_query,
                const char *section, xmlNode * req, xmlNode * input,
                gboolean manage_counters, gboolean * config_changed,
                xmlNode * current_cib, xmlNode ** result_cib, xmlNode ** diff, xmlNode ** output)
 {
     int rc = pcmk_ok;
     gboolean check_schema = TRUE;
     xmlNode *top = NULL;
     xmlNode *scratch = NULL;
     xmlNode *local_diff = NULL;
 
     const char *new_version = NULL;
     static struct qb_log_callsite *diff_cs = NULL;
     const char *user = crm_element_value(req, F_CIB_USER);
     bool with_digest = FALSE;
 
     crm_trace("Begin %s%s%s op",
               (pcmk_is_set(call_options, cib_dryrun)? "dry run of " : ""),
               (is_query? "read-only " : ""), op);
 
     CRM_CHECK(output != NULL, return -ENOMSG);
     CRM_CHECK(result_cib != NULL, return -ENOMSG);
     CRM_CHECK(config_changed != NULL, return -ENOMSG);
 
     if(output) {
         *output = NULL;
     }
 
     *result_cib = NULL;
     *config_changed = FALSE;
 
     if (fn == NULL) {
         return -EINVAL;
     }
 
     if (is_query) {
         xmlNode *cib_ro = current_cib;
         xmlNode *cib_filtered = NULL;
 
         if(cib_acl_enabled(cib_ro, user)) {
             if(xml_acl_filtered_copy(user, current_cib, current_cib, &cib_filtered)) {
                 if (cib_filtered == NULL) {
                     crm_debug("Pre-filtered the entire cib");
                     return -EACCES;
                 }
                 cib_ro = cib_filtered;
                 crm_log_xml_trace(cib_ro, "filtered");
             }
         }
 
         rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output);
 
         if(output == NULL || *output == NULL) {
             /* nothing */
 
         } else if(cib_filtered == *output) {
             cib_filtered = NULL; /* Let them have this copy */
 
         } else if(*output == current_cib) {
             /* They already know not to free it */
 
         } else if(cib_filtered && (*output)->doc == cib_filtered->doc) {
             /* We're about to free the document of which *output is a part */
             *output = copy_xml(*output);
 
         } else if((*output)->doc == current_cib->doc) {
             /* Give them a copy they can free */
             *output = copy_xml(*output);
         }
 
         free_xml(cib_filtered);
         return rc;
     }
 
 
     if (pcmk_is_set(call_options, cib_zero_copy)) {
         /* Conditional on v2 patch style */
 
         scratch = current_cib;
 
         /* Create a shallow copy of current_cib for the version details */
         current_cib = create_xml_node(NULL, (const char *)scratch->name);
         copy_in_properties(current_cib, scratch);
         top = current_cib;
 
         xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
         rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output);
 
     } else {
         scratch = copy_xml(current_cib);
         xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
         rc = (*fn) (op, call_options, section, req, input, current_cib, &scratch, output);
 
         if(scratch && xml_tracking_changes(scratch) == FALSE) {
             crm_trace("Inferring changes after %s op", op);
             xml_track_changes(scratch, user, current_cib, cib_acl_enabled(current_cib, user));
             xml_calculate_changes(current_cib, scratch);
         }
         CRM_CHECK(current_cib != scratch, return -EINVAL);
     }
 
     xml_acl_disable(scratch); /* Allow the system to make any additional changes */
 
     if (rc == pcmk_ok && scratch == NULL) {
         rc = -EINVAL;
         goto done;
 
     } else if(rc == pcmk_ok && xml_acl_denied(scratch)) {
         crm_trace("ACL rejected part or all of the proposed changes");
         rc = -EACCES;
         goto done;
 
     } else if (rc != pcmk_ok) {
         goto done;
     }
 
     if (scratch) {
         new_version = crm_element_value(scratch, XML_ATTR_CRM_VERSION);
 
         if (new_version && compare_version(new_version, CRM_FEATURE_SET) > 0) {
             crm_err("Discarding update with feature set '%s' greater than our own '%s'",
                     new_version, CRM_FEATURE_SET);
             rc = -EPROTONOSUPPORT;
             goto done;
         }
     }
 
     if (current_cib) {
         int old = 0;
         int new = 0;
 
         crm_element_value_int(scratch, XML_ATTR_GENERATION_ADMIN, &new);
         crm_element_value_int(current_cib, XML_ATTR_GENERATION_ADMIN, &old);
 
         if (old > new) {
             crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
                     XML_ATTR_GENERATION_ADMIN, old, new, call_options);
             crm_log_xml_warn(req, "Bad Op");
             crm_log_xml_warn(input, "Bad Data");
             rc = -pcmk_err_old_data;
 
         } else if (old == new) {
             crm_element_value_int(scratch, XML_ATTR_GENERATION, &new);
             crm_element_value_int(current_cib, XML_ATTR_GENERATION, &old);
             if (old > new) {
                 crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
                         XML_ATTR_GENERATION, old, new, call_options);
                 crm_log_xml_warn(req, "Bad Op");
                 crm_log_xml_warn(input, "Bad Data");
                 rc = -pcmk_err_old_data;
             }
         }
     }
 
     crm_trace("Massaging CIB contents");
     pcmk__strip_xml_text(scratch);
     fix_plus_plus_recursive(scratch);
 
     if (pcmk_is_set(call_options, cib_zero_copy)) {
         /* At this point, current_cib is just the 'cib' tag and its properties,
          *
          * The v1 format would barf on this, but we know the v2 patch
          * format only needs it for the top-level version fields
          */
         local_diff = xml_create_patchset(2, current_cib, scratch, (bool*)config_changed, manage_counters);
 
     } else {
         static time_t expires = 0;
         time_t tm_now = time(NULL);
 
         if (expires < tm_now) {
             expires = tm_now + 60;  /* Validate clients are correctly applying v2-style diffs at most once a minute */
             with_digest = TRUE;
         }
 
         local_diff = xml_create_patchset(0, current_cib, scratch, (bool*)config_changed, manage_counters);
     }
 
     xml_log_changes(LOG_TRACE, __func__, scratch);
     xml_accept_changes(scratch);
 
     if (diff_cs == NULL) {
         diff_cs = qb_log_callsite_get(__PRETTY_FUNCTION__, __FILE__, "diff-validation", LOG_DEBUG, __LINE__, crm_trace_nonlog);
     }
 
     if(local_diff) {
         patchset_process_digest(local_diff, current_cib, scratch, with_digest);
 
         xml_log_patchset(LOG_INFO, __func__, local_diff);
         crm_log_xml_trace(local_diff, "raw patch");
     }
 
     if (!pcmk_is_set(call_options, cib_zero_copy) // Original to compare against doesn't exist
         && local_diff
         && crm_is_callsite_active(diff_cs, LOG_TRACE, 0)) {
 
         /* Validate the calculated patch set */
         int test_rc, format = 1;
         xmlNode * c = copy_xml(current_cib);
 
         crm_element_value_int(local_diff, "format", &format);
         test_rc = xml_apply_patchset(c, local_diff, manage_counters);
 
         if(test_rc != pcmk_ok) {
             save_xml_to_file(c,           "PatchApply:calculated", NULL);
             save_xml_to_file(current_cib, "PatchApply:input", NULL);
             save_xml_to_file(scratch,     "PatchApply:actual", NULL);
             save_xml_to_file(local_diff,  "PatchApply:diff", NULL);
             crm_err("v%d patchset error, patch failed to apply: %s (%d)", format, pcmk_strerror(test_rc), test_rc);
         }
         free_xml(c);
     }
 
     if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_casei)) {
         /* Throttle the amount of costly validation we perform due to status updates
          * a) we don't really care whats in the status section
          * b) we don't validate any of its contents at the moment anyway
          */
         check_schema = FALSE;
     }
 
     /* === scratch must not be modified after this point ===
      * Exceptions, anything in:
 
      static filter_t filter[] = {
      { 0, XML_ATTR_ORIGIN },
      { 0, XML_CIB_ATTR_WRITTEN },
      { 0, XML_ATTR_UPDATE_ORIG },
      { 0, XML_ATTR_UPDATE_CLIENT },
      { 0, XML_ATTR_UPDATE_USER },
      };
      */
 
     if (*config_changed && !pcmk_is_set(call_options, cib_no_mtime)) {
         const char *schema = crm_element_value(scratch, XML_ATTR_VALIDATION);
 
         pcmk__xe_add_last_written(scratch);
         if (schema) {
             static int minimum_schema = 0;
             int current_schema = get_schema_version(schema);
 
             if (minimum_schema == 0) {
                 minimum_schema = get_schema_version("pacemaker-1.2");
             }
 
             /* Does the CIB support the "update-*" attributes... */
             if (current_schema >= minimum_schema) {
                 const char *origin = crm_element_value(req, F_ORIG);
 
                 CRM_LOG_ASSERT(origin != NULL);
                 crm_xml_replace(scratch, XML_ATTR_UPDATE_ORIG, origin);
                 crm_xml_replace(scratch, XML_ATTR_UPDATE_CLIENT,
                                 crm_element_value(req, F_CIB_CLIENTNAME));
                 crm_xml_replace(scratch, XML_ATTR_UPDATE_USER, crm_element_value(req, F_CIB_USER));
             }
         }
     }
 
     crm_trace("Perform validation: %s", pcmk__btoa(check_schema));
     if ((rc == pcmk_ok) && check_schema && !validate_xml(scratch, NULL, TRUE)) {
         const char *current_schema = crm_element_value(scratch,
                                                        XML_ATTR_VALIDATION);
 
         crm_warn("Updated CIB does not validate against %s schema",
                  crm_str(current_schema));
         rc = -pcmk_err_schema_validation;
     }
 
   done:
 
     *result_cib = scratch;
     if(rc != pcmk_ok && cib_acl_enabled(current_cib, user)) {
         if(xml_acl_filtered_copy(user, current_cib, scratch, result_cib)) {
             if (*result_cib == NULL) {
                 crm_debug("Pre-filtered the entire cib result");
             }
             free_xml(scratch);
         }
     }
 
     if(diff) {
         *diff = local_diff;
     } else {
         free_xml(local_diff);
     }
 
     free_xml(top);
     crm_trace("Done");
     return rc;
 }
 
 xmlNode *
 cib_create_op(int call_id, const char *token, const char *op, const char *host, const char *section,
               xmlNode * data, int call_options, const char *user_name)
 {
     xmlNode *op_msg = create_xml_node(NULL, "cib_command");
 
     CRM_CHECK(op_msg != NULL, return NULL);
     CRM_CHECK(token != NULL, return NULL);
 
     crm_xml_add(op_msg, F_XML_TAGNAME, "cib_command");
 
     crm_xml_add(op_msg, F_TYPE, T_CIB);
     crm_xml_add(op_msg, F_CIB_CALLBACK_TOKEN, token);
     crm_xml_add(op_msg, F_CIB_OPERATION, op);
     crm_xml_add(op_msg, F_CIB_HOST, host);
     crm_xml_add(op_msg, F_CIB_SECTION, section);
     crm_xml_add_int(op_msg, F_CIB_CALLID, call_id);
     if (user_name) {
         crm_xml_add(op_msg, F_CIB_USER, user_name);
     }
     crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
     crm_xml_add_int(op_msg, F_CIB_CALLOPTS, call_options);
 
     if (data != NULL) {
         add_message_xml(op_msg, F_CIB_CALLDATA, data);
     }
 
     if (call_options & cib_inhibit_bcast) {
         CRM_CHECK((call_options & cib_scope_local), return NULL);
     }
     return op_msg;
 }
 
 void
 cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
 {
     xmlNode *output = NULL;
     cib_callback_client_t *blob = NULL;
 
     if (msg != NULL) {
         crm_element_value_int(msg, F_CIB_RC, &rc);
         crm_element_value_int(msg, F_CIB_CALLID, &call_id);
         output = get_message_xml(msg, F_CIB_CALLDATA);
     }
 
     blob = cib__lookup_id(call_id);
 
     if (blob == NULL) {
         crm_trace("No callback found for call %d", call_id);
     }
 
     if (cib == NULL) {
         crm_debug("No cib object supplied");
     }
 
     if (rc == -pcmk_err_diff_resync) {
         /* This is an internal value that clients do not and should not care about */
         rc = pcmk_ok;
     }
 
     if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
         crm_trace("Invoking callback %s for call %d", crm_str(blob->id), call_id);
         blob->callback(msg, call_id, rc, output, blob->user_data);
 
     } else if (cib && cib->op_callback == NULL && rc != pcmk_ok) {
         crm_warn("CIB command failed: %s", pcmk_strerror(rc));
         crm_log_xml_debug(msg, "Failed CIB Update");
     }
 
     /* This may free user_data, so do it after the callback */
     if (blob) {
         remove_cib_op_callback(call_id, FALSE);
     }
 
     if (cib && cib->op_callback != NULL) {
         crm_trace("Invoking global callback for call %d", call_id);
         cib->op_callback(msg, call_id, rc, output);
     }
     crm_trace("OP callback activated for %d", call_id);
 }
 
 void
 cib_native_notify(gpointer data, gpointer user_data)
 {
     xmlNode *msg = user_data;
     cib_notify_client_t *entry = data;
     const char *event = NULL;
 
     if (msg == NULL) {
         crm_warn("Skipping callback - NULL message");
         return;
     }
 
     event = crm_element_value(msg, F_SUBTYPE);
 
     if (entry == NULL) {
         crm_warn("Skipping callback - NULL callback client");
         return;
 
     } else if (entry->callback == NULL) {
         crm_warn("Skipping callback - NULL callback");
         return;
 
     } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
         crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
         return;
     }
 
     crm_trace("Invoking callback for %p/%s event...", entry, event);
     entry->callback(event, msg);
     crm_trace("Callback invoked...");
 }
 
 static pcmk__cluster_option_t cib_opts[] = {
     /* name, legacy name, type, allowed values,
      * default value, validator,
      * short description,
      * long description
      */
     {
         "enable-acl", NULL, "boolean", NULL,
         "false", pcmk__valid_boolean,
         "Enable Access Control Lists (ACLs) for the CIB",
         NULL
     },
     {
         "cluster-ipc-limit", NULL, "integer", NULL,
         "500", pcmk__valid_positive_number,
         "Maximum IPC message backlog before disconnecting a cluster daemon",
         "Raise this if log has \"Evicting client\" messages for cluster daemon"
             " PIDs (a good value is the number of resources in the cluster"
             " multiplied by the number of nodes)."
     },
 };
 
 void
 cib_metadata(void)
 {
-    pcmk__print_option_metadata("pacemaker-based", "1.0",
+    pcmk__print_option_metadata("pacemaker-based",
                                 "Cluster Information Base manager options",
                                 "Cluster options used by Pacemaker's "
                                     "Cluster Information Base manager",
                                 cib_opts, PCMK__NELEM(cib_opts));
 }
 
 void
 verify_cib_options(GHashTable * options)
 {
     pcmk__validate_cluster_options(options, cib_opts, PCMK__NELEM(cib_opts));
 }
 
 const char *
 cib_pref(GHashTable * options, const char *name)
 {
     return pcmk__cluster_option(options, cib_opts, PCMK__NELEM(cib_opts),
                                 name);
 }
 
 gboolean
 cib_read_config(GHashTable * options, xmlNode * current_cib)
 {
     xmlNode *config = NULL;
     crm_time_t *now = NULL;
 
     if (options == NULL || current_cib == NULL) {
         return FALSE;
     }
 
     now = crm_time_new(NULL);
 
     g_hash_table_remove_all(options);
 
     config = get_object_root(XML_CIB_TAG_CRMCONFIG, current_cib);
     if (config) {
         pe_unpack_nvpairs(current_cib, config, XML_CIB_TAG_PROPSET, NULL,
                           options, CIB_OPTIONS_FIRST, TRUE, now, NULL);
     }
 
     verify_cib_options(options);
 
     crm_time_free(now);
 
     return TRUE;
 }
 
 /* v2 and v2 patch formats */
 #define XPATH_CONFIG_CHANGE \
     "//" XML_CIB_TAG_CRMCONFIG " | " \
     "//" XML_DIFF_CHANGE "[contains(@" XML_DIFF_PATH ",'/" XML_CIB_TAG_CRMCONFIG "/')]"
 
 gboolean
 cib_internal_config_changed(xmlNode *diff)
 {
     gboolean changed = FALSE;
 
     if (diff) {
         xmlXPathObject *xpathObj = xpath_search(diff, XPATH_CONFIG_CHANGE);
 
         if (numXpathResults(xpathObj) > 0) {
             changed = TRUE;
         }
         freeXpathObject(xpathObj);
     }
     return changed;
 }
 
 int
 cib_internal_op(cib_t * cib, const char *op, const char *host,
                 const char *section, xmlNode * data,
                 xmlNode ** output_data, int call_options, const char *user_name)
 {
     int (*delegate) (cib_t * cib, const char *op, const char *host,
                      const char *section, xmlNode * data,
                      xmlNode ** output_data, int call_options, const char *user_name) =
         cib->delegate_fn;
 
     if(user_name == NULL) {
         user_name = getenv("CIB_user");
     }
 
     return delegate(cib, op, host, section, data, output_data, call_options, user_name);
 }
 
 /*!
  * \brief Apply a CIB update patch to a given CIB
  *
  * \param[in]  event   CIB update patch
  * \param[in]  input   CIB to patch
  * \param[out] output  Resulting CIB after patch
  * \param[in]  level   Log the patch at this log level (unless LOG_CRIT)
  *
  * \return Legacy Pacemaker return code
  * \note sbd calls this function
  */
 int
 cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
                       int level)
 {
     int rc = pcmk_err_generic;
 
     xmlNode *diff = NULL;
 
     CRM_ASSERT(event);
     CRM_ASSERT(input);
     CRM_ASSERT(output);
 
     crm_element_value_int(event, F_CIB_RC, &rc);
     diff = get_message_xml(event, F_CIB_UPDATE_RESULT);
 
     if (rc < pcmk_ok || diff == NULL) {
         return rc;
     }
 
     if (level > LOG_CRIT) {
         xml_log_patchset(level, "Config update", diff);
     }
 
     if (input != NULL) {
         rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output,
                               NULL);
 
         if (rc != pcmk_ok) {
             crm_debug("Update didn't apply: %s (%d) %p",
                       pcmk_strerror(rc), rc, *output);
 
             if (rc == -pcmk_err_old_data) {
                 crm_trace("Masking error, we already have the supplied update");
                 return pcmk_ok;
             }
             free_xml(*output);
             *output = NULL;
             return rc;
         }
     }
     return rc;
 }
 
 int
 cib__signon_query(cib_t **cib, xmlNode **cib_object)
 {
     int rc = pcmk_rc_ok;
     cib_t *cib_conn = NULL;
 
     if (cib == NULL) {
         cib_conn = cib_new();
     } else {
         *cib = cib_new();
         cib_conn = *cib;
     }
 
     if (cib_conn == NULL) {
         return ENOMEM;
     }
 
     rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
     rc = pcmk_legacy2rc(rc);
 
     if (rc == pcmk_rc_ok) {
         rc = cib_conn->cmds->query(cib_conn, NULL, cib_object, cib_scope_local | cib_sync_call);
         rc = pcmk_legacy2rc(rc);
     }
 
     if (cib == NULL) {
         cib_conn->cmds->signoff(cib_conn);
         cib_delete(cib_conn);
         cib_conn = NULL;
     }
 
     if (cib_object == NULL) {
         return pcmk_rc_no_input;
     } else {
         return rc;
     }
 }
diff --git a/lib/common/options.c b/lib/common/options.c
index 1321bb6c14..c11228ac66 100644
--- a/lib/common/options.c
+++ b/lib/common/options.c
@@ -1,603 +1,627 @@
 /*
  * Copyright 2004-2021 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 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()
 {
     free(crm_short_options);
     crm_short_options = NULL;
 }
 
 static struct option *
 create_long_opts(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,
                       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;
                 }
             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\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
  */
 const char *
 pcmk__env_option(const char *option)
 {
     char env_name[NAME_MAX];
     const char *value = NULL;
 
     snprintf(env_name, NAME_MAX, "PCMK_%s", option);
     value = getenv(env_name);
     if (value != NULL) {
         crm_trace("Found %s = %s", env_name, value);
         return value;
     }
 
     snprintf(env_name, NAME_MAX, "HA_%s", option);
     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)
 {
     char env_name[NAME_MAX];
 
     snprintf(env_name, NAME_MAX, "PCMK_%s", option);
     if (value) {
         crm_trace("Setting %s to %s", env_name, value);
         setenv(env_name, value, 1);
     } else {
         crm_trace("Unsetting %s", env_name);
         unsetenv(env_name);
     }
 
     snprintf(env_name, NAME_MAX, "HA_%s", option);
     if (value) {
         crm_trace("Setting %s to %s", env_name, value);
         setenv(env_name, value, 1);
     } else {
         crm_trace("Unsetting %s", env_name);
         unsetenv(env_name);
     }
 }
 
 /*!
  * \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
  * \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) || strstr(value, daemon));
 }
 
 
 /*
  * 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_utilization(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] 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] 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, 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;
 }
 
 void
-pcmk__print_option_metadata(const char *name, const char *version,
-                            const char *desc_short, const char *desc_long,
+pcmk__print_option_metadata(const char *name, const char *desc_short,
+                            const char *desc_long,
                             pcmk__cluster_option_t *option_list, int len)
 {
     int lpc = 0;
 
     fprintf(stdout, "<?xml version=\"1.0\"?>"
             "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
             "<resource-agent name=\"%s\">\n"
             "  <version>%s</version>\n"
             "  <longdesc lang=\"en\">%s</longdesc>\n"
             "  <shortdesc lang=\"en\">%s</shortdesc>\n"
-            "  <parameters>\n", name, version, desc_long, desc_short);
+            "  <parameters>\n", name, PCMK_OCF_VERSION, desc_long, desc_short);
 
     for (lpc = 0; lpc < len; lpc++) {
         if ((option_list[lpc].description_long == NULL)
             && (option_list[lpc].description_short == NULL)) {
             continue;
         }
-        fprintf(stdout, "    <parameter name=\"%s\" unique=\"0\">\n"
+
+        fprintf(stdout, "    <parameter name=\"%s\">\n"
                 "      <shortdesc lang=\"en\">%s</shortdesc>\n"
-                "      <content type=\"%s\" default=\"%s\"/>\n"
-                "      <longdesc lang=\"en\">%s%s%s</longdesc>\n"
-                "    </parameter>\n",
+                "      <longdesc lang=\"en\">%s%s%s</longdesc>\n",
                 option_list[lpc].name,
                 option_list[lpc].description_short,
-                option_list[lpc].type,
-                option_list[lpc].default_value,
                 option_list[lpc].description_long?
                     option_list[lpc].description_long :
                     option_list[lpc].description_short,
                 (option_list[lpc].values? "  Allowed values: " : ""),
                 (option_list[lpc].values? option_list[lpc].values : ""));
+
+        if (option_list[lpc].values && !strcmp(option_list[lpc].type, "select")) {
+            char *str = strdup(option_list[lpc].values);
+            char delim[] = ", ";
+            char *ptr = strtok(str, delim);
+
+            fprintf(stdout, "      <content type=\"%s\" default=\"%s\">\n",
+                option_list[lpc].type,
+                option_list[lpc].default_value
+            );
+
+            while (ptr != NULL) {
+                fprintf(stdout, "        <option value=\"%s\" />\n", ptr);
+                ptr = strtok(NULL, delim);
+            }
+
+            fprintf(stdout, "      </content>\n");
+            free(str);
+
+        } else {
+            fprintf(stdout, "      <content type=\"%s\" default=\"%s\"/>\n",
+                option_list[lpc].type,
+                option_list[lpc].default_value
+            );
+        }
+
+        fprintf(stdout, "    </parameter>\n");
     }
     fprintf(stdout, "  </parameters>\n</resource-agent>\n");
 }
 
 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/pengine/common.c b/lib/pengine/common.c
index 91d34a4ef1..236fc26b11 100644
--- a/lib/pengine/common.c
+++ b/lib/pengine/common.c
@@ -1,641 +1,641 @@
 /*
  * Copyright 2004-2021 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>
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/common/util.h>
 
 #include <glib.h>
 
 #include <crm/pengine/internal.h>
 
 gboolean was_processing_error = FALSE;
 gboolean was_processing_warning = FALSE;
 
 static bool
 check_health(const char *value)
 {
     return pcmk__strcase_any_of(value, "none", "custom", "only-green", "progressive",
                            "migrate-on-red", NULL);
 }
 
 static bool
 check_stonith_action(const char *value)
 {
     return pcmk__strcase_any_of(value, "reboot", "poweroff", "off", NULL);
 }
 
 static bool
 check_placement_strategy(const char *value)
 {
     return pcmk__strcase_any_of(value, "default", "utilization", "minimal",
                            "balanced", NULL);
 }
 
 static pcmk__cluster_option_t pe_opts[] = {
     /* name, old name, type, allowed values,
      * default value, validator,
      * short description,
      * long description
      */
     {
-        "no-quorum-policy", NULL, "enum", "stop, freeze, ignore, demote, suicide",
+        "no-quorum-policy", NULL, "select", "stop, freeze, ignore, demote, suicide",
         "stop", pcmk__valid_quorum,
         "What to do when the cluster does not have quorum",
         NULL
     },
     {
         "symmetric-cluster", NULL, "boolean", NULL,
         "true", pcmk__valid_boolean,
         "Whether resources can run on any node by default",
         NULL
     },
     {
         "maintenance-mode", NULL, "boolean", NULL,
         "false", pcmk__valid_boolean,
         "Whether the cluster should refrain from monitoring, starting, "
             "and stopping resources",
         NULL
     },
     {
         "start-failure-is-fatal", NULL, "boolean", NULL,
         "true", pcmk__valid_boolean,
         "Whether a start failure should prevent a resource from being "
             "recovered on the same node",
         "When true, the cluster will immediately ban a resource from a node "
             "if it fails to start there. When false, the cluster will instead "
             "check the resource's fail count against its migration-threshold."
     },
     {
         "enable-startup-probes", NULL, "boolean", NULL,
         "true", pcmk__valid_boolean,
         "Whether the cluster should check for active resources during start-up",
         NULL
     },
     {
         XML_CONFIG_ATTR_SHUTDOWN_LOCK, NULL, "boolean", NULL,
         "false", pcmk__valid_boolean,
         "Whether to lock resources to a cleanly shut down node",
         "When true, resources active on a node when it is cleanly shut down "
             "are kept \"locked\" to that node (not allowed to run elsewhere) "
             "until they start again on that node after it rejoins (or for at "
             "most shutdown-lock-limit, if set). Stonith resources and "
             "Pacemaker Remote connections are never locked. Clone and bundle "
             "instances and the promoted role of promotable clones are currently"
             " never locked, though support could be added in a future release."
     },
     {
         XML_CONFIG_ATTR_SHUTDOWN_LOCK_LIMIT, NULL, "time", NULL,
         "0", pcmk__valid_interval_spec,
         "Do not lock resources to a cleanly shut down node longer than this",
         "If shutdown-lock is true and this is set to a nonzero time duration, "
             "shutdown locks will expire after this much time has passed since "
             "the shutdown was initiated, even if the node has not rejoined."
     },
 
     // Fencing-related options
     {
         "stonith-enabled", NULL, "boolean", NULL,
         "true", pcmk__valid_boolean,
         "*** Advanced Use Only *** "
             "Whether nodes may be fenced as part of recovery",
         "If false, unresponsive nodes are immediately assumed to be harmless, "
             "and resources that were active on them may be recovered "
             "elsewhere. This can result in a \"split-brain\" situation, "
             "potentially leading to data loss and/or service unavailability."
     },
     {
-        "stonith-action", NULL, "enum", "reboot, off, poweroff",
+        "stonith-action", NULL, "select", "reboot, off, poweroff",
         "reboot", check_stonith_action,
         "Action to send to fence device when a node needs to be fenced "
             "(\"poweroff\" is a deprecated alias for \"off\")",
         NULL
     },
     {
         "stonith-timeout", NULL, "time", NULL,
         "60s", pcmk__valid_interval_spec,
         "*** Advanced Use Only *** Unused by Pacemaker",
         "This value is not used by Pacemaker, but is kept for backward "
             "compatibility, and certain legacy fence agents might use it."
     },
     {
         XML_ATTR_HAVE_WATCHDOG, NULL, "boolean", NULL,
         "false", pcmk__valid_boolean,
         "Whether watchdog integration is enabled",
         "This is set automatically by the cluster according to whether SBD "
             "is detected to be in use. User-configured values are ignored. "
             "The value `true` is meaningful if diskless SBD is used and "
             "`stonith-watchdog-timeout` is nonzero. In that case, if fencing "
             "is required, watchdog-based self-fencing will be performed via "
             "SBD without requiring a fencing resource explicitly configured."
     },
     {
         "concurrent-fencing", NULL, "boolean", NULL,
         PCMK__CONCURRENT_FENCING_DEFAULT, pcmk__valid_boolean,
         "Allow performing fencing operations in parallel",
         NULL
     },
     {
         "startup-fencing", NULL, "boolean", NULL,
         "true", pcmk__valid_boolean,
         "*** Advanced Use Only *** Whether to fence unseen nodes at start-up",
         "Setting this to false may lead to a \"split-brain\" situation,"
             "potentially leading to data loss and/or service unavailability."
     },
     {
         XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY, NULL, "time", NULL,
         "0", pcmk__valid_interval_spec,
         "Apply fencing delay targeting the lost nodes with the highest total resource priority",
         "Apply specified delay for the fencings that are targeting the lost "
             "nodes with the highest total resource priority in case we don't "
             "have the majority of the nodes in our cluster partition, so that "
             "the more significant nodes potentially win any fencing match, "
             "which is especially meaningful under split-brain of 2-node "
             "cluster. A promoted resource instance takes the base priority + 1 "
             "on calculation if the base priority is not 0. Any static/random "
             "delays that are introduced by `pcmk_delay_base/max` configured "
             "for the corresponding fencing resources will be added to this "
             "delay. This delay should be significantly greater than, safely "
             "twice, the maximum `pcmk_delay_base/max`. By default, priority "
             "fencing delay is disabled."
     },
 
     {
         "cluster-delay", NULL, "time", NULL,
         "60s", pcmk__valid_interval_spec,
         "Maximum time for node-to-node communication",
         "The node elected Designated Controller (DC) will consider an action "
             "failed if it does not get a response from the node executing the "
             "action within this time (after considering the action's own "
             "timeout). The \"correct\" value will depend on the speed and "
             "load of your network and cluster nodes."
     },
     {
         "batch-limit", NULL, "integer", NULL,
         "0", pcmk__valid_number,
         "Maximum number of jobs that the cluster may execute in parallel "
             "across all nodes",
         "The \"correct\" value will depend on the speed and load of your "
             "network and cluster nodes. If set to 0, the cluster will "
             "impose a dynamically calculated limit when any node has a "
             "high load."
     },
     {
         "migration-limit", NULL, "integer", NULL,
         "-1", pcmk__valid_number,
         "The number of live migration actions that the cluster is allowed "
             "to execute in parallel on a node (-1 means no limit)"
     },
 
     /* Orphans and stopping */
     {
         "stop-all-resources", NULL, "boolean", NULL,
         "false", pcmk__valid_boolean,
         "Whether the cluster should stop all active resources",
         NULL
     },
     {
         "stop-orphan-resources", NULL, "boolean", NULL,
         "true", pcmk__valid_boolean,
         "Whether to stop resources that were removed from the configuration",
         NULL
     },
     {
         "stop-orphan-actions", NULL, "boolean", NULL,
         "true", pcmk__valid_boolean,
         "Whether to cancel recurring actions removed from the configuration",
         NULL
     },
     {
         "remove-after-stop", NULL, "boolean", NULL,
         "false", pcmk__valid_boolean,
         "*** Deprecated *** Whether to remove stopped resources from "
             "the executor",
         "Values other than default are poorly tested and potentially dangerous."
             " This option will be removed in a future release."
     },
 
     /* Storing inputs */
     {
         "pe-error-series-max", NULL, "integer", NULL,
         "-1", pcmk__valid_number,
         "The number of scheduler inputs resulting in errors to save",
         "Zero to disable, -1 to store unlimited."
     },
     {
         "pe-warn-series-max",  NULL, "integer", NULL,
         "5000", pcmk__valid_number,
         "The number of scheduler inputs resulting in warnings to save",
         "Zero to disable, -1 to store unlimited."
     },
     {
         "pe-input-series-max", NULL, "integer", NULL,
         "4000", pcmk__valid_number,
         "The number of scheduler inputs without errors or warnings to save",
         "Zero to disable, -1 to store unlimited."
     },
 
     /* Node health */
     {
-        "node-health-strategy", NULL, "enum",
+        "node-health-strategy", NULL, "select",
         "none, migrate-on-red, only-green, progressive, custom",
         "none", check_health,
         "How cluster should react to node health attributes",
         "Requires external entities to create node attributes (named with "
             "the prefix \"#health\") with values \"red\", \"yellow\" or "
             "\"green\"."
     },
     {
         "node-health-base", NULL, "integer", NULL,
         "0", pcmk__valid_number,
         "Base health score assigned to a node",
         "Only used when node-health-strategy is set to progressive."
     },
     {
         "node-health-green", NULL, "integer", NULL,
         "0", pcmk__valid_number,
         "The score to use for a node health attribute whose value is \"green\"",
         "Only used when node-health-strategy is set to custom or progressive."
     },
     {
         "node-health-yellow", NULL, "integer", NULL,
         "0", pcmk__valid_number,
         "The score to use for a node health attribute whose value is \"yellow\"",
         "Only used when node-health-strategy is set to custom or progressive."
     },
     {
         "node-health-red", NULL, "integer", NULL,
         "-INFINITY", pcmk__valid_number,
         "The score to use for a node health attribute whose value is \"red\"",
         "Only used when node-health-strategy is set to custom or progressive."
     },
 
     /*Placement Strategy*/
     {
-        "placement-strategy", NULL, "enum",
+        "placement-strategy", NULL, "select",
         "default, utilization, minimal, balanced",
         "default", check_placement_strategy,
         "How the cluster should allocate resources to nodes",
         NULL
     },
 };
 
 void
 pe_metadata(void)
 {
-    pcmk__print_option_metadata("pacemaker-schedulerd", "1.0",
+    pcmk__print_option_metadata("pacemaker-schedulerd",
                                 "Pacemaker scheduler options",
                                 "Cluster options used by Pacemaker's scheduler"
                                     " (formerly called pengine)",
                                 pe_opts, PCMK__NELEM(pe_opts));
 }
 
 void
 verify_pe_options(GHashTable * options)
 {
     pcmk__validate_cluster_options(options, pe_opts, PCMK__NELEM(pe_opts));
 }
 
 const char *
 pe_pref(GHashTable * options, const char *name)
 {
     return pcmk__cluster_option(options, pe_opts, PCMK__NELEM(pe_opts), name);
 }
 
 const char *
 fail2text(enum action_fail_response fail)
 {
     const char *result = "<unknown>";
 
     switch (fail) {
         case action_fail_ignore:
             result = "ignore";
             break;
         case action_fail_demote:
             result = "demote";
             break;
         case action_fail_block:
             result = "block";
             break;
         case action_fail_recover:
             result = "recover";
             break;
         case action_fail_migrate:
             result = "migrate";
             break;
         case action_fail_stop:
             result = "stop";
             break;
         case action_fail_fence:
             result = "fence";
             break;
         case action_fail_standby:
             result = "standby";
             break;
         case action_fail_restart_container:
             result = "restart-container";
             break;
         case action_fail_reset_remote:
             result = "reset-remote";
             break;
     }
     return result;
 }
 
 enum action_tasks
 text2task(const char *task)
 {
     if (pcmk__str_eq(task, CRMD_ACTION_STOP, pcmk__str_casei)) {
         return stop_rsc;
     } else if (pcmk__str_eq(task, CRMD_ACTION_STOPPED, pcmk__str_casei)) {
         return stopped_rsc;
     } else if (pcmk__str_eq(task, CRMD_ACTION_START, pcmk__str_casei)) {
         return start_rsc;
     } else if (pcmk__str_eq(task, CRMD_ACTION_STARTED, pcmk__str_casei)) {
         return started_rsc;
     } else if (pcmk__str_eq(task, CRM_OP_SHUTDOWN, pcmk__str_casei)) {
         return shutdown_crm;
     } else if (pcmk__str_eq(task, CRM_OP_FENCE, pcmk__str_casei)) {
         return stonith_node;
     } else if (pcmk__str_eq(task, CRMD_ACTION_STATUS, pcmk__str_casei)) {
         return monitor_rsc;
     } else if (pcmk__str_eq(task, CRMD_ACTION_NOTIFY, pcmk__str_casei)) {
         return action_notify;
     } else if (pcmk__str_eq(task, CRMD_ACTION_NOTIFIED, pcmk__str_casei)) {
         return action_notified;
     } else if (pcmk__str_eq(task, CRMD_ACTION_PROMOTE, pcmk__str_casei)) {
         return action_promote;
     } else if (pcmk__str_eq(task, CRMD_ACTION_DEMOTE, pcmk__str_casei)) {
         return action_demote;
     } else if (pcmk__str_eq(task, CRMD_ACTION_PROMOTED, pcmk__str_casei)) {
         return action_promoted;
     } else if (pcmk__str_eq(task, CRMD_ACTION_DEMOTED, pcmk__str_casei)) {
         return action_demoted;
     }
 #if SUPPORT_TRACING
     if (pcmk__str_eq(task, CRMD_ACTION_CANCEL, pcmk__str_casei)) {
         return no_action;
     } else if (pcmk__str_eq(task, CRMD_ACTION_DELETE, pcmk__str_casei)) {
         return no_action;
     } else if (pcmk__str_eq(task, CRMD_ACTION_STATUS, pcmk__str_casei)) {
         return no_action;
     } else if (pcmk__str_eq(task, CRM_OP_PROBED, pcmk__str_casei)) {
         return no_action;
     } else if (pcmk__str_eq(task, CRM_OP_LRM_REFRESH, pcmk__str_casei)) {
         return no_action;
     } else if (pcmk__str_eq(task, CRMD_ACTION_MIGRATE, pcmk__str_casei)) {
         return no_action;
     } else if (pcmk__str_eq(task, CRMD_ACTION_MIGRATED, pcmk__str_casei)) {
         return no_action;
     }
     crm_trace("Unsupported action: %s", task);
 #endif
 
     return no_action;
 }
 
 const char *
 task2text(enum action_tasks task)
 {
     const char *result = "<unknown>";
 
     switch (task) {
         case no_action:
             result = "no_action";
             break;
         case stop_rsc:
             result = CRMD_ACTION_STOP;
             break;
         case stopped_rsc:
             result = CRMD_ACTION_STOPPED;
             break;
         case start_rsc:
             result = CRMD_ACTION_START;
             break;
         case started_rsc:
             result = CRMD_ACTION_STARTED;
             break;
         case shutdown_crm:
             result = CRM_OP_SHUTDOWN;
             break;
         case stonith_node:
             result = CRM_OP_FENCE;
             break;
         case monitor_rsc:
             result = CRMD_ACTION_STATUS;
             break;
         case action_notify:
             result = CRMD_ACTION_NOTIFY;
             break;
         case action_notified:
             result = CRMD_ACTION_NOTIFIED;
             break;
         case action_promote:
             result = CRMD_ACTION_PROMOTE;
             break;
         case action_promoted:
             result = CRMD_ACTION_PROMOTED;
             break;
         case action_demote:
             result = CRMD_ACTION_DEMOTE;
             break;
         case action_demoted:
             result = CRMD_ACTION_DEMOTED;
             break;
     }
 
     return result;
 }
 
 const char *
 role2text(enum rsc_role_e role)
 {
     switch (role) {
         case RSC_ROLE_UNKNOWN:
             return RSC_ROLE_UNKNOWN_S;
         case RSC_ROLE_STOPPED:
             return RSC_ROLE_STOPPED_S;
         case RSC_ROLE_STARTED:
             return RSC_ROLE_STARTED_S;
         case RSC_ROLE_UNPROMOTED:
 #ifdef PCMK__COMPAT_2_0
             return RSC_ROLE_UNPROMOTED_LEGACY_S;
 #else
             return RSC_ROLE_UNPROMOTED_S;
 #endif
         case RSC_ROLE_PROMOTED:
 #ifdef PCMK__COMPAT_2_0
             return RSC_ROLE_PROMOTED_LEGACY_S;
 #else
             return RSC_ROLE_PROMOTED_S;
 #endif
     }
     CRM_CHECK(role >= RSC_ROLE_UNKNOWN, return RSC_ROLE_UNKNOWN_S);
     CRM_CHECK(role < RSC_ROLE_MAX, return RSC_ROLE_UNKNOWN_S);
     // coverity[dead_error_line]
     return RSC_ROLE_UNKNOWN_S;
 }
 
 enum rsc_role_e
 text2role(const char *role)
 {
     CRM_ASSERT(role != NULL);
     if (pcmk__str_eq(role, RSC_ROLE_STOPPED_S, pcmk__str_casei)) {
         return RSC_ROLE_STOPPED;
     } else if (pcmk__str_eq(role, RSC_ROLE_STARTED_S, pcmk__str_casei)) {
         return RSC_ROLE_STARTED;
     } else if (pcmk__strcase_any_of(role, RSC_ROLE_UNPROMOTED_S,
                                     RSC_ROLE_UNPROMOTED_LEGACY_S, NULL)) {
         return RSC_ROLE_UNPROMOTED;
     } else if (pcmk__strcase_any_of(role, RSC_ROLE_PROMOTED_S,
                                     RSC_ROLE_PROMOTED_LEGACY_S, NULL)) {
         return RSC_ROLE_PROMOTED;
     } else if (pcmk__str_eq(role, RSC_ROLE_UNKNOWN_S, pcmk__str_casei)) {
         return RSC_ROLE_UNKNOWN;
     }
     crm_err("Unknown role: %s", role);
     return RSC_ROLE_UNKNOWN;
 }
 
 /*!
  * \internal
  * \brief Add two scores (bounding to +/- INFINITY)
  *
  * \param[in] score1  First score to add
  * \param[in] score2  Second score to add
  */
 int
 pe__add_scores(int score1, int score2)
 {
     int result = score1 + score2;
 
     // First handle the cases where one or both is infinite
 
     if (score1 <= -CRM_SCORE_INFINITY) {
 
         if (score2 <= -CRM_SCORE_INFINITY) {
             crm_trace("-INFINITY + -INFINITY = -INFINITY");
         } else if (score2 >= CRM_SCORE_INFINITY) {
             crm_trace("-INFINITY + +INFINITY = -INFINITY");
         } else {
             crm_trace("-INFINITY + %d = -INFINITY", score2);
         }
 
         return -CRM_SCORE_INFINITY;
 
     } else if (score2 <= -CRM_SCORE_INFINITY) {
 
         if (score1 >= CRM_SCORE_INFINITY) {
             crm_trace("+INFINITY + -INFINITY = -INFINITY");
         } else {
             crm_trace("%d + -INFINITY = -INFINITY", score1);
         }
 
         return -CRM_SCORE_INFINITY;
 
     } else if (score1 >= CRM_SCORE_INFINITY) {
 
         if (score2 >= CRM_SCORE_INFINITY) {
             crm_trace("+INFINITY + +INFINITY = +INFINITY");
         } else {
             crm_trace("+INFINITY + %d = +INFINITY", score2);
         }
 
         return CRM_SCORE_INFINITY;
 
     } else if (score2 >= CRM_SCORE_INFINITY) {
         crm_trace("%d + +INFINITY = +INFINITY", score1);
         return CRM_SCORE_INFINITY;
     }
 
     /* As long as CRM_SCORE_INFINITY is less than half of the maximum integer,
      * we can ignore the possibility of integer overflow
      */
 
     // Bound result to infinity
 
     if (result >= CRM_SCORE_INFINITY) {
         crm_trace("%d + %d = +INFINITY", score1, score2);
         return CRM_SCORE_INFINITY;
 
     } else if (result <= -CRM_SCORE_INFINITY) {
         crm_trace("%d + %d = -INFINITY", score1, score2);
         return -CRM_SCORE_INFINITY;
     }
 
     crm_trace("%d + %d = %d", score1, score2, result);
     return result;
 }
 
 void
 add_hash_param(GHashTable * hash, const char *name, const char *value)
 {
     CRM_CHECK(hash != NULL, return);
 
     crm_trace("adding: name=%s value=%s", crm_str(name), crm_str(value));
     if (name == NULL || value == NULL) {
         return;
 
     } else if (pcmk__str_eq(value, "#default", pcmk__str_casei)) {
         return;
 
     } else if (g_hash_table_lookup(hash, name) == NULL) {
         g_hash_table_insert(hash, strdup(name), strdup(value));
     }
 }
 
 const char *
 pe_node_attribute_calculated(const pe_node_t *node, const char *name,
                              const pe_resource_t *rsc)
 {
     const char *source;
 
     if(node == NULL) {
         return NULL;
 
     } else if(rsc == NULL) {
         return g_hash_table_lookup(node->details->attrs, name);
     }
 
     source = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET);
     if(source == NULL || !pcmk__str_eq("host", source, pcmk__str_casei)) {
         return g_hash_table_lookup(node->details->attrs, name);
     }
 
     /* Use attributes set for the containers location
      * instead of for the container itself
      *
      * Useful when the container is using the host's local
      * storage
      */
 
     CRM_ASSERT(node->details->remote_rsc);
     CRM_ASSERT(node->details->remote_rsc->container);
 
     if(node->details->remote_rsc->container->running_on) {
         pe_node_t *host = node->details->remote_rsc->container->running_on->data;
         pe_rsc_trace(rsc, "%s: Looking for %s on the container host %s", rsc->id, name, host->details->uname);
         return g_hash_table_lookup(host->details->attrs, name);
     }
 
     pe_rsc_trace(rsc, "%s: Not looking for %s on the container host: %s is inactive",
                  rsc->id, name, node->details->remote_rsc->container->id);
     return NULL;
 }
 
 const char *
 pe_node_attribute_raw(pe_node_t *node, const char *name)
 {
     if(node == NULL) {
         return NULL;
     }
     return g_hash_table_lookup(node->details->attrs, name);
 }