1    	/*
2    	 * Copyright 2004-2025 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   	#include <dlfcn.h>
12   	
13   	#include <inttypes.h>               // PRIu32
14   	#include <stdbool.h>
15   	#include <stdio.h>
16   	#include <unistd.h>
17   	#include <string.h>
18   	#include <stdlib.h>
19   	#include <time.h>
20   	#include <sys/param.h>
21   	#include <sys/types.h>
22   	#include <sys/utsname.h>            // uname()
23   	
24   	#include <glib.h>                   // gboolean
25   	
26   	#include <crm/crm.h>
27   	
28   	#include <crm/common/ipc.h>
29   	#include <crm/common/xml.h>
30   	#include <crm/cluster/internal.h>
31   	#include "crmcluster_private.h"
32   	
33   	CRM_TRACE_INIT_DATA(cluster);
34   	
35   	/*!
36   	 * \internal
37   	 * \brief Get a node's XML ID in the CIB, setting it if not already set
38   	 *
39   	 * \param[in,out] node  Node to check
40   	 *
41   	 * \return CIB XML ID of \p node if known, otherwise \c NULL
42   	 */
43   	const char *
44   	pcmk__cluster_get_xml_id(pcmk__node_status_t *node)
45   	{
46   	    const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
47   	
48   	    if (node == NULL) {
49   	        return NULL;
50   	    }
51   	    if (node->xml_id != NULL) {
52   	        return node->xml_id;
53   	    }
54   	
55   	    // xml_id is always set when a Pacemaker Remote node entry is created
56   	    CRM_CHECK(!pcmk__is_set(node->flags, pcmk__node_status_remote),
57   	              return NULL);
58   	
59   	    switch (cluster_layer) {
60   	#if SUPPORT_COROSYNC
61   	        case pcmk_cluster_layer_corosync:
62   	            node->xml_id = pcmk__corosync_uuid(node);
63   	            return node->xml_id;
64   	#endif  // SUPPORT_COROSYNC
65   	
66   	        default:
67   	            pcmk__err("Unsupported cluster layer %s",
68   	                      pcmk_cluster_layer_text(cluster_layer));
69   	            return NULL;
70   	    }
71   	}
72   	
73   	/*!
74   	 * \internal
75   	 * \brief Connect to the cluster layer
76   	 *
77   	 * \param[in,out] cluster  Initialized cluster object to connect
78   	 *
79   	 * \return Standard Pacemaker return code
80   	 */
81   	int
82   	pcmk_cluster_connect(pcmk_cluster_t *cluster)
83   	{
84   	    const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
85   	    const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
86   	
87   	    if (cluster == NULL) {
88   	        return EINVAL;
89   	    }
90   	
91   	    // cts-lab looks for this message
92   	    pcmk__notice("Connecting to %s cluster layer", cluster_layer_s);
93   	
94   	    switch (cluster_layer) {
95   	#if SUPPORT_COROSYNC
96   	        case pcmk_cluster_layer_corosync:
97   	            return pcmk__corosync_connect(cluster);
98   	#endif // SUPPORT_COROSYNC
99   	
100  	        default:
101  	            break;
102  	    }
103  	
104  	    pcmk__err("Failed to connect to unsupported cluster layer %s",
105  	              cluster_layer_s);
106  	    return EPROTONOSUPPORT;
107  	}
108  	
109  	/*!
110  	 * \brief Disconnect from the cluster layer
111  	 *
112  	 * \param[in,out] cluster  Cluster object to disconnect
113  	 *
114  	 * \return Standard Pacemaker return code
115  	 */
116  	int
117  	pcmk_cluster_disconnect(pcmk_cluster_t *cluster)
118  	{
119  	    const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
120  	    const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
121  	
122  	    pcmk__info("Disconnecting from %s cluster layer", cluster_layer_s);
123  	
124  	    switch (cluster_layer) {
125  	#if SUPPORT_COROSYNC
126  	        case pcmk_cluster_layer_corosync:
127  	            pcmk__corosync_disconnect(cluster);
128  	            pcmk__cluster_destroy_node_caches();
129  	            return pcmk_rc_ok;
130  	#endif // SUPPORT_COROSYNC
131  	
132  	        default:
133  	            break;
134  	    }
135  	
136  	    pcmk__err("Failed to disconnect from unsupported cluster layer %s",
137  	              cluster_layer_s);
138  	    return EPROTONOSUPPORT;
139  	}
140  	
141  	/*!
142  	 * \brief Allocate a new \p pcmk_cluster_t object
143  	 *
144  	 * \return A newly allocated \p pcmk_cluster_t object (guaranteed not \c NULL)
145  	 * \note The caller is responsible for freeing the return value using
146  	 *       \p pcmk_cluster_free().
147  	 */
148  	pcmk_cluster_t *
149  	pcmk_cluster_new(void)
150  	{
151  	    pcmk_cluster_t *cluster = pcmk__assert_alloc(1, sizeof(pcmk_cluster_t));
152  	
153  	    cluster->priv = pcmk__assert_alloc(1, sizeof(pcmk__cluster_private_t));
154  	    cluster->priv->server = pcmk__parse_server(crm_system_name);
155  	    return cluster;
156  	}
157  	
158  	/*!
159  	 * \brief Free a \p pcmk_cluster_t object and its dynamically allocated members
160  	 *
161  	 * \param[in,out] cluster  Cluster object to free
162  	 */
163  	void
(3) Event deallocator: Deallocator for "struct pcmk__cluster".
Also see events: [allocation][allocation]
164  	pcmk_cluster_free(pcmk_cluster_t *cluster)
165  	{
166  	    if (cluster == NULL) {
167  	        return;
168  	    }
169  	    election_fini(cluster);
170  	    free(cluster->priv->node_xml_id);
171  	    free(cluster->priv->node_name);
172  	    free(cluster->priv);
173  	    free(cluster);
174  	}
175  	
176  	/*!
177  	 * \brief Set the destroy function for a cluster object
178  	 *
179  	 * \param[in,out] cluster  Cluster object
180  	 * \param[in]     fn       Destroy function to set
181  	 *
182  	 * \return Standard Pacemaker return code
183  	 */
184  	int
185  	pcmk_cluster_set_destroy_fn(pcmk_cluster_t *cluster, void (*fn)(gpointer))
186  	{
187  	    if (cluster == NULL) {
188  	        return EINVAL;
189  	    }
190  	    cluster->destroy = fn;
191  	    return pcmk_rc_ok;
192  	}
193  	
194  	/*!
195  	 * \internal
196  	 * \brief Send an XML message via the cluster messaging layer
197  	 *
198  	 * \param[in] node     Cluster node to send message to
199  	 * \param[in] service  Message type to use in message host info
200  	 * \param[in] data     XML message to send
201  	 *
202  	 * \return \c true on success, or \c false otherwise
203  	 */
204  	bool
205  	pcmk__cluster_send_message(const pcmk__node_status_t *node,
206  	                           enum pcmk_ipc_server service, const xmlNode *data)
207  	{
208  	    // @TODO Return standard Pacemaker return code
209  	    switch (pcmk_get_cluster_layer()) {
210  	#if SUPPORT_COROSYNC
211  	        case pcmk_cluster_layer_corosync:
212  	            return pcmk__cpg_send_xml(data, node, service);
213  	#endif  // SUPPORT_COROSYNC
214  	
215  	        default:
216  	            break;
217  	    }
218  	    return false;
219  	}
220  	
221  	/*!
222  	 * \internal
223  	 * \brief Get the node name corresponding to a cluster-layer node ID
224  	 *
225  	 * Get the node name from the cluster layer if possible. Otherwise, if for the
226  	 * local node, call \c uname() and get the \c nodename member from the
227  	 * <tt>struct utsname</tt> object.
228  	 *
229  	 * \param[in] nodeid  Node ID to check (or 0 for the local node)
230  	 *
231  	 * \return Node name corresponding to \p nodeid
232  	 *
233  	 * \note This will fatally exit if \c uname() fails to get the local node name
234  	 *       or we run out of memory.
235  	 * \note The caller is responsible for freeing the return value using \c free().
236  	 */
237  	char *
238  	pcmk__cluster_node_name(uint32_t nodeid)
239  	{
240  	    char *name = NULL;
241  	    const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer();
242  	    const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
243  	
244  	    switch (cluster_layer) {
245  	#if SUPPORT_COROSYNC
246  	        case pcmk_cluster_layer_corosync:
247  	            name = pcmk__corosync_name(0, nodeid);
248  	            if (name != NULL) {
249  	                return name;
250  	            }
251  	            break;
252  	#endif // SUPPORT_COROSYNC
253  	
254  	        default:
255  	            pcmk__err("Unsupported cluster layer: %s", cluster_layer_s);
256  	            break;
257  	    }
258  	
259  	    if (nodeid == 0) {
260  	        struct utsname hostinfo;
261  	
262  	        pcmk__notice("Could not get local node name from %s cluster layer, "
263  	                     "defaulting to local hostname",
264  	                     cluster_layer_s);
265  	
266  	        if (uname(&hostinfo) < 0) {
267  	            // @TODO Maybe let the caller decide what to do
268  	            pcmk__err("Failed to get the local hostname");
269  	            crm_exit(CRM_EX_FATAL);
270  	        }
271  	        return pcmk__str_copy(hostinfo.nodename);
272  	    }
273  	
274  	    pcmk__notice("Could not obtain a node name for node with "
275  	                 PCMK_XA_ID "=%" PRIu32,
276  	                 nodeid);
277  	    return NULL;
278  	}
279  	
280  	/*!
281  	 * \internal
282  	 * \brief Get the local node's cluster-layer node name
283  	 *
284  	 * If getting the node name from the cluster layer is impossible, call
285  	 * \c uname() and get the \c nodename member from the <tt>struct utsname</tt>
286  	 * object.
287  	 *
288  	 * \return Local node's name
289  	 *
290  	 * \note This will fatally exit if \c uname() fails to get the local node name
291  	 *       or we run out of memory.
292  	 */
293  	const char *
294  	pcmk__cluster_local_node_name(void)
295  	{
296  	    // @TODO Refactor to avoid trivially leaking name at exit
297  	    static char *name = NULL;
298  	
299  	    if (name == NULL) {
300  	        name = pcmk__cluster_node_name(0);
301  	    }
302  	    return name;
303  	}
304  	
305  	/*!
306  	 * \internal
307  	 * \brief Get the node name corresonding to a node UUID
308  	 *
309  	 * Look for the UUID in both the remote node cache and the cluster member cache.
310  	 *
311  	 * \param[in] uuid  UUID to search for
312  	 *
313  	 * \return Node name corresponding to \p uuid if found, or \c NULL otherwise
314  	 */
315  	const char *
316  	pcmk__node_name_from_uuid(const char *uuid)
317  	{
318  	    /* @TODO There are too many functions in libcrmcluster that look up a node
319  	     * from the node caches (possibly creating a cache entry if none exists).
320  	     * There are at least the following:
321  	     * * pcmk__cluster_lookup_remote_node()
322  	     * * pcmk__get_node()
323  	     * * pcmk__node_name_from_uuid()
324  	     * * pcmk__search_node_caches()
325  	     *
326  	     * There's a lot of duplication among them, but they all do slightly
327  	     * different things. We should try to clean them up and consolidate them to
328  	     * the extent possible, likely with new helper functions.
329  	     */
330  	    GHashTableIter iter;
331  	    pcmk__node_status_t *node = NULL;
332  	
333  	    CRM_CHECK(uuid != NULL, return NULL);
334  	
335  	    // Remote nodes have the same uname and uuid
336  	    if (g_hash_table_lookup(pcmk__remote_peer_cache, uuid)) {
337  	        return uuid;
338  	    }
339  	
340  	    g_hash_table_iter_init(&iter, pcmk__peer_cache);
341  	    while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
342  	        if (pcmk__str_eq(uuid, pcmk__cluster_get_xml_id(node),
343  	                         pcmk__str_none)) {
344  	            return node->name;
345  	        }
346  	    }
347  	    return NULL;
348  	}
349  	
350  	/*!
351  	 * \brief Get a log-friendly string equivalent of a cluster layer
352  	 *
353  	 * \param[in] layer  Cluster layer
354  	 *
355  	 * \return Log-friendly string corresponding to \p layer
356  	 */
357  	const char *
358  	pcmk_cluster_layer_text(enum pcmk_cluster_layer layer)
359  	{
360  	    switch (layer) {
361  	        case pcmk_cluster_layer_corosync:
362  	            return "corosync";
363  	        case pcmk_cluster_layer_unknown:
364  	            return "unknown";
365  	        case pcmk_cluster_layer_invalid:
366  	            return "invalid";
367  	        default:
368  	            pcmk__err("Invalid cluster layer: %d", layer);
369  	            return "invalid";
370  	    }
371  	}
372  	
373  	/*!
374  	 * \brief Get and validate the local cluster layer
375  	 *
376  	 * If a cluster layer is not configured via the \c PCMK__ENV_CLUSTER_TYPE local
377  	 * option, this will try to detect an active cluster from among the supported
378  	 * cluster layers.
379  	 *
380  	 * \return Local cluster layer
381  	 *
382  	 * \note This will fatally exit if the configured cluster layer is invalid.
383  	 */
384  	enum pcmk_cluster_layer
385  	pcmk_get_cluster_layer(void)
386  	{
387  	    static enum pcmk_cluster_layer cluster_layer = pcmk_cluster_layer_unknown;
388  	    const char *cluster = NULL;
389  	
390  	    // Cluster layer is stable once set
391  	    if (cluster_layer != pcmk_cluster_layer_unknown) {
392  	        return cluster_layer;
393  	    }
394  	
395  	    cluster = pcmk__env_option(PCMK__ENV_CLUSTER_TYPE);
396  	
397  	    if (cluster != NULL) {
398  	        pcmk__info("Verifying configured cluster layer '%s'", cluster);
399  	        cluster_layer = pcmk_cluster_layer_invalid;
400  	
401  	#if SUPPORT_COROSYNC
402  	        if (pcmk__str_eq(cluster, PCMK_VALUE_COROSYNC, pcmk__str_casei)) {
403  	            cluster_layer = pcmk_cluster_layer_corosync;
404  	        }
405  	#endif  // SUPPORT_COROSYNC
406  	
407  	        if (cluster_layer == pcmk_cluster_layer_invalid) {
408  	            pcmk__notice("This installation does not support the '%s' cluster "
409  	                         "infrastructure: terminating",
410  	                         cluster);
411  	            crm_exit(CRM_EX_FATAL);
412  	        }
413  	        pcmk__info("Assuming an active '%s' cluster", cluster);
414  	
415  	    } else {
416  	        // Nothing configured, so test supported cluster layers
417  	#if SUPPORT_COROSYNC
418  	        pcmk__debug("Testing with Corosync");
419  	        if (pcmk__corosync_is_active()) {
420  	            cluster_layer = pcmk_cluster_layer_corosync;
421  	        }
422  	#endif  // SUPPORT_COROSYNC
423  	
424  	        if (cluster_layer == pcmk_cluster_layer_unknown) {
425  	            pcmk__notice("Could not determine the current cluster layer");
426  	        } else {
427  	            pcmk__info("Detected an active '%s' cluster",
428  	                       pcmk_cluster_layer_text(cluster_layer));
429  	        }
430  	    }
431  	
432  	    return cluster_layer;
433  	}
434  	
435  	// Deprecated functions kept only for backward API compatibility
436  	// LCOV_EXCL_START
437  	
438  	#include <crm/cluster/compat.h>
439  	
440  	gboolean
441  	crm_cluster_connect(pcmk_cluster_t *cluster)
442  	{
443  	    if (cluster == NULL) {
444  	        return FALSE;
445  	    }
446  	    if (cluster->priv == NULL) {
447  	        /* sbd (as of at least 1.5.2) doesn't call pcmk_cluster_new() to
448  	         * allocate the pcmk_cluster_t
449  	         */
450  	        cluster->priv = pcmk__assert_alloc(1, sizeof(pcmk__cluster_private_t));
451  	    }
452  	    return pcmk_cluster_connect(cluster) == pcmk_rc_ok;
453  	}
454  	
455  	const char *
456  	name_for_cluster_type(enum cluster_type_e type)
457  	{
458  	    switch (type) {
459  	        case pcmk_cluster_corosync:
460  	            return "corosync";
461  	        case pcmk_cluster_unknown:
462  	            return "unknown";
463  	        case pcmk_cluster_invalid:
464  	            return "invalid";
465  	    }
466  	    pcmk__err("Invalid cluster type: %d", type);
467  	    return "invalid";
468  	}
469  	
470  	enum cluster_type_e
471  	get_cluster_type(void)
472  	{
473  	    return (enum cluster_type_e) pcmk_get_cluster_layer();
474  	}
475  	
476  	// LCOV_EXCL_STOP
477  	// End deprecated API
478