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