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 pcmk__assert((operation != NULL) && (op_function != NULL));
158
159 rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options,
160 cib_none);
161 if (rc != pcmk_rc_ok) {
162 pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc));
163 }
164
165 read_only = !pcmk__is_set(operation->flags, cib__op_attr_modifies);
166
167 if (read_only) {
168 rc = cib__perform_op_ro(op_function, request, &private->cib_xml,
169 output);
170 } else {
171 result_cib = private->cib_xml;
172 rc = cib__perform_op_rw(cib_file, op_function, request, &changed,
173 &result_cib, &cib_diff, output);
174 }
175
176 if (pcmk__is_set(call_options, cib_transaction)) {
177 /* The rest of the logic applies only to the transaction as a whole, not
178 * to individual requests.
179 */
180 goto done;
181 }
182
183 if (rc == pcmk_rc_schema_validation) {
184 // Show validation errors to stderr
185 pcmk__validate_xml(result_cib, NULL, NULL);
186
187 } else if ((rc == pcmk_rc_ok) && !read_only) {
188 if (result_cib != private->cib_xml) {
189 pcmk__xml_free(private->cib_xml);
190 private->cib_xml = result_cib;
191 }
192 set_file_flags(private, file_flag_dirty);
193 }
194
195 if (*output == NULL) {
196 goto done;
197 }
198
199 if ((*output)->doc == private->cib_xml->doc) {
200 *output = pcmk__xml_copy(NULL, *output);
201 }
202
203 done:
204 if (result_cib != private->cib_xml) {
205 pcmk__xml_free(result_cib);
206 }
207 pcmk__xml_free(cib_diff);
208 return rc;
209 }
210
211 /*!
212 * \internal
213 * \brief Process requests in a CIB transaction
214 *
215 * Stop when a request fails or when all requests have been processed.
216 *
217 * \param[in,out] cib CIB client
218 * \param[in,out] transaction CIB transaction
219 *
220 * \return Standard Pacemaker return code
221 */
222 static int
223 process_transaction_requests(cib_t *cib, xmlNode *transaction)
224 {
225 file_opaque_t *private = cib->variant_opaque;
226
227 for (xmlNode *request = pcmk__xe_first_child(transaction,
228 PCMK__XE_CIB_COMMAND, NULL,
229 NULL);
230 request != NULL;
231 request = pcmk__xe_next(request, PCMK__XE_CIB_COMMAND)) {
232
233 xmlNode *output = NULL;
234 const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP);
235
236 int rc = process_request(cib, request, &output);
237
238 pcmk__xml_free(output);
239
240 if (rc != pcmk_rc_ok) {
241 pcmk__err("Aborting transaction for CIB file client (%s) on file "
242 "'%s' due to failed %s request: %s",
243 private->id, private->filename, op, pcmk_rc_str(rc));
244 pcmk__log_xml_info(request, "Failed request");
245 return rc;
246 }
247
248 pcmk__trace("Applied %s request to transaction working CIB for CIB "
249 "file client (%s) on file '%s'",
250 op, private->id, private->filename);
251 pcmk__log_xml_trace(request, "Successful request");
252 }
253
254 return pcmk_rc_ok;
255 }
256
257 /*!
258 * \internal
259 * \brief Commit a given CIB file client's transaction to a working CIB copy
260 *
261 * \param[in,out] cib CIB file client
262 * \param[in] transaction CIB transaction
263 * \param[in,out] result_cib Where to store result CIB
264 *
265 * \return Standard Pacemaker return code
266 *
267 * \note The caller is responsible for replacing the \p cib argument's
268 * \p private->cib_xml with \p result_cib on success, and for freeing
269 * \p result_cib using \p pcmk__xml_free() on failure.
270 */
271 static int
272 commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib)
273 {
274 int rc = pcmk_rc_ok;
275 file_opaque_t *private = cib->variant_opaque;
276 xmlNode *saved_cib = private->cib_xml;
277
278 /* *result_cib should be a copy of private->cib_xml (created by
279 * cib__perform_op_rw())
280 */
281 pcmk__assert((result_cib != NULL) && (*result_cib != NULL)
282 && (*result_cib != private->cib_xml));
283
284 CRM_CHECK(pcmk__xe_is(transaction, PCMK__XE_CIB_TRANSACTION),
285 return pcmk_rc_no_transaction);
286
287 pcmk__trace("Committing transaction for CIB file client (%s) on file '%s' "
288 "to working CIB",
289 private->id, private->filename);
290
291 // Apply all changes to a working copy of the CIB
292 private->cib_xml = *result_cib;
293
294 rc = process_transaction_requests(cib, transaction);
295
296 pcmk__trace("Transaction commit %s for CIB file client (%s) on file '%s'",
297 ((rc == pcmk_rc_ok)? "succeeded" : "failed"),
298 private->id, private->filename);
299
300 /* Some request types (for example, erase) may have freed private->cib_xml
301 * (the working copy) and pointed it at a new XML object. In that case, it
302 * follows that *result_cib (the working copy) was freed.
303 *
304 * Point *result_cib at the updated working copy stored in private->cib_xml.
305 */
306 *result_cib = private->cib_xml;
307
308 // Point private->cib_xml back to the unchanged original copy
309 private->cib_xml = saved_cib;
310
311 return rc;
312 }
313
314 static int
315 process_commit_transact(xmlNode *req, xmlNode **cib_xml, xmlNode **answer)
316 {
317 int rc = pcmk_rc_ok;
318 xmlNode *input = cib__get_calldata(req);
319 const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID);
320 cib_t *cib = NULL;
321
322 CRM_CHECK(client_id != NULL, return -EINVAL);
323
324 cib = get_client(client_id);
325 CRM_CHECK(cib != NULL, return -EINVAL);
326
327 rc = commit_transaction(cib, input, cib_xml);
328 if (rc != pcmk_rc_ok) {
329 file_opaque_t *private = cib->variant_opaque;
330
331 pcmk__err("Could not commit transaction for CIB file client (%s) on "
332 "file '%s': %s",
333 private->id, private->filename, pcmk_rc_str(rc));
334 }
335 return pcmk_rc2legacy(rc);
336 }
337
338 static const cib__op_fn_t op_functions[] = {
339 [cib__op_apply_patch] = cib__process_apply_patch,
340 [cib__op_bump] = cib__process_bump,
341 [cib__op_commit_transact] = process_commit_transact,
342 [cib__op_create] = cib__process_create,
343 [cib__op_delete] = cib__process_delete,
344 [cib__op_erase] = cib__process_erase,
345 [cib__op_modify] = cib__process_modify,
346 [cib__op_query] = cib__process_query,
347 [cib__op_replace] = cib__process_replace,
348 [cib__op_upgrade] = cib__process_upgrade,
349 };
350
351 /*!
352 * \internal
353 * \brief Get the function that performs a given CIB file operation
354 *
355 * \param[in] operation Operation whose function to look up
356 *
357 * \return Function that performs \p operation for a CIB file client
358 */
359 static cib__op_fn_t
360 get_op_function(const cib__operation_t *operation)
361 {
362 enum cib__op_type type = operation->type;
363
364 pcmk__assert(type >= 0);
365
366 if (type >= PCMK__NELEM(op_functions)) {
367 return NULL;
368 }
369 return op_functions[type];
370 }
371
372 /*!
373 * \internal
374 * \brief Check whether a file is the live CIB
375 *
376 * \param[in] filename Name of file to check
377 *
378 * \return \c true if file exists and its real path is same as the live CIB's,
379 * or \c false otherwise
380 */
381 static bool
382 is_live(const char *filename)
383 {
384 bool same = false;
385
386 if (filename != NULL) {
387 // Canonicalize file names for true comparison
388 char *real_filename = NULL;
389
390 if (pcmk__real_path(filename, &real_filename) == pcmk_rc_ok) {
391 char *real_livename = NULL;
392
393 if (pcmk__real_path(CRM_CONFIG_DIR "/" CIB_LIVE_NAME,
394 &real_livename) == pcmk_rc_ok) {
395 same = !strcmp(real_filename, real_livename);
396 free(real_livename);
397 }
398 free(real_filename);
399 }
400 }
401 return same;
402 }
403
404 static int
405 file_perform_op_delegate(cib_t *cib, const char *op, const char *host,
406 const char *section, xmlNode *data,
407 xmlNode **output_data, int call_options,
408 const char *user_name)
409 {
410 int rc = pcmk_ok;
411 xmlNode *request = NULL;
412 xmlNode *output = NULL;
413 file_opaque_t *private = cib->variant_opaque;
414
415 const cib__operation_t *operation = NULL;
416
417 pcmk__info("Handling %s operation for %s as %s", pcmk__s(op, "invalid"),
418 pcmk__s(section, "entire CIB"),
419 pcmk__s(user_name, "default user"));
420
421 if (cib->state == cib_disconnected) {
422 rc = ENOTCONN;
423 goto done;
424 }
425
426 rc = cib__get_operation(op, &operation);
427 if (rc != pcmk_rc_ok) {
428 // @COMPAT: At compatibility break, use rc directly
429 rc = EPROTONOSUPPORT;
430 goto done;
431 }
432
433 if (get_op_function(operation) == NULL) {
434 // @COMPAT: At compatibility break, use EOPNOTSUPP
435 pcmk__err("Operation %s is not supported by CIB file clients", op);
436 rc = EPROTONOSUPPORT;
437 goto done;
438 }
439
440 cib__set_call_options(call_options, "file operation", cib_no_mtime);
441
442 rc = cib__create_op(cib, op, host, section, data, call_options, user_name,
443 NULL, &request);
444 if (rc != pcmk_rc_ok) {
445 goto done;
446 }
447
448 pcmk__xe_set(request, PCMK__XA_ACL_TARGET, user_name);
449 pcmk__xe_set(request, PCMK__XA_CIB_CLIENTID, private->id);
450
451 if (pcmk__is_set(call_options, cib_transaction)) {
452 rc = cib__extend_transaction(cib, request);
453 goto done;
454 }
455
456 rc = process_request(cib, request, &output);
457
458 done:
459 pcmk__xml_free(request);
460
461 if (output_data != NULL) {
462 *output_data = output;
463
464 } else {
465 pcmk__xml_free(output);
466 }
467
468 return pcmk_rc2legacy(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
|
(1) Event path: |
Condition "cib_location == NULL", taking true branch. |
753 if (cib_location == NULL) {
754 cib_location = getenv("CIB_file");
|
(2) Event path: |
Condition "cib_location == NULL", taking false branch. |
755 if (cib_location == NULL) {
756 return NULL; // Shouldn't be possible if we were called internally
757 }
758 }
759
|
(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] |
760 cib = cib_new_variant();
|
(5) Event path: |
Condition "cib == NULL", taking false branch. |
761 if (cib == NULL) {
762 return NULL;
763 }
764
765 filename = strdup(cib_location);
|
(6) Event path: |
Condition "filename == NULL", taking true branch. |
766 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] |
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