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

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