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