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