This source file includes following definitions.
- cib_file_is_live
 
- cib_file_perform_op_delegate
 
- load_file_cib
 
- cib_file_signon
 
- cib_file_write_live
 
- cib_file_signoff
 
- cib_file_free
 
- cib_file_inputfd
 
- cib_file_register_notification
 
- cib_file_set_connection_dnotify
 
- cib_file_client_id
 
- cib_file_new
 
- cib_file_verify_digest
 
- cib_file_read_and_verify
 
- cib_file_backup
 
- cib_file_prepare_xml
 
- cib_file_write_with_digest
 
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   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 
  35 
  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; 
  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 
  71 
  72 
  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 
  98 
  99 
 100 
 101 
 102 
 103 
 104 static gboolean
 105 cib_file_is_live(const char *filename)
     
 106 {
 107     gboolean same = FALSE;
 108 
 109     if (filename != NULL) {
 110         
 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,
     
 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     
 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         
 231 
 232 
 233         free_xml(output);
 234     }
 235     free(effective_user);
 236     return rc;
 237 }
 238 
 239 
 240 
 241 
 242 
 243 
 244 
 245 
 246 
 247 
 248 
 249 
 250 
 251 
 252 
 253 static int
 254 load_file_cib(const char *filename)
     
 255 {
 256     struct stat buf;
 257     xmlNode *root = NULL;
 258 
 259     
 260     if (strcmp(filename, "-") && (stat(filename, &buf) < 0)) {
 261         return -ENXIO;
 262     }
 263 
 264     
 265     root = filename2xml(filename);
 266     if (root == NULL) {
 267         return -pcmk_err_schema_validation;
 268     }
 269 
 270     
 271     if (find_xml_node(root, XML_CIB_TAG_STATUS, FALSE) == NULL) {
 272         create_xml_node(root, XML_CIB_TAG_STATUS);
 273     }
 274 
 275     
 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     
 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)
     
 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 
 316 
 317 
 318 
 319 
 320 
 321 
 322 static int
 323 cib_file_write_live(char *path)
     
 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     
 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     
 340 
 341 
 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     
 350 
 351 
 352 
 353     if (sep == NULL) { 
 354         cib_dirname = "./";
 355         cib_filename = path;
 356     } else if (sep == path) { 
 357         cib_dirname = "/";
 358         cib_filename = path + 1;
 359     } else { 
 360         *sep = '\0';
 361         cib_dirname = path;
 362         cib_filename = sep + 1;
 363     }
 364 
 365     
 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     
 373     if (cib_file_write_with_digest(in_mem_cib, cib_dirname,
 374                                    cib_filename) != pcmk_ok) {
 375         rc = -1;
 376     }
 377 
 378     
 379     if (uid == 0) {
 380         cib_do_chown = FALSE;
 381     }
 382 
 383     
 384     if ((sep != NULL) && (*sep == '\0')) {
 385         *sep = '/';
 386     }
 387 
 388     return rc;
 389 }
 390 
 391 
 392 
 393 
 394 
 395 
 396 
 397 
 398 
 399 
 400 
 401 
 402 
 403 
 404 static int
 405 cib_file_signoff(cib_t *cib)
     
 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     
 415     if (pcmk_is_set(private->flags, cib_file_flag_dirty)) {
 416 
 417         
 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         
 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     
 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)
     
 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)
     
 472 {
 473     return -EPROTONOSUPPORT;
 474 }
 475 
 476 static int
 477 cib_file_register_notification(cib_t *cib, const char *callback, int enabled)
     
 478 {
 479     return -EPROTONOSUPPORT;
 480 }
 481 
 482 static int
 483 cib_file_set_connection_dnotify(cib_t *cib,
     
 484                                 void (*dnotify) (gpointer user_data))
 485 {
 486     return -EPROTONOSUPPORT;
 487 }
 488 
 489 
 490 
 491 
 492 
 493 
 494 
 495 
 496 
 497 
 498 
 499 
 500 
 501 
 502 
 503 
 504 static int
 505 cib_file_client_id(const cib_t *cib, const char **async_id,
     
 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)
     
 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); 
 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     
 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 
 565 
 566 
 567 
 568 
 569 
 570 
 571 
 572 static gboolean
 573 cib_file_verify_digest(xmlNode *root, const char *sigfile)
     
 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 
 601 
 602 
 603 
 604 
 605 
 606 
 607 
 608 
 609 
 610 
 611 
 612 
 613 
 614 int
 615 cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **root)
     
 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     
 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     
 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     
 645     if (sigfile == NULL) {
 646         sigfile = local_sigfile = crm_strdup_printf("%s.sig", filename);
 647     }
 648 
 649     
 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 
 667 
 668 
 669 
 670 
 671 
 672 
 673 
 674 static int
 675 cib_file_backup(const char *cib_dirname, const char *cib_filename)
     
 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     
 685     if (pcmk__read_series_sequence(cib_dirname, CIB_SERIES,
 686                                    &seq) != pcmk_rc_ok) {
 687         
 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     
 695     unlink(backup_path);
 696     unlink(backup_digest);
 697 
 698     
 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     
 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     
 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 
 748 
 749 
 750 
 751 
 752 
 753 
 754 
 755 
 756 
 757 static void
 758 cib_file_prepare_xml(xmlNode *root)
     
 759 {
 760     xmlNode *cib_status_root = NULL;
 761 
 762     
 763     crm_xml_add(root, XML_ATTR_NUMUPDATES, "0");
 764     pcmk__xe_add_last_written(root);
 765 
 766     
 767 
 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 
 777 
 778 
 779 
 780 
 781 
 782 
 783 
 784 
 785 
 786 
 787 
 788 int
 789 cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname,
     
 790                            const char *cib_filename)
 791 {
 792     int exit_rc = pcmk_ok;
 793     int rc, fd;
 794     char *digest = NULL;
 795 
 796     
 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     
 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     
 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     
 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     
 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     
 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     
 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     
 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     
 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     
 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     
 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     
 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 }