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

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