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-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 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 
  74     crm_info("Reading cluster configuration file %s (digest: %s)",
  75              filename, sigfile);
  76     switch (cib_file_read_and_verify(filename, sigfile, &root)) {
  77         case -pcmk_err_cib_corrupt:
  78             crm_warn("Continuing but %s will NOT be used.", filename);
  79             break;
  80 
  81         case -pcmk_err_cib_modified:
  82             /* Archive the original files so the contents are not lost */
  83             crm_warn("Continuing but %s will NOT be used.", filename);
  84             cib_rename(filename);
  85             cib_rename(sigfile);
  86             break;
  87     }
  88     return root;
  89 }
  90 
  91 /*
  92  * for OSs without support for direntry->d_type, like Solaris
  93  */
  94 #ifndef DT_UNKNOWN
  95 # define DT_UNKNOWN     0
  96 # define DT_FIFO        1
  97 # define DT_CHR         2
  98 # define DT_DIR         4
  99 # define DT_BLK         6
 100 # define DT_REG         8
 101 # define DT_LNK         10
 102 # define DT_SOCK        12
 103 # define DT_WHT         14
 104 #endif /*DT_UNKNOWN*/
 105 
 106 static int cib_archive_filter(const struct dirent * a)
     /* [previous][next][first][last][top][bottom][index][help] */
 107 {
 108     int rc = 0;
 109     /* Looking for regular files (d_type = 8) starting with 'cib-' and not ending in .sig */
 110     struct stat s;
 111     char *a_path = crm_strdup_printf("%s/%s", cib_root, a->d_name);
 112 
 113     if(stat(a_path, &s) != 0) {
 114         rc = errno;
 115         crm_trace("%s - stat failed: %s (%d)", a->d_name, pcmk_rc_str(rc), rc);
 116         rc = 0;
 117 
 118     } else if ((s.st_mode & S_IFREG) != S_IFREG) {
 119         unsigned char dtype;
 120 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
 121         dtype = a->d_type;
 122 #else
 123         switch (s.st_mode & S_IFMT) {
 124             case S_IFREG:  dtype = DT_REG;      break;
 125             case S_IFDIR:  dtype = DT_DIR;      break;
 126             case S_IFCHR:  dtype = DT_CHR;      break;
 127             case S_IFBLK:  dtype = DT_BLK;      break;
 128             case S_IFLNK:  dtype = DT_LNK;      break;
 129             case S_IFIFO:  dtype = DT_FIFO;     break;
 130             case S_IFSOCK: dtype = DT_SOCK;     break;
 131             default:       dtype = DT_UNKNOWN;  break;
 132         }
 133 #endif
 134          crm_trace("%s - wrong type (%d)", a->d_name, dtype);
 135 
 136     } else if(strstr(a->d_name, "cib-") != a->d_name) {
 137         crm_trace("%s - wrong prefix", a->d_name);
 138 
 139     } else if (pcmk__ends_with_ext(a->d_name, ".sig")) {
 140         crm_trace("%s - wrong suffix", a->d_name);
 141 
 142     } else {
 143         crm_debug("%s - candidate", a->d_name);
 144         rc = 1;
 145     }
 146 
 147     free(a_path);
 148     return rc;
 149 }
 150 
 151 static int cib_archive_sort(const struct dirent ** a, const struct dirent **b)
     /* [previous][next][first][last][top][bottom][index][help] */
 152 {
 153     /* Order by creation date - most recently created file first */
 154     int rc = 0;
 155     struct stat buf;
 156 
 157     time_t a_age = 0;
 158     time_t b_age = 0;
 159 
 160     char *a_path = crm_strdup_printf("%s/%s", cib_root, a[0]->d_name);
 161     char *b_path = crm_strdup_printf("%s/%s", cib_root, b[0]->d_name);
 162 
 163     if(stat(a_path, &buf) == 0) {
 164         a_age = buf.st_ctime;
 165     }
 166     if(stat(b_path, &buf) == 0) {
 167         b_age = buf.st_ctime;
 168     }
 169 
 170     free(a_path);
 171     free(b_path);
 172 
 173     if(a_age > b_age) {
 174         rc = 1;
 175     } else if(a_age < b_age) {
 176         rc = -1;
 177     }
 178 
 179     crm_trace("%s (%lu) vs. %s (%lu) : %d",
 180         a[0]->d_name, (unsigned long)a_age,
 181         b[0]->d_name, (unsigned long)b_age, rc);
 182     return rc;
 183 }
 184 
 185 xmlNode *
 186 readCibXmlFile(const char *dir, const char *file, gboolean discard_status)
     /* [previous][next][first][last][top][bottom][index][help] */
 187 {
 188     struct dirent **namelist = NULL;
 189 
 190     int lpc = 0;
 191     char *sigfile = NULL;
 192     char *sigfilepath = NULL;
 193     char *filename = NULL;
 194     const char *name = NULL;
 195     const char *value = NULL;
 196     const char *validation = NULL;
 197     const char *use_valgrind = pcmk__env_option(PCMK__ENV_VALGRIND_ENABLED);
 198 
 199     xmlNode *root = NULL;
 200     xmlNode *status = NULL;
 201 
 202     sigfile = crm_strdup_printf("%s.sig", file);
 203     if (pcmk__daemon_can_write(dir, file) == FALSE
 204             || pcmk__daemon_can_write(dir, sigfile) == FALSE) {
 205         cib_status = -EACCES;
 206         return NULL;
 207     }
 208 
 209     filename = crm_strdup_printf("%s/%s", dir, file);
 210     sigfilepath = crm_strdup_printf("%s/%s", dir, sigfile);
 211     free(sigfile);
 212 
 213     cib_status = pcmk_ok;
 214     root = retrieveCib(filename, sigfilepath);
 215     free(filename);
 216     free(sigfilepath);
 217 
 218     if (root == NULL) {
 219         crm_warn("Primary configuration corrupt or unusable, trying backups in %s", cib_root);
 220         lpc = scandir(cib_root, &namelist, cib_archive_filter, cib_archive_sort);
 221         if (lpc < 0) {
 222             crm_err("scandir(%s) failed: %s", cib_root, pcmk_rc_str(errno));
 223         }
 224     }
 225 
 226     while (root == NULL && lpc > 1) {
 227         crm_debug("Testing %d candidates", lpc);
 228 
 229         lpc--;
 230 
 231         filename = crm_strdup_printf("%s/%s", cib_root, namelist[lpc]->d_name);
 232         sigfile = crm_strdup_printf("%s.sig", filename);
 233 
 234         crm_info("Reading cluster configuration file %s (digest: %s)",
 235                  filename, sigfile);
 236         if (cib_file_read_and_verify(filename, sigfile, &root) < 0) {
 237             crm_warn("Continuing but %s will NOT be used.", filename);
 238         } else {
 239             crm_notice("Continuing with last valid configuration archive: %s", filename);
 240         }
 241 
 242         free(namelist[lpc]);
 243         free(filename);
 244         free(sigfile);
 245     }
 246     free(namelist);
 247 
 248     if (root == NULL) {
 249         root = createEmptyCib(0);
 250         crm_warn("Continuing with an empty configuration.");
 251     }
 252 
 253     if (cib_writes_enabled && use_valgrind &&
 254         (crm_is_true(use_valgrind) || strstr(use_valgrind, "pacemaker-based"))) {
 255 
 256         cib_writes_enabled = FALSE;
 257         crm_err("*** Disabling disk writes to avoid confusing Valgrind ***");
 258     }
 259 
 260     status = pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL);
 261     if (discard_status && status != NULL) {
 262         // Strip out the PCMK_XE_STATUS section if there is one
 263         free_xml(status);
 264         status = NULL;
 265     }
 266     if (status == NULL) {
 267         pcmk__xe_create(root, PCMK_XE_STATUS);
 268     }
 269 
 270     /* Do this before schema validation happens */
 271 
 272     /* fill in some defaults */
 273     name = PCMK_XA_ADMIN_EPOCH;
 274     value = crm_element_value(root, name);
 275     if (value == NULL) {
 276         crm_warn("No value for %s was specified in the configuration.", name);
 277         crm_warn("The recommended course of action is to shutdown,"
 278                  " run crm_verify and fix any errors it reports.");
 279         crm_warn("We will default to zero and continue but may get"
 280                  " confused about which configuration to use if"
 281                  " multiple nodes are powered up at the same time.");
 282         crm_xml_add_int(root, name, 0);
 283     }
 284 
 285     name = PCMK_XA_EPOCH;
 286     value = crm_element_value(root, name);
 287     if (value == NULL) {
 288         crm_xml_add_int(root, name, 0);
 289     }
 290 
 291     name = PCMK_XA_NUM_UPDATES;
 292     value = crm_element_value(root, name);
 293     if (value == NULL) {
 294         crm_xml_add_int(root, name, 0);
 295     }
 296 
 297     // Unset (DC should set appropriate value)
 298     pcmk__xe_remove_attr(root, PCMK_XA_DC_UUID);
 299 
 300     if (discard_status) {
 301         crm_log_xml_trace(root, "[on-disk]");
 302     }
 303 
 304     validation = crm_element_value(root, PCMK_XA_VALIDATE_WITH);
 305     if (!pcmk__configured_schema_validates(root)) {
 306         crm_err("CIB does not validate with %s",
 307                 pcmk__s(validation, "no schema specified"));
 308         cib_status = -pcmk_err_schema_validation;
 309 
 310     // @COMPAT Not specifying validate-with is deprecated since 2.1.8
 311     } else if (validation == NULL) {
 312         pcmk__update_schema(&root, NULL, false, false);
 313         validation = crm_element_value(root, PCMK_XA_VALIDATE_WITH);
 314         if (validation != NULL) {
 315             crm_notice("Enabling %s validation on"
 316                        " the existing (sane) configuration", validation);
 317         } else {
 318             crm_err("CIB does not validate with any known schema");
 319             cib_status = -pcmk_err_schema_validation;
 320         }
 321     }
 322 
 323     return root;
 324 }
 325 
 326 gboolean
 327 uninitializeCib(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 328 {
 329     xmlNode *tmp_cib = the_cib;
 330 
 331     if (tmp_cib == NULL) {
 332         crm_debug("The CIB has already been deallocated.");
 333         return FALSE;
 334     }
 335 
 336     the_cib = NULL;
 337 
 338     crm_debug("Deallocating the CIB.");
 339 
 340     free_xml(tmp_cib);
 341 
 342     crm_debug("The CIB has been deallocated.");
 343 
 344     return TRUE;
 345 }
 346 
 347 /*
 348  * This method will free the old CIB pointer on success and the new one
 349  * on failure.
 350  */
 351 int
 352 activateCibXml(xmlNode * new_cib, gboolean to_disk, const char *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 353 {
 354     if (new_cib) {
 355         xmlNode *saved_cib = the_cib;
 356 
 357         CRM_ASSERT(new_cib != saved_cib);
 358         the_cib = new_cib;
 359         free_xml(saved_cib);
 360         if (cib_writes_enabled && cib_status == pcmk_ok && to_disk) {
 361             crm_debug("Triggering CIB write for %s op", op);
 362             mainloop_set_trigger(cib_writer);
 363         }
 364         return pcmk_ok;
 365     }
 366 
 367     crm_err("Ignoring invalid CIB");
 368     if (the_cib) {
 369         crm_warn("Reverting to last known CIB");
 370     } else {
 371         crm_crit("Could not write out new CIB and no saved version to revert to");
 372     }
 373     return -ENODATA;
 374 }
 375 
 376 static void
 377 cib_diskwrite_complete(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)
     /* [previous][next][first][last][top][bottom][index][help] */
 378 {
 379     const char *errmsg = "Could not write CIB to disk";
 380 
 381     if ((exitcode != 0) && cib_writes_enabled) {
 382         cib_writes_enabled = FALSE;
 383         errmsg = "Disabling CIB disk writes after failure";
 384     }
 385 
 386     if ((signo == 0) && (exitcode == 0)) {
 387         crm_trace("Disk write [%d] succeeded", (int) pid);
 388 
 389     } else if (signo == 0) {
 390         crm_err("%s: process %d exited %d", errmsg, (int) pid, exitcode);
 391 
 392     } else {
 393         crm_err("%s: process %d terminated with signal %d (%s)%s",
 394                 errmsg, (int) pid, signo, strsignal(signo),
 395                 (core? " and dumped core" : ""));
 396     }
 397 
 398     mainloop_trigger_complete(cib_writer);
 399 }
 400 
 401 int
 402 write_cib_contents(gpointer p)
     /* [previous][next][first][last][top][bottom][index][help] */
 403 {
 404     int exit_rc = pcmk_ok;
 405     xmlNode *cib_local = NULL;
 406 
 407     /* Make a copy of the CIB to write (possibly in a forked child) */
 408     if (p) {
 409         /* Synchronous write out */
 410         cib_local = pcmk__xml_copy(NULL, p);
 411 
 412     } else {
 413         int pid = 0;
 414         int bb_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0);
 415 
 416         /* Turn it off before the fork() to avoid:
 417          * - 2 processes writing to the same shared mem
 418          * - the child needing to disable it
 419          *   (which would close it from underneath the parent)
 420          * This way, the shared mem files are already closed
 421          */
 422         qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
 423 
 424         pid = fork();
 425         if (pid < 0) {
 426             crm_err("Disabling disk writes after fork failure: %s", pcmk_rc_str(errno));
 427             cib_writes_enabled = FALSE;
 428             return FALSE;
 429         }
 430 
 431         if (pid) {
 432             /* Parent */
 433             mainloop_child_add(pid, 0, "disk-writer", NULL, cib_diskwrite_complete);
 434             if (bb_state == QB_LOG_STATE_ENABLED) {
 435                 /* Re-enable now that it it safe */
 436                 qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
 437             }
 438 
 439             return -1;          /* -1 means 'still work to do' */
 440         }
 441 
 442         /* Asynchronous write-out after a fork() */
 443 
 444         /* In theory, we can scribble on the_cib here and not affect the parent,
 445          * but let's be safe anyway.
 446          */
 447         cib_local = pcmk__xml_copy(NULL, the_cib);
 448     }
 449 
 450     /* Write the CIB */
 451     exit_rc = cib_file_write_with_digest(cib_local, cib_root, "cib.xml");
 452 
 453     /* A nonzero exit code will cause further writes to be disabled */
 454     free_xml(cib_local);
 455     if (p == NULL) {
 456         crm_exit_t exit_code = CRM_EX_OK;
 457 
 458         switch (exit_rc) {
 459             case pcmk_ok:
 460                 exit_code = CRM_EX_OK;
 461                 break;
 462             case pcmk_err_cib_modified:
 463                 exit_code = CRM_EX_DIGEST; // Existing CIB doesn't match digest
 464                 break;
 465             case pcmk_err_cib_backup: // Existing CIB couldn't be backed up
 466             case pcmk_err_cib_save:   // New CIB couldn't be saved
 467                 exit_code = CRM_EX_CANTCREAT;
 468                 break;
 469             default:
 470                 exit_code = CRM_EX_ERROR;
 471                 break;
 472         }
 473 
 474         /* Use _exit() because exit() could affect the parent adversely */
 475         _exit(exit_code);
 476     }
 477     return exit_rc;
 478 }

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