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 General Public License version 2
7 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8 */
9
10 #include <crm_internal.h>
11
12 #include <dirent.h> // dirent, scandir
13 #include <errno.h> // errno, EACCES, ENODATA
14 #include <signal.h> // SIGPIPE
15 #include <stdbool.h>
16 #include <stddef.h> // NULL
17 #include <stdio.h> // rename
18 #include <stdlib.h> // free, mkdtemp
19 #include <string.h> // strerror, strrchr, etc.
20 #include <sys/stat.h> // stat, umask, etc.
21 #include <sys/types.h> // pid_t
22 #include <time.h> // time_t
23 #include <unistd.h> // _exit, fork
24
25 #include <glib.h> // g_*, G_*
26 #include <libxml/tree.h> // xmlNode
27 #include <qb/qbdefs.h> // QB_FALSE, QB_TRUE
28 #include <qb/qblog.h> // qb_log_*
29
30 #include <crm/cib/internal.h> // cib_file_*
31 #include <crm/cib/util.h> // createEmptyCib
32 #include <crm/common/internal.h> // pcmk__assert_asprintf, PCMK__XE_*, etc.
33 #include <crm/common/logging.h> // CRM_CHECK
34 #include <crm/common/mainloop.h> // mainloop_add_signal
35 #include <crm/common/results.h> // pcmk_legacy2rc, pcmk_rc_*
36 #include <crm/common/util.h> // pcmk_common_cleanup
37 #include <crm/common/xml.h> // PCMK_XA_*, PCMK_XE_*
38
39 #include "pacemaker-based.h"
40
41 static bool writes_enabled = true;
42 static crm_trigger_t *write_trigger = NULL;
43
44 /*!
45 * \internal
46 * \brief Process the exit status of a child forked from \c write_cib_async()
47 *
48 * \param[in] child Mainloop child data
49 * \param[in] core If set to 1, the child process dumped core
50 * \param[in] signo Signal that the child process exited with
51 * \param[in] exit_code Child process's exit code
52 */
53 static void
54 write_cib_cb(mainloop_child_t *child, int core, int signo, int exit_code)
55 {
56 const char *error = "Could not write CIB to disk";
57
58 if ((exit_code != 0) && writes_enabled) {
59 writes_enabled = false;
60 error = "Disabling CIB disk writes after failure";
61 }
62
63 if ((signo == 0) && (exit_code == 0)) {
64 pcmk__trace("Disk write [%lld] succeeded", (long long) child->pid);
65
66 } else if (signo == 0) {
67 pcmk__err("%s: process %lld exited with code %d", error,
68 (long long) child->pid, exit_code);
69
70 } else {
71 pcmk__err("%s: process %lld terminated with signal %d (%s)%s",
72 error, (long long) child->pid, signo, strsignal(signo),
73 ((core != 0)? " and dumped core" : ""));
74 }
75
76 mainloop_trigger_complete(write_trigger);
77 }
78
79 /*!
80 * \internal
81 * \brief Write the CIB to disk in a forked child
82 *
83 * This avoids blocking in the parent. The child writes synchronously. The
84 * parent tracks the child via the mainloop and runs a callback when the child
85 * exits.
86 *
87 * \param[in] user_data Ignored
88 */
89 static int
90 write_cib_async(gpointer user_data)
91 {
92 int rc = pcmk_rc_ok;
93 pid_t pid = 0;
94 int blackbox_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0);
95
96 /* Disable blackbox logging before the fork to avoid two processes writing
97 * to the same shared memory. The disable should not be done in the child,
98 * because this would close shared memory files in the parent.
99 *
100 * @TODO How? What is meant by this last sentence?
101 */
102 qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
103
104 pid = fork();
105 if (pid < 0) {
106 pcmk__err("Disabling disk writes after fork failure: %s",
107 strerror(errno));
108 writes_enabled = false;
109
110 // Remove trigger from main loop
111 return 0;
112 }
113
114 if (pid > 0) {
115 // Parent
116 mainloop_child_add(pid, 0, "disk-writer", NULL, write_cib_cb);
117
118 if (blackbox_state == QB_LOG_STATE_ENABLED) {
119 qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
120 }
121
122 /* Mark job as running and keep trigger. write_cib_cb() will mark it as
123 * complete.
124 */
125 return -1;
126 }
127
128 /* Write the CIB. Note that this modifies the_cib, but this child is about
129 * to exit. The parent's copy of the_cib won't be affected.
130 */
131 rc = cib_file_write_with_digest(the_cib, cib_root, "cib.xml");
132 rc = pcmk_legacy2rc(rc);
133
134 pcmk_common_cleanup();
135
136 /* A nonzero exit code will cause further writes to be disabled. Use _exit()
137 * because exit() could affect the parent adversely.
138 *
139 * @TODO Investigate whether _exit() instead of exit() is really necessary.
140 * This goes back to commit 58cb43dc, which states that exit() may close
141 * things it shoudn't close. There is no explanation of what these things
142 * might be. The exit(2) man page states that exit() calls atexit/on_exit
143 * handlers and flushes open stdio streams. The exit(3) man page states that
144 * file created with tmpfile() are removed. But neither Pacemaker nor libqb
145 * uses atexit or on_exit, and it's not clear why we'd be worried about
146 * stdio streams.
147 */
148 switch (rc) {
149 case pcmk_rc_ok:
150 _exit(CRM_EX_OK);
151
152 case pcmk_rc_cib_modified:
153 _exit(CRM_EX_DIGEST);
154
155 case pcmk_rc_cib_backup:
156 case pcmk_rc_cib_save:
157 _exit(CRM_EX_CANTCREAT);
158
159 default:
160 _exit(CRM_EX_ERROR);
161 }
162 }
163
164 /*!
165 * \internal
166 * \brief Enable CIB writes to disk (signal handler)
167 *
168 * \param[in] nsig Ignored
169 */
170 void
171 based_enable_writes(int nsig)
172 {
173 pcmk__info("(Re)enabling disk writes");
174 writes_enabled = true;
175 }
176
177 /*!
178 * \internal
179 * \brief Initialize data structures for \c pacemaker-based I/O
180 */
181 void
182 based_io_init(void)
183 {
184 writes_enabled = !stand_alone;
185 if (writes_enabled
186 && pcmk__env_option_enabled(PCMK__SERVER_BASED,
187 PCMK__ENV_VALGRIND_ENABLED)) {
188
189 writes_enabled = false;
190 pcmk__err("*** Disabling disk writes to avoid confusing Valgrind ***");
191 }
192
193 /* @TODO Should we be setting this up if we've explicitly disabled writes
194 * already?
195 */
196 mainloop_add_signal(SIGPIPE, based_enable_writes);
197
198 write_trigger = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_async, NULL);
199 }
200
201 /*!
202 * \internal
203 * \brief Rename a CIB or digest file after digest mismatch
204 *
205 * This is just a wrapper for logging an error. The caller should disable writes
206 * on error.
207 *
208 * \param[in] old_path Original file path
209 * \param[in] new_path New file path
210 *
211 * \return Standard Pacemaker return code
212 */
213 static int
214 rename_one(const char *old_path, const char *new_path)
215 {
216 int rc = rename(old_path, new_path);
217
218 if (rc == 0) {
219 return pcmk_rc_ok;
220 }
221
222 rc = errno;
223 pcmk__err("Failed to rename %s to %s after digest mismatch: %s. Disabling "
224 "disk writes.", old_path, new_path, strerror(rc));
225 return rc;
226 }
227
228 #define CIBFILE "cib.xml"
229
230 /*!
231 * \internal
232 * \brief Archive the current CIB file in \c cib_root with its saved digest file
233 *
234 * When a CIB file's calculated digest doesn't match its saved one, we archive
235 * both the CIB file and its digest (".sig") file. This way the contents can be
236 * inspected for troubleshooting purposes.
237 *
238 * A subdirectory with a unique name is created in \c cib_root, using the
239 * \c mkdtemp() template \c "cib.auto.XXXXXX". Then \c CIB_FILE and
240 * <tt>CIB_FILE ".sig"</tt> are moved to that directory.
241 *
242 * \param[in] old_cibfile_path Original path of CIB file
243 * \param[in] old_sigfile_path Original path of digest file
244 */
245 static void
246 archive_on_digest_mismatch(const char *old_cibfile_path,
247 const char *old_sigfile_path)
248 {
249 char *new_dir = pcmk__assert_asprintf("%s/cib.auto.XXXXXX", cib_root);
250 char *new_cibfile_path = NULL;
251 char *new_sigfile_path = NULL;
252
253 umask(S_IWGRP | S_IWOTH | S_IROTH);
254
255 if (mkdtemp(new_dir) == NULL) {
256 pcmk__err("Failed to create directory to archive %s and %s after "
257 "digest mismatch: %s. Disabling disk writes.",
258 old_cibfile_path, old_sigfile_path, strerror(errno));
259 writes_enabled = false;
260 goto done;
261 }
262
263 new_cibfile_path = pcmk__assert_asprintf("%s/%s", new_dir, CIBFILE);
264 new_sigfile_path = pcmk__assert_asprintf("%s.sig", new_cibfile_path);
265
266 if ((rename_one(old_cibfile_path, new_cibfile_path) != pcmk_rc_ok)
267 || (rename_one(old_sigfile_path, new_sigfile_path) != pcmk_rc_ok)) {
268
269 writes_enabled = false;
270 goto done;
271 }
272
273 pcmk__err("Archived %s and %s in %s after digest mismatch",
274 old_cibfile_path, old_sigfile_path, new_dir);
275
276 done:
277 free(new_dir);
278 free(new_cibfile_path);
279 free(new_sigfile_path);
280 }
281
282 /*!
283 * \internal
284 * \brief Read CIB XML from \c CIBFILE in the \c cib_root directory
285 *
286 * \return CIB XML parsed from \c CIBFILE in \c cib_root , or \c NULL if the
287 * file was not found or if parsing failed
288 *
289 * \note The caller is responsible for freeing the return value using
290 * \c pcmk__xml_free().
291 */
292 static xmlNode *
293 read_current_cib(void)
294 {
295 char *cibfile_path = pcmk__assert_asprintf("%s/%s", cib_root, CIBFILE);
296 char *sigfile_path = pcmk__assert_asprintf("%s.sig", cibfile_path);
297 const char *sigfile = strrchr(sigfile_path, '/') + 1;
298
299 xmlNode *cib_xml = NULL;
300 int rc = pcmk_rc_ok;
301
302 if (!pcmk__daemon_can_write(cib_root, CIBFILE)
303 || !pcmk__daemon_can_write(cib_root, sigfile)) {
304
305 cib_status = EACCES;
306 goto done;
307 }
308
309 cib_status = pcmk_rc_ok;
310
311 rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &cib_xml);
312 rc = pcmk_legacy2rc(rc);
313
314 if (rc == pcmk_rc_ok) {
315 pcmk__info("Loaded CIB from %s (with digest %s)", cibfile_path,
316 sigfile_path);
317 goto done;
318 }
319
320 pcmk__warn("Continuing but NOT using CIB from %s (with digest %s): %s",
321 cibfile_path, sigfile_path, pcmk_rc_str(rc));
322
323 if (rc == pcmk_rc_cib_modified) {
324 // Archive the original files so the contents are not lost
325 archive_on_digest_mismatch(cibfile_path, sigfile_path);
326 }
327
328 done:
329 free(cibfile_path);
330 free(sigfile_path);
331 return cib_xml;
332 }
333
334 /*!
335 * \internal
336 * \brief \c scandir() filter for backup CIB files in \c cib_root
337 *
338 * \param[in] entry Directory entry
339 *
340 * \retval 1 if the entry is a regular file whose name begins with \c "cib-" and
341 * does not end with ".sig"
342 * \retval 0 otherwise
343 */
344 static int
345 backup_cib_filter(const struct dirent *entry)
346 {
347 char *path = pcmk__assert_asprintf("%s/%s", cib_root, entry->d_name);
348 struct stat sb;
349 int rc = stat(path, &sb);
350
351 free(path);
352
353 if (rc != 0) {
354 pcmk__warn("Filtering %s/%s during scan for backup CIB: stat() failed: "
355 "%s", cib_root, entry->d_name, strerror(errno));
356 return 0;
357 }
358
359 return S_ISREG(sb.st_mode)
360 && g_str_has_prefix(entry->d_name, "cib-")
361 && !g_str_has_suffix(entry->d_name, ".sig");
362 }
363
364 /*!
365 * \internal
366 * \brief Get a file's last change time (\c ctime)
367 *
368 * The file is assumed to be a backup CIB file in the \c cib_root directory.
369 *
370 * \param[in] file Base name of file
371 *
372 * \return Last change time of \p file, or 0 on \c stat() failure
373 */
374 static time_t
375 get_backup_cib_ctime(const char *file)
376 {
377 char *path = pcmk__assert_asprintf("%s/%s", cib_root, file);
378 struct stat sb;
379 int rc = stat(path, &sb);
380
381 free(path);
382
383 if (rc != 0) {
384 pcmk__warn("Failed to stat() %s/%s while sorting backup CIBs: %s",
385 cib_root, file, strerror(errno));
386 return 0;
387 }
388
389 return sb.st_ctime;
390 }
391
392 /*!
393 * \internal
394 * \brief Compare directory entries based on their last change times
395 *
396 * The entries are assumed to be CIB files in the \c cib_root directory.
397 *
398 * \param[in] entry1 First directory entry to compare
399 * \param[in] entry2 Second directory entry to compare
400 *
401 * \retval -1 if \p entry1 was changed more recently than \p entry2
402 * \retval 0 if \p entry1 was last changed at the same timestamp as \p entry2
403 * \retval 1 if \p entry1 was changed less recently than \p entry2
404 */
405 static int
406 compare_backup_cibs(const struct dirent **entry1, const struct dirent **entry2)
407 {
408 time_t ctime1 = get_backup_cib_ctime((*entry1)->d_name);
409 time_t ctime2 = get_backup_cib_ctime((*entry2)->d_name);
410
411 if (ctime1 > ctime2) {
412 pcmk__trace("%s/%s (%lld) newer than %s/%s (%lld)",
413 cib_root, (*entry1)->d_name, (long long) ctime1,
414 cib_root, (*entry2)->d_name, (long long) ctime2);
415 return -1;
416 }
417
418 if (ctime1 < ctime2) {
419 pcmk__trace("%s/%s (%lld) older than %s/%s (%lld)",
420 cib_root, (*entry1)->d_name, (long long) ctime1,
421 cib_root, (*entry2)->d_name, (long long) ctime2);
422 return 1;
423 }
424
425 pcmk__trace("%s/%s (%lld) same age as %s/%s (%lld)",
426 cib_root, (*entry1)->d_name, (long long) ctime1,
427 cib_root, (*entry2)->d_name, (long long) ctime2);
428 return 0;
429 }
430
431 /*!
432 * \internal
433 * \brief Read CIB XML from the last valid backup file in \c cib_root
434 *
435 * \return CIB XML parsed from the last valid backup file, or \c NULL if none
436 * was found
437 */
438 static xmlNode *
439 read_backup_cib(void)
440 {
441 xmlNode *cib_xml = NULL;
442 struct dirent **namelist = NULL;
443 int num_files = scandir(cib_root, &namelist, backup_cib_filter,
444 compare_backup_cibs);
445
446 if (num_files < 0) {
447 pcmk__err("Could not check for CIB backups in %s: %s", cib_root,
448 pcmk_rc_str(errno));
449 goto done;
450 }
451
452 for (int i = 0; i < num_files; i++) {
453 const char *cibfile = namelist[i]->d_name;
454 char *cibfile_path = pcmk__assert_asprintf("%s/%s", cib_root, cibfile);
455 char *sigfile_path = pcmk__assert_asprintf("%s.sig", cibfile_path);
456
457 int rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &cib_xml);
458
459 rc = pcmk_legacy2rc(rc);
460
461 if (rc == pcmk_rc_ok) {
462 pcmk__notice("Loaded CIB from last valid backup %s (with digest "
463 "%s)", cibfile_path, sigfile_path);
464 } else {
465 pcmk__warn("Not using next most recent CIB backup from %s (with "
466 "digest %s): %s", cibfile_path, sigfile_path,
467 pcmk_rc_str(rc));
468 }
469
470 free(cibfile_path);
471 free(sigfile_path);
472
473 if (rc == pcmk_rc_ok) {
474 break;
475 }
476 }
477
478 done:
479 for (int i = 0; i < num_files; i++) {
480 free(namelist[i]);
481 }
482 free(namelist);
483
484 return cib_xml;
485 }
486
487 /*!
488 * \internal
489 * \brief Set the CIB XML's \c PCMK_XE_STATUS element to empty if appropriate
490 *
491 * Delete the current \c PCMK_XE_STATUS element if not running in stand-alone
492 * mode. Then create an empty \c PCMK_XE_STATUS child if either of the following
493 * is true:
494 * * not running in stand-alone mode
495 * * running in stand-alone mode with no \c PCMK_XE_STATUS element
496 *
497 * \param[in,out] cib_xml CIB XML
498 */
499 static void
500 set_empty_status(xmlNode *cib_xml)
501 {
502 xmlNode *status = pcmk__xe_first_child(cib_xml, PCMK_XE_STATUS, NULL, NULL);
503
|
(1) Event path: |
Condition "!stand_alone", taking true branch. |
504 if (!stand_alone) {
|
CID (unavailable; MK=8a6526783325e097e0be9b22aa0fbe7c) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS): |
|
(2) Event assign_union_field: |
The union field "in" of "_pp" is written. |
|
(3) Event inconsistent_union_field_access: |
In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in". |
505 g_clear_pointer(&status, pcmk__xml_free);
506 }
507
508 if (status == NULL) {
509 pcmk__xe_create(cib_xml, PCMK_XE_STATUS);
510 }
511 }
512
513 /*!
514 * \internal
515 * \brief Set the given CIB version attribute to 0 if it's not already set
516 *
517 * \param[in,out] cib_xml CIB XML
518 * \param[in] version_attr Version attribute
519 */
520 static void
521 set_default_if_unset(xmlNode *cib_xml, const char *version_attr)
522 {
523 if (pcmk__xe_get(cib_xml, version_attr) != NULL) {
524 return;
525 }
526
527 pcmk__warn("Defaulting missing %s to 0, but cluster may get confused about "
528 "which node's configuration is most recent", version_attr);
529 pcmk__xe_set_int(cib_xml, version_attr, 0);
530 }
531
532 /*!
533 * \internal
534 * \brief Read the most recent CIB from a file in \c cib_root
535 *
536 * This function first tries to read the CIB from a file called \c "cib.xml" in
537 * the \c cib_root directory.
538 *
539 * If that fails or there is a digest mismatch, it tries all the backup CIB
540 * files in \c cib_root, in order from most recently changed to least, moving to
541 * the next backup file on failure or digest mismatch.
542 *
543 * If no valid CIB file is found, this function generates an empty CIB.
544 *
545 * \return The most current CIB XML available, or an empty CIB if none is
546 * available (guaranteed not to be \c NULL)
547 *
548 * \note The caller is responsible for freeing the return value using
549 * \c pcmk__xml_free().
550 */
551 xmlNode *
552 based_read_cib(void)
553 {
554 static const char *version_attrs[] = {
555 PCMK_XA_ADMIN_EPOCH,
556 PCMK_XA_EPOCH,
557 PCMK_XA_NUM_UPDATES,
558 };
559
560 xmlNode *cib_xml = read_current_cib();
561
562 if (cib_xml == NULL) {
563 cib_xml = read_backup_cib();
564 }
565
566 if (cib_xml == NULL) {
567 cib_xml = createEmptyCib(0);
568 pcmk__warn("Continuing with an empty configuration");
569 }
570
571 set_empty_status(cib_xml);
572
573 /* Default the three version attributes to 0 if unset. The schema requires
574 * them to be set, so:
575 * * It's not possible for them to be unset if schema validation was enabled
576 * when the CIB file was generated, or if it was generated by Pacemaker
577 * and then unmodified.
578 * * We need to set these defaults before schema validation happens.
579 */
580 for (int i = 0; i < PCMK__NELEM(version_attrs); i++) {
581 set_default_if_unset(cib_xml, version_attrs[i]);
582 }
583
584 // The DC should set appropriate value for PCMK_XA_DC_UUID
585 pcmk__xe_remove_attr(cib_xml, PCMK_XA_DC_UUID);
586
587 if (!stand_alone) {
588 pcmk__log_xml_trace(cib_xml, "on-disk");
589 }
590
591 if (!pcmk__configured_schema_validates(cib_xml)) {
592 cib_status = pcmk_rc_schema_validation;
593 }
594
595 return cib_xml;
596 }
597
598 /*!
599 * \internal
600 * \brief Activate new CIB XML
601 *
602 * This function frees the existing \c the_cib and points it to \p new_cib.
603 *
604 * \param[in] new_cib CIB XML to activate (must not be \c NULL or equal to
605 * \c the_cib)
606 * \param[in] to_disk If \c true and if the CIB status is OK and writes are
607 * enabled, trigger the new CIB to be written to disk
608 * \param[in] op Operation that triggered the activation (for logging
609 * only)
610 *
611 * \return Standard Pacemaker return code
612 *
613 * \note This function takes ownership of \p new_cib by assigning it to
614 * \c the_cib. The caller should not free it.
615 */
616 int
617 based_activate_cib(xmlNode *new_cib, bool to_disk, const char *op)
618 {
619 CRM_CHECK((new_cib != NULL) && (new_cib != the_cib), return ENODATA);
620
621 pcmk__xml_free(the_cib);
622 the_cib = new_cib;
623
624 if (to_disk && writes_enabled && (cib_status == pcmk_rc_ok)) {
625 pcmk__debug("Triggering CIB write for %s op", op);
626 mainloop_set_trigger(write_trigger);
627 }
628
629 return pcmk_rc_ok;
630 }
631