root/daemons/based/based_io.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cib_rename
  2. retrieveCib
  3. cib_archive_filter
  4. cib_archive_sort
  5. readCibXmlFile
  6. uninitializeCib
  7. activateCibXml
  8. cib_diskwrite_complete
  9. write_cib_contents

   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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdio.h>
  13 #include <unistd.h>
  14 #include <string.h>
  15 #include <stdlib.h>
  16 #include <errno.h>
  17 #include <fcntl.h>
  18 #include <dirent.h>
  19 
  20 #include <sys/param.h>
  21 #include <sys/types.h>
  22 #include <sys/wait.h>
  23 #include <sys/stat.h>
  24 
  25 #include <glib.h>
  26 #include <libxml/tree.h>
  27 
  28 #include <crm/crm.h>
  29 
  30 #include <crm/cib.h>
  31 #include <crm/common/util.h>
  32 #include <crm/common/xml.h>
  33 #include <crm/cib/internal.h>
  34 #include <crm/cluster.h>
  35 
  36 #include <pacemaker-based.h>
  37 
  38 crm_trigger_t *cib_writer = NULL;
  39 
  40 int write_cib_contents(gpointer p);
  41 
  42 static void
  43 cib_rename(const char *old)
     /* [previous][next][first][last][top][bottom][index][help] */
  44 {
  45     int new_fd;
  46     char *new = crm_strdup_printf("%s/cib.auto.XXXXXX", cib_root);
  47 
  48     umask(S_IWGRP | S_IWOTH | S_IROTH);
  49     new_fd = mkstemp(new);
  50 
  51     if ((new_fd < 0) || (rename(old, new) < 0)) {
  52         crm_err("Couldn't archive unusable file %s (disabling disk writes and continuing)",
  53                 old);
  54         cib_writes_enabled = FALSE;
  55     } else {
  56         crm_err("Archived unusable file %s as %s", old, new);
  57     }
  58 
  59     if (new_fd > 0) {
  60         close(new_fd);
  61     }
  62     free(new);
  63 }
  64 
  65 /*
  66  * It is the callers responsibility to free the output of this function
  67  */
  68 
  69 static xmlNode *
  70 retrieveCib(const char *filename, const char *sigfile)
     /* [previous][next][first][last][top][bottom][index][help] */
  71 {
  72     xmlNode *root = NULL;
  73     int rc = cib_file_read_and_verify(filename, sigfile, &root);
  74 
  75     if (rc == pcmk_ok) {
  76         crm_info("Loaded CIB from %s (with digest %s)", filename, sigfile);
  77     } else {
  78         crm_warn("Continuing but NOT using CIB from %s (with digest %s): %s",
  79                  filename, sigfile, pcmk_strerror(rc));
  80         if (rc == -pcmk_err_cib_modified) {
  81             // Archive the original files so the contents are not lost
  82             cib_rename(filename);
  83             cib_rename(sigfile);
  84         }
  85     }
  86     return root;
  87 }
  88 
  89 static int cib_archive_filter(const struct dirent * a)
     /* [previous][next][first][last][top][bottom][index][help] */
  90 {
  91     int rc = 0;
  92     // Looking for regular files starting with "cib-" and not ending in .sig
  93     struct stat s;
  94     char *a_path = crm_strdup_printf("%s/%s", cib_root, a->d_name);
  95 
  96     if(stat(a_path, &s) != 0) {
  97         rc = errno;
  98         crm_trace("%s - stat failed: %s (%d)", a->d_name, pcmk_rc_str(rc), rc);
  99         rc = 0;
 100 
 101     } else if (!S_ISREG(s.st_mode)) {
 102         crm_trace("%s - wrong type (%#o)",
 103                   a->d_name, (unsigned int) (s.st_mode & S_IFMT));
 104 
 105     } else if(strstr(a->d_name, "cib-") != a->d_name) {
 106         crm_trace("%s - wrong prefix", a->d_name);
 107 
 108     } else if (pcmk__ends_with_ext(a->d_name, ".sig")) {
 109         crm_trace("%s - wrong suffix", a->d_name);
 110 
 111     } else {
 112         crm_debug("%s - candidate", a->d_name);
 113         rc = 1;
 114     }
 115 
 116     free(a_path);
 117     return rc;
 118 }
 119 
 120 static int cib_archive_sort(const struct dirent ** a, const struct dirent **b)
     /* [previous][next][first][last][top][bottom][index][help] */
 121 {
 122     /* Order by creation date - most recently created file first */
 123     int rc = 0;
 124     struct stat buf;
 125 
 126     time_t a_age = 0;
 127     time_t b_age = 0;
 128 
 129     char *a_path = crm_strdup_printf("%s/%s", cib_root, a[0]->d_name);
 130     char *b_path = crm_strdup_printf("%s/%s", cib_root, b[0]->d_name);
 131 
 132     if(stat(a_path, &buf) == 0) {
 133         a_age = buf.st_ctime;
 134     }
 135     if(stat(b_path, &buf) == 0) {
 136         b_age = buf.st_ctime;
 137     }
 138 
 139     free(a_path);
 140     free(b_path);
 141 
 142     if(a_age > b_age) {
 143         rc = 1;
 144     } else if(a_age < b_age) {
 145         rc = -1;
 146     }
 147 
 148     crm_trace("%s (%lu) vs. %s (%lu) : %d",
 149         a[0]->d_name, (unsigned long)a_age,
 150         b[0]->d_name, (unsigned long)b_age, rc);
 151     return rc;
 152 }
 153 
 154 xmlNode *
 155 readCibXmlFile(const char *dir, const char *file, gboolean discard_status)
     /* [previous][next][first][last][top][bottom][index][help] */
 156 {
 157     struct dirent **namelist = NULL;
 158 
 159     int lpc = 0;
 160     char *sigfile = NULL;
 161     char *sigfilepath = NULL;
 162     char *filename = NULL;
 163     const char *name = NULL;
 164     const char *value = NULL;
 165     const char *use_valgrind = pcmk__env_option(PCMK__ENV_VALGRIND_ENABLED);
 166 
 167     xmlNode *root = NULL;
 168     xmlNode *status = NULL;
 169 
 170     sigfile = crm_strdup_printf("%s.sig", file);
 171     if (pcmk__daemon_can_write(dir, file) == FALSE
 172             || pcmk__daemon_can_write(dir, sigfile) == FALSE) {
 173         cib_status = -EACCES;
 174         return NULL;
 175     }
 176 
 177     filename = crm_strdup_printf("%s/%s", dir, file);
 178     sigfilepath = crm_strdup_printf("%s/%s", dir, sigfile);
 179     free(sigfile);
 180 
 181     cib_status = pcmk_ok;
 182     root = retrieveCib(filename, sigfilepath);
 183     free(filename);
 184     free(sigfilepath);
 185 
 186     if (root == NULL) {
 187         lpc = scandir(cib_root, &namelist, cib_archive_filter, cib_archive_sort);
 188         if (lpc < 0) {
 189             crm_err("Could not check for CIB backups in %s: %s",
 190                     cib_root, pcmk_rc_str(errno));
 191         }
 192     }
 193 
 194     while (root == NULL && lpc > 1) {
 195         int rc = pcmk_ok;
 196 
 197         lpc--;
 198 
 199         filename = crm_strdup_printf("%s/%s", cib_root, namelist[lpc]->d_name);
 200         sigfile = crm_strdup_printf("%s.sig", filename);
 201 
 202         rc = cib_file_read_and_verify(filename, sigfile, &root);
 203         if (rc == pcmk_ok) {
 204             crm_notice("Loaded CIB from last valid backup %s (with digest %s)",
 205                        filename, sigfile);
 206         } else {
 207             crm_warn("Not using next most recent CIB backup from %s "
 208                      "(with digest %s): %s",
 209                      filename, sigfile, pcmk_strerror(rc));
 210         }
 211 
 212         free(namelist[lpc]);
 213         free(filename);
 214         free(sigfile);
 215     }
 216     free(namelist);
 217 
 218     if (root == NULL) {
 219         root = createEmptyCib(0);
 220         crm_warn("Continuing with an empty configuration");
 221     }
 222 
 223     if (cib_writes_enabled && (use_valgrind != NULL)
 224         && (crm_is_true(use_valgrind)
 225             || (strstr(use_valgrind, PCMK__SERVER_BASED) != NULL))) {
 226 
 227         cib_writes_enabled = FALSE;
 228         crm_err("*** Disabling disk writes to avoid confusing Valgrind ***");
 229     }
 230 
 231     status = pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL);
 232     if (discard_status && status != NULL) {
 233         // Strip out the PCMK_XE_STATUS section if there is one
 234         pcmk__xml_free(status);
 235         status = NULL;
 236     }
 237     if (status == NULL) {
 238         pcmk__xe_create(root, PCMK_XE_STATUS);
 239     }
 240 
 241     /* Do this before schema validation happens */
 242 
 243     /* fill in some defaults */
 244     value = crm_element_value(root, PCMK_XA_ADMIN_EPOCH);
 245     if (value == NULL) { // Not possible with schema validation enabled
 246         crm_warn("Defaulting missing " PCMK_XA_ADMIN_EPOCH " to 0, but "
 247                  "cluster may get confused about which node's configuration "
 248                  "is most recent");
 249         crm_xml_add_int(root, PCMK_XA_ADMIN_EPOCH, 0);
 250     }
 251 
 252     name = PCMK_XA_EPOCH;
 253     value = crm_element_value(root, name);
 254     if (value == NULL) {
 255         crm_xml_add_int(root, name, 0);
 256     }
 257 
 258     name = PCMK_XA_NUM_UPDATES;
 259     value = crm_element_value(root, name);
 260     if (value == NULL) {
 261         crm_xml_add_int(root, name, 0);
 262     }
 263 
 264     // Unset (DC should set appropriate value)
 265     pcmk__xe_remove_attr(root, PCMK_XA_DC_UUID);
 266 
 267     if (discard_status) {
 268         crm_log_xml_trace(root, "[on-disk]");
 269     }
 270 
 271     if (!pcmk__configured_schema_validates(root)) {
 272         cib_status = -pcmk_err_schema_validation;
 273     }
 274     return root;
 275 }
 276 
 277 gboolean
 278 uninitializeCib(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 279 {
 280     xmlNode *tmp_cib = the_cib;
 281 
 282     if (tmp_cib == NULL) {
 283         return FALSE;
 284     }
 285     the_cib = NULL;
 286     pcmk__xml_free(tmp_cib);
 287     return TRUE;
 288 }
 289 
 290 /*
 291  * This method will free the old CIB pointer on success and the new one
 292  * on failure.
 293  */
 294 int
 295 activateCibXml(xmlNode * new_cib, gboolean to_disk, const char *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 296 {
 297     if (new_cib) {
 298         xmlNode *saved_cib = the_cib;
 299 
 300         pcmk__assert(new_cib != saved_cib);
 301         the_cib = new_cib;
 302         pcmk__xml_free(saved_cib);
 303         if (cib_writes_enabled && cib_status == pcmk_ok && to_disk) {
 304             crm_debug("Triggering CIB write for %s op", op);
 305             mainloop_set_trigger(cib_writer);
 306         }
 307         return pcmk_ok;
 308     }
 309 
 310     crm_err("Ignoring invalid CIB");
 311     if (the_cib) {
 312         crm_warn("Reverting to last known CIB");
 313     } else {
 314         crm_crit("Could not write out new CIB and no saved version to revert to");
 315     }
 316     return -ENODATA;
 317 }
 318 
 319 static void
 320 cib_diskwrite_complete(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)
     /* [previous][next][first][last][top][bottom][index][help] */
 321 {
 322     const char *errmsg = "Could not write CIB to disk";
 323 
 324     if ((exitcode != 0) && cib_writes_enabled) {
 325         cib_writes_enabled = FALSE;
 326         errmsg = "Disabling CIB disk writes after failure";
 327     }
 328 
 329     if ((signo == 0) && (exitcode == 0)) {
 330         crm_trace("Disk write [%d] succeeded", (int) pid);
 331 
 332     } else if (signo == 0) {
 333         crm_err("%s: process %d exited %d", errmsg, (int) pid, exitcode);
 334 
 335     } else {
 336         crm_err("%s: process %d terminated with signal %d (%s)%s",
 337                 errmsg, (int) pid, signo, strsignal(signo),
 338                 (core? " and dumped core" : ""));
 339     }
 340 
 341     mainloop_trigger_complete(cib_writer);
 342 }
 343 
 344 int
 345 write_cib_contents(gpointer p)
     /* [previous][next][first][last][top][bottom][index][help] */
 346 {
 347     int exit_rc = pcmk_ok;
 348     xmlNode *cib_local = NULL;
 349 
 350     /* Make a copy of the CIB to write (possibly in a forked child) */
 351     if (p) {
 352         /* Synchronous write out */
 353         cib_local = pcmk__xml_copy(NULL, p);
 354 
 355     } else {
 356         int pid = 0;
 357         int bb_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0);
 358 
 359         /* Turn it off before the fork() to avoid:
 360          * - 2 processes writing to the same shared mem
 361          * - the child needing to disable it
 362          *   (which would close it from underneath the parent)
 363          * This way, the shared mem files are already closed
 364          */
 365         qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
 366 
 367         pid = fork();
 368         if (pid < 0) {
 369             crm_err("Disabling disk writes after fork failure: %s", pcmk_rc_str(errno));
 370             cib_writes_enabled = FALSE;
 371             return FALSE;
 372         }
 373 
 374         if (pid) {
 375             /* Parent */
 376             mainloop_child_add(pid, 0, "disk-writer", NULL, cib_diskwrite_complete);
 377             if (bb_state == QB_LOG_STATE_ENABLED) {
 378                 /* Re-enable now that it it safe */
 379                 qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
 380             }
 381 
 382             return -1;          /* -1 means 'still work to do' */
 383         }
 384 
 385         /* Asynchronous write-out after a fork() */
 386 
 387         /* In theory, we can scribble on the_cib here and not affect the parent,
 388          * but let's be safe anyway.
 389          */
 390         cib_local = pcmk__xml_copy(NULL, the_cib);
 391     }
 392 
 393     /* Write the CIB */
 394     exit_rc = cib_file_write_with_digest(cib_local, cib_root, "cib.xml");
 395 
 396     /* A nonzero exit code will cause further writes to be disabled */
 397     pcmk__xml_free(cib_local);
 398     if (p == NULL) {
 399         crm_exit_t exit_code = CRM_EX_OK;
 400 
 401         switch (exit_rc) {
 402             case pcmk_ok:
 403                 exit_code = CRM_EX_OK;
 404                 break;
 405             case pcmk_err_cib_modified:
 406                 exit_code = CRM_EX_DIGEST; // Existing CIB doesn't match digest
 407                 break;
 408             case pcmk_err_cib_backup: // Existing CIB couldn't be backed up
 409             case pcmk_err_cib_save:   // New CIB couldn't be saved
 410                 exit_code = CRM_EX_CANTCREAT;
 411                 break;
 412             default:
 413                 exit_code = CRM_EX_ERROR;
 414                 break;
 415         }
 416 
 417         /* Use _exit() because exit() could affect the parent adversely */
 418         pcmk_common_cleanup();
 419         _exit(exit_code);
 420     }
 421     return exit_rc;
 422 }

/* [previous][next][first][last][top][bottom][index][help] */