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