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