1    	/*
2    	 * Copyright 2004-2026 the Pacemaker project contributors
3    	 *
4    	 * The version control history for this file may have further details.
5    	 *
6    	 * This source code is licensed under the GNU Lesser General Public License
7    	 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <arpa/inet.h>
13   	#include <inttypes.h>                   // PRIu64, etc.
14   	#include <netdb.h>
15   	#include <netinet/in.h>
16   	#include <stdbool.h>
17   	#include <stdint.h>                     // uint32_t, etc.
18   	#include <sys/socket.h>
19   	#include <sys/utsname.h>
20   	
21   	#include <bzlib.h>
22   	#include <corosync/cfg.h>
23   	#include <corosync/cmap.h>
24   	#include <corosync/corodefs.h>
25   	#include <corosync/corotypes.h>
26   	#include <corosync/hdb.h>
27   	#include <corosync/quorum.h>
28   	#include <qb/qbipcc.h>
29   	#include <qb/qbutil.h>
30   	
31   	#include <crm/cluster/internal.h>
32   	#include <crm/common/ipc.h>
33   	#include <crm/common/mainloop.h>
34   	#include <crm/common/xml.h>
35   	
36   	#include "crmcluster_private.h"
37   	
38   	static quorum_handle_t pcmk_quorum_handle = 0;
39   	
40   	static gboolean (*quorum_app_callback)(unsigned long long seq,
41   	                                       gboolean quorate) = NULL;
42   	
43   	/*!
44   	 * \internal
45   	 * \brief Get the Corosync UUID associated with a Pacemaker node
46   	 *
47   	 * \param[in] node  Pacemaker node
48   	 *
49   	 * \return Newly allocated string with node's Corosync UUID, or NULL if unknown
50   	 * \note It is the caller's responsibility to free the result with free().
51   	 */
52   	char *
53   	pcmk__corosync_uuid(const pcmk__node_status_t *node)
54   	{
55   	    pcmk__assert(pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync);
56   	
57   	    if (node != NULL) {
58   	        if (node->cluster_layer_id > 0) {
59   	            return pcmk__assert_asprintf("%" PRIu32, node->cluster_layer_id);
60   	        } else {
61   	            pcmk__info("Node %s is not yet known by Corosync", node->name);
62   	        }
63   	    }
64   	    return NULL;
65   	}
66   	
67   	static bool
68   	node_name_is_valid(const char *key, const char *name)
69   	{
70   	    int octet;
71   	
72   	    if (name == NULL) {
73   	        pcmk__trace("%s is empty", key);
74   	        return false;
75   	
76   	    } else if (sscanf(name, "%d.%d.%d.%d", &octet, &octet, &octet, &octet) == 4) {
77   	        pcmk__trace("%s contains an IPv4 address (%s), ignoring", key, name);
78   	        return false;
79   	
80   	    } else if (strchr(name, ':') != NULL) {
81   	        pcmk__trace("%s contains an IPv6 address (%s), ignoring", key, name);
82   	        return false;
83   	    }
84   	    pcmk__trace("'%s: %s' is valid", key, name);
85   	    return true;
86   	}
87   	
88   	/*
89   	 * \internal
90   	 * \brief Get Corosync node name corresponding to a node ID
91   	 *
92   	 * \param[in] cmap_handle  Connection to Corosync CMAP
93   	 * \param[in] nodeid       Node ID to check
94   	 *
95   	 * \return Newly allocated string with name or (if no name) IP address
96   	 *         associated with first address assigned to a Corosync node ID (or NULL
97   	 *         if unknown)
98   	 * \note It is the caller's responsibility to free the result with free().
99   	 */
100  	char *
101  	pcmk__corosync_name(uint64_t /*cmap_handle_t */ cmap_handle, uint32_t nodeid)
102  	{
103  	    // Originally based on corosync-quorumtool.c:node_name()
104  	
105  	    int lpc = 0;
106  	    cs_error_t rc = CS_OK;
107  	    int retries = 0;
108  	    char *name = NULL;
109  	    cmap_handle_t local_handle = 0;
110  	    int fd = -1;
111  	    uid_t found_uid = 0;
112  	    gid_t found_gid = 0;
113  	    pid_t found_pid = 0;
114  	    int rv;
115  	
(1) Event path: Condition "nodeid == 0", taking true branch.
116  	    if (nodeid == 0) {
117  	        nodeid = pcmk__cpg_local_nodeid(0);
118  	    }
119  	
(2) Event path: Condition "cmap_handle == 0", taking false branch.
120  	    if (cmap_handle == 0 && local_handle == 0) {
121  	        retries = 0;
122  	        pcmk__trace("Initializing CMAP connection");
123  	        do {
124  	            rc = pcmk__init_cmap(&local_handle);
125  	            if (rc != CS_OK) {
126  	                retries++;
127  	                pcmk__debug("API connection setup failed: %s.  Retrying in %ds",
128  	                            pcmk_rc_str(pcmk__corosync2rc(rc)), retries);
129  	                sleep(retries);
130  	            }
131  	
132  	        } while (retries < 5 && rc != CS_OK);
133  	
134  	        if (rc != CS_OK) {
135  	            pcmk__warn("Could not connect to Cluster Configuration Database "
136  	                       "API, error %s",
137  	                       pcmk_rc_str(pcmk__corosync2rc(rc)));
138  	            local_handle = 0;
139  	        }
140  	    }
141  	
(3) Event path: Condition "cmap_handle == 0", taking false branch.
142  	    if (cmap_handle == 0) {
143  	        cmap_handle = local_handle;
144  	
145  	        rc = cmap_fd_get(cmap_handle, &fd);
146  	        if (rc != CS_OK) {
147  	            pcmk__err("Could not obtain the CMAP API connection: %s (%d)",
148  	                      pcmk_rc_str(pcmk__corosync2rc(rc)), rc);
149  	            goto bail;
150  	        }
151  	
152  	        /* CMAP provider run as root (in given user namespace, anyway)? */
153  	        if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
154  	                                                &found_uid, &found_gid))) {
155  	            pcmk__err("CMAP provider is not authentic: process %lld "
156  	                      "(uid: %lld, gid: %lld)",
157  	                      (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
158  	                      (long long) found_uid, (long long) found_gid);
159  	            goto bail;
160  	        } else if (rv < 0) {
161  	            pcmk__err("Could not verify authenticity of CMAP provider: %s (%d)",
162  	                      strerror(-rv), -rv);
163  	            goto bail;
164  	        }
165  	    }
166  	
(4) Event path: Condition "name == NULL", taking true branch.
(5) Event path: Condition "cmap_handle != 0", taking true branch.
167  	    while (name == NULL && cmap_handle != 0) {
168  	        uint32_t id = 0;
169  	        char *key = NULL;
170  	
171  	        key = pcmk__assert_asprintf("nodelist.node.%d.nodeid", lpc);
172  	        rc = cmap_get_uint32(cmap_handle, key, &id);
(6) Event path: Switch case default.
(7) Event path: Condition "trace_cs == NULL", taking true branch.
(8) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(9) Event path: Breaking from switch.
173  	        pcmk__trace("Checking %u vs %u from %s", nodeid, id, key);
174  	        free(key);
175  	
(10) Event path: Condition "rc != CS_OK", taking false branch.
176  	        if (rc != CS_OK) {
177  	            break;
178  	        }
179  	
(11) Event path: Condition "nodeid == id", taking true branch.
180  	        if (nodeid == id) {
(12) Event path: Switch case default.
(13) Event path: Condition "trace_cs == NULL", taking true branch.
(14) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(15) Event path: Breaking from switch.
181  	            pcmk__trace("Searching for node name for %u in nodelist.node.%d %s",
182  	                        nodeid, lpc, pcmk__s(name, "<null>"));
(16) Event path: Condition "name == NULL", taking true branch.
183  	            if (name == NULL) {
184  	                key = pcmk__assert_asprintf("nodelist.node.%d.name", lpc);
185  	                cmap_get_string(cmap_handle, key, &name);
(17) Event path: Switch case default.
(18) Event path: Condition "trace_cs == NULL", taking true branch.
(19) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(20) Event path: Breaking from switch.
186  	                pcmk__trace("%s = %s", key, pcmk__s(name, "<null>"));
187  	                free(key);
188  	            }
(21) Event path: Condition "name == NULL", taking true branch.
189  	            if (name == NULL) {
190  	                key = pcmk__assert_asprintf("nodelist.node.%d.ring0_addr", lpc);
191  	                cmap_get_string(cmap_handle, key, &name);
(22) Event path: Switch case default.
(23) Event path: Condition "trace_cs == NULL", taking true branch.
(24) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(25) Event path: Breaking from switch.
192  	                pcmk__trace("%s = %s", key, pcmk__s(name, "<null>"));
193  	
(26) Event path: Condition "!node_name_is_valid(key, name)", taking true branch.
194  	                if (!node_name_is_valid(key, name)) {
CID (unavailable; MK=41eccb620fec53b7e90838650eed36fa) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(27) Event assign_union_field: The union field "in" of "_pp" is written.
(28) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
195  	                    g_clear_pointer(&name, free);
196  	                }
197  	
198  	                free(key);
199  	            }
200  	            break;
201  	        }
202  	
203  	        lpc++;
204  	    }
205  	
206  	bail:
207  	    if(local_handle) {
208  	        cmap_finalize(local_handle);
209  	    }
210  	
211  	    if (name == NULL) {
212  	        pcmk__info("Unable to get node name for nodeid %u", nodeid);
213  	    }
214  	    return name;
215  	}
216  	
217  	/*!
218  	 * \internal
219  	 * \brief Disconnect from Corosync cluster
220  	 *
221  	 * \param[in,out] cluster  Cluster object to disconnect
222  	 */
223  	void
224  	pcmk__corosync_disconnect(pcmk_cluster_t *cluster)
225  	{
226  	    pcmk__cpg_disconnect(cluster);
227  	
228  	    if (pcmk_quorum_handle != 0) {
229  	        quorum_finalize(pcmk_quorum_handle);
230  	        pcmk_quorum_handle = 0;
231  	    }
232  	    pcmk__notice("Disconnected from Corosync");
233  	}
234  	
235  	/*!
236  	 * \internal
237  	 * \brief Dispatch function for quorum connection file descriptor
238  	 *
239  	 * \param[in] user_data  Ignored
240  	 *
241  	 * \return 0 on success, -1 on error (per mainloop_io_t interface)
242  	 */
243  	static int
244  	quorum_dispatch_cb(gpointer user_data)
245  	{
246  	    int rc = quorum_dispatch(pcmk_quorum_handle, CS_DISPATCH_ALL);
247  	
248  	    if (rc < 0) {
249  	        pcmk__err("Connection to the Quorum API failed: %d", rc);
250  	        quorum_finalize(pcmk_quorum_handle);
251  	        pcmk_quorum_handle = 0;
252  	        return -1;
253  	    }
254  	    return 0;
255  	}
256  	
257  	/*!
258  	 * \internal
259  	 * \brief Notification callback for Corosync quorum connection
260  	 *
261  	 * \param[in] handle             Corosync quorum connection
262  	 * \param[in] quorate            Whether cluster is quorate
263  	 * \param[in] ring_id            Corosync ring ID
264  	 * \param[in] view_list_entries  Number of entries in \p view_list
265  	 * \param[in] view_list          Corosync node IDs in membership
266  	 */
267  	static void
268  	quorum_notification_cb(quorum_handle_t handle, uint32_t quorate,
269  	                       uint64_t ring_id, uint32_t view_list_entries,
270  	                       uint32_t *view_list)
271  	{
272  	    int i;
273  	    GHashTableIter iter;
274  	    pcmk__node_status_t *node = NULL;
275  	    static gboolean init_phase = TRUE;
276  	
277  	    bool is_quorate = (quorate != 0);
278  	    bool was_quorate = pcmk__cluster_has_quorum();
279  	
280  	    if (is_quorate && !was_quorate) {
281  	        pcmk__notice("Quorum acquired " QB_XS " membership=%" PRIu64
282  	                     " members=%" PRIu32,
283  	                     ring_id, view_list_entries);
284  	        pcmk__cluster_set_quorum(true);
285  	
286  	    } else if (!is_quorate && was_quorate) {
287  	        pcmk__warn("Quorum lost " QB_XS " membership=%" PRIu64
288  	                   " members=%" PRIu32,
289  	                   ring_id, view_list_entries);
290  	        pcmk__cluster_set_quorum(false);
291  	
292  	    } else {
293  	        pcmk__info("Quorum %s " QB_XS " membership=%" PRIu64
294  	                   " members=%" PRIu32,
295  	                   (is_quorate? "retained" : "still lost"), ring_id,
296  	                   view_list_entries);
297  	    }
298  	
299  	    if (view_list_entries == 0 && init_phase) {
300  	        pcmk__info("Corosync membership is still forming, ignoring");
301  	        return;
302  	    }
303  	
304  	    init_phase = FALSE;
305  	
306  	    /* Reset membership_id for all cached nodes so we can tell which ones aren't
307  	     * in the view list */
308  	    g_hash_table_iter_init(&iter, pcmk__peer_cache);
309  	    while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
310  	        node->membership_id = 0;
311  	    }
312  	
313  	    /* Update the peer cache for each node in view list */
314  	    for (i = 0; i < view_list_entries; i++) {
315  	        uint32_t id = view_list[i];
316  	
317  	        pcmk__debug("Member[%d] %" PRIu32, i, id);
318  	
319  	        /* Get this node's peer cache entry (adding one if not already there) */
320  	        node = pcmk__get_node(id, NULL, NULL, pcmk__node_search_cluster_member);
321  	        if (node->name == NULL) {
322  	            char *name = pcmk__corosync_name(0, id);
323  	
324  	            pcmk__info("Obtaining name for new node %u", id);
325  	            node = pcmk__get_node(id, name, NULL,
326  	                                  pcmk__node_search_cluster_member);
327  	            free(name);
328  	        }
329  	
330  	        // Update the node state (including updating membership_id to ring_id)
331  	        pcmk__update_peer_state(__func__, node, PCMK_VALUE_MEMBER, ring_id);
332  	    }
333  	
334  	    /* Remove any peer cache entries we didn't update */
335  	    pcmk__reap_unseen_nodes(ring_id);
336  	
337  	    if (quorum_app_callback) {
338  	        quorum_app_callback(ring_id, is_quorate);
339  	    }
340  	}
341  	
342  	/*!
343  	 * \internal
344  	 * \brief Connect to Corosync quorum service
345  	 *
346  	 * \param[in] dispatch   Connection dispatch callback
347  	 * \param[in] destroy    Connection destroy callback
348  	 */
349  	void
350  	pcmk__corosync_quorum_connect(gboolean (*dispatch)(unsigned long long,
351  	                                                   gboolean),
352  	                              void (*destroy)(gpointer))
353  	{
354  	    cs_error_t rc;
355  	    int fd = 0;
356  	    int quorate = 0;
357  	    uint32_t quorum_type = 0;
358  	    struct mainloop_fd_callbacks quorum_fd_callbacks;
359  	    uid_t found_uid = 0;
360  	    gid_t found_gid = 0;
361  	    pid_t found_pid = 0;
362  	    int rv;
363  	
364  	    quorum_fd_callbacks.dispatch = quorum_dispatch_cb;
365  	    quorum_fd_callbacks.destroy = destroy;
366  	
367  	    pcmk__debug("Configuring Pacemaker to obtain quorum from Corosync");
368  	
369  	    {
370  	#if 0
371  	        // New way but not supported by all Corosync 2 versions
372  	        quorum_model_v0_data_t quorum_model_data = {
373  	            .model = QUORUM_MODEL_V0,
374  	            .quorum_notify_fn = quorum_notification_cb,
375  	        };
376  	
377  	        rc = quorum_model_initialize(&pcmk_quorum_handle, QUORUM_MODEL_V0,
378  	                                     (quorum_model_data_t *) &quorum_model_data,
379  	                                     &quorum_type, NULL);
380  	#else
381  	        quorum_callbacks_t quorum_callbacks = {
382  	            .quorum_notify_fn = quorum_notification_cb,
383  	        };
384  	
385  	        rc = quorum_initialize(&pcmk_quorum_handle, &quorum_callbacks,
386  	                               &quorum_type);
387  	#endif
388  	    }
389  	
390  	    if (rc != CS_OK) {
391  	        pcmk__err("Could not connect to the Quorum API: %s (%d)",
392  	                  pcmk_rc_str(pcmk__corosync2rc(rc)), rc);
393  	        goto bail;
394  	
395  	    } else if (quorum_type != QUORUM_SET) {
396  	        pcmk__err("Corosync quorum is not configured");
397  	        goto bail;
398  	    }
399  	
400  	    rc = quorum_fd_get(pcmk_quorum_handle, &fd);
401  	    if (rc != CS_OK) {
402  	        pcmk__err("Could not obtain the Quorum API connection: %s (%d)",
403  	                  strerror(rc), rc);
404  	        goto bail;
405  	    }
406  	
407  	    /* Quorum provider run as root (in given user namespace, anyway)? */
408  	    if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
409  	                                            &found_uid, &found_gid))) {
410  	        pcmk__err("Quorum provider is not authentic: process %lld "
411  	                  "(uid: %lld, gid: %lld)",
412  	                  (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
413  	                  (long long) found_uid, (long long) found_gid);
414  	        rc = CS_ERR_ACCESS;
415  	        goto bail;
416  	    } else if (rv < 0) {
417  	        pcmk__err("Could not verify authenticity of Quorum provider: %s (%d)",
418  	                  strerror(-rv), -rv);
419  	        rc = CS_ERR_ACCESS;
420  	        goto bail;
421  	    }
422  	
423  	    rc = quorum_getquorate(pcmk_quorum_handle, &quorate);
424  	    if (rc != CS_OK) {
425  	        pcmk__err("Could not obtain the current Quorum API state: %d", rc);
426  	        goto bail;
427  	    }
428  	
429  	    if (quorate) {
430  	        pcmk__notice("Quorum acquired");
431  	    } else {
432  	        pcmk__warn("No quorum");
433  	    }
434  	    quorum_app_callback = dispatch;
435  	    pcmk__cluster_set_quorum(quorate != 0);
436  	
437  	    rc = quorum_trackstart(pcmk_quorum_handle, CS_TRACK_CHANGES | CS_TRACK_CURRENT);
438  	    if (rc != CS_OK) {
439  	        pcmk__err("Could not setup Quorum API notifications: %d", rc);
440  	        goto bail;
441  	    }
442  	
443  	    mainloop_add_fd("quorum", G_PRIORITY_HIGH, fd, dispatch, &quorum_fd_callbacks);
444  	
445  	    pcmk__corosync_add_nodes(NULL);
446  	
447  	  bail:
448  	    if (rc != CS_OK) {
449  	        quorum_finalize(pcmk_quorum_handle);
450  	    }
451  	}
452  	
453  	/*!
454  	 * \internal
455  	 * \brief Connect to Corosync cluster layer
456  	 *
457  	 * \param[in,out] cluster  Initialized cluster object to connect
458  	 *
459  	 * \return Standard Pacemaker return code
460  	 */
461  	int
462  	pcmk__corosync_connect(pcmk_cluster_t *cluster)
463  	{
464  	    const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
465  	    const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
466  	    pcmk__node_status_t *local_node = NULL;
467  	    int rc = pcmk_rc_ok;
468  	
469  	    pcmk__cluster_init_node_caches();
470  	
471  	    if (cluster_layer != pcmk_cluster_layer_corosync) {
472  	        pcmk__err("Invalid cluster layer: %s " QB_XS " cluster_layer=%d",
473  	                  cluster_layer_s, cluster_layer);
474  	        return EINVAL;
475  	    }
476  	
477  	    rc = pcmk__cpg_connect(cluster);
478  	    if (rc != pcmk_rc_ok) {
479  	        // Error message was logged by pcmk__cpg_connect()
480  	        return rc;
481  	    }
482  	    pcmk__info("Connection to %s established", cluster_layer_s);
483  	
484  	    cluster->priv->node_id = pcmk__cpg_local_nodeid(0);
485  	    if (cluster->priv->node_id == 0) {
486  	        pcmk__err("Could not determine local node ID");
487  	        return ENXIO;
488  	    }
489  	
490  	    cluster->priv->node_name = pcmk__cluster_node_name(0);
491  	    if (cluster->priv->node_name == NULL) {
492  	        pcmk__err("Could not determine local node name");
493  	        return ENXIO;
494  	    }
495  	
496  	    // Ensure local node always exists in peer cache
497  	    local_node = pcmk__get_node(cluster->priv->node_id,
498  	                                cluster->priv->node_name, NULL,
499  	                                pcmk__node_search_cluster_member);
500  	
501  	    cluster->priv->node_xml_id = pcmk__corosync_uuid(local_node);
502  	    CRM_LOG_ASSERT(cluster->priv->node_xml_id != NULL);
503  	
504  	    return pcmk_rc_ok;
505  	}
506  	
507  	/*!
508  	 * \internal
509  	 * \brief Check whether a Corosync cluster is active
510  	 *
511  	 * \return \c true if Corosync is found active, or \c false otherwise
512  	 */
513  	bool
514  	pcmk__corosync_is_active(void)
515  	{
516  	    cmap_handle_t handle;
517  	    int rc = pcmk__init_cmap(&handle);
518  	
519  	    if (rc == CS_OK) {
520  	        cmap_finalize(handle);
521  	        return true;
522  	    }
523  	
524  	    pcmk__info("Failed to initialize the cmap API: %s (%d)",
525  	               pcmk_rc_str(pcmk__corosync2rc(rc)), rc);
526  	    return false;
527  	}
528  	
529  	/*!
530  	 * \internal
531  	 * \brief Check whether a Corosync cluster peer is active
532  	 *
533  	 * \param[in] node  Node to check
534  	 *
535  	 * \return \c true if \p node is an active Corosync peer, or \c false otherwise
536  	 */
537  	bool
538  	pcmk__corosync_is_peer_active(const pcmk__node_status_t *node)
539  	{
540  	    if (node == NULL) {
541  	        pcmk__trace("Corosync peer inactive: NULL");
542  	        return false;
543  	    }
544  	    if (!pcmk__str_eq(node->state, PCMK_VALUE_MEMBER, pcmk__str_none)) {
545  	        pcmk__trace("Corosync peer %s inactive: state=%s", node->name,
546  	                    node->state);
547  	        return false;
548  	    }
549  	    if (!pcmk__is_set(node->processes, crm_proc_cpg)) {
550  	        pcmk__trace("Corosync peer %s inactive " QB_XS " processes=%.16" PRIx32,
551  	                    node->name, node->processes);
552  	        return false;
553  	    }
554  	    return true;
555  	}
556  	
557  	/*!
558  	 * \internal
559  	 * \brief Load Corosync node list (via CMAP) into peer cache and optionally XML
560  	 *
561  	 * \param[in,out] xml_parent  If not NULL, add <node> entry here for each node
562  	 *
563  	 * \return true if any nodes were found, false otherwise
564  	 */
565  	bool
566  	pcmk__corosync_add_nodes(xmlNode *xml_parent)
567  	{
568  	    int lpc = 0;
569  	    cs_error_t rc = CS_OK;
570  	    int retries = 0;
571  	    bool any = false;
572  	    cmap_handle_t cmap_handle;
573  	    int fd = -1;
574  	    uid_t found_uid = 0;
575  	    gid_t found_gid = 0;
576  	    pid_t found_pid = 0;
577  	    int rv;
578  	
579  	    do {
580  	        rc = pcmk__init_cmap(&cmap_handle);
581  	        if (rc != CS_OK) {
582  	            retries++;
583  	            pcmk__debug("API connection setup failed: %s.  Retrying in %ds",
584  	                        pcmk_rc_str(pcmk__corosync2rc(rc)), retries);
585  	            sleep(retries);
586  	        }
587  	
588  	    } while (retries < 5 && rc != CS_OK);
589  	
590  	    if (rc != CS_OK) {
591  	        pcmk__warn("Could not connect to Cluster Configuration Database API, "
592  	                   "error %d",
593  	                   rc);
594  	        return false;
595  	    }
596  	
597  	    rc = cmap_fd_get(cmap_handle, &fd);
598  	    if (rc != CS_OK) {
599  	        pcmk__err("Could not obtain the CMAP API connection: %s (%d)",
600  	                  pcmk_rc_str(pcmk__corosync2rc(rc)), rc);
601  	        goto bail;
602  	    }
603  	
604  	    /* CMAP provider run as root (in given user namespace, anyway)? */
605  	    if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
606  	                                            &found_uid, &found_gid))) {
607  	        pcmk__err("CMAP provider is not authentic: process %lld "
608  	                  "(uid: %lld, gid: %lld)",
609  	                  (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
610  	                  (long long) found_uid, (long long) found_gid);
611  	        goto bail;
612  	    } else if (rv < 0) {
613  	        pcmk__err("Could not verify authenticity of CMAP provider: %s (%d)",
614  	                  strerror(-rv), -rv);
615  	        goto bail;
616  	    }
617  	
618  	    pcmk__cluster_init_node_caches();
619  	    pcmk__trace("Initializing Corosync node list");
620  	    for (lpc = 0; TRUE; lpc++) {
621  	        uint32_t nodeid = 0;
622  	        char *name = NULL;
623  	        char *key = NULL;
624  	
625  	        key = pcmk__assert_asprintf("nodelist.node.%d.nodeid", lpc);
626  	        rc = cmap_get_uint32(cmap_handle, key, &nodeid);
627  	        free(key);
628  	
629  	        if (rc != CS_OK) {
630  	            break;
631  	        }
632  	
633  	        name = pcmk__corosync_name(cmap_handle, nodeid);
634  	        if (name != NULL) {
635  	            GHashTableIter iter;
636  	            pcmk__node_status_t *node = NULL;
637  	
638  	            g_hash_table_iter_init(&iter, pcmk__peer_cache);
639  	            while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
640  	                if ((node != NULL)
641  	                    && (node->cluster_layer_id > 0)
642  	                    && (node->cluster_layer_id != nodeid)
643  	                    && pcmk__str_eq(node->name, name, pcmk__str_casei)) {
644  	
645  	                    pcmk__crit("Nodes %" PRIu32 " and %" PRIu32 " share the "
646  	                               "same name '%s': shutting down",
647  	                               node->cluster_layer_id, nodeid, name);
648  	                    crm_exit(CRM_EX_FATAL);
649  	                }
650  	            }
651  	        }
652  	
653  	        if (nodeid > 0 || name != NULL) {
654  	            pcmk__trace("Initializing node[%d] %u = %s", lpc, nodeid, name);
655  	            pcmk__get_node(nodeid, name, NULL, pcmk__node_search_cluster_member);
656  	        }
657  	
658  	        if (nodeid > 0 && name != NULL) {
659  	            any = true;
660  	
661  	            if (xml_parent) {
662  	                xmlNode *node = pcmk__xe_create(xml_parent, PCMK_XE_NODE);
663  	
664  	                pcmk__xe_set_ll(node, PCMK_XA_ID, (long long) nodeid);
665  	                pcmk__xe_set(node, PCMK_XA_UNAME, name);
666  	            }
667  	        }
668  	
669  	        free(name);
670  	    }
671  	bail:
672  	    cmap_finalize(cmap_handle);
673  	    return any;
674  	}
675  	
676  	/*!
677  	 * \internal
678  	 * \brief Get cluster name from Corosync configuration (via CMAP)
679  	 *
680  	 * \return Newly allocated string with cluster name if configured, or NULL
681  	 */
682  	char *
683  	pcmk__corosync_cluster_name(void)
684  	{
685  	    cmap_handle_t handle;
686  	    char *cluster_name = NULL;
687  	    cs_error_t rc = CS_OK;
688  	    int fd = -1;
689  	    uid_t found_uid = 0;
690  	    gid_t found_gid = 0;
691  	    pid_t found_pid = 0;
692  	    int rv;
693  	
694  	    rc = pcmk__init_cmap(&handle);
695  	    if (rc != CS_OK) {
696  	        pcmk__info("Failed to initialize the cmap API: %s (%d)",
697  	                   pcmk_rc_str(pcmk__corosync2rc(rc)), rc);
698  	        return NULL;
699  	    }
700  	
701  	    rc = cmap_fd_get(handle, &fd);
702  	    if (rc != CS_OK) {
703  	        pcmk__err("Could not obtain the CMAP API connection: %s (%d)",
704  	                  pcmk_rc_str(pcmk__corosync2rc(rc)), rc);
705  	        goto bail;
706  	    }
707  	
708  	    /* CMAP provider run as root (in given user namespace, anyway)? */
709  	    if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
710  	                                            &found_uid, &found_gid))) {
711  	        pcmk__err("CMAP provider is not authentic: process %lld "
712  	                  "(uid: %lld, gid: %lld)",
713  	                  (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
714  	                  (long long) found_uid, (long long) found_gid);
715  	        goto bail;
716  	    } else if (rv < 0) {
717  	        pcmk__err("Could not verify authenticity of CMAP provider: %s (%d)",
718  	                  strerror(-rv), -rv);
719  	        goto bail;
720  	    }
721  	
722  	    rc = cmap_get_string(handle, "totem.cluster_name", &cluster_name);
723  	    if (rc != CS_OK) {
724  	        pcmk__info("Cannot get totem.cluster_name: %s (%d)",
725  	                   pcmk_rc_str(pcmk__corosync2rc(rc)), rc);
726  	
727  	    } else {
728  	        pcmk__debug("cmap totem.cluster_name = '%s'", cluster_name);
729  	    }
730  	
731  	bail:
732  	    cmap_finalize(handle);
733  	    return cluster_name;
734  	}
735  	
736  	/*!
737  	 * \internal
738  	 * \brief Check (via CMAP) whether Corosync configuration has a node list
739  	 *
740  	 * \return true if Corosync has node list, otherwise false
741  	 */
742  	bool
743  	pcmk__corosync_has_nodelist(void)
744  	{
745  	    cs_error_t cs_rc = CS_OK;
746  	    int retries = 0;
747  	    cmap_handle_t cmap_handle;
748  	    cmap_iter_handle_t iter_handle;
749  	    char key_name[CMAP_KEYNAME_MAXLEN + 1];
750  	    int fd = -1;
751  	    uid_t found_uid = 0;
752  	    gid_t found_gid = 0;
753  	    pid_t found_pid = 0;
754  	    int rc = pcmk_ok;
755  	
756  	    static bool got_result = false;
757  	    static bool result = false;
758  	
759  	    if (got_result) {
760  	        return result;
761  	    }
762  	
763  	    // Connect to CMAP
764  	    do {
765  	        cs_rc = pcmk__init_cmap(&cmap_handle);
766  	        if (cs_rc != CS_OK) {
767  	            retries++;
768  	            pcmk__debug("CMAP connection failed: %s (rc=%d, retrying in %ds)",
769  	                        pcmk_rc_str(pcmk__corosync2rc(cs_rc)), cs_rc, retries);
770  	            sleep(retries);
771  	        }
772  	    } while ((retries < 5) && (cs_rc != CS_OK));
773  	    if (cs_rc != CS_OK) {
774  	        pcmk__warn("Assuming Corosync does not have node list: CMAP connection "
775  	                   "failed (%s) " QB_XS " rc=%d",
776  	                   pcmk_rc_str(pcmk__corosync2rc(cs_rc)), cs_rc);
777  	        return false;
778  	    }
779  	
780  	    // Get CMAP connection file descriptor
781  	    cs_rc = cmap_fd_get(cmap_handle, &fd);
782  	    if (cs_rc != CS_OK) {
783  	        pcmk__warn("Assuming Corosync does not have node list: CMAP unusable "
784  	                   "(%s) " QB_XS " rc=%d",
785  	                   pcmk_rc_str(pcmk__corosync2rc(cs_rc)), cs_rc);
786  	        goto bail;
787  	    }
788  	
789  	    // Check whether CMAP connection is authentic (i.e. provided by root)
790  	    rc = crm_ipc_is_authentic_process(fd, (uid_t) 0, (gid_t) 0,
791  	                                      &found_pid, &found_uid, &found_gid);
792  	    if (rc == 0) {
793  	        pcmk__warn("Assuming Corosync does not have node list: CMAP provider "
794  	                   "is inauthentic "
795  	                   QB_XS " pid=%lld uid=%lld gid=%lld",
796  	                   (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
797  	                   (long long) found_uid, (long long) found_gid);
798  	        goto bail;
799  	    } else if (rc < 0) {
800  	        pcmk__warn("Assuming Corosync does not have node list: Could not "
801  	                   "verify CMAP authenticity (%s) " QB_XS " rc=%d",
802  	                   pcmk_strerror(rc), rc);
803  	        goto bail;
804  	    }
805  	
806  	    // Check whether nodelist section is presetn
807  	    cs_rc = cmap_iter_init(cmap_handle, "nodelist", &iter_handle);
808  	    if (cs_rc != CS_OK) {
809  	        pcmk__warn("Assuming Corosync does not have node list: CMAP not "
810  	                   "readable (%s) " QB_XS " rc=%d",
811  	                   pcmk_rc_str(pcmk__corosync2rc(cs_rc)), cs_rc);
812  	        goto bail;
813  	    }
814  	
815  	    cs_rc = cmap_iter_next(cmap_handle, iter_handle, key_name, NULL, NULL);
816  	    if (cs_rc == CS_OK) {
817  	        result = true;
818  	    }
819  	
820  	    cmap_iter_finalize(cmap_handle, iter_handle);
821  	    got_result = true;
822  	    pcmk__debug("Corosync %s node list", (result? "has" : "does not have"));
823  	
824  	bail:
825  	    cmap_finalize(cmap_handle);
826  	    return result;
827  	}
828