pacemaker  2.1.7-0f7f88312f
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-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 *)
41 static GHashTable *client_table = NULL;
42 
44  cib_file_flag_dirty = (1 << 0),
45  cib_file_flag_live = (1 << 1),
46 };
47 
48 typedef 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 
55 static 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 
68 static void
69 register_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 
85 static void
86 unregister_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 
113 static cib_t *
114 get_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 
122 static 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  */
139 static uid_t cib_file_owner = 0;
140 static uid_t cib_file_group = 0;
141 static 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 
169 static cib__op_fn_t
170 file_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 
190 static gboolean
191 cib_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 
213 static int
214 cib_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 
277 done:
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 
285 static int
286 cib_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 
348 done:
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 
374 static int
375 load_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 
410 static int
411 cib_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 
445 static int
446 cib_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 
527 static int
528 cib_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 
572 static int
573 cib_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 
598 static int
599 cib_file_inputfd(cib_t *cib)
600 {
601  return -EPROTONOSUPPORT;
602 }
603 
604 static int
605 cib_file_register_notification(cib_t *cib, const char *callback, int enabled)
606 {
607  return -EPROTONOSUPPORT;
608 }
609 
610 static int
611 cib_file_set_connection_dnotify(cib_t *cib,
612  void (*dnotify) (gpointer user_data))
613 {
614  return -EPROTONOSUPPORT;
615 }
616 
630 static int
631 cib_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 }
644 
645 cib_t *
646 cib_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 
701 static gboolean
702 cib_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 
743 int
744 cib_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 
803 static int
804 cib_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 
886 static void
887 cib_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 */
892  crm_xml_add(root, XML_ATTR_NUMUPDATES, "0");
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 
917 int
918 cib_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 
1061 static int
1062 cib_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 
1106 static int
1107 cib_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 
1154 static int
1155 cib_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 }
enum cib__op_type type
Definition: internal.h:125
pcmk__cpg_host_t host
Definition: cpg.c:49
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
#define pcmk_err_schema_validation
Definition: results.h:73
A dumping ground.
const char * pcmk_strerror(int rc)
Definition: results.c:149
#define CIB_SERIES_MAX
Definition: cib_file.c:33
char data[0]
Definition: cpg.c:55
char * crm_generate_uuid(void)
Definition: utils.c:509
#define XML_ATTR_NUMUPDATES
Definition: msg_xml.h:149
int pcmk_rc2legacy(int rc)
Definition: results.c:546
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:421
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition: xml.c:1089
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
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:132
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2484
int pcmk__chown_series_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition: io.c:238
xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
Definition: xml.c:384
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
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
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_generic
Definition: results.h:71
int write_xml_file(const xmlNode *xml, const char *filename, gboolean compress)
Write XML to a file.
Definition: xml.c:1284
gboolean validate_xml_verbose(const xmlNode *xml_blob)
Definition: schemas.c:645
int pcmk__read_series_sequence(const char *directory, const char *series, unsigned int *seq)
Definition: io.c:140
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:222
char * pcmk__series_filename(const char *directory, const char *series, int sequence, bool bzip)
Definition: io.c:121
int(* inputfd)(cib_t *cib)
Definition: cib_types.h:150
enum crm_ais_msg_types type
Definition: cpg.c:48
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:918
xmlNode * filename2xml(const char *filename)
Definition: xml.c:990
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:501
#define pcmk_err_cib_backup
Definition: results.h:79
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
#define CIB_SERIES_BZIP
Definition: cib_file.c:34
#define F_CIB_SECTION
Definition: internal.h:42
#define XML_ATTR_GENERATION
Definition: msg_xml.h:147
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
Definition: schemas.c:673
cib_file_flags
Definition: cib_file.c:43
#define CIB_SERIES
Definition: cib_file.c:32
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:789
cib_t * cib_new_variant(void)
Definition: cib_client.c:665
cib__op_type
Definition: internal.h:93
int(* set_connection_dnotify)(cib_t *cib, void(*dnotify)(gpointer user_data))
Definition: cib_types.h:146
#define crm_warn(fmt, args...)
Definition: logging.h:382
#define pcmk_err_cib_save
Definition: results.h:80
Modifies CIB.
Definition: internal.h:80
cib_api_operations_t * cmds
Definition: cib_types.h:345
#define crm_debug(fmt, args...)
Definition: logging.h:386
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
cib_conn_type
Definition: cib_types.h:46
int(* signon)(cib_t *cib, const char *name, enum cib_conn_type type)
Definition: cib_types.h:126
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
#define F_CIB_OPERATION
Definition: internal.h:40
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:387
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
void(* op_callback)(const xmlNode *msg, int call_id, int rc, xmlNode *output)
Definition: cib_types.h:342
void pcmk__sync_directory(const char *name)
Definition: io.c:396
Wrappers for and extensions to libxml2.
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
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:638
#define PCMK__NELEM(a)
Definition: internal.h:46
#define XML_ATTR_VALIDATION
Definition: msg_xml.h:142
#define CRM_DAEMON_USER
Definition: config.h:30
int pcmk_legacy2rc(int legacy_rc)
Definition: results.c:559
#define pcmk_err_cib_modified
Definition: results.h:78
#define CIB_LIVE_NAME
Definition: cib_file.c:38
void free_xml(xmlNode *child)
Definition: xml.c:783
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
#define cib_clear_file_flags(cibfile, flags_to_clear)
Definition: cib_file.c:152
xmlNode * get_message_xml(const xmlNode *msg, const char *field)
Definition: messages.c:154
#define T_CIB_COMMAND
Definition: internal.h:65
uint32_t id
Definition: cpg.c:45
#define CRM_CONFIG_DIR
Definition: config.h:17
int(* register_notification)(cib_t *cib, const char *callback, int enabled)
Definition: cib_types.h:200
char * calculate_on_disk_digest(xmlNode *local_cib)
Calculate and return digest of XML tree, suitable for storing on disk.
Definition: digest.c:131
void * variant_opaque
Definition: cib_types.h:336
int(* cib__op_fn_t)(const char *, int, const char *, xmlNode *, xmlNode *, xmlNode *, xmlNode **, xmlNode **)
Definition: internal.h:120
char * user
Definition: cib_types.h:349
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
#define XML_TAG_CIB
Definition: msg_xml.h:137
#define cib_set_file_flags(cibfile, flags_to_set)
Definition: cib_file.c:143
int write_xml_fd(const xmlNode *xml, const char *filename, int fd, gboolean compress)
Write XML to a file descriptor.
Definition: xml.c:1255
int cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **root)
Definition: cib_file.c:744
int pcmk__file_contents(const char *filename, char **contents)
Definition: io.c:432
#define T_CIB_TRANSACTION
Definition: internal.h:70
#define F_CIB_CALLOPTS
Definition: internal.h:37
#define cib__set_call_options(cib_call_opts, call_for, flags_to_set)
Definition: internal.h:153
cib_t * cib_file_new(const char *cib_location)
Definition: cib_file.c:646
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:314
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:608
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:323
#define F_CIB_CALLDATA
Definition: internal.h:39
const char * path
Definition: cib.c:28
#define crm_err(fmt, args...)
Definition: logging.h:381
#define CRM_ASSERT(expr)
Definition: results.h:42
xmlNode * input
int cib__extend_transaction(cib_t *cib, xmlNode *request)
Definition: cib_utils.c:758
#define crm_log_xml_info(xml, text)
Definition: logging.h:393
#define XML_ATTR_GENERATION_ADMIN
Definition: msg_xml.h:148
uint32_t flags
Group of enum cib__op_attr flags.
Definition: internal.h:126
enum cib_variant variant
Definition: cib_types.h:332
#define pcmk_ok
Definition: results.h:68
IPC interface to Pacemaker daemons.
bool pcmk__verify_digest(xmlNode *input, const char *expected)
Definition: digest.c:202
#define XML_CIB_TAG_STATUS
Definition: msg_xml.h:204
#define pcmk__log_xml_patchset(level, patchset)
#define F_CIB_CLIENTID
Definition: internal.h:36
#define crm_log_xml_trace(xml, text)
Definition: logging.h:395
#define F_CIB_CALLID
Definition: internal.h:38
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:142
#define XML_ACL_TAG_USER
Definition: msg_xml.h:428
enum cib_conn_type type
Definition: cib_types.h:331
enum cib_state state
Definition: cib_types.h:330
int(* free)(cib_t *cib)
Definition: cib_types.h:133
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:251
#define pcmk_err_cib_corrupt
Definition: results.h:82
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
#define crm_info(fmt, args...)
Definition: logging.h:384
Process request when the client commits the active transaction.
Definition: cib_types.h:100
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
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:560
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:701
void * delegate_fn
Definition: cib_types.h:337
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2510