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     private = calloc(1, sizeof(cib_file_opaque_t));
 501     CRM_ASSERT((cib != NULL) && (private != NULL));
 502 
 503     cib->variant = cib_file;
 504     cib->variant_opaque = private;
 505 
 506     if (cib_location == NULL) {
 507         cib_location = getenv("CIB_file");
 508     }
 509     private->flags = 0;
 510     if (cib_file_is_live(cib_location)) {
 511         cib_set_file_flags(private, cib_file_flag_live);
 512         crm_trace("File %s detected as live CIB", cib_location);
 513     }
 514     private->filename = strdup(cib_location);
 515 
 516     /* assign variant specific ops */
 517     cib->delegate_fn = cib_file_perform_op_delegate;
 518     cib->cmds->signon = cib_file_signon;
 519     cib->cmds->signoff = cib_file_signoff;
 520     cib->cmds->free = cib_file_free;
 521     cib->cmds->inputfd = cib_file_inputfd;
 522 
 523     cib->cmds->register_notification = cib_file_register_notification;
 524     cib->cmds->set_connection_dnotify = cib_file_set_connection_dnotify;
 525 
 526     return cib;
 527 }
 528 
 529 static xmlNode *in_mem_cib = NULL;
 530 
 531 /*!
 532  * \internal
 533  * \brief Read CIB from disk and validate it against XML schema
 534  *
 535  * \param[in] filename Name of file to read CIB from
 536  *
 537  * \return pcmk_ok on success,
 538  *         -ENXIO if file does not exist (or stat() otherwise fails), or
 539  *         -pcmk_err_schema_validation if XML doesn't parse or validate
 540  * \note If filename is the live CIB, this will *not* verify its digest,
 541  *       though that functionality would be trivial to add here.
 542  *       Also, this will *not* verify that the file is writable,
 543  *       because some callers might not need to write.
 544  */
 545 static int
 546 load_file_cib(const char *filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 547 {
 548     struct stat buf;
 549     xmlNode *root = NULL;
 550 
 551     /* Ensure file is readable */
 552     if (strcmp(filename, "-") && (stat(filename, &buf) < 0)) {
 553         return -ENXIO;
 554     }
 555 
 556     /* Parse XML from file */
 557     root = filename2xml(filename);
 558     if (root == NULL) {
 559         return -pcmk_err_schema_validation;
 560     }
 561 
 562     /* Add a status section if not already present */
 563     if (find_xml_node(root, XML_CIB_TAG_STATUS, FALSE) == NULL) {
 564         create_xml_node(root, XML_CIB_TAG_STATUS);
 565     }
 566 
 567     /* Validate XML against its specified schema */
 568     if (validate_xml(root, NULL, TRUE) == FALSE) {
 569         const char *schema = crm_element_value(root, XML_ATTR_VALIDATION);
 570 
 571         crm_err("CIB does not validate against %s", schema);
 572         free_xml(root);
 573         return -pcmk_err_schema_validation;
 574     }
 575 
 576     /* Remember the parsed XML for later use */
 577     in_mem_cib = root;
 578     return pcmk_ok;
 579 }
 580 
 581 int
 582 cib_file_signon(cib_t * cib, const char *name, enum cib_conn_type type)
     /* [previous][next][first][last][top][bottom][index][help] */
 583 {
 584     int rc = pcmk_ok;
 585     cib_file_opaque_t *private = cib->variant_opaque;
 586 
 587     if (private->filename == NULL) {
 588         rc = -EINVAL;
 589     } else {
 590         rc = load_file_cib(private->filename);
 591     }
 592 
 593     if (rc == pcmk_ok) {
 594         crm_debug("Opened connection to local file '%s' for %s",
 595                   private->filename, name);
 596         cib->state = cib_connected_command;
 597         cib->type = cib_command;
 598 
 599     } else {
 600         crm_info("Connection to local file '%s' for %s failed: %s\n",
 601                  private->filename, name, pcmk_strerror(rc));
 602     }
 603     return rc;
 604 }
 605 
 606 /*!
 607  * \internal
 608  * \brief Write out the in-memory CIB to a live CIB file
 609  *
 610  * param[in] path Full path to file to write
 611  *
 612  * \return 0 on success, -1 on failure
 613  */
 614 static int
 615 cib_file_write_live(char *path)
     /* [previous][next][first][last][top][bottom][index][help] */
 616 {
 617     uid_t uid = geteuid();
 618     struct passwd *daemon_pwent;
 619     char *sep = strrchr(path, '/');
 620     const char *cib_dirname, *cib_filename;
 621     int rc = 0;
 622 
 623     /* Get the desired uid/gid */
 624     errno = 0;
 625     daemon_pwent = getpwnam(CRM_DAEMON_USER);
 626     if (daemon_pwent == NULL) {
 627         crm_perror(LOG_ERR, "Could not find %s user", CRM_DAEMON_USER);
 628         return -1;
 629     }
 630 
 631     /* If we're root, we can change the ownership;
 632      * if we're daemon, anything we create will be OK;
 633      * otherwise, block access so we don't create wrong owner
 634      */
 635     if ((uid != 0) && (uid != daemon_pwent->pw_uid)) {
 636         crm_perror(LOG_ERR, "Must be root or %s to modify live CIB",
 637                    CRM_DAEMON_USER);
 638         return 0;
 639     }
 640 
 641     /* fancy footwork to separate dirname from filename
 642      * (we know the canonical name maps to the live CIB,
 643      * but the given name might be relative, or symlinked)
 644      */
 645     if (sep == NULL) { /* no directory component specified */
 646         cib_dirname = "./";
 647         cib_filename = path;
 648     } else if (sep == path) { /* given name is in / */
 649         cib_dirname = "/";
 650         cib_filename = path + 1;
 651     } else { /* typical case; split given name into parts */
 652         *sep = '\0';
 653         cib_dirname = path;
 654         cib_filename = sep + 1;
 655     }
 656 
 657     /* if we're root, we want to update the file ownership */
 658     if (uid == 0) {
 659         cib_file_owner = daemon_pwent->pw_uid;
 660         cib_file_group = daemon_pwent->pw_gid;
 661         cib_do_chown = TRUE;
 662     }
 663 
 664     /* write the file */
 665     if (cib_file_write_with_digest(in_mem_cib, cib_dirname,
 666                                    cib_filename) != pcmk_ok) {
 667         rc = -1;
 668     }
 669 
 670     /* turn off file ownership changes, for other callers */
 671     if (uid == 0) {
 672         cib_do_chown = FALSE;
 673     }
 674 
 675     /* undo fancy stuff */
 676     if ((sep != NULL) && (*sep == '\0')) {
 677         *sep = '/';
 678     }
 679 
 680     return rc;
 681 }
 682 
 683 /*!
 684  * \internal
 685  * \brief Sign-off method for CIB file variants
 686  *
 687  * This will write the file to disk if needed, and free the in-memory CIB. If
 688  * the file is the live CIB, it will compute and write a signature as well.
 689  *
 690  * \param[in] cib CIB object to sign off
 691  *
 692  * \return pcmk_ok on success, pcmk_err_generic on failure
 693  * \todo This method should refuse to write the live CIB if the CIB manager is
 694  *       running.
 695  */
 696 int
 697 cib_file_signoff(cib_t * cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 698 {
 699     int rc = pcmk_ok;
 700     cib_file_opaque_t *private = cib->variant_opaque;
 701 
 702     crm_debug("Disconnecting from the CIB manager");
 703     cib->state = cib_disconnected;
 704     cib->type = cib_no_connection;
 705 
 706     /* If the in-memory CIB has been changed, write it to disk */
 707     if (pcmk_is_set(private->flags, cib_file_flag_dirty)) {
 708 
 709         /* If this is the live CIB, write it out with a digest */
 710         if (pcmk_is_set(private->flags, cib_file_flag_live)) {
 711             if (cib_file_write_live(private->filename) < 0) {
 712                 rc = pcmk_err_generic;
 713             }
 714 
 715         /* Otherwise, it's a simple write */
 716         } else {
 717             gboolean do_bzip = pcmk__ends_with_ext(private->filename, ".bz2");
 718 
 719             if (write_xml_file(in_mem_cib, private->filename, do_bzip) <= 0) {
 720                 rc = pcmk_err_generic;
 721             }
 722         }
 723 
 724         if (rc == pcmk_ok) {
 725             crm_info("Wrote CIB to %s", private->filename);
 726             cib_clear_file_flags(private, cib_file_flag_dirty);
 727         } else {
 728             crm_err("Could not write CIB to %s", private->filename);
 729         }
 730     }
 731 
 732     /* Free the in-memory CIB */
 733     free_xml(in_mem_cib);
 734     in_mem_cib = NULL;
 735     return rc;
 736 }
 737 
 738 int
 739 cib_file_free(cib_t * cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 740 {
 741     int rc = pcmk_ok;
 742 
 743     if (cib->state != cib_disconnected) {
 744         rc = cib_file_signoff(cib);
 745     }
 746 
 747     if (rc == pcmk_ok) {
 748         cib_file_opaque_t *private = cib->variant_opaque;
 749 
 750         free(private->filename);
 751         free(cib->cmds);
 752         free(private);
 753         free(cib);
 754 
 755     } else {
 756         fprintf(stderr, "Couldn't sign off: %d\n", rc);
 757     }
 758 
 759     return rc;
 760 }
 761 
 762 struct cib_func_entry {
 763     const char *op;
 764     gboolean read_only;
 765     cib_op_t fn;
 766 };
 767 
 768 /* *INDENT-OFF* */
 769 static struct cib_func_entry cib_file_ops[] = {
 770     {CIB_OP_QUERY,      TRUE,  cib_process_query},
 771     {CIB_OP_MODIFY,     FALSE, cib_process_modify},
 772     {CIB_OP_APPLY_DIFF, FALSE, cib_process_diff},
 773     {CIB_OP_BUMP,       FALSE, cib_process_bump},
 774     {CIB_OP_REPLACE,    FALSE, cib_process_replace},
 775     {CIB_OP_CREATE,     FALSE, cib_process_create},
 776     {CIB_OP_DELETE,     FALSE, cib_process_delete},
 777     {CIB_OP_ERASE,      FALSE, cib_process_erase},
 778     {CIB_OP_UPGRADE,    FALSE, cib_process_upgrade},
 779 };
 780 /* *INDENT-ON* */
 781 
 782 int
 783 cib_file_perform_op(cib_t * cib, const char *op, const char *host, const char *section,
     /* [previous][next][first][last][top][bottom][index][help] */
 784                     xmlNode * data, xmlNode ** output_data, int call_options)
 785 {
 786     return cib_file_perform_op_delegate(cib, op, host, section, data, output_data, call_options,
 787                                         NULL);
 788 }
 789 
 790 int
 791 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] */
 792                              xmlNode * data, xmlNode ** output_data, int call_options,
 793                              const char *user_name)
 794 {
 795     int rc = pcmk_ok;
 796     char *effective_user = NULL;
 797     gboolean query = FALSE;
 798     gboolean changed = FALSE;
 799     xmlNode *request = NULL;
 800     xmlNode *output = NULL;
 801     xmlNode *cib_diff = NULL;
 802     xmlNode *result_cib = NULL;
 803     cib_op_t *fn = NULL;
 804     int lpc = 0;
 805     static int max_msg_types = PCMK__NELEM(cib_file_ops);
 806     cib_file_opaque_t *private = cib->variant_opaque;
 807 
 808     crm_info("Handling %s operation for %s as %s",
 809              (op? op : "invalid"), (section? section : "entire CIB"),
 810              (user_name? user_name : "default user"));
 811 
 812     cib__set_call_options(call_options, "file operation",
 813                           cib_no_mtime|cib_inhibit_bcast|cib_scope_local);
 814 
 815     if (cib->state == cib_disconnected) {
 816         return -ENOTCONN;
 817     }
 818 
 819     if (output_data != NULL) {
 820         *output_data = NULL;
 821     }
 822 
 823     if (op == NULL) {
 824         return -EINVAL;
 825     }
 826 
 827     for (lpc = 0; lpc < max_msg_types; lpc++) {
 828         if (pcmk__str_eq(op, cib_file_ops[lpc].op, pcmk__str_casei)) {
 829             fn = &(cib_file_ops[lpc].fn);
 830             query = cib_file_ops[lpc].read_only;
 831             break;
 832         }
 833     }
 834 
 835     if (fn == NULL) {
 836         return -EPROTONOSUPPORT;
 837     }
 838 
 839     cib->call_id++;
 840     request = cib_create_op(cib->call_id, "dummy-token", op, host, section, data, call_options, user_name);
 841     if(user_name) {
 842         crm_xml_add(request, XML_ACL_TAG_USER, user_name);
 843     }
 844 
 845     /* Mirror the logic in cib_prepare_common() */
 846     if (section != NULL && data != NULL && pcmk__str_eq(crm_element_name(data), XML_TAG_CIB, pcmk__str_none)) {
 847         data = get_object_root(section, data);
 848     }
 849 
 850     rc = cib_perform_op(op, call_options, fn, query,
 851                         section, request, data, TRUE, &changed, in_mem_cib, &result_cib, &cib_diff,
 852                         &output);
 853 
 854     free_xml(request);
 855     if (rc == -pcmk_err_schema_validation) {
 856         validate_xml_verbose(result_cib);
 857     }
 858 
 859     if (rc != pcmk_ok) {
 860         free_xml(result_cib);
 861 
 862     } else if (query == FALSE) {
 863         xml_log_patchset(LOG_DEBUG, "cib:diff", cib_diff);
 864         free_xml(in_mem_cib);
 865         in_mem_cib = result_cib;
 866         cib_set_file_flags(private, cib_file_flag_dirty);
 867     }
 868 
 869     free_xml(cib_diff);
 870 
 871     if (cib->op_callback != NULL) {
 872         cib->op_callback(NULL, cib->call_id, rc, output);
 873     }
 874 
 875     if (output_data && output) {
 876         if(output == in_mem_cib) {
 877             *output_data = copy_xml(output);
 878         } else {
 879             *output_data = output;
 880         }
 881 
 882     } else if(output != in_mem_cib) {
 883         free_xml(output);
 884     }
 885 
 886     free(effective_user);
 887     return rc;
 888 }

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