root/lib/cib/cib_file.c

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

DEFINITIONS

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

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

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