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