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

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