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