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

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