root/lib/cib/cib_file.c

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

DEFINITIONS

This source file includes following definitions.
  1. cib_file_is_live
  2. cib_file_perform_op_delegate
  3. load_file_cib
  4. cib_file_signon
  5. cib_file_write_live
  6. cib_file_signoff
  7. cib_file_free
  8. cib_file_inputfd
  9. cib_file_register_notification
  10. cib_file_set_connection_dnotify
  11. cib_file_client_id
  12. cib_file_new
  13. cib_file_verify_digest
  14. cib_file_read_and_verify
  15. cib_file_backup
  16. cib_file_prepare_xml
  17. cib_file_write_with_digest

   1 /*
   2  * Original copyright 2004 International Business Machines
   3  * Later changes copyright 2008-2023 the Pacemaker project contributors
   4  *
   5  * The version control history for this file may have further details.
   6  *
   7  * This source code is licensed under the GNU Lesser General Public License
   8  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   9  */
  10 
  11 #include <crm_internal.h>
  12 #include <unistd.h>
  13 #include <limits.h>
  14 #include <stdlib.h>
  15 #include <stdint.h>
  16 #include <stdio.h>
  17 #include <stdarg.h>
  18 #include <string.h>
  19 #include <pwd.h>
  20 
  21 #include <sys/stat.h>
  22 #include <sys/types.h>
  23 #include <glib.h>
  24 
  25 #include <crm/crm.h>
  26 #include <crm/cib/internal.h>
  27 #include <crm/msg_xml.h>
  28 #include <crm/common/ipc.h>
  29 #include <crm/common/xml.h>
  30 #include <crm/common/xml_internal.h>
  31 
  32 #define CIB_SERIES "cib"
  33 #define CIB_SERIES_MAX 100
  34 #define CIB_SERIES_BZIP FALSE /* Must be false because archived copies are
  35                                  created with hard links
  36                                */
  37 
  38 #define CIB_LIVE_NAME CIB_SERIES ".xml"
  39 
  40 enum cib_file_flags {
  41     cib_file_flag_dirty = (1 << 0),
  42     cib_file_flag_live  = (1 << 1),
  43 };
  44 
  45 typedef struct cib_file_opaque_s {
  46     uint32_t flags; // Group of enum cib_file_flags
  47     char *filename;
  48 } cib_file_opaque_t;
  49 
  50 struct cib_func_entry {
  51     const char *op;
  52     gboolean read_only;
  53     cib_op_t fn;
  54 };
  55 
  56 static struct cib_func_entry cib_file_ops[] = {
  57     { PCMK__CIB_REQUEST_QUERY,       TRUE,   cib_process_query },
  58     { PCMK__CIB_REQUEST_MODIFY,      FALSE,  cib_process_modify },
  59     { PCMK__CIB_REQUEST_APPLY_PATCH, FALSE,  cib_process_diff },
  60     { PCMK__CIB_REQUEST_BUMP,        FALSE,  cib_process_bump },
  61     { PCMK__CIB_REQUEST_REPLACE,     FALSE,  cib_process_replace },
  62     { PCMK__CIB_REQUEST_CREATE,      FALSE,  cib_process_create },
  63     { PCMK__CIB_REQUEST_DELETE,      FALSE,  cib_process_delete },
  64     { PCMK__CIB_REQUEST_ERASE,       FALSE,  cib_process_erase },
  65     { PCMK__CIB_REQUEST_UPGRADE,     FALSE,  cib_process_upgrade },
  66 };
  67 
  68 static xmlNode *in_mem_cib = NULL;
  69 
  70 /* cib_file_backup() and cib_file_write_with_digest() need to chown the
  71  * written files only in limited circumstances, so these variables allow
  72  * that to be indicated without affecting external callers
  73  */
  74 static uid_t cib_file_owner = 0;
  75 static uid_t cib_file_group = 0;
  76 static gboolean cib_do_chown = FALSE;
  77 
  78 #define cib_set_file_flags(cibfile, flags_to_set) do {                  \
  79         (cibfile)->flags = pcmk__set_flags_as(__func__, __LINE__,       \
  80                                               LOG_TRACE, "CIB file",    \
  81                                               cibfile->filename,        \
  82                                               (cibfile)->flags,         \
  83                                               (flags_to_set),           \
  84                                               #flags_to_set);           \
  85     } while (0)
  86 
  87 #define cib_clear_file_flags(cibfile, flags_to_clear) do {              \
  88         (cibfile)->flags = pcmk__clear_flags_as(__func__, __LINE__,     \
  89                                                 LOG_TRACE, "CIB file",  \
  90                                                 cibfile->filename,      \
  91                                                 (cibfile)->flags,       \
  92                                                 (flags_to_clear),       \
  93                                                 #flags_to_clear);       \
  94     } while (0)
  95 
  96 /*!
  97  * \internal
  98  * \brief Check whether a file is the live CIB
  99  *
 100  * \param[in] filename Name of file to check
 101  *
 102  * \return TRUE if file exists and its real path is same as live CIB's
 103  */
 104 static gboolean
 105 cib_file_is_live(const char *filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 106 {
 107     gboolean same = FALSE;
 108 
 109     if (filename != NULL) {
 110         // Canonicalize file names for true comparison
 111         char *real_filename = NULL;
 112 
 113         if (pcmk__real_path(filename, &real_filename) == pcmk_rc_ok) {
 114             char *real_livename = NULL;
 115 
 116             if (pcmk__real_path(CRM_CONFIG_DIR "/" CIB_LIVE_NAME,
 117                                 &real_livename) == pcmk_rc_ok) {
 118                 same = !strcmp(real_filename, real_livename);
 119                 free(real_livename);
 120             }
 121             free(real_filename);
 122         }
 123     }
 124     return same;
 125 }
 126 
 127 static int
 128 cib_file_perform_op_delegate(cib_t *cib, const char *op, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 129                              const char *section, xmlNode *data,
 130                              xmlNode **output_data, int call_options,
 131                              const char *user_name)
 132 {
 133     int rc = pcmk_ok;
 134     char *effective_user = NULL;
 135     gboolean query = FALSE;
 136     gboolean changed = FALSE;
 137     xmlNode *request = NULL;
 138     xmlNode *output = NULL;
 139     xmlNode *cib_diff = NULL;
 140     xmlNode *result_cib = NULL;
 141     cib_op_t *fn = NULL;
 142     int lpc = 0;
 143     static int max_msg_types = PCMK__NELEM(cib_file_ops);
 144     cib_file_opaque_t *private = cib->variant_opaque;
 145 
 146     crm_info("Handling %s operation for %s as %s",
 147              (op? op : "invalid"), (section? section : "entire CIB"),
 148              (user_name? user_name : "default user"));
 149 
 150     cib__set_call_options(call_options, "file operation",
 151                           cib_no_mtime|cib_inhibit_bcast|cib_scope_local);
 152 
 153     if (cib->state == cib_disconnected) {
 154         return -ENOTCONN;
 155     }
 156 
 157     if (output_data != NULL) {
 158         *output_data = NULL;
 159     }
 160 
 161     if (op == NULL) {
 162         return -EINVAL;
 163     }
 164 
 165     for (lpc = 0; lpc < max_msg_types; lpc++) {
 166         if (pcmk__str_eq(op, cib_file_ops[lpc].op, pcmk__str_casei)) {
 167             fn = &(cib_file_ops[lpc].fn);
 168             query = cib_file_ops[lpc].read_only;
 169             break;
 170         }
 171     }
 172 
 173     if (fn == NULL) {
 174         return -EPROTONOSUPPORT;
 175     }
 176 
 177     cib->call_id++;
 178     request = cib_create_op(cib->call_id, op, host, section, data, call_options,
 179                             user_name);
 180     if(user_name) {
 181         crm_xml_add(request, XML_ACL_TAG_USER, user_name);
 182     }
 183 
 184     /* Mirror the logic in cib_prepare_common() */
 185     if (section != NULL && data != NULL && pcmk__str_eq(crm_element_name(data), XML_TAG_CIB, pcmk__str_none)) {
 186         data = pcmk_find_cib_element(data, section);
 187     }
 188 
 189     rc = cib_perform_op(op, call_options, fn, query,
 190                         section, request, data, TRUE, &changed, in_mem_cib, &result_cib, &cib_diff,
 191                         &output);
 192 
 193     free_xml(request);
 194     if (rc == -pcmk_err_schema_validation) {
 195         validate_xml_verbose(result_cib);
 196     }
 197 
 198     if (rc != pcmk_ok) {
 199         free_xml(result_cib);
 200 
 201     } else if (query == FALSE) {
 202         pcmk__output_t *out = NULL;
 203 
 204         rc = pcmk_rc2legacy(pcmk__log_output_new(&out));
 205         CRM_CHECK(rc == pcmk_ok, goto done);
 206 
 207         pcmk__output_set_log_level(out, LOG_DEBUG);
 208         rc = out->message(out, "xml-patchset", cib_diff);
 209         out->finish(out, pcmk_rc2exitc(rc), true, NULL);
 210         pcmk__output_free(out);
 211         rc = pcmk_ok;
 212 
 213         free_xml(in_mem_cib);
 214         in_mem_cib = result_cib;
 215         cib_set_file_flags(private, cib_file_flag_dirty);
 216     }
 217 
 218     if (cib->op_callback != NULL) {
 219         cib->op_callback(NULL, cib->call_id, rc, output);
 220     }
 221 
 222     if ((output_data != NULL) && (output != NULL)) {
 223         *output_data = (output == in_mem_cib)? copy_xml(output) : output;
 224     }
 225 
 226 done:
 227     free_xml(cib_diff);
 228 
 229     if ((output_data == NULL) && (output != in_mem_cib)) {
 230         /* Don't free output if we're still using it. (output_data != NULL)
 231          * means we may have assigned *output_data = output above.
 232          */
 233         free_xml(output);
 234     }
 235     free(effective_user);
 236     return rc;
 237 }
 238 
 239 /*!
 240  * \internal
 241  * \brief Read CIB from disk and validate it against XML schema
 242  *
 243  * \param[in] filename Name of file to read CIB from
 244  *
 245  * \return pcmk_ok on success,
 246  *         -ENXIO if file does not exist (or stat() otherwise fails), or
 247  *         -pcmk_err_schema_validation if XML doesn't parse or validate
 248  * \note If filename is the live CIB, this will *not* verify its digest,
 249  *       though that functionality would be trivial to add here.
 250  *       Also, this will *not* verify that the file is writable,
 251  *       because some callers might not need to write.
 252  */
 253 static int
 254 load_file_cib(const char *filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 255 {
 256     struct stat buf;
 257     xmlNode *root = NULL;
 258 
 259     /* Ensure file is readable */
 260     if (strcmp(filename, "-") && (stat(filename, &buf) < 0)) {
 261         return -ENXIO;
 262     }
 263 
 264     /* Parse XML from file */
 265     root = filename2xml(filename);
 266     if (root == NULL) {
 267         return -pcmk_err_schema_validation;
 268     }
 269 
 270     /* Add a status section if not already present */
 271     if (find_xml_node(root, XML_CIB_TAG_STATUS, FALSE) == NULL) {
 272         create_xml_node(root, XML_CIB_TAG_STATUS);
 273     }
 274 
 275     /* Validate XML against its specified schema */
 276     if (validate_xml(root, NULL, TRUE) == FALSE) {
 277         const char *schema = crm_element_value(root, XML_ATTR_VALIDATION);
 278 
 279         crm_err("CIB does not validate against %s", schema);
 280         free_xml(root);
 281         return -pcmk_err_schema_validation;
 282     }
 283 
 284     /* Remember the parsed XML for later use */
 285     in_mem_cib = root;
 286     return pcmk_ok;
 287 }
 288 
 289 static int
 290 cib_file_signon(cib_t *cib, const char *name, enum cib_conn_type type)
     /* [previous][next][first][last][top][bottom][index][help] */
 291 {
 292     int rc = pcmk_ok;
 293     cib_file_opaque_t *private = cib->variant_opaque;
 294 
 295     if (private->filename == NULL) {
 296         rc = -EINVAL;
 297     } else {
 298         rc = load_file_cib(private->filename);
 299     }
 300 
 301     if (rc == pcmk_ok) {
 302         crm_debug("Opened connection to local file '%s' for %s",
 303                   private->filename, name);
 304         cib->state = cib_connected_command;
 305         cib->type = cib_command;
 306 
 307     } else {
 308         crm_info("Connection to local file '%s' for %s failed: %s\n",
 309                  private->filename, name, pcmk_strerror(rc));
 310     }
 311     return rc;
 312 }
 313 
 314 /*!
 315  * \internal
 316  * \brief Write out the in-memory CIB to a live CIB file
 317  *
 318  * param[in,out] path  Full path to file to write
 319  *
 320  * \return 0 on success, -1 on failure
 321  */
 322 static int
 323 cib_file_write_live(char *path)
     /* [previous][next][first][last][top][bottom][index][help] */
 324 {
 325     uid_t uid = geteuid();
 326     struct passwd *daemon_pwent;
 327     char *sep = strrchr(path, '/');
 328     const char *cib_dirname, *cib_filename;
 329     int rc = 0;
 330 
 331     /* Get the desired uid/gid */
 332     errno = 0;
 333     daemon_pwent = getpwnam(CRM_DAEMON_USER);
 334     if (daemon_pwent == NULL) {
 335         crm_perror(LOG_ERR, "Could not find %s user", CRM_DAEMON_USER);
 336         return -1;
 337     }
 338 
 339     /* If we're root, we can change the ownership;
 340      * if we're daemon, anything we create will be OK;
 341      * otherwise, block access so we don't create wrong owner
 342      */
 343     if ((uid != 0) && (uid != daemon_pwent->pw_uid)) {
 344         crm_perror(LOG_ERR, "Must be root or %s to modify live CIB",
 345                    CRM_DAEMON_USER);
 346         return 0;
 347     }
 348 
 349     /* fancy footwork to separate dirname from filename
 350      * (we know the canonical name maps to the live CIB,
 351      * but the given name might be relative, or symlinked)
 352      */
 353     if (sep == NULL) { /* no directory component specified */
 354         cib_dirname = "./";
 355         cib_filename = path;
 356     } else if (sep == path) { /* given name is in / */
 357         cib_dirname = "/";
 358         cib_filename = path + 1;
 359     } else { /* typical case; split given name into parts */
 360         *sep = '\0';
 361         cib_dirname = path;
 362         cib_filename = sep + 1;
 363     }
 364 
 365     /* if we're root, we want to update the file ownership */
 366     if (uid == 0) {
 367         cib_file_owner = daemon_pwent->pw_uid;
 368         cib_file_group = daemon_pwent->pw_gid;
 369         cib_do_chown = TRUE;
 370     }
 371 
 372     /* write the file */
 373     if (cib_file_write_with_digest(in_mem_cib, cib_dirname,
 374                                    cib_filename) != pcmk_ok) {
 375         rc = -1;
 376     }
 377 
 378     /* turn off file ownership changes, for other callers */
 379     if (uid == 0) {
 380         cib_do_chown = FALSE;
 381     }
 382 
 383     /* undo fancy stuff */
 384     if ((sep != NULL) && (*sep == '\0')) {
 385         *sep = '/';
 386     }
 387 
 388     return rc;
 389 }
 390 
 391 /*!
 392  * \internal
 393  * \brief Sign-off method for CIB file variants
 394  *
 395  * This will write the file to disk if needed, and free the in-memory CIB. If
 396  * the file is the live CIB, it will compute and write a signature as well.
 397  *
 398  * \param[in,out] cib  CIB object to sign off
 399  *
 400  * \return pcmk_ok on success, pcmk_err_generic on failure
 401  * \todo This method should refuse to write the live CIB if the CIB manager is
 402  *       running.
 403  */
 404 static int
 405 cib_file_signoff(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 406 {
 407     int rc = pcmk_ok;
 408     cib_file_opaque_t *private = cib->variant_opaque;
 409 
 410     crm_debug("Disconnecting from the CIB manager");
 411     cib->state = cib_disconnected;
 412     cib->type = cib_no_connection;
 413 
 414     /* If the in-memory CIB has been changed, write it to disk */
 415     if (pcmk_is_set(private->flags, cib_file_flag_dirty)) {
 416 
 417         /* If this is the live CIB, write it out with a digest */
 418         if (pcmk_is_set(private->flags, cib_file_flag_live)) {
 419             if (cib_file_write_live(private->filename) < 0) {
 420                 rc = pcmk_err_generic;
 421             }
 422 
 423         /* Otherwise, it's a simple write */
 424         } else {
 425             gboolean do_bzip = pcmk__ends_with_ext(private->filename, ".bz2");
 426 
 427             if (write_xml_file(in_mem_cib, private->filename, do_bzip) <= 0) {
 428                 rc = pcmk_err_generic;
 429             }
 430         }
 431 
 432         if (rc == pcmk_ok) {
 433             crm_info("Wrote CIB to %s", private->filename);
 434             cib_clear_file_flags(private, cib_file_flag_dirty);
 435         } else {
 436             crm_err("Could not write CIB to %s", private->filename);
 437         }
 438     }
 439 
 440     /* Free the in-memory CIB */
 441     free_xml(in_mem_cib);
 442     in_mem_cib = NULL;
 443     return rc;
 444 }
 445 
 446 static int
 447 cib_file_free(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 448 {
 449     int rc = pcmk_ok;
 450 
 451     if (cib->state != cib_disconnected) {
 452         rc = cib_file_signoff(cib);
 453     }
 454 
 455     if (rc == pcmk_ok) {
 456         cib_file_opaque_t *private = cib->variant_opaque;
 457 
 458         free(private->filename);
 459         free(cib->cmds);
 460         free(private);
 461         free(cib);
 462 
 463     } else {
 464         fprintf(stderr, "Couldn't sign off: %d\n", rc);
 465     }
 466 
 467     return rc;
 468 }
 469 
 470 static int
 471 cib_file_inputfd(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 472 {
 473     return -EPROTONOSUPPORT;
 474 }
 475 
 476 static int
 477 cib_file_register_notification(cib_t *cib, const char *callback, int enabled)
     /* [previous][next][first][last][top][bottom][index][help] */
 478 {
 479     return -EPROTONOSUPPORT;
 480 }
 481 
 482 static int
 483 cib_file_set_connection_dnotify(cib_t *cib,
     /* [previous][next][first][last][top][bottom][index][help] */
 484                                 void (*dnotify) (gpointer user_data))
 485 {
 486     return -EPROTONOSUPPORT;
 487 }
 488 
 489 /*!
 490  * \internal
 491  * \brief Get the given CIB connection's unique client identifier
 492  *
 493  * \param[in]  cib       CIB connection
 494  * \param[out] async_id  If not \p NULL, where to store asynchronous client ID
 495  * \param[out] sync_id   If not \p NULL, where to store synchronous client ID
 496  *
 497  * \return Legacy Pacemaker return code (specifically, \p -EPROTONOSUPPORT)
 498  *
 499  * \note This is the \p cib_file variant implementation of
 500  *       \p cib_api_operations_t:client_id().
 501  * \note A \p cib_file object doesn't connect to the CIB and is never assigned a
 502  *       client ID.
 503  */
 504 static int
 505 cib_file_client_id(const cib_t *cib, const char **async_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 506                    const char **sync_id)
 507 {
 508     if (async_id != NULL) {
 509         *async_id = NULL;
 510     }
 511     if (sync_id != NULL) {
 512         *sync_id = NULL;
 513     }
 514     return -EPROTONOSUPPORT;
 515 }
 516 
 517 cib_t *
 518 cib_file_new(const char *cib_location)
     /* [previous][next][first][last][top][bottom][index][help] */
 519 {
 520     cib_file_opaque_t *private = NULL;
 521     cib_t *cib = cib_new_variant();
 522 
 523     if (cib == NULL) {
 524         return NULL;
 525     }
 526 
 527     private = calloc(1, sizeof(cib_file_opaque_t));
 528 
 529     if (private == NULL) {
 530         free(cib);
 531         return NULL;
 532     }
 533 
 534     cib->variant = cib_file;
 535     cib->variant_opaque = private;
 536 
 537     if (cib_location == NULL) {
 538         cib_location = getenv("CIB_file");
 539         CRM_CHECK(cib_location != NULL, return NULL); // Shouldn't be possible
 540     }
 541     private->flags = 0;
 542     if (cib_file_is_live(cib_location)) {
 543         cib_set_file_flags(private, cib_file_flag_live);
 544         crm_trace("File %s detected as live CIB", cib_location);
 545     }
 546     private->filename = strdup(cib_location);
 547 
 548     /* assign variant specific ops */
 549     cib->delegate_fn = cib_file_perform_op_delegate;
 550     cib->cmds->signon = cib_file_signon;
 551     cib->cmds->signoff = cib_file_signoff;
 552     cib->cmds->free = cib_file_free;
 553     cib->cmds->inputfd = cib_file_inputfd;
 554 
 555     cib->cmds->register_notification = cib_file_register_notification;
 556     cib->cmds->set_connection_dnotify = cib_file_set_connection_dnotify;
 557 
 558     cib->cmds->client_id = cib_file_client_id;
 559 
 560     return cib;
 561 }
 562 
 563 /*!
 564  * \internal
 565  * \brief Compare the calculated digest of an XML tree against a signature file
 566  *
 567  * \param[in] root     Root of XML tree to compare
 568  * \param[in] sigfile  Name of signature file containing digest to compare
 569  *
 570  * \return TRUE if digests match or signature file does not exist, else FALSE
 571  */
 572 static gboolean
 573 cib_file_verify_digest(xmlNode *root, const char *sigfile)
     /* [previous][next][first][last][top][bottom][index][help] */
 574 {
 575     gboolean passed = FALSE;
 576     char *expected;
 577     int rc = pcmk__file_contents(sigfile, &expected);
 578 
 579     switch (rc) {
 580         case pcmk_rc_ok:
 581             if (expected == NULL) {
 582                 crm_err("On-disk digest at %s is empty", sigfile);
 583                 return FALSE;
 584             }
 585             break;
 586         case ENOENT:
 587             crm_warn("No on-disk digest present at %s", sigfile);
 588             return TRUE;
 589         default:
 590             crm_err("Could not read on-disk digest from %s: %s",
 591                     sigfile, pcmk_rc_str(rc));
 592             return FALSE;
 593     }
 594     passed = pcmk__verify_digest(root, expected);
 595     free(expected);
 596     return passed;
 597 }
 598 
 599 /*!
 600  * \internal
 601  * \brief Read an XML tree from a file and verify its digest
 602  *
 603  * \param[in]  filename  Name of XML file to read
 604  * \param[in]  sigfile   Name of signature file containing digest to compare
 605  * \param[out] root      If non-NULL, will be set to pointer to parsed XML tree
 606  *
 607  * \return 0 if file was successfully read, parsed and verified, otherwise:
 608  *         -errno on stat() failure,
 609  *         -pcmk_err_cib_corrupt if file size is 0 or XML is not parseable, or
 610  *         -pcmk_err_cib_modified if digests do not match
 611  * \note If root is non-NULL, it is the caller's responsibility to free *root on
 612  *       successful return.
 613  */
 614 int
 615 cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **root)
     /* [previous][next][first][last][top][bottom][index][help] */
 616 {
 617     int s_res;
 618     struct stat buf;
 619     char *local_sigfile = NULL;
 620     xmlNode *local_root = NULL;
 621 
 622     CRM_ASSERT(filename != NULL);
 623     if (root) {
 624         *root = NULL;
 625     }
 626 
 627     /* Verify that file exists and its size is nonzero */
 628     s_res = stat(filename, &buf);
 629     if (s_res < 0) {
 630         crm_perror(LOG_WARNING, "Could not verify cluster configuration file %s", filename);
 631         return -errno;
 632     } else if (buf.st_size == 0) {
 633         crm_warn("Cluster configuration file %s is corrupt (size is zero)", filename);
 634         return -pcmk_err_cib_corrupt;
 635     }
 636 
 637     /* Parse XML */
 638     local_root = filename2xml(filename);
 639     if (local_root == NULL) {
 640         crm_warn("Cluster configuration file %s is corrupt (unparseable as XML)", filename);
 641         return -pcmk_err_cib_corrupt;
 642     }
 643 
 644     /* If sigfile is not specified, use original file name plus .sig */
 645     if (sigfile == NULL) {
 646         sigfile = local_sigfile = crm_strdup_printf("%s.sig", filename);
 647     }
 648 
 649     /* Verify that digests match */
 650     if (cib_file_verify_digest(local_root, sigfile) == FALSE) {
 651         free(local_sigfile);
 652         free_xml(local_root);
 653         return -pcmk_err_cib_modified;
 654     }
 655 
 656     free(local_sigfile);
 657     if (root) {
 658         *root = local_root;
 659     } else {
 660         free_xml(local_root);
 661     }
 662     return pcmk_ok;
 663 }
 664 
 665 /*!
 666  * \internal
 667  * \brief Back up a CIB
 668  *
 669  * \param[in] cib_dirname Directory containing CIB file and backups
 670  * \param[in] cib_filename Name (relative to cib_dirname) of CIB file to back up
 671  *
 672  * \return 0 on success, -1 on error
 673  */
 674 static int
 675 cib_file_backup(const char *cib_dirname, const char *cib_filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 676 {
 677     int rc = 0;
 678     unsigned int seq;
 679     char *cib_path = crm_strdup_printf("%s/%s", cib_dirname, cib_filename);
 680     char *cib_digest = crm_strdup_printf("%s.sig", cib_path);
 681     char *backup_path;
 682     char *backup_digest;
 683 
 684     // Determine backup and digest file names
 685     if (pcmk__read_series_sequence(cib_dirname, CIB_SERIES,
 686                                    &seq) != pcmk_rc_ok) {
 687         // @TODO maybe handle errors better ...
 688         seq = 0;
 689     }
 690     backup_path = pcmk__series_filename(cib_dirname, CIB_SERIES, seq,
 691                                         CIB_SERIES_BZIP);
 692     backup_digest = crm_strdup_printf("%s.sig", backup_path);
 693 
 694     /* Remove the old backups if they exist */
 695     unlink(backup_path);
 696     unlink(backup_digest);
 697 
 698     /* Back up the CIB, by hard-linking it to the backup name */
 699     if ((link(cib_path, backup_path) < 0) && (errno != ENOENT)) {
 700         crm_perror(LOG_ERR, "Could not archive %s by linking to %s",
 701                    cib_path, backup_path);
 702         rc = -1;
 703 
 704     /* Back up the CIB signature similarly */
 705     } else if ((link(cib_digest, backup_digest) < 0) && (errno != ENOENT)) {
 706         crm_perror(LOG_ERR, "Could not archive %s by linking to %s",
 707                    cib_digest, backup_digest);
 708         rc = -1;
 709 
 710     /* Update the last counter and ensure everything is sync'd to media */
 711     } else {
 712         pcmk__write_series_sequence(cib_dirname, CIB_SERIES, ++seq,
 713                                     CIB_SERIES_MAX);
 714         if (cib_do_chown) {
 715             int rc2;
 716 
 717             if ((chown(backup_path, cib_file_owner, cib_file_group) < 0)
 718                     && (errno != ENOENT)) {
 719                 crm_perror(LOG_ERR, "Could not set owner of %s", backup_path);
 720                 rc = -1;
 721             }
 722             if ((chown(backup_digest, cib_file_owner, cib_file_group) < 0)
 723                     && (errno != ENOENT)) {
 724                 crm_perror(LOG_ERR, "Could not set owner of %s", backup_digest);
 725                 rc = -1;
 726             }
 727             rc2 = pcmk__chown_series_sequence(cib_dirname, CIB_SERIES,
 728                                               cib_file_owner, cib_file_group);
 729             if (rc2 != pcmk_rc_ok) {
 730                 crm_err("Could not set owner of sequence file in %s: %s",
 731                         cib_dirname, pcmk_rc_str(rc2));
 732                 rc = -1;
 733             }
 734         }
 735         pcmk__sync_directory(cib_dirname);
 736         crm_info("Archived previous version as %s", backup_path);
 737     }
 738 
 739     free(cib_path);
 740     free(cib_digest);
 741     free(backup_path);
 742     free(backup_digest);
 743     return rc;
 744 }
 745 
 746 /*!
 747  * \internal
 748  * \brief Prepare CIB XML to be written to disk
 749  *
 750  * Set num_updates to 0, set cib-last-written to the current timestamp,
 751  * and strip out the status section.
 752  *
 753  * \param[in,out] root  Root of CIB XML tree
 754  *
 755  * \return void
 756  */
 757 static void
 758 cib_file_prepare_xml(xmlNode *root)
     /* [previous][next][first][last][top][bottom][index][help] */
 759 {
 760     xmlNode *cib_status_root = NULL;
 761 
 762     /* Always write out with num_updates=0 and current last-written timestamp */
 763     crm_xml_add(root, XML_ATTR_NUMUPDATES, "0");
 764     pcmk__xe_add_last_written(root);
 765 
 766     /* Delete status section before writing to file, because
 767      * we discard it on startup anyway, and users get confused by it */
 768     cib_status_root = find_xml_node(root, XML_CIB_TAG_STATUS, TRUE);
 769     CRM_LOG_ASSERT(cib_status_root != NULL);
 770     if (cib_status_root != NULL) {
 771         free_xml(cib_status_root);
 772     }
 773 }
 774 
 775 /*!
 776  * \internal
 777  * \brief Write CIB to disk, along with a signature file containing its digest
 778  *
 779  * \param[in,out] cib_root      Root of XML tree to write
 780  * \param[in]     cib_dirname   Directory containing CIB and signature files
 781  * \param[in]     cib_filename  Name (relative to cib_dirname) of file to write
 782  *
 783  * \return pcmk_ok on success,
 784  *         pcmk_err_cib_modified if existing cib_filename doesn't match digest,
 785  *         pcmk_err_cib_backup if existing cib_filename couldn't be backed up,
 786  *         or pcmk_err_cib_save if new cib_filename couldn't be saved
 787  */
 788 int
 789 cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname,
     /* [previous][next][first][last][top][bottom][index][help] */
 790                            const char *cib_filename)
 791 {
 792     int exit_rc = pcmk_ok;
 793     int rc, fd;
 794     char *digest = NULL;
 795 
 796     /* Detect CIB version for diagnostic purposes */
 797     const char *epoch = crm_element_value(cib_root, XML_ATTR_GENERATION);
 798     const char *admin_epoch = crm_element_value(cib_root,
 799                                                 XML_ATTR_GENERATION_ADMIN);
 800 
 801     /* Determine full CIB and signature pathnames */
 802     char *cib_path = crm_strdup_printf("%s/%s", cib_dirname, cib_filename);
 803     char *digest_path = crm_strdup_printf("%s.sig", cib_path);
 804 
 805     /* Create temporary file name patterns for writing out CIB and signature */
 806     char *tmp_cib = crm_strdup_printf("%s/cib.XXXXXX", cib_dirname);
 807     char *tmp_digest = crm_strdup_printf("%s/cib.XXXXXX", cib_dirname);
 808 
 809     CRM_ASSERT((cib_path != NULL) && (digest_path != NULL)
 810                && (tmp_cib != NULL) && (tmp_digest != NULL));
 811 
 812     /* Ensure the admin didn't modify the existing CIB underneath us */
 813     crm_trace("Reading cluster configuration file %s", cib_path);
 814     rc = cib_file_read_and_verify(cib_path, NULL, NULL);
 815     if ((rc != pcmk_ok) && (rc != -ENOENT)) {
 816         crm_err("%s was manually modified while the cluster was active!",
 817                 cib_path);
 818         exit_rc = pcmk_err_cib_modified;
 819         goto cleanup;
 820     }
 821 
 822     /* Back up the existing CIB */
 823     if (cib_file_backup(cib_dirname, cib_filename) < 0) {
 824         exit_rc = pcmk_err_cib_backup;
 825         goto cleanup;
 826     }
 827 
 828     crm_debug("Writing CIB to disk");
 829     umask(S_IWGRP | S_IWOTH | S_IROTH);
 830     cib_file_prepare_xml(cib_root);
 831 
 832     /* Write the CIB to a temporary file, so we can deploy (near) atomically */
 833     fd = mkstemp(tmp_cib);
 834     if (fd < 0) {
 835         crm_perror(LOG_ERR, "Couldn't open temporary file %s for writing CIB",
 836                    tmp_cib);
 837         exit_rc = pcmk_err_cib_save;
 838         goto cleanup;
 839     }
 840 
 841     /* Protect the temporary file */
 842     if (fchmod(fd, S_IRUSR | S_IWUSR) < 0) {
 843         crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
 844                    tmp_cib);
 845         exit_rc = pcmk_err_cib_save;
 846         goto cleanup;
 847     }
 848     if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) {
 849         crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
 850                    tmp_cib);
 851         exit_rc = pcmk_err_cib_save;
 852         goto cleanup;
 853     }
 854 
 855     /* Write out the CIB */
 856     if (write_xml_fd(cib_root, tmp_cib, fd, FALSE) <= 0) {
 857         crm_err("Changes couldn't be written to %s", tmp_cib);
 858         exit_rc = pcmk_err_cib_save;
 859         goto cleanup;
 860     }
 861 
 862     /* Calculate CIB digest */
 863     digest = calculate_on_disk_digest(cib_root);
 864     CRM_ASSERT(digest != NULL);
 865     crm_info("Wrote version %s.%s.0 of the CIB to disk (digest: %s)",
 866              (admin_epoch ? admin_epoch : "0"), (epoch ? epoch : "0"), digest);
 867 
 868     /* Write the CIB digest to a temporary file */
 869     fd = mkstemp(tmp_digest);
 870     if (fd < 0) {
 871         crm_perror(LOG_ERR, "Could not create temporary file for CIB digest");
 872         exit_rc = pcmk_err_cib_save;
 873         goto cleanup;
 874     }
 875     if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) {
 876         crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
 877                    tmp_cib);
 878         exit_rc = pcmk_err_cib_save;
 879         close(fd);
 880         goto cleanup;
 881     }
 882     rc = pcmk__write_sync(fd, digest);
 883     if (rc != pcmk_rc_ok) {
 884         crm_err("Could not write digest to %s: %s",
 885                 tmp_digest, pcmk_rc_str(rc));
 886         exit_rc = pcmk_err_cib_save;
 887         close(fd);
 888         goto cleanup;
 889     }
 890     close(fd);
 891     crm_debug("Wrote digest %s to disk", digest);
 892 
 893     /* Verify that what we wrote is sane */
 894     crm_info("Reading cluster configuration file %s (digest: %s)",
 895              tmp_cib, tmp_digest);
 896     rc = cib_file_read_and_verify(tmp_cib, tmp_digest, NULL);
 897     CRM_ASSERT(rc == 0);
 898 
 899     /* Rename temporary files to live, and sync directory changes to media */
 900     crm_debug("Activating %s", tmp_cib);
 901     if (rename(tmp_cib, cib_path) < 0) {
 902         crm_perror(LOG_ERR, "Couldn't rename %s as %s", tmp_cib, cib_path);
 903         exit_rc = pcmk_err_cib_save;
 904     }
 905     if (rename(tmp_digest, digest_path) < 0) {
 906         crm_perror(LOG_ERR, "Couldn't rename %s as %s", tmp_digest,
 907                    digest_path);
 908         exit_rc = pcmk_err_cib_save;
 909     }
 910     pcmk__sync_directory(cib_dirname);
 911 
 912   cleanup:
 913     free(cib_path);
 914     free(digest_path);
 915     free(digest);
 916     free(tmp_digest);
 917     free(tmp_cib);
 918     return exit_rc;
 919 }

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