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

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