LCOV - code coverage report
Current view: top level - cib - cib_file.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 459 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 25 0.0 %

          Line data    Source code
       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           0 : register_client(const cib_t *cib)
      69             : {
      70           0 :     cib_file_opaque_t *private = cib->variant_opaque;
      71             : 
      72           0 :     if (client_table == NULL) {
      73           0 :         client_table = pcmk__strkey_table(NULL, NULL);
      74             :     }
      75           0 :     g_hash_table_insert(client_table, private->id, (gpointer) cib);
      76           0 : }
      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           0 : unregister_client(const cib_t *cib)
      86             : {
      87           0 :     cib_file_opaque_t *private = cib->variant_opaque;
      88             : 
      89           0 :     if (client_table == NULL) {
      90           0 :         return;
      91             :     }
      92             : 
      93           0 :     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           0 :     if (g_hash_table_size(client_table) == 0) {
      99           0 :         g_hash_table_destroy(client_table);
     100           0 :         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           0 : get_client(const char *client_id)
     114             : {
     115           0 :     if (client_table == NULL) {
     116           0 :         return NULL;
     117             :     }
     118           0 :     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           0 : file_get_op_function(const cib__operation_t *operation)
     170             : {
     171           0 :     enum cib__op_type type = operation->type;
     172             : 
     173             :     CRM_ASSERT(type >= 0);
     174             : 
     175           0 :     if (type >= PCMK__NELEM(cib_op_functions)) {
     176           0 :         return NULL;
     177             :     }
     178           0 :     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           0 : cib_file_is_live(const char *filename)
     191             : {
     192           0 :     gboolean same = FALSE;
     193             : 
     194           0 :     if (filename != NULL) {
     195             :         // Canonicalize file names for true comparison
     196           0 :         char *real_filename = NULL;
     197             : 
     198           0 :         if (pcmk__real_path(filename, &real_filename) == pcmk_rc_ok) {
     199           0 :             char *real_livename = NULL;
     200             : 
     201           0 :             if (pcmk__real_path(CRM_CONFIG_DIR "/" CIB_LIVE_NAME,
     202             :                                 &real_livename) == pcmk_rc_ok) {
     203           0 :                 same = !strcmp(real_filename, real_livename);
     204           0 :                 free(real_livename);
     205             :             }
     206           0 :             free(real_filename);
     207             :         }
     208             :     }
     209           0 :     return same;
     210             : }
     211             : 
     212             : static int
     213           0 : cib_file_process_request(cib_t *cib, xmlNode *request, xmlNode **output)
     214             : {
     215           0 :     int rc = pcmk_ok;
     216           0 :     const cib__operation_t *operation = NULL;
     217           0 :     cib__op_fn_t op_function = NULL;
     218             : 
     219           0 :     int call_id = 0;
     220           0 :     int call_options = cib_none;
     221           0 :     const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
     222           0 :     const char *section = crm_element_value(request, PCMK__XA_CIB_SECTION);
     223           0 :     xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA,
     224             :                                             NULL, NULL);
     225           0 :     xmlNode *data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
     226             : 
     227           0 :     bool changed = false;
     228           0 :     bool read_only = false;
     229           0 :     xmlNode *result_cib = NULL;
     230           0 :     xmlNode *cib_diff = NULL;
     231             : 
     232           0 :     cib_file_opaque_t *private = cib->variant_opaque;
     233             : 
     234             :     // We error checked these in callers
     235           0 :     cib__get_operation(op, &operation);
     236           0 :     op_function = file_get_op_function(operation);
     237             : 
     238           0 :     crm_element_value_int(request, PCMK__XA_CIB_CALLID, &call_id);
     239           0 :     crm_element_value_int(request, PCMK__XA_CIB_CALLOPT, &call_options);
     240             : 
     241           0 :     read_only = !pcmk_is_set(operation->flags, cib__op_attr_modifies);
     242             : 
     243             :     // Mirror the logic in prepare_input() in pacemaker-based
     244           0 :     if ((section != NULL) && pcmk__xe_is(data, PCMK_XE_CIB)) {
     245             : 
     246           0 :         data = pcmk_find_cib_element(data, section);
     247             :     }
     248             : 
     249           0 :     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           0 :     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           0 :         goto done;
     258             :     }
     259             : 
     260           0 :     if (rc == -pcmk_err_schema_validation) {
     261             :         // Show validation errors to stderr
     262           0 :         pcmk__validate_xml(result_cib, NULL, NULL, NULL);
     263             : 
     264           0 :     } else if ((rc == pcmk_ok) && !read_only) {
     265           0 :         pcmk__log_xml_patchset(LOG_DEBUG, cib_diff);
     266             : 
     267           0 :         if (result_cib != private->cib_xml) {
     268           0 :             free_xml(private->cib_xml);
     269           0 :             private->cib_xml = result_cib;
     270             :         }
     271           0 :         cib_set_file_flags(private, cib_file_flag_dirty);
     272             :     }
     273             : 
     274             :     // Global operation callback (deprecated)
     275           0 :     if (cib->op_callback != NULL) {
     276           0 :         cib->op_callback(NULL, call_id, rc, *output);
     277             :     }
     278             : 
     279           0 : done:
     280           0 :     if ((result_cib != private->cib_xml) && (result_cib != *output)) {
     281           0 :         free_xml(result_cib);
     282             :     }
     283           0 :     free_xml(cib_diff);
     284           0 :     return rc;
     285             : }
     286             : 
     287             : static int
     288           0 : cib_file_perform_op_delegate(cib_t *cib, const char *op, const char *host,
     289             :                              const char *section, xmlNode *data,
     290             :                              xmlNode **output_data, int call_options,
     291             :                              const char *user_name)
     292             : {
     293           0 :     int rc = pcmk_ok;
     294           0 :     xmlNode *request = NULL;
     295           0 :     xmlNode *output = NULL;
     296           0 :     cib_file_opaque_t *private = cib->variant_opaque;
     297             : 
     298           0 :     const cib__operation_t *operation = NULL;
     299             : 
     300           0 :     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           0 :     if (output_data != NULL) {
     305           0 :         *output_data = NULL;
     306             :     }
     307             : 
     308           0 :     if (cib->state == cib_disconnected) {
     309           0 :         return -ENOTCONN;
     310             :     }
     311             : 
     312           0 :     rc = cib__get_operation(op, &operation);
     313           0 :     rc = pcmk_rc2legacy(rc);
     314           0 :     if (rc != pcmk_ok) {
     315             :         // @COMPAT: At compatibility break, use rc directly
     316           0 :         return -EPROTONOSUPPORT;
     317             :     }
     318             : 
     319           0 :     if (file_get_op_function(operation) == NULL) {
     320             :         // @COMPAT: At compatibility break, use EOPNOTSUPP
     321           0 :         crm_err("Operation %s is not supported by CIB file clients", op);
     322           0 :         return -EPROTONOSUPPORT;
     323             :     }
     324             : 
     325           0 :     cib__set_call_options(call_options, "file operation", cib_no_mtime);
     326             : 
     327           0 :     rc = cib__create_op(cib, op, host, section, data, call_options, user_name,
     328             :                         NULL, &request);
     329           0 :     if (rc != pcmk_ok) {
     330           0 :         return rc;
     331             :     }
     332           0 :     crm_xml_add(request, PCMK_XE_ACL_TARGET, user_name);
     333           0 :     crm_xml_add(request, PCMK__XA_CIB_CLIENTID, private->id);
     334             : 
     335           0 :     if (pcmk_is_set(call_options, cib_transaction)) {
     336           0 :         rc = cib__extend_transaction(cib, request);
     337           0 :         goto done;
     338             :     }
     339             : 
     340           0 :     rc = cib_file_process_request(cib, request, &output);
     341             : 
     342           0 :     if ((output_data != NULL) && (output != NULL)) {
     343           0 :         if (output->doc == private->cib_xml->doc) {
     344           0 :             *output_data = pcmk__xml_copy(NULL, output);
     345             :         } else {
     346           0 :             *output_data = output;
     347             :         }
     348             :     }
     349             : 
     350           0 : done:
     351           0 :     if ((output != NULL)
     352           0 :         && (output->doc != private->cib_xml->doc)
     353           0 :         && ((output_data == NULL) || (output != *output_data))) {
     354             : 
     355           0 :         free_xml(output);
     356             :     }
     357           0 :     free_xml(request);
     358           0 :     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           0 : load_file_cib(const char *filename, xmlNode **output)
     378             : {
     379             :     struct stat buf;
     380           0 :     xmlNode *root = NULL;
     381             : 
     382             :     /* Ensure file is readable */
     383           0 :     if (strcmp(filename, "-") && (stat(filename, &buf) < 0)) {
     384           0 :         return -ENXIO;
     385             :     }
     386             : 
     387             :     /* Parse XML from file */
     388           0 :     root = pcmk__xml_read(filename);
     389           0 :     if (root == NULL) {
     390           0 :         return -pcmk_err_schema_validation;
     391             :     }
     392             : 
     393             :     /* Add a status section if not already present */
     394           0 :     if (pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL) == NULL) {
     395           0 :         pcmk__xe_create(root, PCMK_XE_STATUS);
     396             :     }
     397             : 
     398             :     /* Validate XML against its specified schema */
     399           0 :     if (!pcmk__configured_schema_validates(root)) {
     400           0 :         const char *schema = crm_element_value(root, PCMK_XA_VALIDATE_WITH);
     401             : 
     402           0 :         crm_err("CIB does not validate against %s, or that schema is unknown", schema);
     403           0 :         free_xml(root);
     404           0 :         return -pcmk_err_schema_validation;
     405             :     }
     406             : 
     407             :     /* Remember the parsed XML for later use */
     408           0 :     *output = root;
     409           0 :     return pcmk_ok;
     410             : }
     411             : 
     412             : static int
     413           0 : cib_file_signon(cib_t *cib, const char *name, enum cib_conn_type type)
     414             : {
     415           0 :     int rc = pcmk_ok;
     416           0 :     cib_file_opaque_t *private = cib->variant_opaque;
     417             : 
     418           0 :     if (private->filename == NULL) {
     419           0 :         rc = -EINVAL;
     420             :     } else {
     421           0 :         rc = load_file_cib(private->filename, &private->cib_xml);
     422             :     }
     423             : 
     424           0 :     if (rc == pcmk_ok) {
     425           0 :         crm_debug("Opened connection to local file '%s' for %s",
     426             :                   private->filename, name);
     427           0 :         cib->state = cib_connected_command;
     428           0 :         cib->type = cib_command;
     429           0 :         register_client(cib);
     430             : 
     431             :     } else {
     432           0 :         crm_info("Connection to local file '%s' for %s (client %s) failed: %s",
     433             :                  private->filename, name, private->id, pcmk_strerror(rc));
     434             :     }
     435           0 :     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           0 : cib_file_write_live(xmlNode *cib_root, char *path)
     449             : {
     450           0 :     uid_t uid = geteuid();
     451             :     struct passwd *daemon_pwent;
     452           0 :     char *sep = strrchr(path, '/');
     453             :     const char *cib_dirname, *cib_filename;
     454           0 :     int rc = 0;
     455             : 
     456             :     /* Get the desired uid/gid */
     457           0 :     errno = 0;
     458           0 :     daemon_pwent = getpwnam(CRM_DAEMON_USER);
     459           0 :     if (daemon_pwent == NULL) {
     460           0 :         crm_perror(LOG_ERR, "Could not find %s user", CRM_DAEMON_USER);
     461           0 :         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           0 :     if ((uid != 0) && (uid != daemon_pwent->pw_uid)) {
     469           0 :         crm_perror(LOG_ERR, "Must be root or %s to modify live CIB",
     470             :                    CRM_DAEMON_USER);
     471           0 :         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           0 :     if (sep == NULL) { /* no directory component specified */
     479           0 :         cib_dirname = "./";
     480           0 :         cib_filename = path;
     481           0 :     } else if (sep == path) { /* given name is in / */
     482           0 :         cib_dirname = "/";
     483           0 :         cib_filename = path + 1;
     484             :     } else { /* typical case; split given name into parts */
     485           0 :         *sep = '\0';
     486           0 :         cib_dirname = path;
     487           0 :         cib_filename = sep + 1;
     488             :     }
     489             : 
     490             :     /* if we're root, we want to update the file ownership */
     491           0 :     if (uid == 0) {
     492           0 :         cib_file_owner = daemon_pwent->pw_uid;
     493           0 :         cib_file_group = daemon_pwent->pw_gid;
     494           0 :         cib_do_chown = TRUE;
     495             :     }
     496             : 
     497             :     /* write the file */
     498           0 :     if (cib_file_write_with_digest(cib_root, cib_dirname,
     499             :                                    cib_filename) != pcmk_ok) {
     500           0 :         rc = -1;
     501             :     }
     502             : 
     503             :     /* turn off file ownership changes, for other callers */
     504           0 :     if (uid == 0) {
     505           0 :         cib_do_chown = FALSE;
     506             :     }
     507             : 
     508             :     /* undo fancy stuff */
     509           0 :     if ((sep != NULL) && (*sep == '\0')) {
     510           0 :         *sep = '/';
     511             :     }
     512             : 
     513           0 :     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           0 : cib_file_signoff(cib_t *cib)
     531             : {
     532           0 :     int rc = pcmk_ok;
     533           0 :     cib_file_opaque_t *private = cib->variant_opaque;
     534             : 
     535           0 :     crm_debug("Disconnecting from the CIB manager");
     536           0 :     cib->state = cib_disconnected;
     537           0 :     cib->type = cib_no_connection;
     538           0 :     unregister_client(cib);
     539           0 :     cib->cmds->end_transaction(cib, false, cib_none);
     540             : 
     541             :     /* If the in-memory CIB has been changed, write it to disk */
     542           0 :     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           0 :         if (pcmk_is_set(private->flags, cib_file_flag_live)) {
     546           0 :             if (cib_file_write_live(private->cib_xml, private->filename) < 0) {
     547           0 :                 rc = pcmk_err_generic;
     548             :             }
     549             : 
     550             :         /* Otherwise, it's a simple write */
     551             :         } else {
     552           0 :             bool compress = pcmk__ends_with_ext(private->filename, ".bz2");
     553             : 
     554           0 :             if (pcmk__xml_write_file(private->cib_xml, private->filename,
     555             :                                      compress, NULL) != pcmk_rc_ok) {
     556           0 :                 rc = pcmk_err_generic;
     557             :             }
     558             :         }
     559             : 
     560           0 :         if (rc == pcmk_ok) {
     561           0 :             crm_info("Wrote CIB to %s", private->filename);
     562           0 :             cib_clear_file_flags(private, cib_file_flag_dirty);
     563             :         } else {
     564           0 :             crm_err("Could not write CIB to %s", private->filename);
     565             :         }
     566             :     }
     567             : 
     568             :     /* Free the in-memory CIB */
     569           0 :     free_xml(private->cib_xml);
     570           0 :     private->cib_xml = NULL;
     571           0 :     return rc;
     572             : }
     573             : 
     574             : static int
     575           0 : cib_file_free(cib_t *cib)
     576             : {
     577           0 :     int rc = pcmk_ok;
     578             : 
     579           0 :     if (cib->state != cib_disconnected) {
     580           0 :         rc = cib_file_signoff(cib);
     581             :     }
     582             : 
     583           0 :     if (rc == pcmk_ok) {
     584           0 :         cib_file_opaque_t *private = cib->variant_opaque;
     585             : 
     586           0 :         free(private->id);
     587           0 :         free(private->filename);
     588           0 :         free(private);
     589           0 :         free(cib->cmds);
     590           0 :         free(cib->user);
     591           0 :         free(cib);
     592             : 
     593             :     } else {
     594           0 :         fprintf(stderr, "Couldn't sign off: %d\n", rc);
     595             :     }
     596             : 
     597           0 :     return rc;
     598             : }
     599             : 
     600             : static int
     601           0 : cib_file_inputfd(cib_t *cib)
     602             : {
     603           0 :     return -EPROTONOSUPPORT;
     604             : }
     605             : 
     606             : static int
     607           0 : cib_file_register_notification(cib_t *cib, const char *callback, int enabled)
     608             : {
     609           0 :     return -EPROTONOSUPPORT;
     610             : }
     611             : 
     612             : static int
     613           0 : cib_file_set_connection_dnotify(cib_t *cib,
     614             :                                 void (*dnotify) (gpointer user_data))
     615             : {
     616           0 :     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           0 : cib_file_client_id(const cib_t *cib, const char **async_id,
     634             :                    const char **sync_id)
     635             : {
     636           0 :     cib_file_opaque_t *private = cib->variant_opaque;
     637             : 
     638           0 :     if (async_id != NULL) {
     639           0 :         *async_id = private->id;
     640             :     }
     641           0 :     if (sync_id != NULL) {
     642           0 :         *sync_id = private->id;
     643             :     }
     644           0 :     return pcmk_ok;
     645             : }
     646             : 
     647             : cib_t *
     648           0 : cib_file_new(const char *cib_location)
     649             : {
     650           0 :     cib_file_opaque_t *private = NULL;
     651           0 :     cib_t *cib = cib_new_variant();
     652             : 
     653           0 :     if (cib == NULL) {
     654           0 :         return NULL;
     655             :     }
     656             : 
     657           0 :     private = calloc(1, sizeof(cib_file_opaque_t));
     658             : 
     659           0 :     if (private == NULL) {
     660           0 :         free(cib);
     661           0 :         return NULL;
     662             :     }
     663           0 :     private->id = crm_generate_uuid();
     664             : 
     665           0 :     cib->variant = cib_file;
     666           0 :     cib->variant_opaque = private;
     667             : 
     668           0 :     if (cib_location == NULL) {
     669           0 :         cib_location = getenv("CIB_file");
     670           0 :         CRM_CHECK(cib_location != NULL, return NULL); // Shouldn't be possible
     671             :     }
     672           0 :     private->flags = 0;
     673           0 :     if (cib_file_is_live(cib_location)) {
     674           0 :         cib_set_file_flags(private, cib_file_flag_live);
     675           0 :         crm_trace("File %s detected as live CIB", cib_location);
     676             :     }
     677           0 :     private->filename = strdup(cib_location);
     678             : 
     679             :     /* assign variant specific ops */
     680           0 :     cib->delegate_fn = cib_file_perform_op_delegate;
     681           0 :     cib->cmds->signon = cib_file_signon;
     682           0 :     cib->cmds->signoff = cib_file_signoff;
     683           0 :     cib->cmds->free = cib_file_free;
     684           0 :     cib->cmds->inputfd = cib_file_inputfd; // Deprecated method
     685             : 
     686           0 :     cib->cmds->register_notification = cib_file_register_notification;
     687           0 :     cib->cmds->set_connection_dnotify = cib_file_set_connection_dnotify;
     688             : 
     689           0 :     cib->cmds->client_id = cib_file_client_id;
     690             : 
     691           0 :     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           0 : cib_file_verify_digest(xmlNode *root, const char *sigfile)
     705             : {
     706           0 :     gboolean passed = FALSE;
     707             :     char *expected;
     708           0 :     int rc = pcmk__file_contents(sigfile, &expected);
     709             : 
     710           0 :     switch (rc) {
     711           0 :         case pcmk_rc_ok:
     712           0 :             if (expected == NULL) {
     713           0 :                 crm_err("On-disk digest at %s is empty", sigfile);
     714           0 :                 return FALSE;
     715             :             }
     716           0 :             break;
     717           0 :         case ENOENT:
     718           0 :             crm_warn("No on-disk digest present at %s", sigfile);
     719           0 :             return TRUE;
     720           0 :         default:
     721           0 :             crm_err("Could not read on-disk digest from %s: %s",
     722             :                     sigfile, pcmk_rc_str(rc));
     723           0 :             return FALSE;
     724             :     }
     725           0 :     passed = pcmk__verify_digest(root, expected);
     726           0 :     free(expected);
     727           0 :     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           0 : cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **root)
     747             : {
     748             :     int s_res;
     749             :     struct stat buf;
     750           0 :     char *local_sigfile = NULL;
     751           0 :     xmlNode *local_root = NULL;
     752             : 
     753           0 :     CRM_ASSERT(filename != NULL);
     754           0 :     if (root) {
     755           0 :         *root = NULL;
     756             :     }
     757             : 
     758             :     /* Verify that file exists and its size is nonzero */
     759           0 :     s_res = stat(filename, &buf);
     760           0 :     if (s_res < 0) {
     761           0 :         crm_perror(LOG_WARNING, "Could not verify cluster configuration file %s", filename);
     762           0 :         return -errno;
     763           0 :     } else if (buf.st_size == 0) {
     764           0 :         crm_warn("Cluster configuration file %s is corrupt (size is zero)", filename);
     765           0 :         return -pcmk_err_cib_corrupt;
     766             :     }
     767             : 
     768             :     /* Parse XML */
     769           0 :     local_root = pcmk__xml_read(filename);
     770           0 :     if (local_root == NULL) {
     771           0 :         crm_warn("Cluster configuration file %s is corrupt (unparseable as XML)", filename);
     772           0 :         return -pcmk_err_cib_corrupt;
     773             :     }
     774             : 
     775             :     /* If sigfile is not specified, use original file name plus .sig */
     776           0 :     if (sigfile == NULL) {
     777           0 :         sigfile = local_sigfile = crm_strdup_printf("%s.sig", filename);
     778             :     }
     779             : 
     780             :     /* Verify that digests match */
     781           0 :     if (cib_file_verify_digest(local_root, sigfile) == FALSE) {
     782           0 :         free(local_sigfile);
     783           0 :         free_xml(local_root);
     784           0 :         return -pcmk_err_cib_modified;
     785             :     }
     786             : 
     787           0 :     free(local_sigfile);
     788           0 :     if (root) {
     789           0 :         *root = local_root;
     790             :     } else {
     791           0 :         free_xml(local_root);
     792             :     }
     793           0 :     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           0 : cib_file_backup(const char *cib_dirname, const char *cib_filename)
     807             : {
     808           0 :     int rc = 0;
     809             :     unsigned int seq;
     810           0 :     char *cib_path = crm_strdup_printf("%s/%s", cib_dirname, cib_filename);
     811           0 :     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           0 :     if (pcmk__read_series_sequence(cib_dirname, CIB_SERIES,
     817             :                                    &seq) != pcmk_rc_ok) {
     818             :         // @TODO maybe handle errors better ...
     819           0 :         seq = 0;
     820             :     }
     821           0 :     backup_path = pcmk__series_filename(cib_dirname, CIB_SERIES, seq,
     822             :                                         CIB_SERIES_BZIP);
     823           0 :     backup_digest = crm_strdup_printf("%s.sig", backup_path);
     824             : 
     825             :     /* Remove the old backups if they exist */
     826           0 :     unlink(backup_path);
     827           0 :     unlink(backup_digest);
     828             : 
     829             :     /* Back up the CIB, by hard-linking it to the backup name */
     830           0 :     if ((link(cib_path, backup_path) < 0) && (errno != ENOENT)) {
     831           0 :         crm_perror(LOG_ERR, "Could not archive %s by linking to %s",
     832             :                    cib_path, backup_path);
     833           0 :         rc = -1;
     834             : 
     835             :     /* Back up the CIB signature similarly */
     836           0 :     } else if ((link(cib_digest, backup_digest) < 0) && (errno != ENOENT)) {
     837           0 :         crm_perror(LOG_ERR, "Could not archive %s by linking to %s",
     838             :                    cib_digest, backup_digest);
     839           0 :         rc = -1;
     840             : 
     841             :     /* Update the last counter and ensure everything is sync'd to media */
     842             :     } else {
     843           0 :         pcmk__write_series_sequence(cib_dirname, CIB_SERIES, ++seq,
     844             :                                     CIB_SERIES_MAX);
     845           0 :         if (cib_do_chown) {
     846             :             int rc2;
     847             : 
     848           0 :             if ((chown(backup_path, cib_file_owner, cib_file_group) < 0)
     849           0 :                     && (errno != ENOENT)) {
     850           0 :                 crm_perror(LOG_ERR, "Could not set owner of %s", backup_path);
     851           0 :                 rc = -1;
     852             :             }
     853           0 :             if ((chown(backup_digest, cib_file_owner, cib_file_group) < 0)
     854           0 :                     && (errno != ENOENT)) {
     855           0 :                 crm_perror(LOG_ERR, "Could not set owner of %s", backup_digest);
     856           0 :                 rc = -1;
     857             :             }
     858           0 :             rc2 = pcmk__chown_series_sequence(cib_dirname, CIB_SERIES,
     859             :                                               cib_file_owner, cib_file_group);
     860           0 :             if (rc2 != pcmk_rc_ok) {
     861           0 :                 crm_err("Could not set owner of sequence file in %s: %s",
     862             :                         cib_dirname, pcmk_rc_str(rc2));
     863           0 :                 rc = -1;
     864             :             }
     865             :         }
     866           0 :         pcmk__sync_directory(cib_dirname);
     867           0 :         crm_info("Archived previous version as %s", backup_path);
     868             :     }
     869             : 
     870           0 :     free(cib_path);
     871           0 :     free(cib_digest);
     872           0 :     free(backup_path);
     873           0 :     free(backup_digest);
     874           0 :     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           0 : cib_file_prepare_xml(xmlNode *root)
     890             : {
     891           0 :     xmlNode *cib_status_root = NULL;
     892             : 
     893             :     /* Always write out with num_updates=0 and current last-written timestamp */
     894           0 :     crm_xml_add(root, PCMK_XA_NUM_UPDATES, "0");
     895           0 :     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           0 :     cib_status_root = pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL);
     900           0 :     CRM_CHECK(cib_status_root != NULL, return);
     901           0 :     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           0 : cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname,
     919             :                            const char *cib_filename)
     920             : {
     921           0 :     int exit_rc = pcmk_ok;
     922             :     int rc, fd;
     923           0 :     char *digest = NULL;
     924             : 
     925             :     /* Detect CIB version for diagnostic purposes */
     926           0 :     const char *epoch = crm_element_value(cib_root, PCMK_XA_EPOCH);
     927           0 :     const char *admin_epoch = crm_element_value(cib_root, PCMK_XA_ADMIN_EPOCH);
     928             : 
     929             :     /* Determine full CIB and signature pathnames */
     930           0 :     char *cib_path = crm_strdup_printf("%s/%s", cib_dirname, cib_filename);
     931           0 :     char *digest_path = crm_strdup_printf("%s.sig", cib_path);
     932             : 
     933             :     /* Create temporary file name patterns for writing out CIB and signature */
     934           0 :     char *tmp_cib = crm_strdup_printf("%s/cib.XXXXXX", cib_dirname);
     935           0 :     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           0 :     crm_trace("Reading cluster configuration file %s", cib_path);
     939           0 :     rc = cib_file_read_and_verify(cib_path, NULL, NULL);
     940           0 :     if ((rc != pcmk_ok) && (rc != -ENOENT)) {
     941           0 :         crm_err("%s was manually modified while the cluster was active!",
     942             :                 cib_path);
     943           0 :         exit_rc = pcmk_err_cib_modified;
     944           0 :         goto cleanup;
     945             :     }
     946             : 
     947             :     /* Back up the existing CIB */
     948           0 :     if (cib_file_backup(cib_dirname, cib_filename) < 0) {
     949           0 :         exit_rc = pcmk_err_cib_backup;
     950           0 :         goto cleanup;
     951             :     }
     952             : 
     953           0 :     crm_debug("Writing CIB to disk");
     954           0 :     umask(S_IWGRP | S_IWOTH | S_IROTH);
     955           0 :     cib_file_prepare_xml(cib_root);
     956             : 
     957             :     /* Write the CIB to a temporary file, so we can deploy (near) atomically */
     958           0 :     fd = mkstemp(tmp_cib);
     959           0 :     if (fd < 0) {
     960           0 :         crm_perror(LOG_ERR, "Couldn't open temporary file %s for writing CIB",
     961             :                    tmp_cib);
     962           0 :         exit_rc = pcmk_err_cib_save;
     963           0 :         goto cleanup;
     964             :     }
     965             : 
     966             :     /* Protect the temporary file */
     967           0 :     if (fchmod(fd, S_IRUSR | S_IWUSR) < 0) {
     968           0 :         crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
     969             :                    tmp_cib);
     970           0 :         exit_rc = pcmk_err_cib_save;
     971           0 :         goto cleanup;
     972             :     }
     973           0 :     if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) {
     974           0 :         crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
     975             :                    tmp_cib);
     976           0 :         exit_rc = pcmk_err_cib_save;
     977           0 :         goto cleanup;
     978             :     }
     979             : 
     980             :     /* Write out the CIB */
     981           0 :     if (pcmk__xml_write_fd(cib_root, tmp_cib, fd, false, NULL) != pcmk_rc_ok) {
     982           0 :         crm_err("Changes couldn't be written to %s", tmp_cib);
     983           0 :         exit_rc = pcmk_err_cib_save;
     984           0 :         goto cleanup;
     985             :     }
     986             : 
     987             :     /* Calculate CIB digest */
     988           0 :     digest = calculate_on_disk_digest(cib_root);
     989           0 :     CRM_ASSERT(digest != NULL);
     990           0 :     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           0 :     fd = mkstemp(tmp_digest);
     995           0 :     if (fd < 0) {
     996           0 :         crm_perror(LOG_ERR, "Could not create temporary file for CIB digest");
     997           0 :         exit_rc = pcmk_err_cib_save;
     998           0 :         goto cleanup;
     999             :     }
    1000           0 :     if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) {
    1001           0 :         crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
    1002             :                    tmp_cib);
    1003           0 :         exit_rc = pcmk_err_cib_save;
    1004           0 :         close(fd);
    1005           0 :         goto cleanup;
    1006             :     }
    1007           0 :     rc = pcmk__write_sync(fd, digest);
    1008           0 :     if (rc != pcmk_rc_ok) {
    1009           0 :         crm_err("Could not write digest to %s: %s",
    1010             :                 tmp_digest, pcmk_rc_str(rc));
    1011           0 :         exit_rc = pcmk_err_cib_save;
    1012           0 :         close(fd);
    1013           0 :         goto cleanup;
    1014             :     }
    1015           0 :     close(fd);
    1016           0 :     crm_debug("Wrote digest %s to disk", digest);
    1017             : 
    1018             :     /* Verify that what we wrote is sane */
    1019           0 :     crm_info("Reading cluster configuration file %s (digest: %s)",
    1020             :              tmp_cib, tmp_digest);
    1021           0 :     rc = cib_file_read_and_verify(tmp_cib, tmp_digest, NULL);
    1022           0 :     CRM_ASSERT(rc == 0);
    1023             : 
    1024             :     /* Rename temporary files to live, and sync directory changes to media */
    1025           0 :     crm_debug("Activating %s", tmp_cib);
    1026           0 :     if (rename(tmp_cib, cib_path) < 0) {
    1027           0 :         crm_perror(LOG_ERR, "Couldn't rename %s as %s", tmp_cib, cib_path);
    1028           0 :         exit_rc = pcmk_err_cib_save;
    1029             :     }
    1030           0 :     if (rename(tmp_digest, digest_path) < 0) {
    1031           0 :         crm_perror(LOG_ERR, "Couldn't rename %s as %s", tmp_digest,
    1032             :                    digest_path);
    1033           0 :         exit_rc = pcmk_err_cib_save;
    1034             :     }
    1035           0 :     pcmk__sync_directory(cib_dirname);
    1036             : 
    1037           0 :   cleanup:
    1038           0 :     free(cib_path);
    1039           0 :     free(digest_path);
    1040           0 :     free(digest);
    1041           0 :     free(tmp_digest);
    1042           0 :     free(tmp_cib);
    1043           0 :     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           0 : cib_file_process_transaction_requests(cib_t *cib, xmlNode *transaction)
    1059             : {
    1060           0 :     cib_file_opaque_t *private = cib->variant_opaque;
    1061             : 
    1062           0 :     for (xmlNode *request = pcmk__xe_first_child(transaction,
    1063             :                                                  PCMK__XE_CIB_COMMAND, NULL,
    1064             :                                                  NULL);
    1065           0 :          request != NULL; request = pcmk__xe_next_same(request)) {
    1066             : 
    1067           0 :         xmlNode *output = NULL;
    1068           0 :         const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
    1069             : 
    1070           0 :         int rc = cib_file_process_request(cib, request, &output);
    1071             : 
    1072           0 :         rc = pcmk_legacy2rc(rc);
    1073           0 :         if (rc != pcmk_rc_ok) {
    1074           0 :             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           0 :             crm_log_xml_info(request, "Failed request");
    1078           0 :             return rc;
    1079             :         }
    1080             : 
    1081           0 :         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           0 :         crm_log_xml_trace(request, "Successful request");
    1085             :     }
    1086             : 
    1087           0 :     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           0 : cib_file_commit_transaction(cib_t *cib, xmlNode *transaction,
    1106             :                             xmlNode **result_cib)
    1107             : {
    1108           0 :     int rc = pcmk_rc_ok;
    1109           0 :     cib_file_opaque_t *private = cib->variant_opaque;
    1110           0 :     xmlNode *saved_cib = private->cib_xml;
    1111             : 
    1112           0 :     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           0 :     CRM_CHECK((*result_cib != NULL) && (*result_cib != private->cib_xml),
    1123             :               *result_cib = pcmk__xml_copy(NULL, private->cib_xml));
    1124             : 
    1125           0 :     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           0 :     private->cib_xml = *result_cib;
    1131             : 
    1132           0 :     rc = cib_file_process_transaction_requests(cib, transaction);
    1133             : 
    1134           0 :     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           0 :     *result_cib = private->cib_xml;
    1145             : 
    1146             :     // Point private->cib_xml back to the unchanged original copy
    1147           0 :     private->cib_xml = saved_cib;
    1148             : 
    1149           0 :     return rc;
    1150             : }
    1151             : 
    1152             : static int
    1153           0 : cib_file_process_commit_transaction(const char *op, int options,
    1154             :                                     const char *section, xmlNode *req,
    1155             :                                     xmlNode *input, xmlNode *existing_cib,
    1156             :                                     xmlNode **result_cib, xmlNode **answer)
    1157             : {
    1158           0 :     int rc = pcmk_rc_ok;
    1159           0 :     const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID);
    1160           0 :     cib_t *cib = NULL;
    1161             : 
    1162           0 :     CRM_CHECK(client_id != NULL, return -EINVAL);
    1163             : 
    1164           0 :     cib = get_client(client_id);
    1165           0 :     CRM_CHECK(cib != NULL, return -EINVAL);
    1166             : 
    1167           0 :     rc = cib_file_commit_transaction(cib, input, result_cib);
    1168           0 :     if (rc != pcmk_rc_ok) {
    1169           0 :         cib_file_opaque_t *private = cib->variant_opaque;
    1170             : 
    1171           0 :         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           0 :     return pcmk_rc2legacy(rc);
    1176             : }

Generated by: LCOV version 1.14