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