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