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