pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
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 *)
40 static GHashTable *client_table = NULL;
41 
43  cib_file_flag_dirty = (1 << 0),
44  cib_file_flag_live = (1 << 1),
45 };
46 
47 typedef 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 
54 static 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 
67 static void
68 register_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 
84 static void
85 unregister_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 
112 static cib_t *
113 get_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 
121 static 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  */
138 static uid_t cib_file_owner = 0;
139 static uid_t cib_file_group = 0;
140 static 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 
168 static cib__op_fn_t
169 file_get_op_function(const cib__operation_t *operation)
170 {
171  enum cib__op_type type = operation->type;
172 
173  pcmk__assert(type >= 0);
174 
175  if (type >= PCMK__NELEM(cib_op_functions)) {
176  return NULL;
177  }
178  return cib_op_functions[type];
179 }
180 
189 static gboolean
190 cib_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 
212 static int
213 cib_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  uint32_t 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  rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options,
240  cib_none);
241  if (rc != pcmk_rc_ok) {
242  crm_warn("Couldn't parse options from request: %s", pcmk_rc_str(rc));
243  }
244 
245  read_only = !pcmk_is_set(operation->flags, cib__op_attr_modifies);
246 
247  // Mirror the logic in prepare_input() in pacemaker-based
248  if ((section != NULL) && pcmk__xe_is(data, PCMK_XE_CIB)) {
249 
250  data = pcmk_find_cib_element(data, section);
251  }
252 
253  rc = cib_perform_op(cib, op, call_options, op_function, read_only, section,
254  request, data, true, &changed, &private->cib_xml,
255  &result_cib, &cib_diff, output);
256 
257  if (pcmk_is_set(call_options, cib_transaction)) {
258  /* The rest of the logic applies only to the transaction as a whole, not
259  * to individual requests.
260  */
261  goto done;
262  }
263 
264  if (rc == -pcmk_err_schema_validation) {
265  // Show validation errors to stderr
266  pcmk__validate_xml(result_cib, NULL, NULL, NULL);
267 
268  } else if ((rc == pcmk_ok) && !read_only) {
269  pcmk__log_xml_patchset(LOG_DEBUG, cib_diff);
270 
271  if (result_cib != private->cib_xml) {
272  free_xml(private->cib_xml);
273  private->cib_xml = result_cib;
274  }
276  }
277 
278  // Global operation callback (deprecated)
279  if (cib->op_callback != NULL) {
280  cib->op_callback(NULL, call_id, rc, *output);
281  }
282 
283 done:
284  if ((result_cib != private->cib_xml) && (result_cib != *output)) {
285  free_xml(result_cib);
286  }
287  free_xml(cib_diff);
288  return rc;
289 }
290 
291 static int
292 cib_file_perform_op_delegate(cib_t *cib, const char *op, const char *host,
293  const char *section, xmlNode *data,
294  xmlNode **output_data, int call_options,
295  const char *user_name)
296 {
297  int rc = pcmk_ok;
298  xmlNode *request = NULL;
299  xmlNode *output = NULL;
300  cib_file_opaque_t *private = cib->variant_opaque;
301 
302  const cib__operation_t *operation = NULL;
303 
304  crm_info("Handling %s operation for %s as %s",
305  pcmk__s(op, "invalid"), pcmk__s(section, "entire CIB"),
306  pcmk__s(user_name, "default user"));
307 
308  if (output_data != NULL) {
309  *output_data = NULL;
310  }
311 
312  if (cib->state == cib_disconnected) {
313  return -ENOTCONN;
314  }
315 
316  rc = cib__get_operation(op, &operation);
317  rc = pcmk_rc2legacy(rc);
318  if (rc != pcmk_ok) {
319  // @COMPAT: At compatibility break, use rc directly
320  return -EPROTONOSUPPORT;
321  }
322 
323  if (file_get_op_function(operation) == NULL) {
324  // @COMPAT: At compatibility break, use EOPNOTSUPP
325  crm_err("Operation %s is not supported by CIB file clients", op);
326  return -EPROTONOSUPPORT;
327  }
328 
329  cib__set_call_options(call_options, "file operation", cib_no_mtime);
330 
331  rc = cib__create_op(cib, op, host, section, data, call_options, user_name,
332  NULL, &request);
333  if (rc != pcmk_ok) {
334  return rc;
335  }
336  crm_xml_add(request, PCMK_XE_ACL_TARGET, user_name);
337  crm_xml_add(request, PCMK__XA_CIB_CLIENTID, private->id);
338 
339  if (pcmk_is_set(call_options, cib_transaction)) {
340  rc = cib__extend_transaction(cib, request);
341  goto done;
342  }
343 
344  rc = cib_file_process_request(cib, request, &output);
345 
346  if ((output_data != NULL) && (output != NULL)) {
347  if (output->doc == private->cib_xml->doc) {
348  *output_data = pcmk__xml_copy(NULL, output);
349  } else {
350  *output_data = output;
351  }
352  }
353 
354 done:
355  if ((output != NULL)
356  && (output->doc != private->cib_xml->doc)
357  && ((output_data == NULL) || (output != *output_data))) {
358 
359  free_xml(output);
360  }
361  free_xml(request);
362  return rc;
363 }
364 
380 static int
381 load_file_cib(const char *filename, xmlNode **output)
382 {
383  struct stat buf;
384  xmlNode *root = NULL;
385 
386  /* Ensure file is readable */
387  if (strcmp(filename, "-") && (stat(filename, &buf) < 0)) {
388  return -ENXIO;
389  }
390 
391  /* Parse XML from file */
392  root = pcmk__xml_read(filename);
393  if (root == NULL) {
395  }
396 
397  /* Add a status section if not already present */
398  if (pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL) == NULL) {
400  }
401 
402  /* Validate XML against its specified schema */
404  const char *schema = crm_element_value(root, PCMK_XA_VALIDATE_WITH);
405 
406  crm_err("CIB does not validate against %s, or that schema is unknown", schema);
407  free_xml(root);
409  }
410 
411  /* Remember the parsed XML for later use */
412  *output = root;
413  return pcmk_ok;
414 }
415 
416 static int
417 cib_file_signon(cib_t *cib, const char *name, enum cib_conn_type type)
418 {
419  int rc = pcmk_ok;
420  cib_file_opaque_t *private = cib->variant_opaque;
421 
422  if (private->filename == NULL) {
423  rc = -EINVAL;
424  } else {
425  rc = load_file_cib(private->filename, &private->cib_xml);
426  }
427 
428  if (rc == pcmk_ok) {
429  crm_debug("Opened connection to local file '%s' for %s",
430  private->filename, name);
432  cib->type = cib_command;
433  register_client(cib);
434 
435  } else {
436  crm_info("Connection to local file '%s' for %s (client %s) failed: %s",
437  private->filename, name, private->id, pcmk_strerror(rc));
438  }
439  return rc;
440 }
441 
451 static int
452 cib_file_write_live(xmlNode *cib_root, char *path)
453 {
454  uid_t uid = geteuid();
455  struct passwd *daemon_pwent;
456  char *sep = strrchr(path, '/');
457  const char *cib_dirname, *cib_filename;
458  int rc = 0;
459 
460  /* Get the desired uid/gid */
461  errno = 0;
462  daemon_pwent = getpwnam(CRM_DAEMON_USER);
463  if (daemon_pwent == NULL) {
464  crm_perror(LOG_ERR, "Could not find %s user", CRM_DAEMON_USER);
465  return -1;
466  }
467 
468  /* If we're root, we can change the ownership;
469  * if we're daemon, anything we create will be OK;
470  * otherwise, block access so we don't create wrong owner
471  */
472  if ((uid != 0) && (uid != daemon_pwent->pw_uid)) {
473  crm_perror(LOG_ERR, "Must be root or %s to modify live CIB",
475  return 0;
476  }
477 
478  /* fancy footwork to separate dirname from filename
479  * (we know the canonical name maps to the live CIB,
480  * but the given name might be relative, or symlinked)
481  */
482  if (sep == NULL) { /* no directory component specified */
483  cib_dirname = "./";
484  cib_filename = path;
485  } else if (sep == path) { /* given name is in / */
486  cib_dirname = "/";
487  cib_filename = path + 1;
488  } else { /* typical case; split given name into parts */
489  *sep = '\0';
490  cib_dirname = path;
491  cib_filename = sep + 1;
492  }
493 
494  /* if we're root, we want to update the file ownership */
495  if (uid == 0) {
496  cib_file_owner = daemon_pwent->pw_uid;
497  cib_file_group = daemon_pwent->pw_gid;
498  cib_do_chown = TRUE;
499  }
500 
501  /* write the file */
502  if (cib_file_write_with_digest(cib_root, cib_dirname,
503  cib_filename) != pcmk_ok) {
504  rc = -1;
505  }
506 
507  /* turn off file ownership changes, for other callers */
508  if (uid == 0) {
509  cib_do_chown = FALSE;
510  }
511 
512  /* undo fancy stuff */
513  if ((sep != NULL) && (*sep == '\0')) {
514  *sep = '/';
515  }
516 
517  return rc;
518 }
519 
533 static int
534 cib_file_signoff(cib_t *cib)
535 {
536  int rc = pcmk_ok;
537  cib_file_opaque_t *private = cib->variant_opaque;
538 
539  crm_debug("Disconnecting from the CIB manager");
540  cib->state = cib_disconnected;
541  cib->type = cib_no_connection;
542  unregister_client(cib);
543  cib->cmds->end_transaction(cib, false, cib_none);
544 
545  /* If the in-memory CIB has been changed, write it to disk */
546  if (pcmk_is_set(private->flags, cib_file_flag_dirty)) {
547 
548  /* If this is the live CIB, write it out with a digest */
549  if (pcmk_is_set(private->flags, cib_file_flag_live)) {
550  if (cib_file_write_live(private->cib_xml, private->filename) < 0) {
551  rc = pcmk_err_generic;
552  }
553 
554  /* Otherwise, it's a simple write */
555  } else {
556  bool compress = pcmk__ends_with_ext(private->filename, ".bz2");
557 
558  if (pcmk__xml_write_file(private->cib_xml, private->filename,
559  compress, NULL) != pcmk_rc_ok) {
560  rc = pcmk_err_generic;
561  }
562  }
563 
564  if (rc == pcmk_ok) {
565  crm_info("Wrote CIB to %s", private->filename);
567  } else {
568  crm_err("Could not write CIB to %s", private->filename);
569  }
570  }
571 
572  /* Free the in-memory CIB */
573  free_xml(private->cib_xml);
574  private->cib_xml = NULL;
575  return rc;
576 }
577 
578 static int
579 cib_file_free(cib_t *cib)
580 {
581  int rc = pcmk_ok;
582 
583  if (cib->state != cib_disconnected) {
584  rc = cib_file_signoff(cib);
585  }
586 
587  if (rc == pcmk_ok) {
588  cib_file_opaque_t *private = cib->variant_opaque;
589 
590  free(private->id);
591  free(private->filename);
592  free(private);
593  free(cib->cmds);
594  free(cib->user);
595  free(cib);
596 
597  } else {
598  fprintf(stderr, "Couldn't sign off: %d\n", rc);
599  }
600 
601  return rc;
602 }
603 
604 static int
605 cib_file_inputfd(cib_t *cib)
606 {
607  return -EPROTONOSUPPORT;
608 }
609 
610 static int
611 cib_file_register_notification(cib_t *cib, const char *callback, int enabled)
612 {
613  return -EPROTONOSUPPORT;
614 }
615 
616 static int
617 cib_file_set_connection_dnotify(cib_t *cib,
618  void (*dnotify) (gpointer user_data))
619 {
620  return -EPROTONOSUPPORT;
621 }
622 
636 static int
637 cib_file_client_id(const cib_t *cib, const char **async_id,
638  const char **sync_id)
639 {
640  cib_file_opaque_t *private = cib->variant_opaque;
641 
642  if (async_id != NULL) {
643  *async_id = private->id;
644  }
645  if (sync_id != NULL) {
646  *sync_id = private->id;
647  }
648  return pcmk_ok;
649 }
650 
651 cib_t *
652 cib_file_new(const char *cib_location)
653 {
654  cib_t *cib = NULL;
655  cib_file_opaque_t *private = NULL;
656  char *filename = NULL;
657 
658  if (cib_location == NULL) {
659  cib_location = getenv("CIB_file");
660  if (cib_location == NULL) {
661  return NULL; // Shouldn't be possible if we were called internally
662  }
663  }
664 
665  cib = cib_new_variant();
666  if (cib == NULL) {
667  return NULL;
668  }
669 
670  filename = strdup(cib_location);
671  if (filename == NULL) {
672  free(cib);
673  return NULL;
674  }
675 
676  private = calloc(1, sizeof(cib_file_opaque_t));
677  if (private == NULL) {
678  free(cib);
679  free(filename);
680  return NULL;
681  }
682 
683  private->id = crm_generate_uuid();
684  private->filename = filename;
685 
686  cib->variant = cib_file;
687  cib->variant_opaque = private;
688 
689  private->flags = 0;
690  if (cib_file_is_live(cib_location)) {
692  crm_trace("File %s detected as live CIB", cib_location);
693  }
694 
695  /* assign variant specific ops */
696  cib->delegate_fn = cib_file_perform_op_delegate;
697  cib->cmds->signon = cib_file_signon;
698  cib->cmds->signoff = cib_file_signoff;
699  cib->cmds->free = cib_file_free;
700  cib->cmds->inputfd = cib_file_inputfd; // Deprecated method
701 
702  cib->cmds->register_notification = cib_file_register_notification;
703  cib->cmds->set_connection_dnotify = cib_file_set_connection_dnotify;
704 
705  cib->cmds->client_id = cib_file_client_id;
706 
707  return cib;
708 }
709 
719 static gboolean
720 cib_file_verify_digest(xmlNode *root, const char *sigfile)
721 {
722  gboolean passed = FALSE;
723  char *expected;
724  int rc = pcmk__file_contents(sigfile, &expected);
725 
726  switch (rc) {
727  case pcmk_rc_ok:
728  if (expected == NULL) {
729  crm_err("On-disk digest at %s is empty", sigfile);
730  return FALSE;
731  }
732  break;
733  case ENOENT:
734  crm_warn("No on-disk digest present at %s", sigfile);
735  return TRUE;
736  default:
737  crm_err("Could not read on-disk digest from %s: %s",
738  sigfile, pcmk_rc_str(rc));
739  return FALSE;
740  }
741  passed = pcmk__verify_digest(root, expected);
742  free(expected);
743  return passed;
744 }
745 
761 int
762 cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **root)
763 {
764  int s_res;
765  struct stat buf;
766  char *local_sigfile = NULL;
767  xmlNode *local_root = NULL;
768 
769  pcmk__assert(filename != NULL);
770  if (root) {
771  *root = NULL;
772  }
773 
774  /* Verify that file exists and its size is nonzero */
775  s_res = stat(filename, &buf);
776  if (s_res < 0) {
777  crm_perror(LOG_WARNING, "Could not verify cluster configuration file %s", filename);
778  return -errno;
779  } else if (buf.st_size == 0) {
780  crm_warn("Cluster configuration file %s is corrupt (size is zero)", filename);
781  return -pcmk_err_cib_corrupt;
782  }
783 
784  /* Parse XML */
785  local_root = pcmk__xml_read(filename);
786  if (local_root == NULL) {
787  crm_warn("Cluster configuration file %s is corrupt (unparseable as XML)", filename);
788  return -pcmk_err_cib_corrupt;
789  }
790 
791  /* If sigfile is not specified, use original file name plus .sig */
792  if (sigfile == NULL) {
793  sigfile = local_sigfile = crm_strdup_printf("%s.sig", filename);
794  }
795 
796  /* Verify that digests match */
797  if (cib_file_verify_digest(local_root, sigfile) == FALSE) {
798  free(local_sigfile);
799  free_xml(local_root);
800  return -pcmk_err_cib_modified;
801  }
802 
803  free(local_sigfile);
804  if (root) {
805  *root = local_root;
806  } else {
807  free_xml(local_root);
808  }
809  return pcmk_ok;
810 }
811 
821 static int
822 cib_file_backup(const char *cib_dirname, const char *cib_filename)
823 {
824  int rc = 0;
825  unsigned int seq = 0U;
826  char *cib_path = crm_strdup_printf("%s/%s", cib_dirname, cib_filename);
827  char *cib_digest = crm_strdup_printf("%s.sig", cib_path);
828  char *backup_path;
829  char *backup_digest;
830 
831  // Determine backup and digest file names
832  if (pcmk__read_series_sequence(cib_dirname, CIB_SERIES,
833  &seq) != pcmk_rc_ok) {
834  // @TODO maybe handle errors better ...
835  seq = 0U;
836  }
837  backup_path = pcmk__series_filename(cib_dirname, CIB_SERIES, seq,
839  backup_digest = crm_strdup_printf("%s.sig", backup_path);
840 
841  /* Remove the old backups if they exist */
842  unlink(backup_path);
843  unlink(backup_digest);
844 
845  /* Back up the CIB, by hard-linking it to the backup name */
846  if ((link(cib_path, backup_path) < 0) && (errno != ENOENT)) {
847  crm_perror(LOG_ERR, "Could not archive %s by linking to %s",
848  cib_path, backup_path);
849  rc = -1;
850 
851  /* Back up the CIB signature similarly */
852  } else if ((link(cib_digest, backup_digest) < 0) && (errno != ENOENT)) {
853  crm_perror(LOG_ERR, "Could not archive %s by linking to %s",
854  cib_digest, backup_digest);
855  rc = -1;
856 
857  /* Update the last counter and ensure everything is sync'd to media */
858  } else {
859  pcmk__write_series_sequence(cib_dirname, CIB_SERIES, ++seq,
861  if (cib_do_chown) {
862  int rc2;
863 
864  if ((chown(backup_path, cib_file_owner, cib_file_group) < 0)
865  && (errno != ENOENT)) {
866  crm_perror(LOG_ERR, "Could not set owner of %s", backup_path);
867  rc = -1;
868  }
869  if ((chown(backup_digest, cib_file_owner, cib_file_group) < 0)
870  && (errno != ENOENT)) {
871  crm_perror(LOG_ERR, "Could not set owner of %s", backup_digest);
872  rc = -1;
873  }
874  rc2 = pcmk__chown_series_sequence(cib_dirname, CIB_SERIES,
875  cib_file_owner, cib_file_group);
876  if (rc2 != pcmk_rc_ok) {
877  crm_err("Could not set owner of sequence file in %s: %s",
878  cib_dirname, pcmk_rc_str(rc2));
879  rc = -1;
880  }
881  }
882  pcmk__sync_directory(cib_dirname);
883  crm_info("Archived previous version as %s", backup_path);
884  }
885 
886  free(cib_path);
887  free(cib_digest);
888  free(backup_path);
889  free(backup_digest);
890  return rc;
891 }
892 
904 static void
905 cib_file_prepare_xml(xmlNode *root)
906 {
907  xmlNode *cib_status_root = NULL;
908 
909  /* Always write out with num_updates=0 and current last-written timestamp */
910  crm_xml_add(root, PCMK_XA_NUM_UPDATES, "0");
912 
913  /* Delete status section before writing to file, because
914  * we discard it on startup anyway, and users get confused by it */
915  cib_status_root = pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL);
916  CRM_CHECK(cib_status_root != NULL, return);
917  free_xml(cib_status_root);
918 }
919 
933 int
934 cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname,
935  const char *cib_filename)
936 {
937  int exit_rc = pcmk_ok;
938  int rc, fd;
939  char *digest = NULL;
940 
941  /* Detect CIB version for diagnostic purposes */
942  const char *epoch = crm_element_value(cib_root, PCMK_XA_EPOCH);
943  const char *admin_epoch = crm_element_value(cib_root, PCMK_XA_ADMIN_EPOCH);
944 
945  /* Determine full CIB and signature pathnames */
946  char *cib_path = crm_strdup_printf("%s/%s", cib_dirname, cib_filename);
947  char *digest_path = crm_strdup_printf("%s.sig", cib_path);
948 
949  /* Create temporary file name patterns for writing out CIB and signature */
950  char *tmp_cib = crm_strdup_printf("%s/cib.XXXXXX", cib_dirname);
951  char *tmp_digest = crm_strdup_printf("%s/cib.XXXXXX", cib_dirname);
952 
953  /* Ensure the admin didn't modify the existing CIB underneath us */
954  crm_trace("Reading cluster configuration file %s", cib_path);
955  rc = cib_file_read_and_verify(cib_path, NULL, NULL);
956  if ((rc != pcmk_ok) && (rc != -ENOENT)) {
957  crm_err("%s was manually modified while the cluster was active!",
958  cib_path);
959  exit_rc = pcmk_err_cib_modified;
960  goto cleanup;
961  }
962 
963  /* Back up the existing CIB */
964  if (cib_file_backup(cib_dirname, cib_filename) < 0) {
965  exit_rc = pcmk_err_cib_backup;
966  goto cleanup;
967  }
968 
969  crm_debug("Writing CIB to disk");
970  umask(S_IWGRP | S_IWOTH | S_IROTH);
971  cib_file_prepare_xml(cib_root);
972 
973  /* Write the CIB to a temporary file, so we can deploy (near) atomically */
974  fd = mkstemp(tmp_cib);
975  if (fd < 0) {
976  crm_perror(LOG_ERR, "Couldn't open temporary file %s for writing CIB",
977  tmp_cib);
978  exit_rc = pcmk_err_cib_save;
979  goto cleanup;
980  }
981 
982  /* Protect the temporary file */
983  if (fchmod(fd, S_IRUSR | S_IWUSR) < 0) {
984  crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
985  tmp_cib);
986  exit_rc = pcmk_err_cib_save;
987  goto cleanup;
988  }
989  if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) {
990  crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
991  tmp_cib);
992  exit_rc = pcmk_err_cib_save;
993  goto cleanup;
994  }
995 
996  /* Write out the CIB */
997  if (pcmk__xml_write_fd(cib_root, tmp_cib, fd, false, NULL) != pcmk_rc_ok) {
998  crm_err("Changes couldn't be written to %s", tmp_cib);
999  exit_rc = pcmk_err_cib_save;
1000  goto cleanup;
1001  }
1002 
1003  /* Calculate CIB digest */
1004  digest = calculate_on_disk_digest(cib_root);
1005  pcmk__assert(digest != NULL);
1006  crm_info("Wrote version %s.%s.0 of the CIB to disk (digest: %s)",
1007  (admin_epoch ? admin_epoch : "0"), (epoch ? epoch : "0"), digest);
1008 
1009  /* Write the CIB digest to a temporary file */
1010  fd = mkstemp(tmp_digest);
1011  if (fd < 0) {
1012  crm_perror(LOG_ERR, "Could not create temporary file for CIB digest");
1013  exit_rc = pcmk_err_cib_save;
1014  goto cleanup;
1015  }
1016  if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) {
1017  crm_perror(LOG_ERR, "Couldn't protect temporary file %s for writing CIB",
1018  tmp_cib);
1019  exit_rc = pcmk_err_cib_save;
1020  close(fd);
1021  goto cleanup;
1022  }
1023  rc = pcmk__write_sync(fd, digest);
1024  if (rc != pcmk_rc_ok) {
1025  crm_err("Could not write digest to %s: %s",
1026  tmp_digest, pcmk_rc_str(rc));
1027  exit_rc = pcmk_err_cib_save;
1028  close(fd);
1029  goto cleanup;
1030  }
1031  close(fd);
1032  crm_debug("Wrote digest %s to disk", digest);
1033 
1034  /* Verify that what we wrote is sane */
1035  crm_info("Reading cluster configuration file %s (digest: %s)",
1036  tmp_cib, tmp_digest);
1037  rc = cib_file_read_and_verify(tmp_cib, tmp_digest, NULL);
1038  pcmk__assert(rc == 0);
1039 
1040  /* Rename temporary files to live, and sync directory changes to media */
1041  crm_debug("Activating %s", tmp_cib);
1042  if (rename(tmp_cib, cib_path) < 0) {
1043  crm_perror(LOG_ERR, "Couldn't rename %s as %s", tmp_cib, cib_path);
1044  exit_rc = pcmk_err_cib_save;
1045  }
1046  if (rename(tmp_digest, digest_path) < 0) {
1047  crm_perror(LOG_ERR, "Couldn't rename %s as %s", tmp_digest,
1048  digest_path);
1049  exit_rc = pcmk_err_cib_save;
1050  }
1051  pcmk__sync_directory(cib_dirname);
1052 
1053  cleanup:
1054  free(cib_path);
1055  free(digest_path);
1056  free(digest);
1057  free(tmp_digest);
1058  free(tmp_cib);
1059  return exit_rc;
1060 }
1061 
1073 static int
1074 cib_file_process_transaction_requests(cib_t *cib, xmlNode *transaction)
1075 {
1076  cib_file_opaque_t *private = cib->variant_opaque;
1077 
1078  for (xmlNode *request = pcmk__xe_first_child(transaction,
1079  PCMK__XE_CIB_COMMAND, NULL,
1080  NULL);
1081  request != NULL; request = pcmk__xe_next_same(request)) {
1082 
1083  xmlNode *output = NULL;
1084  const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
1085 
1086  int rc = cib_file_process_request(cib, request, &output);
1087 
1088  rc = pcmk_legacy2rc(rc);
1089  if (rc != pcmk_rc_ok) {
1090  crm_err("Aborting transaction for CIB file client (%s) on file "
1091  "'%s' due to failed %s request: %s",
1092  private->id, private->filename, op, pcmk_rc_str(rc));
1093  crm_log_xml_info(request, "Failed request");
1094  return rc;
1095  }
1096 
1097  crm_trace("Applied %s request to transaction working CIB for CIB file "
1098  "client (%s) on file '%s'",
1099  op, private->id, private->filename);
1100  crm_log_xml_trace(request, "Successful request");
1101  }
1102 
1103  return pcmk_rc_ok;
1104 }
1105 
1120 static int
1121 cib_file_commit_transaction(cib_t *cib, xmlNode *transaction,
1122  xmlNode **result_cib)
1123 {
1124  int rc = pcmk_rc_ok;
1125  cib_file_opaque_t *private = cib->variant_opaque;
1126  xmlNode *saved_cib = private->cib_xml;
1127 
1128  CRM_CHECK(pcmk__xe_is(transaction, PCMK__XE_CIB_TRANSACTION),
1129  return pcmk_rc_no_transaction);
1130 
1131  /* *result_cib should be a copy of private->cib_xml (created by
1132  * cib_perform_op()). If not, make a copy now. Change tracking isn't
1133  * strictly required here because:
1134  * * Each request in the transaction will have changes tracked and ACLs
1135  * checked if appropriate.
1136  * * cib_perform_op() will infer changes for the commit request at the end.
1137  */
1138  CRM_CHECK((*result_cib != NULL) && (*result_cib != private->cib_xml),
1139  *result_cib = pcmk__xml_copy(NULL, private->cib_xml));
1140 
1141  crm_trace("Committing transaction for CIB file client (%s) on file '%s' to "
1142  "working CIB",
1143  private->id, private->filename);
1144 
1145  // Apply all changes to a working copy of the CIB
1146  private->cib_xml = *result_cib;
1147 
1148  rc = cib_file_process_transaction_requests(cib, transaction);
1149 
1150  crm_trace("Transaction commit %s for CIB file client (%s) on file '%s'",
1151  ((rc == pcmk_rc_ok)? "succeeded" : "failed"),
1152  private->id, private->filename);
1153 
1154  /* Some request types (for example, erase) may have freed private->cib_xml
1155  * (the working copy) and pointed it at a new XML object. In that case, it
1156  * follows that *result_cib (the working copy) was freed.
1157  *
1158  * Point *result_cib at the updated working copy stored in private->cib_xml.
1159  */
1160  *result_cib = private->cib_xml;
1161 
1162  // Point private->cib_xml back to the unchanged original copy
1163  private->cib_xml = saved_cib;
1164 
1165  return rc;
1166 }
1167 
1168 static int
1169 cib_file_process_commit_transaction(const char *op, int options,
1170  const char *section, xmlNode *req,
1171  xmlNode *input, xmlNode *existing_cib,
1172  xmlNode **result_cib, xmlNode **answer)
1173 {
1174  int rc = pcmk_rc_ok;
1175  const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID);
1176  cib_t *cib = NULL;
1177 
1178  CRM_CHECK(client_id != NULL, return -EINVAL);
1179 
1180  cib = get_client(client_id);
1181  CRM_CHECK(cib != NULL, return -EINVAL);
1182 
1183  rc = cib_file_commit_transaction(cib, input, result_cib);
1184  if (rc != pcmk_rc_ok) {
1185  cib_file_opaque_t *private = cib->variant_opaque;
1186 
1187  crm_err("Could not commit transaction for CIB file client (%s) on "
1188  "file '%s': %s",
1189  private->id, private->filename, pcmk_rc_str(rc));
1190  }
1191  return pcmk_rc2legacy(rc);
1192 }
enum cib__op_type type
Definition: internal.h:90
pcmk__cpg_host_t host
Definition: cpg.c:52
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
#define pcmk_err_schema_validation
Definition: results.h:71
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:974
A dumping ground.
const char * pcmk_strerror(int rc)
Definition: results.c:151
#define CIB_SERIES_MAX
Definition: cib_file.c:32
char data[0]
Definition: cpg.c:58
char * crm_generate_uuid(void)
Definition: utils.c:319
int pcmk_rc2legacy(int rc)
Definition: results.c:548
#define PCMK_XE_CIB
Definition: xml_names.h:79
#define PCMK_XE_STATUS
Definition: xml_names.h:204
const char * name
Definition: cib.c:26
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
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition: xml.c:1048
#define PCMK__XA_CIB_CLIENTID
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
void pcmk__write_series_sequence(const char *directory, const char *series, unsigned int sequence, int max)
Definition: io.c:187
int(* signoff)(cib_t *cib)
Definition: cib_types.h:166
#define PCMK__XE_CIB_COMMAND
int pcmk__chown_series_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition: io.c:238
#define PCMK_XA_NUM_UPDATES
Definition: xml_names.h:341
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
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
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:313
#define pcmk_err_generic
Definition: results.h:69
int pcmk__read_series_sequence(const char *directory, const char *series, unsigned int *seq)
Definition: io.c:140
int(* inputfd)(cib_t *cib)
Definition: cib_types.h:189
enum crm_ais_msg_types type
Definition: cpg.c:51
int pcmk__write_sync(int fd, const char *contents)
Definition: io.c:494
int cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, const char *cib_filename)
Definition: cib_file.c:934
#define PCMK__XA_CIB_SECTION
struct cib_file_opaque_s cib_file_opaque_t
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:503
#define pcmk_err_cib_backup
Definition: results.h:82
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: nvpair.c:494
#define CIB_SERIES_BZIP
Definition: cib_file.c:33
cib_file_flags
Definition: cib_file.c:42
#define CIB_SERIES
Definition: cib_file.c:31
cib_t * cib_new_variant(void)
Definition: cib_client.c:656
cib__op_type
Definition: internal.h:57
int(* set_connection_dnotify)(cib_t *cib, void(*dnotify)(gpointer user_data))
Definition: cib_types.h:185
#define crm_warn(fmt, args...)
Definition: logging.h:394
#define pcmk_err_cib_save
Definition: results.h:83
Modifies CIB.
Definition: internal.h:44
cib_api_operations_t * cmds
Definition: cib_types.h:399
#define crm_debug(fmt, args...)
Definition: logging.h:402
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
cib_conn_type
Definition: cib_types.h:50
#define PCMK__XA_CIB_CALLOPT
int(* signon)(cib_t *cib, const char *name, enum cib_conn_type type)
Definition: cib_types.h:159
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:458
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:481
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition: cib.c:172
#define crm_trace(fmt, args...)
Definition: logging.h:404
bool pcmk__validate_xml(xmlNode *xml_blob, const char *validation, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context)
Definition: schemas.c:757
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:94
#define PCMK__XE_CIB_CALLDATA
void(* op_callback)(const xmlNode *msg, int call_id, int rc, xmlNode *output)
Definition: cib_types.h:395
void pcmk__sync_directory(const char *name)
Definition: io.c:396
#define PCMK_XA_EPOCH
Definition: xml_names.h:268
Wrappers for and extensions to libxml2.
#define PCMK__NELEM(a)
Definition: internal.h:48
#define CRM_DAEMON_USER
Definition: config.h:30
int pcmk_legacy2rc(int legacy_rc)
Definition: results.c:561
#define pcmk_err_cib_modified
Definition: results.h:81
#define CIB_LIVE_NAME
Definition: cib_file.c:37
char * pcmk__series_filename(const char *directory, const char *series, unsigned int sequence, bool bzip)
Definition: io.c:121
void free_xml(xmlNode *child)
Definition: xml.c:958
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:661
#define cib_clear_file_flags(cibfile, flags_to_clear)
Definition: cib_file.c:151
#define PCMK_XA_VALIDATE_WITH
Definition: xml_names.h:441
int pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress, unsigned int *nbytes)
Definition: xml_io.c:690
uint32_t id
Definition: cpg.c:48
#define CRM_CONFIG_DIR
Definition: config.h:17
int(* register_notification)(cib_t *cib, const char *callback, int enabled)
Definition: cib_types.h:248
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 * variant_opaque
Definition: cib_types.h:389
xmlNode * pcmk__xml_read(const char *filename)
Definition: xml_io.c:166
#define pcmk__assert(expr)
int(* cib__op_fn_t)(const char *, int, const char *, xmlNode *, xmlNode *, xmlNode *, xmlNode **, xmlNode **)
Definition: internal.h:85
char * user
Definition: cib_types.h:403
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
#define cib_set_file_flags(cibfile, flags_to_set)
Definition: cib_file.c:142
int cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **root)
Definition: cib_file.c:762
int pcmk__file_contents(const char *filename, char **contents)
Definition: io.c:432
#define cib__set_call_options(cib_call_opts, call_for, flags_to_set)
Definition: internal.h:118
#define PCMK_XE_ACL_TARGET
Definition: xml_names.h:58
cib_t * cib_file_new(const char *cib_location)
Definition: cib_file.c:652
int(* end_transaction)(cib_t *cib, bool commit, int call_options)
End and optionally commit this client&#39;s CIB transaction.
Definition: cib_types.h:362
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:695
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:331
const char * path
Definition: cib.c:28
#define crm_err(fmt, args...)
Definition: logging.h:391
xmlNode * input
int cib__extend_transaction(cib_t *cib, xmlNode *request)
Definition: cib_utils.c:744
#define PCMK__XA_CIB_OP
#define crm_log_xml_info(xml, text)
Definition: logging.h:410
uint32_t flags
Group of enum cib__op_attr flags.
Definition: internal.h:91
enum cib_variant variant
Definition: cib_types.h:385
#define pcmk_ok
Definition: results.h:65
IPC interface to Pacemaker daemons.
bool pcmk__verify_digest(xmlNode *input, const char *expected)
Definition: digest.c:200
#define pcmk__log_xml_patchset(level, patchset)
#define crm_log_xml_trace(xml, text)
Definition: logging.h:412
int pcmk__xe_get_flags(const xmlNode *xml, const char *name, uint32_t *dest, uint32_t default_value)
Retrieve a flag group from an XML attribute value.
Definition: nvpair.c:535
bool pcmk__configured_schema_validates(xmlNode *xml)
Definition: schemas.c:809
int pcmk__real_path(const char *path, char **resolved_path)
Definition: io.c:85
int cib__get_operation(const char *op, const cib__operation_t **operation)
Definition: cib_ops.c:144
int pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd, bool compress, unsigned int *nbytes)
Definition: xml_io.c:662
enum cib_conn_type type
Definition: cib_types.h:384
#define PCMK_XA_ADMIN_EPOCH
Definition: xml_names.h:232
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml.c:770
enum cib_state state
Definition: cib_types.h:382
#define PCMK__XE_CIB_TRANSACTION
int(* free)(cib_t *cib)
Definition: cib_types.h:168
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2130
int(* client_id)(const cib_t *cib, const char **async_id, const char **sync_id)
Get the given CIB connection&#39;s unique client identifier(s)
Definition: cib_types.h:299
#define pcmk_err_cib_corrupt
Definition: results.h:85
#define PCMK__XA_CIB_CALLID
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
#define crm_info(fmt, args...)
Definition: logging.h:399
Process request when the client commits the active transaction.
Definition: cib_types.h:108
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
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:647
uint64_t flags
Definition: remote.c:215
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
void * delegate_fn
Definition: cib_types.h:390
int cib_perform_op(cib_t *cib, const char *op, uint32_t 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