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