diff --git a/daemons/controld/controld_corosync.c b/daemons/controld/controld_corosync.c
index d104127370..db99630fbc 100644
--- a/daemons/controld/controld_corosync.c
+++ b/daemons/controld/controld_corosync.c
@@ -1,109 +1,110 @@
 /*
  * Copyright 2004-2019 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/cluster/internal.h>
 #include <crm/common/xml.h>
 
 #include <pacemaker-controld.h>
 
 #if SUPPORT_COROSYNC
 
 extern void post_cache_update(int seq);
 
 /*	 A_HA_CONNECT	*/
 
 static void
 crmd_cs_dispatch(cpg_handle_t handle, const struct cpg_name *groupName,
                  uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
 {
     uint32_t kind = 0;
     const char *from = NULL;
     char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
 
     if(data == NULL) {
         return;
     }
     if (kind == crm_class_cluster) {
         crm_node_t *peer = NULL;
         xmlNode *xml = string2xml(data);
 
         if (xml == NULL) {
             crm_err("Could not parse message content (%d): %.100s", kind, data);
             free(data);
             return;
         }
 
         crm_xml_add(xml, F_ORIG, from);
         /* crm_xml_add_int(xml, F_SEQ, wrapper->id); Fake? */
 
         peer = crm_get_peer(0, from);
         if (!pcmk_is_set(peer->processes, crm_proc_cpg)) {
             /* If we can still talk to our peer process on that node,
              * then it must be part of the corosync membership
              */
             crm_warn("Receiving messages from a node we think is dead: %s[%d]",
                      peer->uname, peer->id);
             crm_update_peer_proc(__func__, peer, crm_proc_cpg,
                                  ONLINESTATUS);
         }
         crmd_ha_msg_filter(xml);
         free_xml(xml);
     } else {
         crm_err("Invalid message class (%d): %.100s", kind, data);
     }
     free(data);
 }
 
 static gboolean
 crmd_quorum_callback(unsigned long long seq, gboolean quorate)
 {
     crm_update_quorum(quorate, FALSE);
     post_cache_update(seq);
     return TRUE;
 }
 
 static void
 crmd_cs_destroy(gpointer user_data)
 {
     if (!pcmk_is_set(fsa_input_register, R_HA_DISCONNECTED)) {
         crm_crit("Lost connection to cluster layer, shutting down");
         crmd_exit(CRM_EX_DISCONNECT);
 
     } else {
         crm_info("Corosync connection closed");
     }
 }
 
 extern gboolean crm_connect_corosync(crm_cluster_t * cluster);
 
 gboolean
 crm_connect_corosync(crm_cluster_t * cluster)
 {
     if (is_corosync_cluster()) {
         crm_set_status_callback(&peer_update_callback);
         cluster->cpg.cpg_deliver_fn = crmd_cs_dispatch;
         cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
         cluster->destroy = crmd_cs_destroy;
 
         if (crm_cluster_connect(cluster)) {
-            cluster_connect_quorum(crmd_quorum_callback, crmd_cs_destroy);
+            pcmk__corosync_quorum_connect(crmd_quorum_callback,
+                                          crmd_cs_destroy);
             return TRUE;
         }
     }
     return FALSE;
 }
 
 #endif
diff --git a/daemons/controld/controld_election.c b/daemons/controld/controld_election.c
index 7e241bdc96..cd1d6b720e 100644
--- a/daemons/controld/controld_election.c
+++ b/daemons/controld/controld_election.c
@@ -1,275 +1,275 @@
 /*
  * Copyright 2004-2020 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/time.h>
 #include <sys/resource.h>
 
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/cluster/internal.h>
 #include <crm/cluster/election.h>
 #include <crm/crm.h>
 
 #include <pacemaker-controld.h>
 
 static election_t *fsa_election = NULL;
 
 static gboolean
 election_win_cb(gpointer data)
 {
     register_fsa_input(C_FSA_INTERNAL, I_ELECTION_DC, NULL);
     return FALSE;
 }
 
 void
 controld_election_init(const char *uname)
 {
     fsa_election = election_init("DC", uname, 60000 /*60s*/, election_win_cb);
 }
 
 void
 controld_remove_voter(const char *uname)
 {
     election_remove(fsa_election, uname);
 
     if (pcmk__str_eq(uname, fsa_our_dc, pcmk__str_casei)) {
         /* Clear any election dampening in effect. Otherwise, if the lost DC had
          * just won, an immediate new election could fizzle out with no new DC.
          */
         election_clear_dampening(fsa_election);
     }
 }
 
 void
 controld_election_fini()
 {
     election_fini(fsa_election);
     fsa_election = NULL;
 }
 
 void
 controld_set_election_period(const char *value)
 {
     election_timeout_set_period(fsa_election, crm_parse_interval_spec(value));
 }
 
 void
 controld_stop_election_timer()
 {
     election_timeout_stop(fsa_election);
 }
 
 /*	A_ELECTION_VOTE	*/
 void
 do_election_vote(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 not_voting = FALSE;
 
     /* don't vote if we're in one of these states or wanting to shut down */
     switch (cur_state) {
         case S_STARTING:
         case S_RECOVERY:
         case S_STOPPING:
         case S_TERMINATE:
             crm_warn("Not voting in election, we're in state %s", fsa_state2string(cur_state));
             not_voting = TRUE;
             break;
         case S_ELECTION:
         case S_INTEGRATION:
         case S_RELEASE_DC:
             break;
         default:
             crm_err("Broken? Voting in state %s", fsa_state2string(cur_state));
             break;
     }
 
     if (not_voting == FALSE) {
         if (pcmk_is_set(fsa_input_register, R_STARTING)) {
             not_voting = TRUE;
         }
     }
 
     if (not_voting) {
         if (AM_I_DC) {
             register_fsa_input(C_FSA_INTERNAL, I_RELEASE_DC, NULL);
 
         } else {
             register_fsa_input(C_FSA_INTERNAL, I_PENDING, NULL);
         }
         return;
     }
 
     election_vote(fsa_election);
     return;
 }
 
 void
 do_election_check(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)
 {
     if (fsa_state == S_ELECTION) {
         election_check(fsa_election);
     } else {
         crm_debug("Ignoring election check because we are not in an election");
     }
 }
 
 /*	A_ELECTION_COUNT	*/
 void
 do_election_count_vote(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)
 {
     enum election_result rc = 0;
     ha_msg_input_t *vote = fsa_typed_data(fsa_dt_ha_msg);
 
     if(crm_peer_cache == NULL) {
         if (!pcmk_is_set(fsa_input_register, R_SHUTDOWN)) {
             crm_err("Internal error, no peer cache");
         }
         return;
     }
 
     rc = election_count_vote(fsa_election, vote->msg, cur_state != S_STARTING);
     switch(rc) {
         case election_start:
             election_reset(fsa_election);
             register_fsa_input(C_FSA_INTERNAL, I_ELECTION, NULL);
             break;
 
         case election_lost:
             update_dc(NULL);
 
             if (fsa_input_register & R_THE_DC) {
                 register_fsa_input(C_FSA_INTERNAL, I_RELEASE_DC, NULL);
                 fsa_cib_conn->cmds->set_slave(fsa_cib_conn, cib_scope_local);
 
             } else if (cur_state != S_STARTING) {
                 register_fsa_input(C_FSA_INTERNAL, I_PENDING, NULL);
             }
             break;
 
         default:
             crm_trace("Election message resulted in state %d", rc);
     }
 }
 
 static void
 feature_update_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
 {
     if (rc != pcmk_ok) {
         fsa_data_t *msg_data = NULL;
 
         crm_notice("Feature update failed: %s "CRM_XS" rc=%d",
                    pcmk_strerror(rc), rc);
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
     }
 }
 
 /*	 A_DC_TAKEOVER	*/
 void
 do_dc_takeover(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)
 {
     int rc = pcmk_ok;
     xmlNode *cib = NULL;
     const char *cluster_type = name_for_cluster_type(get_cluster_type());
     pid_t watchdog = pcmk__locate_sbd();
 
     crm_info("Taking over DC status for this partition");
     controld_set_fsa_input_flags(R_THE_DC);
     execute_stonith_cleanup();
 
     election_reset(fsa_election);
     controld_set_fsa_input_flags(R_JOIN_OK|R_INVOKE_PE);
 
     fsa_cib_conn->cmds->set_master(fsa_cib_conn, cib_scope_local);
 
     cib = create_xml_node(NULL, XML_TAG_CIB);
     crm_xml_add(cib, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
     fsa_cib_update(XML_TAG_CIB, cib, cib_quorum_override, rc, NULL);
     fsa_register_cib_callback(rc, FALSE, NULL, feature_update_callback);
 
     update_attr_delegate(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG, NULL,
                          NULL, NULL, NULL, XML_ATTR_HAVE_WATCHDOG,
                          pcmk__btoa(watchdog), FALSE, NULL, NULL);
 
     update_attr_delegate(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL,
                          "dc-version", PACEMAKER_VERSION "-" BUILD_VERSION, FALSE, NULL, NULL);
 
     update_attr_delegate(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL,
                          "cluster-infrastructure", cluster_type, FALSE, NULL, NULL);
 
 #if SUPPORT_COROSYNC
     if (fsa_cluster_name == NULL && is_corosync_cluster()) {
-        char *cluster_name = corosync_cluster_name();
+        char *cluster_name = pcmk__corosync_cluster_name();
 
         if (cluster_name) {
             update_attr_delegate(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL,
                                  "cluster-name", cluster_name, FALSE, NULL, NULL);
         }
         free(cluster_name);
     }
 #endif
 
     mainloop_set_trigger(config_read);
     free_xml(cib);
 }
 
 /*	 A_DC_RELEASE	*/
 void
 do_dc_release(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)
 {
     if (action & A_DC_RELEASE) {
         crm_debug("Releasing the role of DC");
         controld_clear_fsa_input_flags(R_THE_DC);
         controld_expect_sched_reply(NULL);
 
     } else if (action & A_DC_RELEASED) {
         crm_info("DC role released");
 #if 0
         if (are there errors) {
             /* we can't stay up if not healthy */
             /* or perhaps I_ERROR and go to S_RECOVER? */
             result = I_SHUTDOWN;
         }
 #endif
         if (pcmk_is_set(fsa_input_register, R_SHUTDOWN)) {
             xmlNode *update = NULL;
             crm_node_t *node = crm_get_peer(0, fsa_our_uname);
 
             crm_update_peer_expected(__func__, node, CRMD_JOINSTATE_DOWN);
             update = create_node_state_update(node, node_update_expected, NULL,
                                               __func__);
             /* Don't need a based response because controld will stop. */
             fsa_cib_anon_update_discard_reply(XML_CIB_TAG_STATUS, update);
             free_xml(update);
         }
         register_fsa_input(C_FSA_INTERNAL, I_RELEASE_SUCCESS, NULL);
 
     } else {
         crm_err("Unknown DC action %s", fsa_action2string(action));
     }
 
     crm_trace("Am I still the DC? %s", AM_I_DC ? XML_BOOLEAN_YES : XML_BOOLEAN_NO);
 
 }
diff --git a/daemons/controld/controld_membership.c b/daemons/controld/controld_membership.c
index 71fea6e5d7..550c19c5cb 100644
--- a/daemons/controld/controld_membership.c
+++ b/daemons/controld/controld_membership.c
@@ -1,439 +1,439 @@
 /*
  * Copyright 2004-2020 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.
  */
 
 /* put these first so that uuid_t is defined without conflicts */
 #include <crm_internal.h>
 
 #include <string.h>
 
 #include <crm/crm.h>
 #include <crm/msg_xml.h>
 #include <crm/common/xml.h>
 #include <crm/common/xml_internal.h>
 #include <crm/cluster/internal.h>
 
 #include <pacemaker-controld.h>
 
 gboolean membership_flux_hack = FALSE;
 void post_cache_update(int instance);
 
 int last_peer_update = 0;
 guint highest_born_on = -1;
 
 extern gboolean check_join_state(enum crmd_fsa_state cur_state, const char *source);
 
 static void
 reap_dead_nodes(gpointer key, gpointer value, gpointer user_data)
 {
     crm_node_t *node = value;
 
     if (crm_is_peer_active(node) == FALSE) {
         crm_update_peer_join(__func__, node, crm_join_none);
 
         if(node && node->uname) {
             if (pcmk__str_eq(fsa_our_uname, node->uname, pcmk__str_casei)) {
                 crm_err("We're not part of the cluster anymore");
                 register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL);
 
             } else if (AM_I_DC == FALSE && pcmk__str_eq(node->uname, fsa_our_dc, pcmk__str_casei)) {
                 crm_warn("Our DC node (%s) left the cluster", node->uname);
                 register_fsa_input(C_FSA_INTERNAL, I_ELECTION, NULL);
             }
         }
 
         if (fsa_state == S_INTEGRATION || fsa_state == S_FINALIZE_JOIN) {
             check_join_state(fsa_state, __func__);
         }
         if(node && node->uuid) {
             fail_incompletable_actions(transition_graph, node->uuid);
         }
     }
 }
 
 gboolean ever_had_quorum = FALSE;
 
 void
 post_cache_update(int instance)
 {
     xmlNode *no_op = NULL;
 
     crm_peer_seq = instance;
     crm_debug("Updated cache after membership event %d.", instance);
 
     g_hash_table_foreach(crm_peer_cache, reap_dead_nodes, NULL);
     controld_set_fsa_input_flags(R_MEMBERSHIP);
 
     if (AM_I_DC) {
         populate_cib_nodes(node_update_quick | node_update_cluster | node_update_peer |
                            node_update_expected, __func__);
     }
 
     /*
      * If we lost nodes, we should re-check the election status
      * Safe to call outside of an election
      */
     controld_set_fsa_action_flags(A_ELECTION_CHECK);
     trigger_fsa();
 
     /* Membership changed, remind everyone we're here.
      * This will aid detection of duplicate DCs
      */
     no_op = create_request(CRM_OP_NOOP, NULL, NULL, CRM_SYSTEM_CRMD,
                            AM_I_DC ? CRM_SYSTEM_DC : CRM_SYSTEM_CRMD, NULL);
     send_cluster_message(NULL, crm_msg_crmd, no_op, FALSE);
     free_xml(no_op);
 }
 
 static void
 crmd_node_update_complete(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
 {
     fsa_data_t *msg_data = NULL;
 
     last_peer_update = 0;
 
     if (rc == pcmk_ok) {
         crm_trace("Node update %d complete", call_id);
 
     } else if(call_id < pcmk_ok) {
         crm_err("Node update failed: %s (%d)", pcmk_strerror(call_id), call_id);
         crm_log_xml_debug(msg, "failed");
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
 
     } else {
         crm_err("Node update %d failed: %s (%d)", call_id, pcmk_strerror(rc), rc);
         crm_log_xml_debug(msg, "failed");
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
     }
 }
 
 /*!
  * \internal
  * \brief Create an XML node state tag with updates
  *
  * \param[in,out] node    Node whose state will be used for update
  * \param[in]     flags   Bitmask of node_update_flags indicating what to update
  * \param[in,out] parent  XML node to contain update (or NULL)
  * \param[in]     source  Who requested the update (only used for logging)
  *
  * \return Pointer to created node state tag
  */
 xmlNode *
 create_node_state_update(crm_node_t *node, int flags, xmlNode *parent,
                          const char *source)
 {
     const char *value = NULL;
     xmlNode *node_state;
 
     if (!node->state) {
         crm_info("Node update for %s cancelled: no state, not seen yet", node->uname);
        return NULL;
     }
 
     node_state = create_xml_node(parent, XML_CIB_TAG_STATE);
 
     if (pcmk_is_set(node->flags, crm_remote_node)) {
         crm_xml_add(node_state, XML_NODE_IS_REMOTE, XML_BOOLEAN_TRUE);
     }
 
     set_uuid(node_state, XML_ATTR_UUID, node);
 
     if (crm_element_value(node_state, XML_ATTR_UUID) == NULL) {
         crm_info("Node update for %s cancelled: no id", node->uname);
         free_xml(node_state);
         return NULL;
     }
 
     crm_xml_add(node_state, XML_ATTR_UNAME, node->uname);
 
     if ((flags & node_update_cluster) && node->state) {
         crm_xml_add_boolean(node_state, XML_NODE_IN_CLUSTER,
                             pcmk__str_eq(node->state, CRM_NODE_MEMBER, pcmk__str_casei));
     }
 
     if (!pcmk_is_set(node->flags, crm_remote_node)) {
         if (flags & node_update_peer) {
             value = OFFLINESTATUS;
             if (pcmk_is_set(node->processes, crm_get_cluster_proc())) {
                 value = ONLINESTATUS;
             }
             crm_xml_add(node_state, XML_NODE_IS_PEER, value);
         }
 
         if (flags & node_update_join) {
             if (node->join <= crm_join_none) {
                 value = CRMD_JOINSTATE_DOWN;
             } else {
                 value = CRMD_JOINSTATE_MEMBER;
             }
             crm_xml_add(node_state, XML_NODE_JOIN_STATE, value);
         }
 
         if (flags & node_update_expected) {
             crm_xml_add(node_state, XML_NODE_EXPECTED, node->expected);
         }
     }
 
     crm_xml_add(node_state, XML_ATTR_ORIGIN, source);
 
     return node_state;
 }
 
 static void
 remove_conflicting_node_callback(xmlNode * msg, int call_id, int rc,
                                  xmlNode * output, void *user_data)
 {
     char *node_uuid = user_data;
 
     do_crm_log_unlikely(rc == 0 ? LOG_DEBUG : LOG_NOTICE,
                         "Deletion of the unknown conflicting node \"%s\": %s (rc=%d)",
                         node_uuid, pcmk_strerror(rc), rc);
 }
 
 static void
 search_conflicting_node_callback(xmlNode * msg, int call_id, int rc,
                                  xmlNode * output, void *user_data)
 {
     char *new_node_uuid = user_data;
     xmlNode *node_xml = NULL;
 
     if (rc != pcmk_ok) {
         if (rc != -ENXIO) {
             crm_notice("Searching conflicting nodes for %s failed: %s (%d)",
                        new_node_uuid, pcmk_strerror(rc), rc);
         }
         return;
 
     } else if (output == NULL) {
         return;
     }
 
     if (pcmk__str_eq(crm_element_name(output), XML_CIB_TAG_NODE, pcmk__str_casei)) {
         node_xml = output;
 
     } else {
         node_xml = pcmk__xml_first_child(output);
     }
 
     for (; node_xml != NULL; node_xml = pcmk__xml_next(node_xml)) {
         const char *node_uuid = NULL;
         const char *node_uname = NULL;
         GHashTableIter iter;
         crm_node_t *node = NULL;
         gboolean known = FALSE;
 
         if (!pcmk__str_eq(crm_element_name(node_xml), XML_CIB_TAG_NODE, pcmk__str_casei)) {
             continue;
         }
 
         node_uuid = crm_element_value(node_xml, XML_ATTR_ID);
         node_uname = crm_element_value(node_xml, XML_ATTR_UNAME);
 
         if (node_uuid == NULL || node_uname == NULL) {
             continue;
         }
 
         g_hash_table_iter_init(&iter, crm_peer_cache);
         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
             if (node->uuid
                 && pcmk__str_eq(node->uuid, node_uuid, pcmk__str_casei)
                 && node->uname
                 && pcmk__str_eq(node->uname, node_uname, pcmk__str_casei)) {
 
                 known = TRUE;
                 break;
             }
         }
 
         if (known == FALSE) {
             int delete_call_id = 0;
             xmlNode *node_state_xml = NULL;
 
             crm_notice("Deleting unknown node %s/%s which has conflicting uname with %s",
                        node_uuid, node_uname, new_node_uuid);
 
             delete_call_id = fsa_cib_conn->cmds->remove(fsa_cib_conn, XML_CIB_TAG_NODES, node_xml,
                                                         cib_scope_local | cib_quorum_override);
             fsa_register_cib_callback(delete_call_id, FALSE, strdup(node_uuid),
                                       remove_conflicting_node_callback);
 
             node_state_xml = create_xml_node(NULL, XML_CIB_TAG_STATE);
             crm_xml_add(node_state_xml, XML_ATTR_ID, node_uuid);
             crm_xml_add(node_state_xml, XML_ATTR_UNAME, node_uname);
 
             delete_call_id = fsa_cib_conn->cmds->remove(fsa_cib_conn, XML_CIB_TAG_STATUS, node_state_xml,
                                                         cib_scope_local | cib_quorum_override);
             fsa_register_cib_callback(delete_call_id, FALSE, strdup(node_uuid),
                                       remove_conflicting_node_callback);
             free_xml(node_state_xml);
         }
     }
 }
 
 static void
 node_list_update_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
 {
     fsa_data_t *msg_data = NULL;
 
     if(call_id < pcmk_ok) {
         crm_err("Node list update failed: %s (%d)", pcmk_strerror(call_id), call_id);
         crm_log_xml_debug(msg, "update:failed");
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
 
     } else if(rc < pcmk_ok) {
         crm_err("Node update %d failed: %s (%d)", call_id, pcmk_strerror(rc), rc);
         crm_log_xml_debug(msg, "update:failed");
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
     }
 }
 
 #define NODE_PATH_MAX 512
 
 void
 populate_cib_nodes(enum node_update_flags flags, const char *source)
 {
     int call_id = 0;
     gboolean from_hashtable = TRUE;
     int call_options = cib_scope_local | cib_quorum_override;
     xmlNode *node_list = create_xml_node(NULL, XML_CIB_TAG_NODES);
 
 #if SUPPORT_COROSYNC
     if (!pcmk_is_set(flags, node_update_quick) && is_corosync_cluster()) {
-        from_hashtable = corosync_initialize_nodelist(node_list);
+        from_hashtable = pcmk__corosync_add_nodes(node_list);
     }
 #endif
 
     if (from_hashtable) {
         GHashTableIter iter;
         crm_node_t *node = NULL;
 
         g_hash_table_iter_init(&iter, crm_peer_cache);
         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
             xmlNode *new_node = NULL;
 
             crm_trace("Creating node entry for %s/%s", node->uname, node->uuid);
             if(node->uuid && node->uname) {
                 char xpath[NODE_PATH_MAX];
 
                 /* We need both to be valid */
                 new_node = create_xml_node(node_list, XML_CIB_TAG_NODE);
                 crm_xml_add(new_node, XML_ATTR_ID, node->uuid);
                 crm_xml_add(new_node, XML_ATTR_UNAME, node->uname);
 
                 /* Search and remove unknown nodes with the conflicting uname from CIB */
                 snprintf(xpath, NODE_PATH_MAX,
                          "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_NODES
                          "/" XML_CIB_TAG_NODE "[@uname='%s'][@id!='%s']",
                          node->uname, node->uuid);
 
                 call_id = fsa_cib_conn->cmds->query(fsa_cib_conn, xpath, NULL,
                                                     cib_scope_local | cib_xpath);
                 fsa_register_cib_callback(call_id, FALSE, strdup(node->uuid),
                                           search_conflicting_node_callback);
             }
         }
     }
 
     crm_trace("Populating <nodes> section from %s", from_hashtable ? "hashtable" : "cluster");
 
     fsa_cib_update(XML_CIB_TAG_NODES, node_list, call_options, call_id, NULL);
     fsa_register_cib_callback(call_id, FALSE, NULL, node_list_update_callback);
 
     free_xml(node_list);
 
     if (call_id >= pcmk_ok && crm_peer_cache != NULL && AM_I_DC) {
         /*
          * There is no need to update the local CIB with our values if
          * we've not seen valid membership data
          */
         GHashTableIter iter;
         crm_node_t *node = NULL;
 
         node_list = create_xml_node(NULL, XML_CIB_TAG_STATUS);
 
         g_hash_table_iter_init(&iter, crm_peer_cache);
         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
             create_node_state_update(node, flags, node_list, source);
         }
 
         if (crm_remote_peer_cache) {
             g_hash_table_iter_init(&iter, crm_remote_peer_cache);
             while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
                 create_node_state_update(node, flags, node_list, source);
             }
         }
 
         fsa_cib_update(XML_CIB_TAG_STATUS, node_list, call_options, call_id, NULL);
         fsa_register_cib_callback(call_id, FALSE, NULL, crmd_node_update_complete);
         last_peer_update = call_id;
 
         free_xml(node_list);
     }
 }
 
 static void
 cib_quorum_update_complete(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
 {
     fsa_data_t *msg_data = NULL;
 
     if (rc == pcmk_ok) {
         crm_trace("Quorum update %d complete", call_id);
 
     } else {
         crm_err("Quorum update %d failed: %s (%d)", call_id, pcmk_strerror(rc), rc);
         crm_log_xml_debug(msg, "failed");
         register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
     }
 }
 
 void
 crm_update_quorum(gboolean quorum, gboolean force_update)
 {
     ever_had_quorum |= quorum;
 
     if(ever_had_quorum && quorum == FALSE && no_quorum_suicide_escalation) {
         pcmk__panic(__func__);
     }
 
     if (AM_I_DC && (force_update || fsa_has_quorum != quorum)) {
         int call_id = 0;
         xmlNode *update = NULL;
         int call_options = cib_scope_local | cib_quorum_override;
 
         update = create_xml_node(NULL, XML_TAG_CIB);
         crm_xml_add_int(update, XML_ATTR_HAVE_QUORUM, quorum);
         crm_xml_add(update, XML_ATTR_DC_UUID, fsa_our_uuid);
 
         fsa_cib_update(XML_TAG_CIB, update, call_options, call_id, NULL);
         crm_debug("Updating quorum status to %s (call=%d)",
                   pcmk__btoa(quorum), call_id);
         fsa_register_cib_callback(call_id, FALSE, NULL, cib_quorum_update_complete);
         free_xml(update);
 
         /* Quorum changes usually cause a new transition via other activity:
          * quorum gained via a node joining will abort via the node join,
          * and quorum lost via a node leaving will usually abort via resource
          * activity and/or fencing.
          *
          * However, it is possible that nothing else causes a transition (e.g.
          * someone forces quorum via corosync-cmaptcl, or quorum is lost due to
          * a node in standby shutting down cleanly), so here ensure a new
          * transition is triggered.
          */
         if (quorum) {
             /* If quorum was gained, abort after a short delay, in case multiple
              * nodes are joining around the same time, so the one that brings us
              * to quorum doesn't cause all the remaining ones to be fenced.
              */
             abort_after_delay(INFINITY, tg_restart, "Quorum gained", 5000);
         } else {
             abort_transition(INFINITY, tg_restart, "Quorum lost", NULL);
         }
     }
     fsa_has_quorum = quorum;
 }
diff --git a/include/crm/cluster/internal.h b/include/crm/cluster/internal.h
index 9829c49e9f..8321dfd0f2 100644
--- a/include/crm/cluster/internal.h
+++ b/include/crm/cluster/internal.h
@@ -1,351 +1,351 @@
 /*
  * Copyright 2004-2020 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 CRM_CLUSTER_INTERNAL__H
 #  define CRM_CLUSTER_INTERNAL__H
 
 #  include <crm/cluster.h>
 
 #define pcmk__set_peer_flags(peer, flags_to_set) do {                         \
         (peer)->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,     \
                                            "Peer", (peer)->uname,             \
                                            (peer)->flags, (flags_to_set),     \
                                            #flags_to_set);                    \
     } while (0)
 
 #define pcmk__clear_peer_flags(peer, flags_to_clear) do {                     \
         (peer)->flags = pcmk__clear_flags_as(__func__, __LINE__,              \
                                              LOG_TRACE,                       \
                                              "Peer", (peer)->uname,           \
                                              (peer)->flags, (flags_to_clear), \
                                              #flags_to_clear);                \
     } while (0)
 
 typedef struct crm_ais_host_s AIS_Host;
 typedef struct crm_ais_msg_s AIS_Message;
 
 struct crm_ais_host_s {
     uint32_t id;
     uint32_t pid;
     gboolean local;
     enum crm_ais_msg_types type;
     uint32_t size;
     char uname[MAX_NAME];
 
 } __attribute__ ((packed));
 
 #if SUPPORT_COROSYNC
 #  include <qb/qbipc_common.h>
 #  include <corosync/corotypes.h>
 typedef struct qb_ipc_response_header cs_ipc_header_response_t;
 #else
 typedef struct {
     int size __attribute__ ((aligned(8)));
     int id __attribute__ ((aligned(8)));
     int error __attribute__ ((aligned(8)));
 } __attribute__ ((aligned(8))) cs_ipc_header_response_t;
 #endif
 
 struct crm_ais_msg_s {
     cs_ipc_header_response_t header __attribute__ ((aligned(8)));
     uint32_t id;
     gboolean is_compressed;
 
     AIS_Host host;
     AIS_Host sender;
 
     uint32_t size;
     uint32_t compressed_size;
     /* 584 bytes */
     char data[0];
 
 } __attribute__ ((packed));
 
 /* *INDENT-OFF* */
 enum crm_proc_flag {
     crm_proc_none       = 0x00000001,
 
     // Cluster layers
     crm_proc_cpg        = 0x04000000,
 
     // Daemons
     crm_proc_execd      = 0x00000010,
     crm_proc_based      = 0x00000100,
     crm_proc_controld   = 0x00000200,
     crm_proc_attrd      = 0x00001000,
     crm_proc_schedulerd = 0x00010000,
     crm_proc_fenced     = 0x00100000,
 };
 /* *INDENT-ON* */
 
 /*!
  * \internal
  * \brief Return the process bit corresponding to the current cluster stack
  *
  * \return Process flag if detectable, otherwise 0
  */
 static inline uint32_t
 crm_get_cluster_proc(void)
 {
     switch (get_cluster_type()) {
         case pcmk_cluster_corosync:
             return crm_proc_cpg;
 
         default:
             break;
     }
     return crm_proc_none;
 }
 
 static inline const char *
 peer2text(enum crm_proc_flag proc)
 {
     const char *text = "unknown";
 
     switch (proc) {
         case crm_proc_none:
             text = "none";
             break;
         case crm_proc_based:
             text = "pacemaker-based";
             break;
         case crm_proc_controld:
             text = "pacemaker-controld";
             break;
         case crm_proc_schedulerd:
             text = "pacemaker-schedulerd";
             break;
         case crm_proc_execd:
             text = "pacemaker-execd";
             break;
         case crm_proc_attrd:
             text = "pacemaker-attrd";
             break;
         case crm_proc_fenced:
             text = "pacemaker-fenced";
             break;
         case crm_proc_cpg:
             text = "corosync-cpg";
             break;
     }
     return text;
 }
 
 static inline const char *
 ais_dest(const AIS_Host *host)
 {
     if (host->local) {
         return "local";
     } else if (host->size > 0) {
         return host->uname;
     } else {
         return "<all>";
     }
 }
 
 #  define ais_data_len(msg) (msg->is_compressed?msg->compressed_size:msg->size)
 
 /*
 typedef enum {
    CS_OK = 1,
    CS_ERR_LIBRARY = 2,
    CS_ERR_VERSION = 3,
    CS_ERR_INIT = 4,
    CS_ERR_TIMEOUT = 5,
    CS_ERR_TRY_AGAIN = 6,
    CS_ERR_INVALID_PARAM = 7,
    CS_ERR_NO_MEMORY = 8,
    CS_ERR_BAD_HANDLE = 9,
    CS_ERR_BUSY = 10,
    CS_ERR_ACCESS = 11,
    CS_ERR_NOT_EXIST = 12,
    CS_ERR_NAME_TOO_LONG = 13,
    CS_ERR_EXIST = 14,
    CS_ERR_NO_SPACE = 15,
    CS_ERR_INTERRUPT = 16,
    CS_ERR_NAME_NOT_FOUND = 17,
    CS_ERR_NO_RESOURCES = 18,
    CS_ERR_NOT_SUPPORTED = 19,
    CS_ERR_BAD_OPERATION = 20,
    CS_ERR_FAILED_OPERATION = 21,
    CS_ERR_MESSAGE_ERROR = 22,
    CS_ERR_QUEUE_FULL = 23,
    CS_ERR_QUEUE_NOT_AVAILABLE = 24,
    CS_ERR_BAD_FLAGS = 25,
    CS_ERR_TOO_BIG = 26,
    CS_ERR_NO_SECTIONS = 27,
    CS_ERR_CONTEXT_NOT_FOUND = 28,
    CS_ERR_TOO_MANY_GROUPS = 30,
    CS_ERR_SECURITY = 100
 } cs_error_t;
  */
 static inline const char *
 ais_error2text(int error)
 {
     const char *text = "unknown";
 
 #  if SUPPORT_COROSYNC
     switch (error) {
         case CS_OK:
             text = "OK";
             break;
         case CS_ERR_LIBRARY:
             text = "Library error";
             break;
         case CS_ERR_VERSION:
             text = "Version error";
             break;
         case CS_ERR_INIT:
             text = "Initialization error";
             break;
         case CS_ERR_TIMEOUT:
             text = "Timeout";
             break;
         case CS_ERR_TRY_AGAIN:
             text = "Try again";
             break;
         case CS_ERR_INVALID_PARAM:
             text = "Invalid parameter";
             break;
         case CS_ERR_NO_MEMORY:
             text = "No memory";
             break;
         case CS_ERR_BAD_HANDLE:
             text = "Bad handle";
             break;
         case CS_ERR_BUSY:
             text = "Busy";
             break;
         case CS_ERR_ACCESS:
             text = "Access error";
             break;
         case CS_ERR_NOT_EXIST:
             text = "Doesn't exist";
             break;
         case CS_ERR_NAME_TOO_LONG:
             text = "Name too long";
             break;
         case CS_ERR_EXIST:
             text = "Exists";
             break;
         case CS_ERR_NO_SPACE:
             text = "No space";
             break;
         case CS_ERR_INTERRUPT:
             text = "Interrupt";
             break;
         case CS_ERR_NAME_NOT_FOUND:
             text = "Name not found";
             break;
         case CS_ERR_NO_RESOURCES:
             text = "No resources";
             break;
         case CS_ERR_NOT_SUPPORTED:
             text = "Not supported";
             break;
         case CS_ERR_BAD_OPERATION:
             text = "Bad operation";
             break;
         case CS_ERR_FAILED_OPERATION:
             text = "Failed operation";
             break;
         case CS_ERR_MESSAGE_ERROR:
             text = "Message error";
             break;
         case CS_ERR_QUEUE_FULL:
             text = "Queue full";
             break;
         case CS_ERR_QUEUE_NOT_AVAILABLE:
             text = "Queue not available";
             break;
         case CS_ERR_BAD_FLAGS:
             text = "Bad flags";
             break;
         case CS_ERR_TOO_BIG:
             text = "Too big";
             break;
         case CS_ERR_NO_SECTIONS:
             text = "No sections";
             break;
     }
 #  endif
     return text;
 }
 
 static inline const char *
 msg_type2text(enum crm_ais_msg_types type)
 {
     const char *text = "unknown";
 
     switch (type) {
         case crm_msg_none:
             text = "unknown";
             break;
         case crm_msg_ais:
             text = "ais";
             break;
         case crm_msg_cib:
             text = "cib";
             break;
         case crm_msg_crmd:
             text = "crmd";
             break;
         case crm_msg_pe:
             text = "pengine";
             break;
         case crm_msg_te:
             text = "tengine";
             break;
         case crm_msg_lrmd:
             text = "lrmd";
             break;
         case crm_msg_attrd:
             text = "attrd";
             break;
         case crm_msg_stonithd:
             text = "stonithd";
             break;
         case crm_msg_stonith_ng:
             text = "stonith-ng";
             break;
     }
     return text;
 }
 
 #  if SUPPORT_COROSYNC
 
 gboolean send_cpg_iov(struct iovec * iov);
 
-char *corosync_cluster_name(void);
+char *pcmk__corosync_cluster_name(void);
 
-gboolean corosync_initialize_nodelist(xmlNode *xml_parent);
+bool pcmk__corosync_add_nodes(xmlNode *xml_parent);
 
 gboolean send_cluster_message_cs(xmlNode * msg, gboolean local,
                                  crm_node_t * node, enum crm_ais_msg_types dest);
 #  endif
 
 crm_node_t *crm_update_peer_proc(const char *source, crm_node_t * peer,
                                  uint32_t flag, const char *status);
 crm_node_t *crm_update_peer_state(const char *source, crm_node_t * node,
                                   const char *state, uint64_t membership);
 
 void crm_update_peer_uname(crm_node_t *node, const char *uname);
 void crm_update_peer_expected(const char *source, crm_node_t * node, const char *expected);
 void crm_reap_unseen_nodes(uint64_t ring_id);
 
-gboolean cluster_connect_quorum(gboolean(*dispatch) (unsigned long long, gboolean),
-                                void (*destroy) (gpointer));
-
+void pcmk__corosync_quorum_connect(gboolean (*dispatch)(unsigned long long,
+                                                        gboolean),
+                                   void (*destroy) (gpointer));
 crm_node_t * crm_find_peer_full(unsigned int id, const char *uname, int flags);
 crm_node_t * crm_find_peer(unsigned int id, const char *uname);
 
 void crm_peer_caches_refresh(xmlNode *cib);
 crm_node_t *crm_find_known_peer_full(unsigned int id, const char *uname, int flags);
 
 #endif
diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c
index d51a2ce137..453cd8d99d 100644
--- a/lib/cluster/corosync.c
+++ b/lib/cluster/corosync.c
@@ -1,713 +1,798 @@
 /*
  * Copyright 2004-2020 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 <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <inttypes.h>  /* U64T ~ PRIu64 */
 
 #include <bzlib.h>
 
 #include <crm/common/ipc.h>
 #include <crm/cluster/internal.h>
 #include <crm/common/mainloop.h>
 #include <sys/utsname.h>
 
 #include <qb/qbipcc.h>
 #include <qb/qbutil.h>
 
 #include <corosync/corodefs.h>
 #include <corosync/corotypes.h>
 #include <corosync/hdb.h>
 #include <corosync/cfg.h>
 #include <corosync/cmap.h>
 #include <corosync/quorum.h>
 
 #include <crm/msg_xml.h>
 
 #include <crm/common/ipc_internal.h>  /* PCMK__SPECIAL_PID* */
 #include "crmcluster_private.h"
 
-quorum_handle_t pcmk_quorum_handle = 0;
+static quorum_handle_t pcmk_quorum_handle = 0;
 
-gboolean(*quorum_app_callback) (unsigned long long seq, gboolean quorate) = NULL;
+static gboolean (*quorum_app_callback)(unsigned long long seq,
+                                       gboolean quorate) = NULL;
 
+/*!
+ * \internal
+ * \brief Get the Corosync UUID associated with a Pacemaker node
+ *
+ * \param[in] node  Pacemaker node
+ *
+ * \return Newly allocated string with node's Corosync UUID, or NULL if unknown
+ * \note It is the caller's responsibility to free the result with free().
+ */
 char *
 pcmk__corosync_uuid(crm_node_t *node)
 {
-    if (node && is_corosync_cluster()) {
+    if ((node != NULL) && is_corosync_cluster()) {
         if (node->id > 0) {
             return crm_strdup_printf("%u", node->id);
         } else {
             crm_info("Node %s is not yet known by corosync", node->uname);
         }
     }
     return NULL;
 }
 
 static bool
 node_name_is_valid(const char *key, const char *name)
 {
     int octet;
 
     if (name == NULL) {
         crm_trace("%s is empty", key);
         return false;
 
     } else if (sscanf(name, "%d.%d.%d.%d", &octet, &octet, &octet, &octet) == 4) {
         crm_trace("%s contains an IPv4 address (%s), ignoring", key, name);
         return false;
 
     } else if (strstr(name, ":") != NULL) {
         crm_trace("%s contains an IPv6 address (%s), ignoring", key, name);
         return false;
     }
     crm_trace("'%s: %s' is valid", key, name);
     return true;
 }
 
 /*
- * CFG functionality stolen from node_name() in corosync-quorumtool.c
- * This resolves the first address assigned to a node and returns the name or IP address.
+ * \internal
+ * \brief Get Corosync node name corresponding to a node ID
+ *
+ * \param[in] cmap_handle  Connection to Corosync CMAP
+ * \param[in] nodeid       Node ID to check
+ *
+ * \return Newly allocated string with name or (if no name) IP address
+ *         associated with first address assigned to a Corosync node ID (or NULL
+ *         if unknown)
+ * \note It is the caller's responsibility to free the result with free().
  */
 char *
 pcmk__corosync_name(uint64_t /*cmap_handle_t */ cmap_handle, uint32_t nodeid)
 {
+    // Originally based on corosync-quorumtool.c:node_name()
+
     int lpc = 0;
     cs_error_t rc = CS_OK;
     int retries = 0;
     char *name = NULL;
     cmap_handle_t local_handle = 0;
     int fd = -1;
     uid_t found_uid = 0;
     gid_t found_gid = 0;
     pid_t found_pid = 0;
     int rv;
 
     if (nodeid == 0) {
         nodeid = get_local_nodeid(0);
     }
 
     if (cmap_handle == 0 && local_handle == 0) {
         retries = 0;
         crm_trace("Initializing CMAP connection");
         do {
             rc = cmap_initialize(&local_handle);
             if (rc != CS_OK) {
                 retries++;
                 crm_debug("API connection setup failed: %s.  Retrying in %ds", cs_strerror(rc),
                           retries);
                 sleep(retries);
             }
 
         } while (retries < 5 && rc != CS_OK);
 
         if (rc != CS_OK) {
             crm_warn("Could not connect to Cluster Configuration Database API, error %s",
                      cs_strerror(rc));
             local_handle = 0;
         }
     }
 
     if (cmap_handle == 0) {
         cmap_handle = local_handle;
 
         rc = cmap_fd_get(cmap_handle, &fd);
         if (rc != CS_OK) {
             crm_err("Could not obtain the CMAP API connection: %s (%d)",
                     cs_strerror(rc), rc);
             goto bail;
         }
 
         /* CMAP provider run as root (in given user namespace, anyway)? */
         if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
                                                 &found_uid, &found_gid))) {
             crm_err("CMAP provider is not authentic:"
                     " process %lld (uid: %lld, gid: %lld)",
                     (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
                     (long long) found_uid, (long long) found_gid);
             goto bail;
         } else if (rv < 0) {
             crm_err("Could not verify authenticity of CMAP provider: %s (%d)",
                     strerror(-rv), -rv);
             goto bail;
         }
     }
 
     while (name == NULL && cmap_handle != 0) {
         uint32_t id = 0;
         char *key = NULL;
 
         key = crm_strdup_printf("nodelist.node.%d.nodeid", lpc);
         rc = cmap_get_uint32(cmap_handle, key, &id);
         crm_trace("Checking %u vs %u from %s", nodeid, id, key);
         free(key);
 
         if (rc != CS_OK) {
             break;
         }
 
         if (nodeid == id) {
-            crm_trace("Searching for node name for %u in nodelist.node.%d %s", nodeid, lpc, name);
+            crm_trace("Searching for node name for %u in nodelist.node.%d %s",
+                      nodeid, lpc, crm_str(name));
             if (name == NULL) {
                 key = crm_strdup_printf("nodelist.node.%d.name", lpc);
                 cmap_get_string(cmap_handle, key, &name);
-                crm_trace("%s = %s", key, name);
+                crm_trace("%s = %s", key, crm_str(name));
                 free(key);
             }
             if (name == NULL) {
                 key = crm_strdup_printf("nodelist.node.%d.ring0_addr", lpc);
                 cmap_get_string(cmap_handle, key, &name);
-                crm_trace("%s = %s", key, name);
+                crm_trace("%s = %s", key, crm_str(name));
 
                 if (!node_name_is_valid(key, name)) {
                     free(name);
                     name = NULL;
                 }
                 free(key);
             }
             break;
         }
 
         lpc++;
     }
 
 bail:
     if(local_handle) {
         cmap_finalize(local_handle);
     }
 
     if (name == NULL) {
         crm_info("Unable to get node name for nodeid %u", nodeid);
     }
     return name;
 }
 
+/*!
+ * \internal
+ * \brief Disconnect from Corosync cluster
+ *
+ * \param[in] cluster  Cluster connection to disconnect
+ */
 void
 pcmk__corosync_disconnect(crm_cluster_t *cluster)
 {
     cluster_disconnect_cpg(cluster);
     if (pcmk_quorum_handle) {
         quorum_finalize(pcmk_quorum_handle);
         pcmk_quorum_handle = 0;
     }
     crm_notice("Disconnected from Corosync");
 }
 
+/*!
+ * \internal
+ * \brief Dispatch function for quorum connection file descriptor
+ *
+ * \param[in] user_data  Ignored
+ *
+ * \return 0 on success, -1 on error (per mainloop_io_t interface)
+ */
 static int
-pcmk_quorum_dispatch(gpointer user_data)
+quorum_dispatch_cb(gpointer user_data)
 {
-    int rc = 0;
+    int rc = quorum_dispatch(pcmk_quorum_handle, CS_DISPATCH_ALL);
 
-    rc = quorum_dispatch(pcmk_quorum_handle, CS_DISPATCH_ALL);
     if (rc < 0) {
         crm_err("Connection to the Quorum API failed: %d", rc);
         quorum_finalize(pcmk_quorum_handle);
         pcmk_quorum_handle = 0;
         return -1;
     }
     return 0;
 }
 
+/*!
+ * \internal
+ * \brief Notification callback for Corosync quorum connection
+ *
+ * \param[in] handle             Corosync quorum connection
+ * \param[in] quorate            Whether cluster is quorate
+ * \param[in] ring_id            Corosync ring ID
+ * \param[in] view_list_entries  Number of entries in \p view_list
+ * \param[in] view_list          Corosync node IDs in membership
+ */
 static void
-pcmk_quorum_notification(quorum_handle_t handle,
-                         uint32_t quorate,
-                         uint64_t ring_id, uint32_t view_list_entries, uint32_t * view_list)
+quorum_notification_cb(quorum_handle_t handle, uint32_t quorate,
+                       uint64_t ring_id, uint32_t view_list_entries,
+                       uint32_t *view_list)
 {
     int i;
     GHashTableIter iter;
     crm_node_t *node = NULL;
     static gboolean init_phase = TRUE;
 
     if (quorate != crm_have_quorum) {
         if (quorate) {
             crm_notice("Quorum acquired " CRM_XS " membership=%" U64T " members=%lu",
                        ring_id, (long unsigned int)view_list_entries);
         } else {
             crm_warn("Quorum lost " CRM_XS " membership=%" U64T " members=%lu",
                      ring_id, (long unsigned int)view_list_entries);
         }
         crm_have_quorum = quorate;
 
     } else {
         crm_info("Quorum %s " CRM_XS " membership=%" U64T " members=%lu",
                  (quorate? "retained" : "still lost"), ring_id,
                  (long unsigned int)view_list_entries);
     }
 
     if (view_list_entries == 0 && init_phase) {
         crm_info("Corosync membership is still forming, ignoring");
         return;
     }
 
     init_phase = FALSE;
 
     /* Reset last_seen for all cached nodes so we can tell which ones aren't
      * in the view list */
     g_hash_table_iter_init(&iter, crm_peer_cache);
     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
         node->last_seen = 0;
     }
 
     /* Update the peer cache for each node in view list */
     for (i = 0; i < view_list_entries; i++) {
         uint32_t id = view_list[i];
 
         crm_debug("Member[%d] %u ", i, id);
 
         /* Get this node's peer cache entry (adding one if not already there) */
         node = crm_get_peer(id, NULL);
         if (node->uname == NULL) {
             char *name = pcmk__corosync_name(0, id);
 
             crm_info("Obtaining name for new node %u", id);
             node = crm_get_peer(id, name);
             free(name);
         }
 
         /* Update the node state (including updating last_seen to ring_id) */
         crm_update_peer_state(__func__, node, CRM_NODE_MEMBER, ring_id);
     }
 
     /* Remove any peer cache entries we didn't update */
     crm_reap_unseen_nodes(ring_id);
 
     if (quorum_app_callback) {
         quorum_app_callback(ring_id, quorate);
     }
 }
 
 quorum_callbacks_t quorum_callbacks = {
-    .quorum_notify_fn = pcmk_quorum_notification,
+    .quorum_notify_fn = quorum_notification_cb,
 };
 
-gboolean
-cluster_connect_quorum(gboolean(*dispatch) (unsigned long long, gboolean),
-                       void (*destroy) (gpointer))
+/*!
+ * \internal
+ * \brief Connect to Corosync quorum service
+ *
+ * \param[in] dispatch   Connection dispatch callback
+ * \param[in] destroy    Connection destroy callback
+ */
+void
+pcmk__corosync_quorum_connect(gboolean (*dispatch)(unsigned long long,
+                                                   gboolean),
+                              void (*destroy)(gpointer))
 {
     cs_error_t rc;
     int fd = 0;
     int quorate = 0;
     uint32_t quorum_type = 0;
     struct mainloop_fd_callbacks quorum_fd_callbacks;
     uid_t found_uid = 0;
     gid_t found_gid = 0;
     pid_t found_pid = 0;
     int rv;
 
-    quorum_fd_callbacks.dispatch = pcmk_quorum_dispatch;
+    quorum_fd_callbacks.dispatch = quorum_dispatch_cb;
     quorum_fd_callbacks.destroy = destroy;
 
     crm_debug("Configuring Pacemaker to obtain quorum from Corosync");
 
     rc = quorum_initialize(&pcmk_quorum_handle, &quorum_callbacks, &quorum_type);
     if (rc != CS_OK) {
         crm_err("Could not connect to the Quorum API: %s (%d)",
                 cs_strerror(rc), rc);
         goto bail;
 
     } else if (quorum_type != QUORUM_SET) {
         crm_err("Corosync quorum is not configured");
         goto bail;
     }
 
     rc = quorum_fd_get(pcmk_quorum_handle, &fd);
     if (rc != CS_OK) {
         crm_err("Could not obtain the Quorum API connection: %s (%d)",
                 strerror(rc), rc);
         goto bail;
     }
 
     /* Quorum provider run as root (in given user namespace, anyway)? */
     if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
                                             &found_uid, &found_gid))) {
         crm_err("Quorum provider is not authentic:"
                 " process %lld (uid: %lld, gid: %lld)",
                 (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
                 (long long) found_uid, (long long) found_gid);
         rc = CS_ERR_ACCESS;
         goto bail;
     } else if (rv < 0) {
         crm_err("Could not verify authenticity of Quorum provider: %s (%d)",
                 strerror(-rv), -rv);
         rc = CS_ERR_ACCESS;
         goto bail;
     }
 
     rc = quorum_getquorate(pcmk_quorum_handle, &quorate);
     if (rc != CS_OK) {
         crm_err("Could not obtain the current Quorum API state: %d", rc);
         goto bail;
     }
 
     if (quorate) {
         crm_notice("Quorum acquired");
     } else {
         crm_warn("Quorum lost");
     }
     quorum_app_callback = dispatch;
     crm_have_quorum = quorate;
 
     rc = quorum_trackstart(pcmk_quorum_handle, CS_TRACK_CHANGES | CS_TRACK_CURRENT);
     if (rc != CS_OK) {
         crm_err("Could not setup Quorum API notifications: %d", rc);
         goto bail;
     }
 
     mainloop_add_fd("quorum", G_PRIORITY_HIGH, fd, dispatch, &quorum_fd_callbacks);
 
-    corosync_initialize_nodelist(NULL);
+    pcmk__corosync_add_nodes(NULL);
 
   bail:
     if (rc != CS_OK) {
         quorum_finalize(pcmk_quorum_handle);
-        return FALSE;
     }
-    return TRUE;
 }
 
+/*!
+ * \internal
+ * \brief Connect to Corosync cluster layer
+ *
+ * \param[in] cluster   Initialized cluster object to connect
+ */
 gboolean
-pcmk__corosync_connect(crm_cluster_t * cluster)
+pcmk__corosync_connect(crm_cluster_t *cluster)
 {
     crm_node_t *peer = NULL;
     enum cluster_type_e stack = get_cluster_type();
 
     crm_peer_init();
 
     if (stack != pcmk_cluster_corosync) {
         crm_err("Invalid cluster type: %s " CRM_XS " stack=%d",
                 name_for_cluster_type(stack), stack);
         return FALSE;
     }
 
     if (!cluster_connect_cpg(cluster)) {
         // Error message was logged by cluster_connect_cpg()
         return FALSE;
     }
     crm_info("Connection to %s established", name_for_cluster_type(stack));
 
     cluster->nodeid = get_local_nodeid(0);
     if (cluster->nodeid == 0) {
         crm_err("Could not determine local node ID");
         return FALSE;
     }
 
     cluster->uname = get_node_name(0);
     if (cluster->uname == NULL) {
         crm_err("Could not determine local node name");
         return FALSE;
     }
 
     // Ensure local node always exists in peer cache
     peer = crm_get_peer(cluster->nodeid, cluster->uname);
     cluster->uuid = pcmk__corosync_uuid(peer);
 
     return TRUE;
 }
 
+/*!
+ * \internal
+ * \brief Check whether a Corosync cluster is active
+ *
+ * \return pcmk_cluster_corosync if Corosync is found, else pcmk_cluster_unknown
+ */
 enum cluster_type_e
 pcmk__corosync_detect(void)
 {
     int rc = CS_OK;
     cmap_handle_t handle;
 
     rc = cmap_initialize(&handle);
 
     switch(rc) {
         case CS_OK:
             break;
         case CS_ERR_SECURITY:
             crm_debug("Failed to initialize the cmap API: Permission denied (%d)", rc);
             /* It's there, we just can't talk to it.
              * Good enough for us to identify as 'corosync'
              */
             return pcmk_cluster_corosync;
 
         default:
             crm_info("Failed to initialize the cmap API: %s (%d)",
                      ais_error2text(rc), rc);
             return pcmk_cluster_unknown;
     }
 
     cmap_finalize(handle);
     return pcmk_cluster_corosync;
 }
 
+/*!
+ * \brief Check whether a Corosync cluster peer is active
+ *
+ * \param[in] node  Node to check
+ *
+ * \return TRUE if \p node is an active Corosync peer, otherwise FALSE
+ */
 gboolean
-crm_is_corosync_peer_active(const crm_node_t * node)
+crm_is_corosync_peer_active(const crm_node_t *node)
 {
     if (node == NULL) {
-        crm_trace("NULL");
+        crm_trace("Corosync peer inactive: NULL");
         return FALSE;
 
     } else if (!pcmk__str_eq(node->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
-        crm_trace("%s: state=%s", node->uname, node->state);
+        crm_trace("Corosync peer %s inactive: state=%s",
+                  node->uname, node->state);
         return FALSE;
 
-    } else if ((node->processes & crm_proc_cpg) == 0) {
-        crm_trace("%s: processes=%.16x", node->uname, node->processes);
+    } else if (!pcmk_is_set(node->processes, crm_proc_cpg)) {
+        crm_trace("Corosync peer %s inactive: processes=%.16x",
+                  node->uname, node->processes);
         return FALSE;
     }
     return TRUE;
 }
 
-gboolean
-corosync_initialize_nodelist(xmlNode *xml_parent)
+/*!
+ * \internal
+ * \brief Load Corosync node list (via CMAP) into peer cache and optionally XML
+ *
+ * \param[in] xml_parent  If not NULL, add a <node> entry to this for each node
+ *
+ * \return true if any nodes were found, false otherwise
+ */
+bool
+pcmk__corosync_add_nodes(xmlNode *xml_parent)
 {
     int lpc = 0;
     cs_error_t rc = CS_OK;
     int retries = 0;
-    gboolean any = FALSE;
+    bool any = false;
     cmap_handle_t cmap_handle;
     int fd = -1;
     uid_t found_uid = 0;
     gid_t found_gid = 0;
     pid_t found_pid = 0;
     int rv;
 
     do {
         rc = cmap_initialize(&cmap_handle);
         if (rc != CS_OK) {
             retries++;
             crm_debug("API connection setup failed: %s.  Retrying in %ds", cs_strerror(rc),
                       retries);
             sleep(retries);
         }
 
     } while (retries < 5 && rc != CS_OK);
 
     if (rc != CS_OK) {
         crm_warn("Could not connect to Cluster Configuration Database API, error %d", rc);
-        return FALSE;
+        return false;
     }
 
     rc = cmap_fd_get(cmap_handle, &fd);
     if (rc != CS_OK) {
         crm_err("Could not obtain the CMAP API connection: %s (%d)",
                 cs_strerror(rc), rc);
         goto bail;
     }
 
     /* CMAP provider run as root (in given user namespace, anyway)? */
     if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
                                             &found_uid, &found_gid))) {
         crm_err("CMAP provider is not authentic:"
                 " process %lld (uid: %lld, gid: %lld)",
                 (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
                 (long long) found_uid, (long long) found_gid);
         goto bail;
     } else if (rv < 0) {
         crm_err("Could not verify authenticity of CMAP provider: %s (%d)",
                 strerror(-rv), -rv);
         goto bail;
     }
 
     crm_peer_init();
     crm_trace("Initializing corosync nodelist");
     for (lpc = 0; TRUE; lpc++) {
         uint32_t nodeid = 0;
         char *name = NULL;
         char *key = NULL;
 
         key = crm_strdup_printf("nodelist.node.%d.nodeid", lpc);
         rc = cmap_get_uint32(cmap_handle, key, &nodeid);
         free(key);
 
         if (rc != CS_OK) {
             break;
         }
 
         name = pcmk__corosync_name(cmap_handle, nodeid);
         if (name != NULL) {
             GHashTableIter iter;
             crm_node_t *node = NULL;
 
             g_hash_table_iter_init(&iter, crm_peer_cache);
             while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
                 if(node && node->uname && strcasecmp(node->uname, name) == 0) {
                     if (node->id && node->id != nodeid) {
                         crm_crit("Nodes %u and %u share the same name '%s': shutting down", node->id,
                                  nodeid, name);
                         crm_exit(CRM_EX_FATAL);
                     }
                 }
             }
         }
 
         if (nodeid > 0 || name != NULL) {
             crm_trace("Initializing node[%d] %u = %s", lpc, nodeid, name);
             crm_get_peer(nodeid, name);
         }
 
         if (nodeid > 0 && name != NULL) {
-            any = TRUE;
+            any = true;
 
             if (xml_parent) {
                 xmlNode *node = create_xml_node(xml_parent, XML_CIB_TAG_NODE);
 
                 crm_xml_set_id(node, "%u", nodeid);
                 crm_xml_add(node, XML_ATTR_UNAME, name);
             }
         }
 
         free(name);
     }
 bail:
     cmap_finalize(cmap_handle);
     return any;
 }
 
+/*!
+ * \internal
+ * \brief Get cluster name from Corosync configuration (via CMAP)
+ *
+ * \return Newly allocated string with cluster name if configured, or NULL
+ */
 char *
-corosync_cluster_name(void)
+pcmk__corosync_cluster_name(void)
 {
     cmap_handle_t handle;
     char *cluster_name = NULL;
     cs_error_t rc = CS_OK;
     int fd = -1;
     uid_t found_uid = 0;
     gid_t found_gid = 0;
     pid_t found_pid = 0;
     int rv;
 
     rc = cmap_initialize(&handle);
     if (rc != CS_OK) {
         crm_info("Failed to initialize the cmap API: %s (%d)",
                  cs_strerror(rc), rc);
         return NULL;
     }
 
     rc = cmap_fd_get(handle, &fd);
     if (rc != CS_OK) {
         crm_err("Could not obtain the CMAP API connection: %s (%d)",
                 cs_strerror(rc), rc);
         goto bail;
     }
 
     /* CMAP provider run as root (in given user namespace, anyway)? */
     if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
                                             &found_uid, &found_gid))) {
         crm_err("CMAP provider is not authentic:"
                 " process %lld (uid: %lld, gid: %lld)",
                 (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
                 (long long) found_uid, (long long) found_gid);
         goto bail;
     } else if (rv < 0) {
         crm_err("Could not verify authenticity of CMAP provider: %s (%d)",
                 strerror(-rv), -rv);
         goto bail;
     }
 
     rc = cmap_get_string(handle, "totem.cluster_name", &cluster_name);
     if (rc != CS_OK) {
         crm_info("Cannot get totem.cluster_name: %s (%d)", cs_strerror(rc), rc);
 
     } else {
         crm_debug("cmap totem.cluster_name = '%s'", cluster_name);
     }
 
 bail:
     cmap_finalize(handle);
     return cluster_name;
 }
 
 /*!
  * \internal
  * \brief Check (via CMAP) whether Corosync configuration has a node list
  *
  * \return true if Corosync has node list, otherwise false
  */
 bool
 pcmk__corosync_has_nodelist(void)
 {
     cs_error_t cs_rc = CS_OK;
     int retries = 0;
     cmap_handle_t cmap_handle;
     cmap_iter_handle_t iter_handle;
     char key_name[CMAP_KEYNAME_MAXLEN + 1];
     int fd = -1;
     uid_t found_uid = 0;
     gid_t found_gid = 0;
     pid_t found_pid = 0;
     int rc = pcmk_ok;
 
     static bool got_result = false;
     static bool result = false;
 
     if (got_result) {
         return result;
     }
 
     // Connect to CMAP
     do {
         cs_rc = cmap_initialize(&cmap_handle);
         if (cs_rc != CS_OK) {
             retries++;
             crm_debug("CMAP connection failed: %s (rc=%d, retrying in %ds)",
                       cs_strerror(cs_rc), cs_rc, retries);
             sleep(retries);
         }
     } while ((retries < 5) && (cs_rc != CS_OK));
     if (cs_rc != CS_OK) {
         crm_warn("Assuming Corosync does not have node list: "
                  "CMAP connection failed (%s) " CRM_XS " rc=%d",
                  cs_strerror(cs_rc), cs_rc);
         return false;
     }
 
     // Get CMAP connection file descriptor
     cs_rc = cmap_fd_get(cmap_handle, &fd);
     if (cs_rc != CS_OK) {
         crm_warn("Assuming Corosync does not have node list: "
                  "CMAP unusable (%s) " CRM_XS " rc=%d",
                  cs_strerror(cs_rc), cs_rc);
         goto bail;
     }
 
     // Check whether CMAP connection is authentic (i.e. provided by root)
     rc = crm_ipc_is_authentic_process(fd, (uid_t) 0, (gid_t) 0,
                                       &found_pid, &found_uid, &found_gid);
     if (rc == 0) {
         crm_warn("Assuming Corosync does not have node list: "
                  "CMAP provider is inauthentic "
                  CRM_XS " pid=%lld uid=%lld gid=%lld",
                  (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
                  (long long) found_uid, (long long) found_gid);
         goto bail;
     } else if (rc < 0) {
         crm_warn("Assuming Corosync does not have node list: "
                  "Could not verify CMAP authenticity (%s) " CRM_XS " rc=%d",
                   pcmk_strerror(rc), rc);
         goto bail;
     }
 
     // Check whether nodelist section is presetn
     cs_rc = cmap_iter_init(cmap_handle, "nodelist", &iter_handle);
     if (cs_rc != CS_OK) {
         crm_warn("Assuming Corosync does not have node list: "
                  "CMAP not readable (%s) " CRM_XS " rc=%d",
                  cs_strerror(cs_rc), cs_rc);
         goto bail;
     }
 
     cs_rc = cmap_iter_next(cmap_handle, iter_handle, key_name, NULL, NULL);
     if (cs_rc == CS_OK) {
         result = true;
     }
 
     cmap_iter_finalize(cmap_handle, iter_handle);
     got_result = true;
     crm_debug("Corosync %s node list", (result? "has" : "does not have"));
 
 bail:
     cmap_finalize(cmap_handle);
     return result;
 }