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