root/lib/cib/cib_file.c

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

DEFINITIONS

This source file includes following definitions.
  1. register_client
  2. unregister_client
  3. get_client
  4. file_get_op_function
  5. cib_file_is_live
  6. cib_file_process_request
  7. cib_file_perform_op_delegate
  8. load_file_cib
  9. cib_file_signon
  10. cib_file_write_live
  11. cib_file_signoff
  12. cib_file_free
  13. cib_file_register_notification
  14. cib_file_set_connection_dnotify
  15. cib_file_client_id
  16. cib_file_new
  17. cib_file_verify_digest
  18. cib_file_read_and_verify
  19. cib_file_backup
  20. cib_file_prepare_xml
  21. cib_file_write_with_digest
  22. cib_file_process_transaction_requests
  23. cib_file_commit_transaction
  24. cib_file_process_commit_transaction

   1 /*
   2  * Original copyright 2004 International Business Machines
   3  * Later changes copyright 2008-2024 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/common/ipc.h>
  28 #include <crm/common/xml.h>
  29 #include <crm/common/xml_internal.h>
  30 
  31 #define CIB_SERIES "cib"
  32 #define CIB_SERIES_MAX 100
  33 #define CIB_SERIES_BZIP FALSE /* Must be false because archived copies are
  34                                  created with hard links
  35                                */
  36 
  37 #define CIB_LIVE_NAME CIB_SERIES ".xml"
  38 
  39 // key: client ID (const char *) -> value: client (cib_t *)
  40 static GHashTable *client_table = NULL;
  41 
  42 enum cib_file_flags {
  43     cib_file_flag_dirty = (1 << 0),
  44     cib_file_flag_live  = (1 << 1),
  45 };
  46 
  47 typedef struct cib_file_opaque_s {
  48     char *id;
  49     char *filename;
  50     uint32_t flags; // Group of enum cib_file_flags
  51     xmlNode *cib_xml;
  52 } cib_file_opaque_t;
  53 
  54 static int cib_file_process_commit_transaction(const char *op, int options,
  55                                                const char *section,
  56                                                xmlNode *req, xmlNode *input,
  57                                                xmlNode *existing_cib,
  58                                                xmlNode **result_cib,
  59                                                xmlNode **answer);
  60 
  61 /*!
  62  * \internal
  63  * \brief Add a CIB file client to client table
  64  *
  65  * \param[in] cib  CIB client
  66  */
  67 static void
  68 register_client(const cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
  69 {
  70     cib_file_opaque_t *private = cib->variant_opaque;
  71 
  72     if (client_table == NULL) {
  73         client_table = pcmk__strkey_table(NULL, NULL);
  74     }
  75     g_hash_table_insert(client_table, private->id, (gpointer) cib);
  76 }
  77 
  78 /*!
  79  * \internal
  80  * \brief Remove a CIB file client from client table
  81  *
  82  * \param[in] cib  CIB client
  83  */
  84 static void
  85 unregister_client(const cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
  86 {
  87     cib_file_opaque_t *private = cib->variant_opaque;
  88 
  89     if (client_table == NULL) {
  90         return;
  91     }
  92 
  93     g_hash_table_remove(client_table, private->id);
  94 
  95     /* @COMPAT: Add to crm_exit() when libcib and libcrmcommon are merged,
  96      * instead of destroying the client table when there are no more clients.
  97      */
  98     if (g_hash_table_size(client_table) == 0) {
  99         g_hash_table_destroy(client_table);
 100         client_table = NULL;
 101     }
 102 }
 103 
 104 /*!
 105  * \internal
 106  * \brief Look up a CIB file client by its ID
 107  *
 108  * \param[in] client_id  CIB client ID
 109  *
 110  * \return CIB client with matching ID if found, or \p NULL otherwise
 111  */
 112 static cib_t *
 113 get_client(const char *client_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 114 {
 115     if (client_table == NULL) {
 116         return NULL;
 117     }
 118     return g_hash_table_lookup(client_table, (gpointer) client_id);
 119 }
 120 
 121 static const cib__op_fn_t cib_op_functions[] = {
 122     [cib__op_apply_patch]      = cib_process_diff,
 123     [cib__op_bump]             = cib_process_bump,
 124     [cib__op_commit_transact]  = cib_file_process_commit_transaction,
 125     [cib__op_create]           = cib_process_create,
 126     [cib__op_delete]           = cib_process_delete,
 127     [cib__op_erase]            = cib_process_erase,
 128     [cib__op_modify]           = cib_process_modify,
 129     [cib__op_query]            = cib_process_query,
 130     [cib__op_replace]          = cib_process_replace,
 131     [cib__op_upgrade]          = cib_process_upgrade,
 132 };
 133 
 134 /* cib_file_backup() and cib_file_write_with_digest() need to chown the
 135  * written files only in limited circumstances, so these variables allow
 136  * that to be indicated without affecting external callers
 137  */
 138 static uid_t cib_file_owner = 0;
 139 static uid_t cib_file_group = 0;
 140 static gboolean cib_do_chown = FALSE;
 141 
 142 #define cib_set_file_flags(cibfile, flags_to_set) do {                  \
 143         (cibfile)->flags = pcmk__set_flags_as(__func__, __LINE__,       \
 144                                               LOG_TRACE, "CIB file",    \
 145                                               cibfile->filename,        \
 146                                               (cibfile)->flags,         \
 147                                               (flags_to_set),           \
 148                                               #flags_to_set);           \
 149     } while (0)
 150 
 151 #define cib_clear_file_flags(cibfile, flags_to_clear) do {              \
 152         (cibfile)->flags = pcmk__clear_flags_as(__func__, __LINE__,     \
 153                                                 LOG_TRACE, "CIB file",  \
 154                                                 cibfile->filename,      \
 155                                                 (cibfile)->flags,       \
 156                                                 (flags_to_clear),       \
 157                                                 #flags_to_clear);       \
 158     } while (0)
 159 
 160 /*!
 161  * \internal
 162  * \brief Get the function that performs a given CIB file operation
 163  *
 164  * \param[in] operation  Operation whose function to look up
 165  *
 166  * \return Function that performs \p operation for a CIB file client
 167  */
 168 static cib__op_fn_t
 169 file_get_op_function(const cib__operation_t *operation)
     /* [previous][next][first][last][top][bottom][index][help] */
 170 {
 171     enum cib__op_type type = operation->type;
 172 
 173     pcmk__assert(type >= 0);
 174 
 175     if (type >= PCMK__NELEM(cib_op_functions)) {
 176         return NULL;
 177     }
 178     return cib_op_functions[type];
 179 }
 180 
 181 /*!
 182  * \internal
 183  * \brief Check whether a file is the live CIB
 184  *
 185  * \param[in] filename Name of file to check
 186  *
 187  * \return TRUE if file exists and its real path is same as live CIB's
 188  */
 189 static gboolean
 190 cib_file_is_live(const char *filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 191 {
 192     gboolean same = FALSE;
 193 
 194     if (filename != NULL) {
 195         // Canonicalize file names for true comparison
 196         char *real_filename = NULL;
 197 
 198         if (pcmk__real_path(filename, &real_filename) == pcmk_rc_ok) {
 199             char *real_livename = NULL;
 200 
 201             if (pcmk__real_path(CRM_CONFIG_DIR "/" CIB_LIVE_NAME,
 202                                 &real_livename) == pcmk_rc_ok) {
 203                 same = !strcmp(real_filename, real_livename);
 204                 free(real_livename);
 205             }
 206             free(real_filename);
 207         }
 208     }
 209     return same;
 210 }
 211 
 212 static int
 213 cib_file_process_request(cib_t *cib, xmlNode *request, xmlNode **output)
     /* [previous][next][first][last][top][bottom][index][help] */
 214 {
 215     int rc = pcmk_ok;
 216     const cib__operation_t *operation = NULL;
 217     cib__op_fn_t op_function = NULL;
 218 
 219     int call_id = 0;
 220     uint32_t call_options = cib_none;
 221     const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
 222     const char *section = crm_element_value(request, PCMK__XA_CIB_SECTION);
 223     xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA,
 224                                             NULL, NULL);
 225     xmlNode *data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 226 
 227     bool changed = false;
 228     bool read_only = false;
 229     xmlNode *result_cib = NULL;
 230     xmlNode *cib_diff = NULL;
 231 
 232     cib_file_opaque_t *private = cib->variant_opaque;
 233 
 234     // We error checked these in callers
 235     cib__get_operation(op, &operation);
 236     op_function = file_get_op_function(operation);
 237 
 238     crm_element_value_int(request, PCMK__XA_CIB_CALLID, &call_id);
 239     rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options,
 240                             cib_none);
 241     if (rc != pcmk_rc_ok) {
 242         crm_warn("Couldn't parse options from request: %s", pcmk_rc_str(rc));
 243     }
 244 
 245     read_only = !pcmk_is_set(operation->flags, cib__op_attr_modifies);
 246 
 247     // Mirror the logic in prepare_input() in the CIB manager
 248     if ((section != NULL) && pcmk__xe_is(data, PCMK_XE_CIB)) {
 249 
 250         data = pcmk_find_cib_element(data, section);
 251     }
 252 
 253     rc = cib_perform_op(cib, op, call_options, op_function, read_only, section,
 254                         request, data, true, &changed, &private->cib_xml,
 255                         &result_cib, &cib_diff, output);
 256 
 257     if (pcmk_is_set(call_options, cib_transaction)) {
 258         /* The rest of the logic applies only to the transaction as a whole, not
 259          * to individual requests.
 260          */
 261         goto done;
 262     }
 263 
 264     if (rc == -pcmk_err_schema_validation) {
 265         // Show validation errors to stderr
 266         pcmk__validate_xml(result_cib, NULL, NULL, NULL);
 267 
 268     } else if ((rc == pcmk_ok) && !read_only) {
 269         pcmk__log_xml_patchset(LOG_DEBUG, cib_diff);
 270 
 271         if (result_cib != private->cib_xml) {
 272             pcmk__xml_free(private->cib_xml);
 273             private->cib_xml = result_cib;
 274         }
 275         cib_set_file_flags(private, cib_file_flag_dirty);
 276     }
 277 
 278 done:
 279     if ((result_cib != private->cib_xml) && (result_cib != *output)) {
 280         pcmk__xml_free(result_cib);
 281     }
 282     pcmk__xml_free(cib_diff);
 283     return rc;
 284 }
 285 
 286 static int
 287 cib_file_perform_op_delegate(cib_t *cib, const char *op, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 288                              const char *section, xmlNode *data,
 289                              xmlNode **output_data, int call_options,
 290                              const char *user_name)
 291 {
 292     int rc = pcmk_ok;
 293     xmlNode *request = NULL;
 294     xmlNode *output = NULL;
 295     cib_file_opaque_t *private = cib->variant_opaque;
 296 
 297     const cib__operation_t *operation = NULL;
 298 
 299     crm_info("Handling %s operation for %s as %s",
 300              pcmk__s(op, "invalid"), pcmk__s(section, "entire CIB"),
 301              pcmk__s(user_name, "default user"));
 302 
 303     if (output_data != NULL) {
 304         *output_data = NULL;
 305     }
 306 
 307     if (cib->state == cib_disconnected) {
 308         return -ENOTCONN;
 309     }
 310 
 311     rc = cib__get_operation(op, &operation);
 312     rc = pcmk_rc2legacy(rc);
 313     if (rc != pcmk_ok) {
 314         // @COMPAT: At compatibility break, use rc directly
 315         return -EPROTONOSUPPORT;
 316     }
 317 
 318     if (file_get_op_function(operation) == NULL) {
 319         // @COMPAT: At compatibility break, use EOPNOTSUPP
 320         crm_err("Operation %s is not supported by CIB file clients", op);
 321         return -EPROTONOSUPPORT;
 322     }
 323 
 324     cib__set_call_options(call_options, "file operation", cib_no_mtime);
 325 
 326     rc = cib__create_op(cib, op, host, section, data, call_options, user_name,
 327                         NULL, &request);
 328     if (rc != pcmk_ok) {
 329         return rc;
 330     }
 331     crm_xml_add(request, PCMK__XA_ACL_TARGET, user_name);
 332     crm_xml_add(request, PCMK__XA_CIB_CLIENTID, private->id);
 333 
 334     if (pcmk_is_set(call_options, cib_transaction)) {
 335         rc = cib__extend_transaction(cib, request);
 336         goto done;
 337     }
 338 
 339     rc = cib_file_process_request(cib, request, &output);
 340 
 341     if ((output_data != NULL) && (output != NULL)) {
 342         if (output->doc == private->cib_xml->doc) {
 343             *output_data = pcmk__xml_copy(NULL, output);
 344         } else {
 345             *output_data = output;
 346         }
 347     }
 348 
 349 done:
 350     if ((output != NULL)
 351         && (output->doc != private->cib_xml->doc)
 352         && ((output_data == NULL) || (output != *output_data))) {
 353 
 354         pcmk__xml_free(output);
 355     }
 356     pcmk__xml_free(request);
 357     return rc;
 358 }
 359 
 360 /*!
 361  * \internal
 362  * \brief Read CIB from disk and validate it against XML schema
 363  *
 364  * \param[in]   filename  Name of file to read CIB from
 365  * \param[out]  output    Where to store the read CIB XML
 366  *
 367  * \return pcmk_ok on success,
 368  *         -ENXIO if file does not exist (or stat() otherwise fails), or
 369  *         -pcmk_err_schema_validation if XML doesn't parse or validate
 370  * \note If filename is the live CIB, this will *not* verify its digest,
 371  *       though that functionality would be trivial to add here.
 372  *       Also, this will *not* verify that the file is writable,
 373  *       because some callers might not need to write.
 374  */
 375 static int
 376 load_file_cib(const char *filename, xmlNode **output)
     /* [previous][next][first][last][top][bottom][index][help] */
 377 {
 378     struct stat buf;
 379     xmlNode *root = NULL;
 380 
 381     /* Ensure file is readable */
 382     if (strcmp(filename, "-") && (stat(filename, &buf) < 0)) {
 383         return -ENXIO;
 384     }
 385 
 386     /* Parse XML from file */
 387     root = pcmk__xml_read(filename);
 388     if (root == NULL) {
 389         return -pcmk_err_schema_validation;
 390     }
 391 
 392     /* Add a status section if not already present */
 393     if (pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL) == NULL) {
 394         pcmk__xe_create(root, PCMK_XE_STATUS);
 395     }
 396 
 397     /* Validate XML against its specified schema */
 398     if (!pcmk__configured_schema_validates(root)) {
 399         pcmk__xml_free(root);
 400         return -pcmk_err_schema_validation;
 401     }
 402 
 403     /* Remember the parsed XML for later use */
 404     *output = root;
 405     return pcmk_ok;
 406 }
 407 
 408 static int
 409 cib_file_signon(cib_t *cib, const char *name, enum cib_conn_type type)
     /* [previous][next][first][last][top][bottom][index][help] */
 410 {
 411     int rc = pcmk_ok;
 412     cib_file_opaque_t *private = cib->variant_opaque;
 413 
 414     if (private->filename == NULL) {
 415         rc = -EINVAL;
 416     } else {
 417         rc = load_file_cib(private->filename, &private->cib_xml);
 418     }
 419 
 420     if (rc == pcmk_ok) {
 421         crm_debug("Opened connection to local file '%s' for %s",
 422                   private->filename, pcmk__s(name, "client"));
 423         cib->state = cib_connected_command;
 424         cib->type = cib_command;
 425         register_client(cib);
 426 
 427     } else {
 428         crm_info("Connection to local file '%s' for %s (client %s) failed: %s",
 429                  private->filename, pcmk__s(name, "client"), private->id,
 430                  pcmk_strerror(rc));
 431     }
 432     return rc;
 433 }
 434 
 435 /*!
 436  * \internal
 437  * \brief Write out the in-memory CIB to a live CIB file
 438  *
 439  * \param[in]     cib_root  Root of XML tree to write
 440  * \param[in,out] path      Full path to file to write
 441  *
 442  * \return 0 on success, -1 on failure
 443  */
 444 static int
 445 cib_file_write_live(xmlNode *cib_root, char *path)
     /* [previous][next][first][last][top][bottom][index][help] */
 446 {
 447     uid_t uid = geteuid();
 448     struct passwd *daemon_pwent;
 449     char *sep = strrchr(path, '/');
 450     const char *cib_dirname, *cib_filename;
 451     int rc = 0;
 452 
 453     /* Get the desired uid/gid */
 454     errno = 0;
 455     daemon_pwent = getpwnam(CRM_DAEMON_USER);
 456     if (daemon_pwent == NULL) {
 457         crm_perror(LOG_ERR, "Could not find %s user", CRM_DAEMON_USER);
 458         return -1;
 459     }
 460 
 461     /* If we're root, we can change the ownership;
 462      * if we're daemon, anything we create will be OK;
 463      * otherwise, block access so we don't create wrong owner
 464      */
 465     if ((uid != 0) && (uid != daemon_pwent->pw_uid)) {
 466         crm_perror(LOG_ERR, "Must be root or %s to modify live CIB",
 467                    CRM_DAEMON_USER);
 468         return 0;
 469     }
 470 
 471     /* fancy footwork to separate dirname from filename
 472      * (we know the canonical name maps to the live CIB,
 473      * but the given name might be relative, or symlinked)
 474      */
 475     if (sep == NULL) { /* no directory component specified */
 476         cib_dirname = "./";
 477         cib_filename = path;
 478     } else if (sep == path) { /* given name is in / */
 479         cib_dirname = "/";
 480         cib_filename = path + 1;
 481     } else { /* typical case; split given name into parts */
 482         *sep = '\0';
 483         cib_dirname = path;
 484         cib_filename = sep + 1;
 485     }
 486 
 487     /* if we're root, we want to update the file ownership */
 488     if (uid == 0) {
 489         cib_file_owner = daemon_pwent->pw_uid;
 490         cib_file_group = daemon_pwent->pw_gid;
 491         cib_do_chown = TRUE;
 492     }
 493 
 494     /* write the file */
 495     if (cib_file_write_with_digest(cib_root, cib_dirname,
 496                                    cib_filename) != pcmk_ok) {
 497         rc = -1;
 498     }
 499 
 500     /* turn off file ownership changes, for other callers */
 501     if (uid == 0) {
 502         cib_do_chown = FALSE;
 503     }
 504 
 505     /* undo fancy stuff */
 506     if ((sep != NULL) && (*sep == '\0')) {
 507         *sep = '/';
 508     }
 509 
 510     return rc;
 511 }
 512 
 513 /*!
 514  * \internal
 515  * \brief Sign-off method for CIB file variants
 516  *
 517  * This will write the file to disk if needed, and free the in-memory CIB. If
 518  * the file is the live CIB, it will compute and write a signature as well.
 519  *
 520  * \param[in,out] cib  CIB object to sign off
 521  *
 522  * \return pcmk_ok on success, pcmk_err_generic on failure
 523  * \todo This method should refuse to write the live CIB if the CIB manager is
 524  *       running.
 525  */
 526 static int
 527 cib_file_signoff(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 528 {
 529     int rc = pcmk_ok;
 530     cib_file_opaque_t *private = cib->variant_opaque;
 531 
 532     crm_debug("Disconnecting from the CIB manager");
 533     cib->state = cib_disconnected;
 534     cib->type = cib_no_connection;
 535     unregister_client(cib);
 536     cib->cmds->end_transaction(cib, false, cib_none);
 537 
 538     /* If the in-memory CIB has been changed, write it to disk */
 539     if (pcmk_is_set(private->flags, cib_file_flag_dirty)) {
 540 
 541         /* If this is the live CIB, write it out with a digest */
 542         if (pcmk_is_set(private->flags, cib_file_flag_live)) {
 543             if (cib_file_write_live(private->cib_xml, private->filename) < 0) {
 544                 rc = pcmk_err_generic;
 545             }
 546 
 547         /* Otherwise, it's a simple write */
 548         } else {
 549             bool compress = pcmk__ends_with_ext(private->filename, ".bz2");
 550 
 551             if (pcmk__xml_write_file(private->cib_xml, private->filename,
 552                                      compress) != pcmk_rc_ok) {
 553                 rc = pcmk_err_generic;
 554             }
 555         }
 556 
 557         if (rc == pcmk_ok) {
 558             crm_info("Wrote CIB to %s", private->filename);
 559             cib_clear_file_flags(private, cib_file_flag_dirty);
 560         } else {
 561             crm_err("Could not write CIB to %s", private->filename);
 562         }
 563     }
 564 
 565     /* Free the in-memory CIB */
 566     pcmk__xml_free(private->cib_xml);
 567     private->cib_xml = NULL;
 568     return rc;
 569 }
 570 
 571 static int
 572 cib_file_free(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 573 {
 574     int rc = pcmk_ok;
 575 
 576     if (cib->state != cib_disconnected) {
 577         rc = cib_file_signoff(cib);
 578     }
 579 
 580     if (rc == pcmk_ok) {
 581         cib_file_opaque_t *private = cib->variant_opaque;
 582 
 583         free(private->id);
 584         free(private->filename);
 585         free(private);
 586         free(cib->cmds);
 587         free(cib->user);
 588         free(cib);
 589 
 590     } else {
 591         fprintf(stderr, "Couldn't sign off: %d\n", rc);
 592     }
 593 
 594     return rc;
 595 }
 596 
 597 static int
 598 cib_file_register_notification(cib_t *cib, const char *callback, int enabled)
     /* [previous][next][first][last][top][bottom][index][help] */
 599 {
 600     return -EPROTONOSUPPORT;
 601 }
 602 
 603 static int
 604 cib_file_set_connection_dnotify(cib_t *cib,
     /* [previous][next][first][last][top][bottom][index][help] */
 605                                 void (*dnotify) (gpointer user_data))
 606 {
 607     return -EPROTONOSUPPORT;
 608 }
 609 
 610 /*!
 611  * \internal
 612  * \brief Get the given CIB connection's unique client identifier
 613  *
 614  * \param[in]  cib       CIB connection
 615  * \param[out] async_id  If not \p NULL, where to store asynchronous client ID
 616  * \param[out] sync_id   If not \p NULL, where to store synchronous client ID
 617  *
 618  * \return Legacy Pacemaker return code
 619  *
 620  * \note This is the \p cib_file variant implementation of
 621  *       \p cib_api_operations_t:client_id().
 622  */
 623 static int
 624 cib_file_client_id(const cib_t *cib, const char **async_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 625                    const char **sync_id)
 626 {
 627     cib_file_opaque_t *private = cib->variant_opaque;
 628 
 629     if (async_id != NULL) {
 630         *async_id = private->id;
 631     }
 632     if (sync_id != NULL) {
 633         *sync_id = private->id;
 634     }
 635     return pcmk_ok;
 636 }
 637 
 638 cib_t *
 639 cib_file_new(const char *cib_location)
     /* [previous][next][first][last][top][bottom][index][help] */
 640 {
 641     cib_t *cib = NULL;
 642     cib_file_opaque_t *private = NULL;
 643     char *filename = NULL;
 644 
 645     if (cib_location == NULL) {
 646         cib_location = getenv("CIB_file");
 647         if (cib_location == NULL) {
 648             return NULL; // Shouldn't be possible if we were called internally
 649         }
 650     }
 651 
 652     cib = cib_new_variant();
 653     if (cib == NULL) {
 654         return NULL;
 655     }
 656 
 657     filename = strdup(cib_location);
 658     if (filename == NULL) {
 659         free(cib);
 660         return NULL;
 661     }
 662 
 663     private = calloc(1, sizeof(cib_file_opaque_t));
 664     if (private == NULL) {
 665         free(cib);
 666         free(filename);
 667         return NULL;
 668     }
 669 
 670     private->id = crm_generate_uuid();
 671     private->filename = filename;
 672 
 673     cib->variant = cib_file;
 674     cib->variant_opaque = private;
 675 
 676     private->flags = 0;
 677     if (cib_file_is_live(cib_location)) {
 678         cib_set_file_flags(private, cib_file_flag_live);
 679         crm_trace("File %s detected as live CIB", cib_location);
 680     }
 681 
 682     /* assign variant specific ops */
 683     cib->delegate_fn = cib_file_perform_op_delegate;
 684     cib->cmds->signon = cib_file_signon;
 685     cib->cmds->signoff = cib_file_signoff;
 686     cib->cmds->free = cib_file_free;
 687     cib->cmds->register_notification = cib_file_register_notification;
 688     cib->cmds->set_connection_dnotify = cib_file_set_connection_dnotify;
 689 
 690     cib->cmds->client_id = cib_file_client_id;
 691 
 692     return cib;
 693 }
 694 
 695 /*!
 696  * \internal
 697  * \brief Compare the calculated digest of an XML tree against a signature file
 698  *
 699  * \param[in] root     Root of XML tree to compare
 700  * \param[in] sigfile  Name of signature file containing digest to compare
 701  *
 702  * \return TRUE if digests match or signature file does not exist, else FALSE
 703  */
 704 static gboolean
 705 cib_file_verify_digest(xmlNode *root, const char *sigfile)
     /* [previous][next][first][last][top][bottom][index][help] */
 706 {
 707     gboolean passed = FALSE;
 708     char *expected;
 709     int rc = pcmk__file_contents(sigfile, &expected);
 710 
 711     switch (rc) {
 712         case pcmk_rc_ok:
 713             if (expected == NULL) {
 714                 crm_err("On-disk digest at %s is empty", sigfile);
 715                 return FALSE;
 716             }
 717             break;
 718         case ENOENT:
 719             crm_warn("No on-disk digest present at %s", sigfile);
 720             return TRUE;
 721         default:
 722             crm_err("Could not read on-disk digest from %s: %s",
 723                     sigfile, pcmk_rc_str(rc));
 724             return FALSE;
 725     }
 726     passed = pcmk__verify_digest(root, expected);
 727     free(expected);
 728     return passed;
 729 }
 730 
 731 /*!
 732  * \internal
 733  * \brief Read an XML tree from a file and verify its digest
 734  *
 735  * \param[in]  filename  Name of XML file to read
 736  * \param[in]  sigfile   Name of signature file containing digest to compare
 737  * \param[out] root      If non-NULL, will be set to pointer to parsed XML tree
 738  *
 739  * \return 0 if file was successfully read, parsed and verified, otherwise:
 740  *         -errno on stat() failure,
 741  *         -pcmk_err_cib_corrupt if file size is 0 or XML is not parseable, or
 742  *         -pcmk_err_cib_modified if digests do not match
 743  * \note If root is non-NULL, it is the caller's responsibility to free *root on
 744  *       successful return.
 745  */
 746 int
 747 cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **root)
     /* [previous][next][first][last][top][bottom][index][help] */
 748 {
 749     int s_res;
 750     struct stat buf;
 751     char *local_sigfile = NULL;
 752     xmlNode *local_root = NULL;
 753 
 754     pcmk__assert(filename != NULL);
 755     if (root) {
 756         *root = NULL;
 757     }
 758 
 759     /* Verify that file exists and its size is nonzero */
 760     s_res = stat(filename, &buf);
 761     if (s_res < 0) {
 762         crm_perror(LOG_WARNING, "Could not verify cluster configuration file %s", filename);
 763         return -errno;
 764     } else if (buf.st_size == 0) {
 765         crm_warn("Cluster configuration file %s is corrupt (size is zero)", filename);
 766         return -pcmk_err_cib_corrupt;
 767     }
 768 
 769     /* Parse XML */
 770     local_root = pcmk__xml_read(filename);
 771     if (local_root == NULL) {
 772         crm_warn("Cluster configuration file %s is corrupt (unparseable as XML)", filename);
 773         return -pcmk_err_cib_corrupt;
 774     }
 775 
 776     /* If sigfile is not specified, use original file name plus .sig */
 777     if (sigfile == NULL) {
 778         sigfile = local_sigfile = crm_strdup_printf("%s.sig", filename);
 779     }
 780 
 781     /* Verify that digests match */
 782     if (cib_file_verify_digest(local_root, sigfile) == FALSE) {
 783         free(local_sigfile);
 784         pcmk__xml_free(local_root);
 785         return -pcmk_err_cib_modified;
 786     }
 787 
 788     free(local_sigfile);
 789     if (root) {
 790         *root = local_root;
 791     } else {
 792         pcmk__xml_free(local_root);
 793     }
 794     return pcmk_ok;
 795 }
 796 
 797 /*!
 798  * \internal
 799  * \brief Back up a CIB
 800  *
 801  * \param[in] cib_dirname Directory containing CIB file and backups
 802  * \param[in] cib_filename Name (relative to cib_dirname) of CIB file to back up
 803  *
 804  * \return 0 on success, -1 on error
 805  */
 806 static int
 807 cib_file_backup(const char *cib_dirname, const char *cib_filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 808 {
 809     int rc = 0;
 810     unsigned int seq = 0U;
 811     char *cib_path = crm_strdup_printf("%s/%s", cib_dirname, cib_filename);
 812     char *cib_digest = crm_strdup_printf("%s.sig", cib_path);
 813     char *backup_path;
 814     char *backup_digest;
 815 
 816     // Determine backup and digest file names
 817     if (pcmk__read_series_sequence(cib_dirname, CIB_SERIES,
 818                                    &seq) != pcmk_rc_ok) {
 819         // @TODO maybe handle errors better ...
 820         seq = 0U;
 821     }
 822     backup_path = pcmk__series_filename(cib_dirname, CIB_SERIES, seq,
 823                                         CIB_SERIES_BZIP);
 824     backup_digest = crm_strdup_printf("%s.sig", backup_path);
 825 
 826     /* Remove the old backups if they exist */
 827     unlink(backup_path);
 828     unlink(backup_digest);
 829 
 830     /* Back up the CIB, by hard-linking it to the backup name */
 831     if ((link(cib_path, backup_path) < 0) && (errno != ENOENT)) {
 832         crm_perror(LOG_ERR, "Could not archive %s by linking to %s",
 833                    cib_path, backup_path);
 834         rc = -1;
 835 
 836     /* Back up the CIB signature similarly */
 837     } else if ((link(cib_digest, backup_digest) < 0) && (errno != ENOENT)) {
 838         crm_perror(LOG_ERR, "Could not archive %s by linking to %s",
 839                    cib_digest, backup_digest);
 840         rc = -1;
 841 
 842     /* Update the last counter and ensure everything is sync'd to media */
 843     } else {
 844         pcmk__write_series_sequence(cib_dirname, CIB_SERIES, ++seq,
 845                                     CIB_SERIES_MAX);
 846         if (cib_do_chown) {
 847             int rc2;
 848 
 849             if ((chown(backup_path, cib_file_owner, cib_file_group) < 0)
 850                     && (errno != ENOENT)) {
 851                 crm_perror(LOG_ERR, "Could not set owner of %s", backup_path);
 852                 rc = -1;
 853             }
 854             if ((chown(backup_digest, cib_file_owner, cib_file_group) < 0)
 855                     && (errno != ENOENT)) {
 856                 crm_perror(LOG_ERR, "Could not set owner of %s", backup_digest);
 857                 rc = -1;
 858             }
 859             rc2 = pcmk__chown_series_sequence(cib_dirname, CIB_SERIES,
 860                                               cib_file_owner, cib_file_group);
 861             if (rc2 != pcmk_rc_ok) {
 862                 crm_err("Could not set owner of sequence file in %s: %s",
 863                         cib_dirname, pcmk_rc_str(rc2));
 864                 rc = -1;
 865             }
 866         }
 867         pcmk__sync_directory(cib_dirname);
 868         crm_info("Archived previous version as %s", backup_path);
 869     }
 870 
 871     free(cib_path);
 872     free(cib_digest);
 873     free(backup_path);
 874     free(backup_digest);
 875     return rc;
 876 }
 877 
 878 /*!
 879  * \internal
 880  * \brief Prepare CIB XML to be written to disk
 881  *
 882  * Set \c PCMK_XA_NUM_UPDATES to 0, set \c PCMK_XA_CIB_LAST_WRITTEN to the
 883  * current timestamp, and strip out the status section.
 884  *
 885  * \param[in,out] root  Root of CIB XML tree
 886  *
 887  * \return void
 888  */
 889 static void
 890 cib_file_prepare_xml(xmlNode *root)
     /* [previous][next][first][last][top][bottom][index][help] */
 891 {
 892     xmlNode *cib_status_root = NULL;
 893 
 894     /* Always write out with num_updates=0 and current last-written timestamp */
 895     crm_xml_add(root, PCMK_XA_NUM_UPDATES, "0");
 896     pcmk__xe_add_last_written(root);
 897 
 898     /* Delete status section before writing to file, because
 899      * we discard it on startup anyway, and users get confused by it */
 900     cib_status_root = pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL);
 901     CRM_CHECK(cib_status_root != NULL, return);
 902     pcmk__xml_free(cib_status_root);
 903 }
 904 
 905 /*!
 906  * \internal
 907  * \brief Write CIB to disk, along with a signature file containing its digest
 908  *
 909  * \param[in,out] cib_root      Root of XML tree to write
 910  * \param[in]     cib_dirname   Directory containing CIB and signature files
 911  * \param[in]     cib_filename  Name (relative to cib_dirname) of file to write
 912  *
 913  * \return pcmk_ok on success,
 914  *         pcmk_err_cib_modified if existing cib_filename doesn't match digest,
 915  *         pcmk_err_cib_backup if existing cib_filename couldn't be backed up,
 916  *         or pcmk_err_cib_save if new cib_filename couldn't be saved
 917  */
 918 int
 919 cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname,
     /* [previous][next][first][last][top][bottom][index][help] */
 920                            const char *cib_filename)
 921 {
 922     int exit_rc = pcmk_ok;
 923     int rc, fd;
 924     char *digest = NULL;
 925 
 926     /* Detect CIB version for diagnostic purposes */
 927     const char *epoch = crm_element_value(cib_root, PCMK_XA_EPOCH);
 928     const char *admin_epoch = crm_element_value(cib_root, PCMK_XA_ADMIN_EPOCH);
 929 
 930     /* Determine full CIB and signature pathnames */
 931     char *cib_path = crm_strdup_printf("%s/%s", cib_dirname, cib_filename);
 932     char *digest_path = crm_strdup_printf("%s.sig", cib_path);
 933 
 934     /* Create temporary file name patterns for writing out CIB and signature */
 935     char *tmp_cib = crm_strdup_printf("%s/cib.XXXXXX", cib_dirname);
 936     char *tmp_digest = crm_strdup_printf("%s/cib.XXXXXX", cib_dirname);
 937 
 938     /* Ensure the admin didn't modify the existing CIB underneath us */
 939     crm_trace("Reading cluster configuration file %s", cib_path);
 940     rc = cib_file_read_and_verify(cib_path, NULL, NULL);
 941     if ((rc != pcmk_ok) && (rc != -ENOENT)) {
 942         crm_err("%s was manually modified while the cluster was active!",
 943                 cib_path);
 944         exit_rc = pcmk_err_cib_modified;
 945         goto cleanup;
 946     }
 947 
 948     /* Back up the existing CIB */
 949     if (cib_file_backup(cib_dirname, cib_filename) < 0) {
 950         exit_rc = pcmk_err_cib_backup;
 951         goto cleanup;
 952     }
 953 
 954     crm_debug("Writing CIB to disk");
 955     umask(S_IWGRP | S_IWOTH | S_IROTH);
 956     cib_file_prepare_xml(cib_root);
 957 
 958     /* Write the CIB to a temporary file, so we can deploy (near) atomically */
 959     fd = mkstemp(tmp_cib);
 960     if (fd < 0) {
 961         crm_perror(LOG_ERR, "Couldn't open temporary file %s for writing CIB",
 962                    tmp_cib);
 963         exit_rc = pcmk_err_cib_save;
 964         goto cleanup;
 965     }
 966 
 967     /* Protect the temporary file */
 968     if (fchmod(fd, S_IRUSR | S_IWUSR) < 0) {
 969         crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
 970                    tmp_cib);
 971         exit_rc = pcmk_err_cib_save;
 972         goto cleanup;
 973     }
 974     if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) {
 975         crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
 976                    tmp_cib);
 977         exit_rc = pcmk_err_cib_save;
 978         goto cleanup;
 979     }
 980 
 981     /* Write out the CIB */
 982     if (pcmk__xml_write_fd(cib_root, tmp_cib, fd) != pcmk_rc_ok) {
 983         crm_err("Changes couldn't be written to %s", tmp_cib);
 984         exit_rc = pcmk_err_cib_save;
 985         goto cleanup;
 986     }
 987 
 988     /* Calculate CIB digest */
 989     digest = pcmk__digest_on_disk_cib(cib_root);
 990     pcmk__assert(digest != NULL);
 991     crm_info("Wrote version %s.%s.0 of the CIB to disk (digest: %s)",
 992              (admin_epoch ? admin_epoch : "0"), (epoch ? epoch : "0"), digest);
 993 
 994     /* Write the CIB digest to a temporary file */
 995     fd = mkstemp(tmp_digest);
 996     if (fd < 0) {
 997         crm_perror(LOG_ERR, "Could not create temporary file for CIB digest");
 998         exit_rc = pcmk_err_cib_save;
 999         goto cleanup;
1000     }
1001     if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) {
1002         crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
1003                    tmp_cib);
1004         exit_rc = pcmk_err_cib_save;
1005         close(fd);
1006         goto cleanup;
1007     }
1008     rc = pcmk__write_sync(fd, digest);
1009     if (rc != pcmk_rc_ok) {
1010         crm_err("Could not write digest to %s: %s",
1011                 tmp_digest, pcmk_rc_str(rc));
1012         exit_rc = pcmk_err_cib_save;
1013         close(fd);
1014         goto cleanup;
1015     }
1016     close(fd);
1017     crm_debug("Wrote digest %s to disk", digest);
1018 
1019     /* Verify that what we wrote is sane */
1020     crm_info("Reading cluster configuration file %s (digest: %s)",
1021              tmp_cib, tmp_digest);
1022     rc = cib_file_read_and_verify(tmp_cib, tmp_digest, NULL);
1023     pcmk__assert(rc == 0);
1024 
1025     /* Rename temporary files to live, and sync directory changes to media */
1026     crm_debug("Activating %s", tmp_cib);
1027     if (rename(tmp_cib, cib_path) < 0) {
1028         crm_perror(LOG_ERR, "Couldn't rename %s as %s", tmp_cib, cib_path);
1029         exit_rc = pcmk_err_cib_save;
1030     }
1031     if (rename(tmp_digest, digest_path) < 0) {
1032         crm_perror(LOG_ERR, "Couldn't rename %s as %s", tmp_digest,
1033                    digest_path);
1034         exit_rc = pcmk_err_cib_save;
1035     }
1036     pcmk__sync_directory(cib_dirname);
1037 
1038   cleanup:
1039     free(cib_path);
1040     free(digest_path);
1041     free(digest);
1042     free(tmp_digest);
1043     free(tmp_cib);
1044     return exit_rc;
1045 }
1046 
1047 /*!
1048  * \internal
1049  * \brief Process requests in a CIB transaction
1050  *
1051  * Stop when a request fails or when all requests have been processed.
1052  *
1053  * \param[in,out] cib          CIB client
1054  * \param[in,out] transaction  CIB transaction
1055  *
1056  * \return Standard Pacemaker return code
1057  */
1058 static int
1059 cib_file_process_transaction_requests(cib_t *cib, xmlNode *transaction)
     /* [previous][next][first][last][top][bottom][index][help] */
1060 {
1061     cib_file_opaque_t *private = cib->variant_opaque;
1062 
1063     for (xmlNode *request = pcmk__xe_first_child(transaction,
1064                                                  PCMK__XE_CIB_COMMAND, NULL,
1065                                                  NULL);
1066          request != NULL;
1067          request = pcmk__xe_next(request, PCMK__XE_CIB_COMMAND)) {
1068 
1069         xmlNode *output = NULL;
1070         const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
1071 
1072         int rc = cib_file_process_request(cib, request, &output);
1073 
1074         rc = pcmk_legacy2rc(rc);
1075         if (rc != pcmk_rc_ok) {
1076             crm_err("Aborting transaction for CIB file client (%s) on file "
1077                     "'%s' due to failed %s request: %s",
1078                     private->id, private->filename, op, pcmk_rc_str(rc));
1079             crm_log_xml_info(request, "Failed request");
1080             return rc;
1081         }
1082 
1083         crm_trace("Applied %s request to transaction working CIB for CIB file "
1084                   "client (%s) on file '%s'",
1085                   op, private->id, private->filename);
1086         crm_log_xml_trace(request, "Successful request");
1087     }
1088 
1089     return pcmk_rc_ok;
1090 }
1091 
1092 /*!
1093  * \internal
1094  * \brief Commit a given CIB file client's transaction to a working CIB copy
1095  *
1096  * \param[in,out] cib          CIB file client
1097  * \param[in]     transaction  CIB transaction
1098  * \param[in,out] result_cib   Where to store result CIB
1099  *
1100  * \return Standard Pacemaker return code
1101  *
1102  * \note The caller is responsible for replacing the \p cib argument's
1103  *       \p private->cib_xml with \p result_cib on success, and for freeing
1104  *       \p result_cib using \p pcmk__xml_free() on failure.
1105  */
1106 static int
1107 cib_file_commit_transaction(cib_t *cib, xmlNode *transaction,
     /* [previous][next][first][last][top][bottom][index][help] */
1108                             xmlNode **result_cib)
1109 {
1110     int rc = pcmk_rc_ok;
1111     cib_file_opaque_t *private = cib->variant_opaque;
1112     xmlNode *saved_cib = private->cib_xml;
1113 
1114     CRM_CHECK(pcmk__xe_is(transaction, PCMK__XE_CIB_TRANSACTION),
1115               return pcmk_rc_no_transaction);
1116 
1117     /* *result_cib should be a copy of private->cib_xml (created by
1118      * cib_perform_op()). If not, make a copy now. Change tracking isn't
1119      * strictly required here because:
1120      * * Each request in the transaction will have changes tracked and ACLs
1121      *   checked if appropriate.
1122      * * cib_perform_op() will infer changes for the commit request at the end.
1123      */
1124     CRM_CHECK((*result_cib != NULL) && (*result_cib != private->cib_xml),
1125               *result_cib = pcmk__xml_copy(NULL, private->cib_xml));
1126 
1127     crm_trace("Committing transaction for CIB file client (%s) on file '%s' to "
1128               "working CIB",
1129               private->id, private->filename);
1130 
1131     // Apply all changes to a working copy of the CIB
1132     private->cib_xml = *result_cib;
1133 
1134     rc = cib_file_process_transaction_requests(cib, transaction);
1135 
1136     crm_trace("Transaction commit %s for CIB file client (%s) on file '%s'",
1137               ((rc == pcmk_rc_ok)? "succeeded" : "failed"),
1138               private->id, private->filename);
1139 
1140     /* Some request types (for example, erase) may have freed private->cib_xml
1141      * (the working copy) and pointed it at a new XML object. In that case, it
1142      * follows that *result_cib (the working copy) was freed.
1143      *
1144      * Point *result_cib at the updated working copy stored in private->cib_xml.
1145      */
1146     *result_cib = private->cib_xml;
1147 
1148     // Point private->cib_xml back to the unchanged original copy
1149     private->cib_xml = saved_cib;
1150 
1151     return rc;
1152 }
1153 
1154 static int
1155 cib_file_process_commit_transaction(const char *op, int options,
     /* [previous][next][first][last][top][bottom][index][help] */
1156                                     const char *section, xmlNode *req,
1157                                     xmlNode *input, xmlNode *existing_cib,
1158                                     xmlNode **result_cib, xmlNode **answer)
1159 {
1160     int rc = pcmk_rc_ok;
1161     const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID);
1162     cib_t *cib = NULL;
1163 
1164     CRM_CHECK(client_id != NULL, return -EINVAL);
1165 
1166     cib = get_client(client_id);
1167     CRM_CHECK(cib != NULL, return -EINVAL);
1168 
1169     rc = cib_file_commit_transaction(cib, input, result_cib);
1170     if (rc != pcmk_rc_ok) {
1171         cib_file_opaque_t *private = cib->variant_opaque;
1172 
1173         crm_err("Could not commit transaction for CIB file client (%s) on "
1174                 "file '%s': %s",
1175                 private->id, private->filename, pcmk_rc_str(rc));
1176     }
1177     return pcmk_rc2legacy(rc);
1178 }

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