pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
cib_file.c
Go to the documentation of this file.
1/*
2 * Original copyright 2004 International Business Machines
3 * Later changes copyright 2008-2024 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 <stdlib.h>
15#include <stdint.h>
16#include <stdio.h>
17#include <stdarg.h>
18#include <string.h>
19#include <pwd.h>
20
21#include <sys/stat.h>
22#include <sys/types.h>
23#include <glib.h>
24
25#include <crm/crm.h>
26#include <crm/cib/internal.h>
27#include <crm/common/ipc.h>
28#include <crm/common/xml.h>
30
31#define CIB_SERIES "cib"
32#define CIB_SERIES_MAX 100
33#define CIB_SERIES_BZIP FALSE /* Must be false because archived copies are
34 created with hard links
35 */
36
37#define CIB_LIVE_NAME CIB_SERIES ".xml"
38
39// key: client ID (const char *) -> value: client (cib_t *)
40static GHashTable *client_table = NULL;
43 cib_file_flag_dirty = (1 << 0),
44 cib_file_flag_live = (1 << 1),
45};
46
47typedef struct cib_file_opaque_s {
48 char *id;
49 char *filename;
50 uint32_t flags; // Group of enum cib_file_flags
51 xmlNode *cib_xml;
53
54static int cib_file_process_commit_transaction(const char *op, int options,
55 const char *section,
56 xmlNode *req, xmlNode *input,
57 xmlNode *existing_cib,
58 xmlNode **result_cib,
59 xmlNode **answer);
60
67static void
68register_client(const cib_t *cib)
69{
70 cib_file_opaque_t *private = cib->variant_opaque;
71
72 if (client_table == NULL) {
73 client_table = pcmk__strkey_table(NULL, NULL);
74 }
75 g_hash_table_insert(client_table, private->id, (gpointer) cib);
76}
77
84static void
85unregister_client(const cib_t *cib)
86{
87 cib_file_opaque_t *private = cib->variant_opaque;
88
89 if (client_table == NULL) {
90 return;
91 }
92
93 g_hash_table_remove(client_table, private->id);
94
95 /* @COMPAT: Add to crm_exit() when libcib and libcrmcommon are merged,
96 * instead of destroying the client table when there are no more clients.
97 */
98 if (g_hash_table_size(client_table) == 0) {
99 g_hash_table_destroy(client_table);
100 client_table = NULL;
101 }
102}
103
112static cib_t *
113get_client(const char *client_id)
114{
115 if (client_table == NULL) {
116 return NULL;
117 }
118 return g_hash_table_lookup(client_table, (gpointer) client_id);
119}
120
121static const cib__op_fn_t cib_op_functions[] = {
124 [cib__op_commit_transact] = cib_file_process_commit_transaction,
132};
133
134/* cib_file_backup() and cib_file_write_with_digest() need to chown the
135 * written files only in limited circumstances, so these variables allow
136 * that to be indicated without affecting external callers
137 */
138static uid_t cib_file_owner = 0;
139static uid_t cib_file_group = 0;
140static gboolean cib_do_chown = FALSE;
141
142#define cib_set_file_flags(cibfile, flags_to_set) do { \
143 (cibfile)->flags = pcmk__set_flags_as(__func__, __LINE__, \
144 LOG_TRACE, "CIB file", \
145 cibfile->filename, \
146 (cibfile)->flags, \
147 (flags_to_set), \
148 #flags_to_set); \
149 } while (0)
150
151#define cib_clear_file_flags(cibfile, flags_to_clear) do { \
152 (cibfile)->flags = pcmk__clear_flags_as(__func__, __LINE__, \
153 LOG_TRACE, "CIB file", \
154 cibfile->filename, \
155 (cibfile)->flags, \
156 (flags_to_clear), \
157 #flags_to_clear); \
158 } while (0)
159
168static cib__op_fn_t
169file_get_op_function(const cib__operation_t *operation)
170{
171 enum cib__op_type type = operation->type;
172
173 CRM_ASSERT(type >= 0);
174
175 if (type >= PCMK__NELEM(cib_op_functions)) {
176 return NULL;
177 }
178 return cib_op_functions[type];
179}
180
189static gboolean
190cib_file_is_live(const char *filename)
191{
192 gboolean same = FALSE;
193
194 if (filename != NULL) {
195 // Canonicalize file names for true comparison
196 char *real_filename = NULL;
197
198 if (pcmk__real_path(filename, &real_filename) == pcmk_rc_ok) {
199 char *real_livename = NULL;
200
202 &real_livename) == pcmk_rc_ok) {
203 same = !strcmp(real_filename, real_livename);
204 free(real_livename);
205 }
206 free(real_filename);
207 }
208 }
209 return same;
210}
211
212static int
213cib_file_process_request(cib_t *cib, xmlNode *request, xmlNode **output)
214{
215 int rc = pcmk_ok;
216 const cib__operation_t *operation = NULL;
217 cib__op_fn_t op_function = NULL;
218
219 int call_id = 0;
220 int call_options = cib_none;
221 const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
222 const char *section = crm_element_value(request, PCMK__XA_CIB_SECTION);
223 xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA,
224 NULL, NULL);
225 xmlNode *data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
226
227 bool changed = false;
228 bool read_only = false;
229 xmlNode *result_cib = NULL;
230 xmlNode *cib_diff = NULL;
231
232 cib_file_opaque_t *private = cib->variant_opaque;
233
234 // We error checked these in callers
235 cib__get_operation(op, &operation);
236 op_function = file_get_op_function(operation);
237
238 crm_element_value_int(request, PCMK__XA_CIB_CALLID, &call_id);
239 crm_element_value_int(request, PCMK__XA_CIB_CALLOPT, &call_options);
240
241 read_only = !pcmk_is_set(operation->flags, cib__op_attr_modifies);
242
243 // Mirror the logic in prepare_input() in pacemaker-based
244 if ((section != NULL) && pcmk__xe_is(data, PCMK_XE_CIB)) {
245
246 data = pcmk_find_cib_element(data, section);
247 }
248
249 rc = cib_perform_op(cib, op, call_options, op_function, read_only, section,
250 request, data, true, &changed, &private->cib_xml,
251 &result_cib, &cib_diff, output);
252
253 if (pcmk_is_set(call_options, cib_transaction)) {
254 /* The rest of the logic applies only to the transaction as a whole, not
255 * to individual requests.
256 */
257 goto done;
258 }
259
260 if (rc == -pcmk_err_schema_validation) {
261 // Show validation errors to stderr
262 pcmk__validate_xml(result_cib, NULL, NULL, NULL);
263
264 } else if ((rc == pcmk_ok) && !read_only) {
265 pcmk__log_xml_patchset(LOG_DEBUG, cib_diff);
266
267 if (result_cib != private->cib_xml) {
268 free_xml(private->cib_xml);
269 private->cib_xml = result_cib;
270 }
272 }
273
274 // Global operation callback (deprecated)
275 if (cib->op_callback != NULL) {
276 cib->op_callback(NULL, call_id, rc, *output);
277 }
278
279done:
280 if ((result_cib != private->cib_xml) && (result_cib != *output)) {
281 free_xml(result_cib);
282 }
283 free_xml(cib_diff);
284 return rc;
285}
286
287static int
288cib_file_perform_op_delegate(cib_t *cib, const char *op, const char *host,
289 const char *section, xmlNode *data,
290 xmlNode **output_data, int call_options,
291 const char *user_name)
292{
293 int rc = pcmk_ok;
294 xmlNode *request = NULL;
295 xmlNode *output = NULL;
296 cib_file_opaque_t *private = cib->variant_opaque;
297
298 const cib__operation_t *operation = NULL;
299
300 crm_info("Handling %s operation for %s as %s",
301 pcmk__s(op, "invalid"), pcmk__s(section, "entire CIB"),
302 pcmk__s(user_name, "default user"));
303
304 if (output_data != NULL) {
305 *output_data = NULL;
306 }
307
308 if (cib->state == cib_disconnected) {
309 return -ENOTCONN;
310 }
311
312 rc = cib__get_operation(op, &operation);
313 rc = pcmk_rc2legacy(rc);
314 if (rc != pcmk_ok) {
315 // @COMPAT: At compatibility break, use rc directly
316 return -EPROTONOSUPPORT;
317 }
318
319 if (file_get_op_function(operation) == NULL) {
320 // @COMPAT: At compatibility break, use EOPNOTSUPP
321 crm_err("Operation %s is not supported by CIB file clients", op);
322 return -EPROTONOSUPPORT;
323 }
324
325 cib__set_call_options(call_options, "file operation", cib_no_mtime);
326
327 rc = cib__create_op(cib, op, host, section, data, call_options, user_name,
328 NULL, &request);
329 if (rc != pcmk_ok) {
330 return rc;
331 }
332 crm_xml_add(request, PCMK_XE_ACL_TARGET, user_name);
333 crm_xml_add(request, PCMK__XA_CIB_CLIENTID, private->id);
334
335 if (pcmk_is_set(call_options, cib_transaction)) {
336 rc = cib__extend_transaction(cib, request);
337 goto done;
338 }
339
340 rc = cib_file_process_request(cib, request, &output);
341
342 if ((output_data != NULL) && (output != NULL)) {
343 if (output->doc == private->cib_xml->doc) {
344 *output_data = pcmk__xml_copy(NULL, output);
345 } else {
346 *output_data = output;
347 }
348 }
349
350done:
351 if ((output != NULL)
352 && (output->doc != private->cib_xml->doc)
353 && ((output_data == NULL) || (output != *output_data))) {
354
355 free_xml(output);
356 }
357 free_xml(request);
358 return rc;
359}
360
376static int
377load_file_cib(const char *filename, xmlNode **output)
378{
379 struct stat buf;
380 xmlNode *root = NULL;
381
382 /* Ensure file is readable */
383 if (strcmp(filename, "-") && (stat(filename, &buf) < 0)) {
384 return -ENXIO;
385 }
386
387 /* Parse XML from file */
388 root = pcmk__xml_read(filename);
389 if (root == NULL) {
391 }
392
393 /* Add a status section if not already present */
394 if (pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL) == NULL) {
396 }
397
398 /* Validate XML against its specified schema */
400 const char *schema = crm_element_value(root, PCMK_XA_VALIDATE_WITH);
401
402 crm_err("CIB does not validate against %s, or that schema is unknown", schema);
403 free_xml(root);
405 }
406
407 /* Remember the parsed XML for later use */
408 *output = root;
409 return pcmk_ok;
410}
411
412static int
413cib_file_signon(cib_t *cib, const char *name, enum cib_conn_type type)
414{
415 int rc = pcmk_ok;
416 cib_file_opaque_t *private = cib->variant_opaque;
417
418 if (private->filename == NULL) {
419 rc = -EINVAL;
420 } else {
421 rc = load_file_cib(private->filename, &private->cib_xml);
422 }
423
424 if (rc == pcmk_ok) {
425 crm_debug("Opened connection to local file '%s' for %s",
426 private->filename, name);
428 cib->type = cib_command;
429 register_client(cib);
430
431 } else {
432 crm_info("Connection to local file '%s' for %s (client %s) failed: %s",
433 private->filename, name, private->id, pcmk_strerror(rc));
434 }
435 return rc;
436}
437
447static int
448cib_file_write_live(xmlNode *cib_root, char *path)
449{
450 uid_t uid = geteuid();
451 struct passwd *daemon_pwent;
452 char *sep = strrchr(path, '/');
453 const char *cib_dirname, *cib_filename;
454 int rc = 0;
455
456 /* Get the desired uid/gid */
457 errno = 0;
458 daemon_pwent = getpwnam(CRM_DAEMON_USER);
459 if (daemon_pwent == NULL) {
460 crm_perror(LOG_ERR, "Could not find %s user", CRM_DAEMON_USER);
461 return -1;
462 }
463
464 /* If we're root, we can change the ownership;
465 * if we're daemon, anything we create will be OK;
466 * otherwise, block access so we don't create wrong owner
467 */
468 if ((uid != 0) && (uid != daemon_pwent->pw_uid)) {
469 crm_perror(LOG_ERR, "Must be root or %s to modify live CIB",
471 return 0;
472 }
473
474 /* fancy footwork to separate dirname from filename
475 * (we know the canonical name maps to the live CIB,
476 * but the given name might be relative, or symlinked)
477 */
478 if (sep == NULL) { /* no directory component specified */
479 cib_dirname = "./";
480 cib_filename = path;
481 } else if (sep == path) { /* given name is in / */
482 cib_dirname = "/";
483 cib_filename = path + 1;
484 } else { /* typical case; split given name into parts */
485 *sep = '\0';
486 cib_dirname = path;
487 cib_filename = sep + 1;
488 }
489
490 /* if we're root, we want to update the file ownership */
491 if (uid == 0) {
492 cib_file_owner = daemon_pwent->pw_uid;
493 cib_file_group = daemon_pwent->pw_gid;
494 cib_do_chown = TRUE;
495 }
496
497 /* write the file */
498 if (cib_file_write_with_digest(cib_root, cib_dirname,
499 cib_filename) != pcmk_ok) {
500 rc = -1;
501 }
502
503 /* turn off file ownership changes, for other callers */
504 if (uid == 0) {
505 cib_do_chown = FALSE;
506 }
507
508 /* undo fancy stuff */
509 if ((sep != NULL) && (*sep == '\0')) {
510 *sep = '/';
511 }
512
513 return rc;
514}
515
529static int
530cib_file_signoff(cib_t *cib)
531{
532 int rc = pcmk_ok;
533 cib_file_opaque_t *private = cib->variant_opaque;
534
535 crm_debug("Disconnecting from the CIB manager");
536 cib->state = cib_disconnected;
537 cib->type = cib_no_connection;
538 unregister_client(cib);
539 cib->cmds->end_transaction(cib, false, cib_none);
540
541 /* If the in-memory CIB has been changed, write it to disk */
542 if (pcmk_is_set(private->flags, cib_file_flag_dirty)) {
543
544 /* If this is the live CIB, write it out with a digest */
545 if (pcmk_is_set(private->flags, cib_file_flag_live)) {
546 if (cib_file_write_live(private->cib_xml, private->filename) < 0) {
547 rc = pcmk_err_generic;
548 }
549
550 /* Otherwise, it's a simple write */
551 } else {
552 bool compress = pcmk__ends_with_ext(private->filename, ".bz2");
553
554 if (pcmk__xml_write_file(private->cib_xml, private->filename,
555 compress, NULL) != pcmk_rc_ok) {
556 rc = pcmk_err_generic;
557 }
558 }
559
560 if (rc == pcmk_ok) {
561 crm_info("Wrote CIB to %s", private->filename);
563 } else {
564 crm_err("Could not write CIB to %s", private->filename);
565 }
566 }
567
568 /* Free the in-memory CIB */
569 free_xml(private->cib_xml);
570 private->cib_xml = NULL;
571 return rc;
572}
573
574static int
575cib_file_free(cib_t *cib)
576{
577 int rc = pcmk_ok;
578
579 if (cib->state != cib_disconnected) {
580 rc = cib_file_signoff(cib);
581 }
582
583 if (rc == pcmk_ok) {
584 cib_file_opaque_t *private = cib->variant_opaque;
585
586 free(private->id);
587 free(private->filename);
588 free(private);
589 free(cib->cmds);
590 free(cib->user);
591 free(cib);
592
593 } else {
594 fprintf(stderr, "Couldn't sign off: %d\n", rc);
595 }
596
597 return rc;
598}
599
600static int
601cib_file_inputfd(cib_t *cib)
602{
603 return -EPROTONOSUPPORT;
604}
605
606static int
607cib_file_register_notification(cib_t *cib, const char *callback, int enabled)
608{
609 return -EPROTONOSUPPORT;
610}
611
612static int
613cib_file_set_connection_dnotify(cib_t *cib,
614 void (*dnotify) (gpointer user_data))
615{
616 return -EPROTONOSUPPORT;
617}
618
632static int
633cib_file_client_id(const cib_t *cib, const char **async_id,
634 const char **sync_id)
635{
636 cib_file_opaque_t *private = cib->variant_opaque;
637
638 if (async_id != NULL) {
639 *async_id = private->id;
640 }
641 if (sync_id != NULL) {
642 *sync_id = private->id;
643 }
644 return pcmk_ok;
645}
647cib_t *
648cib_file_new(const char *cib_location)
649{
650 cib_file_opaque_t *private = NULL;
651 cib_t *cib = cib_new_variant();
652
653 if (cib == NULL) {
654 return NULL;
655 }
656
657 private = calloc(1, sizeof(cib_file_opaque_t));
658
659 if (private == NULL) {
660 free(cib);
661 return NULL;
662 }
663 private->id = crm_generate_uuid();
664
665 cib->variant = cib_file;
666 cib->variant_opaque = private;
667
668 if (cib_location == NULL) {
669 cib_location = getenv("CIB_file");
670 CRM_CHECK(cib_location != NULL, return NULL); // Shouldn't be possible
671 }
672 private->flags = 0;
673 if (cib_file_is_live(cib_location)) {
675 crm_trace("File %s detected as live CIB", cib_location);
676 }
677 private->filename = strdup(cib_location);
678
679 /* assign variant specific ops */
680 cib->delegate_fn = cib_file_perform_op_delegate;
681 cib->cmds->signon = cib_file_signon;
682 cib->cmds->signoff = cib_file_signoff;
683 cib->cmds->free = cib_file_free;
684 cib->cmds->inputfd = cib_file_inputfd; // Deprecated method
685
686 cib->cmds->register_notification = cib_file_register_notification;
687 cib->cmds->set_connection_dnotify = cib_file_set_connection_dnotify;
688
689 cib->cmds->client_id = cib_file_client_id;
690
691 return cib;
692}
693
703static gboolean
704cib_file_verify_digest(xmlNode *root, const char *sigfile)
705{
706 gboolean passed = FALSE;
707 char *expected;
708 int rc = pcmk__file_contents(sigfile, &expected);
709
710 switch (rc) {
711 case pcmk_rc_ok:
712 if (expected == NULL) {
713 crm_err("On-disk digest at %s is empty", sigfile);
714 return FALSE;
715 }
716 break;
717 case ENOENT:
718 crm_warn("No on-disk digest present at %s", sigfile);
719 return TRUE;
720 default:
721 crm_err("Could not read on-disk digest from %s: %s",
722 sigfile, pcmk_rc_str(rc));
723 return FALSE;
724 }
725 passed = pcmk__verify_digest(root, expected);
726 free(expected);
727 return passed;
728}
729
745int
746cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **root)
747{
748 int s_res;
749 struct stat buf;
750 char *local_sigfile = NULL;
751 xmlNode *local_root = NULL;
752
753 CRM_ASSERT(filename != NULL);
754 if (root) {
755 *root = NULL;
756 }
757
758 /* Verify that file exists and its size is nonzero */
759 s_res = stat(filename, &buf);
760 if (s_res < 0) {
761 crm_perror(LOG_WARNING, "Could not verify cluster configuration file %s", filename);
762 return -errno;
763 } else if (buf.st_size == 0) {
764 crm_warn("Cluster configuration file %s is corrupt (size is zero)", filename);
765 return -pcmk_err_cib_corrupt;
766 }
767
768 /* Parse XML */
769 local_root = pcmk__xml_read(filename);
770 if (local_root == NULL) {
771 crm_warn("Cluster configuration file %s is corrupt (unparseable as XML)", filename);
772 return -pcmk_err_cib_corrupt;
773 }
774
775 /* If sigfile is not specified, use original file name plus .sig */
776 if (sigfile == NULL) {
777 sigfile = local_sigfile = crm_strdup_printf("%s.sig", filename);
778 }
779
780 /* Verify that digests match */
781 if (cib_file_verify_digest(local_root, sigfile) == FALSE) {
782 free(local_sigfile);
783 free_xml(local_root);
784 return -pcmk_err_cib_modified;
785 }
786
787 free(local_sigfile);
788 if (root) {
789 *root = local_root;
790 } else {
791 free_xml(local_root);
792 }
793 return pcmk_ok;
794}
795
805static int
806cib_file_backup(const char *cib_dirname, const char *cib_filename)
807{
808 int rc = 0;
809 unsigned int seq;
810 char *cib_path = crm_strdup_printf("%s/%s", cib_dirname, cib_filename);
811 char *cib_digest = crm_strdup_printf("%s.sig", cib_path);
812 char *backup_path;
813 char *backup_digest;
814
815 // Determine backup and digest file names
816 if (pcmk__read_series_sequence(cib_dirname, CIB_SERIES,
817 &seq) != pcmk_rc_ok) {
818 // @TODO maybe handle errors better ...
819 seq = 0;
820 }
821 backup_path = pcmk__series_filename(cib_dirname, CIB_SERIES, seq,
823 backup_digest = crm_strdup_printf("%s.sig", backup_path);
824
825 /* Remove the old backups if they exist */
826 unlink(backup_path);
827 unlink(backup_digest);
828
829 /* Back up the CIB, by hard-linking it to the backup name */
830 if ((link(cib_path, backup_path) < 0) && (errno != ENOENT)) {
831 crm_perror(LOG_ERR, "Could not archive %s by linking to %s",
832 cib_path, backup_path);
833 rc = -1;
834
835 /* Back up the CIB signature similarly */
836 } else if ((link(cib_digest, backup_digest) < 0) && (errno != ENOENT)) {
837 crm_perror(LOG_ERR, "Could not archive %s by linking to %s",
838 cib_digest, backup_digest);
839 rc = -1;
840
841 /* Update the last counter and ensure everything is sync'd to media */
842 } else {
843 pcmk__write_series_sequence(cib_dirname, CIB_SERIES, ++seq,
845 if (cib_do_chown) {
846 int rc2;
847
848 if ((chown(backup_path, cib_file_owner, cib_file_group) < 0)
849 && (errno != ENOENT)) {
850 crm_perror(LOG_ERR, "Could not set owner of %s", backup_path);
851 rc = -1;
852 }
853 if ((chown(backup_digest, cib_file_owner, cib_file_group) < 0)
854 && (errno != ENOENT)) {
855 crm_perror(LOG_ERR, "Could not set owner of %s", backup_digest);
856 rc = -1;
857 }
858 rc2 = pcmk__chown_series_sequence(cib_dirname, CIB_SERIES,
859 cib_file_owner, cib_file_group);
860 if (rc2 != pcmk_rc_ok) {
861 crm_err("Could not set owner of sequence file in %s: %s",
862 cib_dirname, pcmk_rc_str(rc2));
863 rc = -1;
864 }
865 }
866 pcmk__sync_directory(cib_dirname);
867 crm_info("Archived previous version as %s", backup_path);
868 }
869
870 free(cib_path);
871 free(cib_digest);
872 free(backup_path);
873 free(backup_digest);
874 return rc;
875}
876
888static void
889cib_file_prepare_xml(xmlNode *root)
890{
891 xmlNode *cib_status_root = NULL;
892
893 /* Always write out with num_updates=0 and current last-written timestamp */
896
897 /* Delete status section before writing to file, because
898 * we discard it on startup anyway, and users get confused by it */
899 cib_status_root = pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL);
900 CRM_CHECK(cib_status_root != NULL, return);
901 free_xml(cib_status_root);
902}
903
917int
918cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname,
919 const char *cib_filename)
920{
921 int exit_rc = pcmk_ok;
922 int rc, fd;
923 char *digest = NULL;
924
925 /* Detect CIB version for diagnostic purposes */
926 const char *epoch = crm_element_value(cib_root, PCMK_XA_EPOCH);
927 const char *admin_epoch = crm_element_value(cib_root, PCMK_XA_ADMIN_EPOCH);
928
929 /* Determine full CIB and signature pathnames */
930 char *cib_path = crm_strdup_printf("%s/%s", cib_dirname, cib_filename);
931 char *digest_path = crm_strdup_printf("%s.sig", cib_path);
932
933 /* Create temporary file name patterns for writing out CIB and signature */
934 char *tmp_cib = crm_strdup_printf("%s/cib.XXXXXX", cib_dirname);
935 char *tmp_digest = crm_strdup_printf("%s/cib.XXXXXX", cib_dirname);
936
937 /* Ensure the admin didn't modify the existing CIB underneath us */
938 crm_trace("Reading cluster configuration file %s", cib_path);
939 rc = cib_file_read_and_verify(cib_path, NULL, NULL);
940 if ((rc != pcmk_ok) && (rc != -ENOENT)) {
941 crm_err("%s was manually modified while the cluster was active!",
942 cib_path);
943 exit_rc = pcmk_err_cib_modified;
944 goto cleanup;
945 }
946
947 /* Back up the existing CIB */
948 if (cib_file_backup(cib_dirname, cib_filename) < 0) {
949 exit_rc = pcmk_err_cib_backup;
950 goto cleanup;
951 }
952
953 crm_debug("Writing CIB to disk");
954 umask(S_IWGRP | S_IWOTH | S_IROTH);
955 cib_file_prepare_xml(cib_root);
956
957 /* Write the CIB to a temporary file, so we can deploy (near) atomically */
958 fd = mkstemp(tmp_cib);
959 if (fd < 0) {
960 crm_perror(LOG_ERR, "Couldn't open temporary file %s for writing CIB",
961 tmp_cib);
962 exit_rc = pcmk_err_cib_save;
963 goto cleanup;
964 }
965
966 /* Protect the temporary file */
967 if (fchmod(fd, S_IRUSR | S_IWUSR) < 0) {
968 crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
969 tmp_cib);
970 exit_rc = pcmk_err_cib_save;
971 goto cleanup;
972 }
973 if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) {
974 crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
975 tmp_cib);
976 exit_rc = pcmk_err_cib_save;
977 goto cleanup;
978 }
979
980 /* Write out the CIB */
981 if (pcmk__xml_write_fd(cib_root, tmp_cib, fd, false, NULL) != pcmk_rc_ok) {
982 crm_err("Changes couldn't be written to %s", tmp_cib);
983 exit_rc = pcmk_err_cib_save;
984 goto cleanup;
985 }
986
987 /* Calculate CIB digest */
988 digest = calculate_on_disk_digest(cib_root);
989 CRM_ASSERT(digest != NULL);
990 crm_info("Wrote version %s.%s.0 of the CIB to disk (digest: %s)",
991 (admin_epoch ? admin_epoch : "0"), (epoch ? epoch : "0"), digest);
992
993 /* Write the CIB digest to a temporary file */
994 fd = mkstemp(tmp_digest);
995 if (fd < 0) {
996 crm_perror(LOG_ERR, "Could not create temporary file for CIB digest");
997 exit_rc = pcmk_err_cib_save;
998 goto cleanup;
999 }
1000 if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) {
1001 crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
1002 tmp_cib);
1003 exit_rc = pcmk_err_cib_save;
1004 close(fd);
1005 goto cleanup;
1006 }
1007 rc = pcmk__write_sync(fd, digest);
1008 if (rc != pcmk_rc_ok) {
1009 crm_err("Could not write digest to %s: %s",
1010 tmp_digest, pcmk_rc_str(rc));
1011 exit_rc = pcmk_err_cib_save;
1012 close(fd);
1013 goto cleanup;
1014 }
1015 close(fd);
1016 crm_debug("Wrote digest %s to disk", digest);
1017
1018 /* Verify that what we wrote is sane */
1019 crm_info("Reading cluster configuration file %s (digest: %s)",
1020 tmp_cib, tmp_digest);
1021 rc = cib_file_read_and_verify(tmp_cib, tmp_digest, NULL);
1022 CRM_ASSERT(rc == 0);
1023
1024 /* Rename temporary files to live, and sync directory changes to media */
1025 crm_debug("Activating %s", tmp_cib);
1026 if (rename(tmp_cib, cib_path) < 0) {
1027 crm_perror(LOG_ERR, "Couldn't rename %s as %s", tmp_cib, cib_path);
1028 exit_rc = pcmk_err_cib_save;
1029 }
1030 if (rename(tmp_digest, digest_path) < 0) {
1031 crm_perror(LOG_ERR, "Couldn't rename %s as %s", tmp_digest,
1032 digest_path);
1033 exit_rc = pcmk_err_cib_save;
1034 }
1035 pcmk__sync_directory(cib_dirname);
1036
1037 cleanup:
1038 free(cib_path);
1039 free(digest_path);
1040 free(digest);
1041 free(tmp_digest);
1042 free(tmp_cib);
1043 return exit_rc;
1044}
1045
1057static int
1058cib_file_process_transaction_requests(cib_t *cib, xmlNode *transaction)
1059{
1060 cib_file_opaque_t *private = cib->variant_opaque;
1061
1062 for (xmlNode *request = pcmk__xe_first_child(transaction,
1064 NULL);
1065 request != NULL; request = pcmk__xe_next_same(request)) {
1066
1067 xmlNode *output = NULL;
1068 const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
1069
1070 int rc = cib_file_process_request(cib, request, &output);
1071
1072 rc = pcmk_legacy2rc(rc);
1073 if (rc != pcmk_rc_ok) {
1074 crm_err("Aborting transaction for CIB file client (%s) on file "
1075 "'%s' due to failed %s request: %s",
1076 private->id, private->filename, op, pcmk_rc_str(rc));
1077 crm_log_xml_info(request, "Failed request");
1078 return rc;
1079 }
1080
1081 crm_trace("Applied %s request to transaction working CIB for CIB file "
1082 "client (%s) on file '%s'",
1083 op, private->id, private->filename);
1084 crm_log_xml_trace(request, "Successful request");
1085 }
1086
1087 return pcmk_rc_ok;
1088}
1089
1104static int
1105cib_file_commit_transaction(cib_t *cib, xmlNode *transaction,
1106 xmlNode **result_cib)
1107{
1108 int rc = pcmk_rc_ok;
1109 cib_file_opaque_t *private = cib->variant_opaque;
1110 xmlNode *saved_cib = private->cib_xml;
1111
1112 CRM_CHECK(pcmk__xe_is(transaction, PCMK__XE_CIB_TRANSACTION),
1113 return pcmk_rc_no_transaction);
1114
1115 /* *result_cib should be a copy of private->cib_xml (created by
1116 * cib_perform_op()). If not, make a copy now. Change tracking isn't
1117 * strictly required here because:
1118 * * Each request in the transaction will have changes tracked and ACLs
1119 * checked if appropriate.
1120 * * cib_perform_op() will infer changes for the commit request at the end.
1121 */
1122 CRM_CHECK((*result_cib != NULL) && (*result_cib != private->cib_xml),
1123 *result_cib = pcmk__xml_copy(NULL, private->cib_xml));
1124
1125 crm_trace("Committing transaction for CIB file client (%s) on file '%s' to "
1126 "working CIB",
1127 private->id, private->filename);
1128
1129 // Apply all changes to a working copy of the CIB
1130 private->cib_xml = *result_cib;
1131
1132 rc = cib_file_process_transaction_requests(cib, transaction);
1133
1134 crm_trace("Transaction commit %s for CIB file client (%s) on file '%s'",
1135 ((rc == pcmk_rc_ok)? "succeeded" : "failed"),
1136 private->id, private->filename);
1137
1138 /* Some request types (for example, erase) may have freed private->cib_xml
1139 * (the working copy) and pointed it at a new XML object. In that case, it
1140 * follows that *result_cib (the working copy) was freed.
1141 *
1142 * Point *result_cib at the updated working copy stored in private->cib_xml.
1143 */
1144 *result_cib = private->cib_xml;
1145
1146 // Point private->cib_xml back to the unchanged original copy
1147 private->cib_xml = saved_cib;
1148
1149 return rc;
1150}
1151
1152static int
1153cib_file_process_commit_transaction(const char *op, int options,
1154 const char *section, xmlNode *req,
1155 xmlNode *input, xmlNode *existing_cib,
1156 xmlNode **result_cib, xmlNode **answer)
1157{
1158 int rc = pcmk_rc_ok;
1159 const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID);
1160 cib_t *cib = NULL;
1161
1162 CRM_CHECK(client_id != NULL, return -EINVAL);
1163
1164 cib = get_client(client_id);
1165 CRM_CHECK(cib != NULL, return -EINVAL);
1166
1167 rc = cib_file_commit_transaction(cib, input, result_cib);
1168 if (rc != pcmk_rc_ok) {
1169 cib_file_opaque_t *private = cib->variant_opaque;
1170
1171 crm_err("Could not commit transaction for CIB file client (%s) on "
1172 "file '%s': %s",
1173 private->id, private->filename, pcmk_rc_str(rc));
1174 }
1175 return pcmk_rc2legacy(rc);
1176}
int cib__extend_transaction(cib_t *cib, xmlNode *request)
Definition cib_utils.c:743
int cib_process_delete(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition cib_ops.c:429
int cib__get_operation(const char *op, const cib__operation_t **operation)
Definition cib_ops.c:144
int cib_process_query(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition cib_ops.c:168
int cib_process_modify(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition cib_ops.c:457
cib_t * cib_new_variant(void)
Definition cib_client.c:676
@ cib__op_attr_modifies
Modifies CIB.
Definition internal.h:44
int cib_process_create(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition cib_ops.c:602
int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, const char *user_name, const char *client_name, xmlNode **op_msg)
Definition cib_utils.c:660
int cib_process_bump(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition cib_ops.c:289
int cib_process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition cib_ops.c:304
int cib_perform_op(cib_t *cib, const char *op, int call_options, cib__op_fn_t fn, bool is_query, const char *section, xmlNode *req, xmlNode *input, bool manage_counters, bool *config_changed, xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, xmlNode **output)
Definition cib_utils.c:332
int cib_process_diff(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition cib_ops.c:669
int cib_process_upgrade(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition cib_ops.c:260
int(* cib__op_fn_t)(const char *, int, const char *, xmlNode *, xmlNode *, xmlNode *, xmlNode **, xmlNode **)
Definition internal.h:85
int cib_process_erase(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition cib_ops.c:241
cib__op_type
Definition internal.h:57
@ cib__op_create
Definition internal.h:62
@ cib__op_delete
Definition internal.h:63
@ cib__op_commit_transact
Definition internal.h:61
@ cib__op_apply_patch
Definition internal.h:59
@ cib__op_bump
Definition internal.h:60
@ cib__op_erase
Definition internal.h:64
@ cib__op_replace
Definition internal.h:71
@ cib__op_upgrade
Definition internal.h:76
@ cib__op_query
Definition internal.h:70
@ cib__op_modify
Definition internal.h:66
#define cib__set_call_options(cib_call_opts, call_for, flags_to_set)
Definition internal.h:118
const char * path
Definition cib.c:28
const char * name
Definition cib.c:26
struct cib_file_opaque_s cib_file_opaque_t
#define cib_clear_file_flags(cibfile, flags_to_clear)
Definition cib_file.c:149
#define cib_set_file_flags(cibfile, flags_to_set)
Definition cib_file.c:140
cib_file_flags
Definition cib_file.c:40
@ cib_file_flag_live
Definition cib_file.c:42
@ cib_file_flag_dirty
Definition cib_file.c:41
int cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, const char *cib_filename)
Definition cib_file.c:916
cib_t * cib_file_new(const char *cib_location)
Definition cib_file.c:646
#define CIB_SERIES_BZIP
Definition cib_file.c:33
#define CIB_SERIES_MAX
Definition cib_file.c:32
#define CIB_SERIES
Definition cib_file.c:31
#define CIB_LIVE_NAME
Definition cib_file.c:35
int cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **root)
Definition cib_file.c:744
cib_conn_type
Definition cib_types.h:50
@ cib_no_connection
Definition cib_types.h:56
@ cib_command
Definition cib_types.h:51
@ cib_none
Definition cib_types.h:61
@ cib_transaction
Process request when the client commits the active transaction.
Definition cib_types.h:108
@ cib_no_mtime
Definition cib_types.h:135
@ cib_file
Definition cib_types.h:31
@ cib_connected_command
Definition cib_types.h:42
@ cib_disconnected
Definition cib_types.h:47
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition cib.c:172
#define PCMK__NELEM(a)
Definition internal.h:48
uint64_t flags
Definition remote.c:3
char * crm_generate_uuid(void)
Definition utils.c:431
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:98
#define CRM_DAEMON_USER
Definition config.h:30
#define CRM_CONFIG_DIR
Definition config.h:17
pcmk__cpg_host_t host
Definition cpg.c:4
enum crm_ais_msg_types type
Definition cpg.c:3
char data[0]
Definition cpg.c:10
uint32_t id
Definition cpg.c:0
A dumping ground.
bool pcmk__verify_digest(xmlNode *input, const char *expected)
Definition digest.c:200
void pcmk__write_series_sequence(const char *directory, const char *series, unsigned int sequence, int max)
Definition io.c:187
int pcmk__real_path(const char *path, char **resolved_path)
Definition io.c:85
int pcmk__chown_series_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition io.c:238
void pcmk__sync_directory(const char *name)
Definition io.c:396
int pcmk__file_contents(const char *filename, char **contents)
Definition io.c:432
int pcmk__read_series_sequence(const char *directory, const char *series, unsigned int *seq)
Definition io.c:140
int pcmk__write_sync(int fd, const char *contents)
Definition io.c:494
char * pcmk__series_filename(const char *directory, const char *series, int sequence, bool bzip)
Definition io.c:121
IPC interface to Pacemaker daemons.
#define crm_log_xml_info(xml, text)
Definition logging.h:408
#define crm_info(fmt, args...)
Definition logging.h:397
#define crm_warn(fmt, args...)
Definition logging.h:392
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:331
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_debug(fmt, args...)
Definition logging.h:400
#define crm_err(fmt, args...)
Definition logging.h:389
#define crm_log_xml_trace(xml, text)
Definition logging.h:410
#define crm_trace(fmt, args...)
Definition logging.h:402
#define pcmk__log_xml_patchset(level, patchset)
xmlNode * input
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition nvpair.c:482
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition nvpair.c:301
#define pcmk_err_cib_corrupt
Definition results.h:89
const char * pcmk_strerror(int rc)
Definition results.c:149
#define pcmk_err_generic
Definition results.h:73
#define CRM_ASSERT(expr)
Definition results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:501
#define pcmk_err_cib_modified
Definition results.h:85
#define pcmk_err_schema_validation
Definition results.h:75
@ pcmk_rc_no_transaction
Definition results.h:120
@ pcmk_rc_ok
Definition results.h:162
#define pcmk_err_cib_save
Definition results.h:87
#define pcmk_ok
Definition results.h:69
int pcmk_rc2legacy(int rc)
Definition results.c:546
int pcmk_legacy2rc(int legacy_rc)
Definition results.c:559
#define pcmk_err_cib_backup
Definition results.h:86
bool pcmk__validate_xml(xmlNode *xml_blob, const char *validation, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context)
Definition schemas.c:757
bool pcmk__configured_schema_validates(xmlNode *xml)
Definition schemas.c:809
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:683
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition strings.c:635
uint32_t flags
Group of enum cib__op_attr flags.
Definition internal.h:91
enum cib__op_type type
Definition internal.h:90
int(* set_connection_dnotify)(cib_t *cib, void(*dnotify)(gpointer user_data))
Definition cib_types.h:185
int(* inputfd)(cib_t *cib)
Definition cib_types.h:189
int(* signoff)(cib_t *cib)
Definition cib_types.h:166
int(* end_transaction)(cib_t *cib, bool commit, int call_options)
End and optionally commit this client's CIB transaction.
Definition cib_types.h:362
int(* signon)(cib_t *cib, const char *name, enum cib_conn_type type)
Definition cib_types.h:159
int(* client_id)(const cib_t *cib, const char **async_id, const char **sync_id)
Get the given CIB connection's unique client identifier(s)
Definition cib_types.h:299
int(* register_notification)(cib_t *cib, const char *callback, int enabled)
Definition cib_types.h:248
int(* free)(cib_t *cib)
Definition cib_types.h:168
enum cib_conn_type type
Definition cib_types.h:384
enum cib_state state
Definition cib_types.h:382
void * variant_opaque
Definition cib_types.h:389
void * delegate_fn
Definition cib_types.h:390
cib_api_operations_t * cmds
Definition cib_types.h:399
enum cib_variant variant
Definition cib_types.h:385
char * user
Definition cib_types.h:403
void(* op_callback)(const xmlNode *msg, int call_id, int rc, xmlNode *output)
Definition cib_types.h:395
Wrappers for and extensions to libxml2.
char * calculate_on_disk_digest(xmlNode *local_cib)
Calculate and return digest of XML tree, suitable for storing on disk.
Definition digest.c:129
void free_xml(xmlNode *child)
Definition xml.c:867
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:883
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml.c:440
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition xml.c:2108
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition xml.c:959
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition xml.c:720
int pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress, unsigned int *nbytes)
Definition xml_io.c:688
xmlNode * pcmk__xml_read(const char *filename)
Definition xml_io.c:166
int pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd, bool compress, unsigned int *nbytes)
Definition xml_io.c:660
#define PCMK_XE_CIB
Definition xml_names.h:79
#define PCMK_XE_STATUS
Definition xml_names.h:199
#define PCMK_XA_VALIDATE_WITH
Definition xml_names.h:436
#define PCMK_XE_ACL_TARGET
Definition xml_names.h:58
#define PCMK_XA_EPOCH
Definition xml_names.h:263
#define PCMK_XA_ADMIN_EPOCH
Definition xml_names.h:227
#define PCMK_XA_NUM_UPDATES
Definition xml_names.h:336
#define PCMK__XE_CIB_TRANSACTION
#define PCMK__XA_CIB_CALLOPT
#define PCMK__XA_CIB_OP
#define PCMK__XA_CIB_CALLID
#define PCMK__XA_CIB_CLIENTID
#define PCMK__XE_CIB_COMMAND
#define PCMK__XA_CIB_SECTION
#define PCMK__XE_CIB_CALLDATA