pacemaker  1.1.17-b36b869ca8
Scalable High-Availability cluster resource manager
schemas.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2004-2016 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include <crm_internal.h>
20 
21 #include <stdio.h>
22 #include <string.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <math.h>
26 #include <sys/stat.h>
27 
28 #if HAVE_LIBXML2
29 # include <libxml/relaxng.h>
30 #endif
31 
32 #if HAVE_LIBXSLT
33 # include <libxslt/xslt.h>
34 # include <libxslt/transform.h>
35 #endif
36 
37 #include <crm/msg_xml.h>
38 #include <crm/common/xml.h>
39 
40 typedef struct {
41  xmlRelaxNGPtr rng;
42  xmlRelaxNGValidCtxtPtr valid;
43  xmlRelaxNGParserCtxtPtr parser;
44 } relaxng_ctx_cache_t;
45 
46 struct schema_s {
47  int type;
48  float version;
49  char *name;
50  char *location;
51  char *transform;
52  int after_transform;
53  void *cache;
54 };
55 
56 static struct schema_s *known_schemas = NULL;
57 static int xml_schema_max = 0;
58 
59 void
60 xml_log(int priority, const char *fmt, ...)
61 G_GNUC_PRINTF(2, 3);
62 
63 void
64 xml_log(int priority, const char *fmt, ...)
65 {
66  va_list ap;
67 
68  va_start(ap, fmt);
69  qb_log_from_external_source_va(__FUNCTION__, __FILE__, fmt, priority,
70  __LINE__, 0, ap);
71  va_end(ap);
72 }
73 
74 static int
75 xml_latest_schema_index(void)
76 {
77  return xml_schema_max - 4;
78 }
79 
80 static int
81 xml_minimum_schema_index(void)
82 {
83  static int best = 0;
84  if (best == 0) {
85  int lpc = 0;
86  float target = 0.0;
87 
88  best = xml_latest_schema_index();
89  target = floor(known_schemas[best].version);
90 
91  for (lpc = best; lpc > 0; lpc--) {
92  if (known_schemas[lpc].version < target) {
93  return best;
94  } else {
95  best = lpc;
96  }
97  }
98  best = xml_latest_schema_index();
99  }
100  return best;
101 }
102 
103 const char *
105 {
106  return get_schema_name(xml_latest_schema_index());
107 }
108 
109 static const char *
110 get_schema_root(void)
111 {
112  static const char *base = NULL;
113 
114  if (base == NULL) {
115  base = getenv("PCMK_schema_directory");
116  }
117  if (base == NULL || strlen(base) == 0) {
118  base = CRM_DTD_DIRECTORY;
119  }
120  return base;
121 }
122 
123 static char *
124 get_schema_path(const char *name, const char *file)
125 {
126  const char *base = get_schema_root();
127 
128  if (file) {
129  return crm_strdup_printf("%s/%s", base, file);
130  }
131  return crm_strdup_printf("%s/%s.rng", base, name);
132 }
133 
134 static int
135 schema_filter(const struct dirent *a)
136 {
137  int rc = 0;
138  float version = 0;
139 
140  if (strstr(a->d_name, "pacemaker-") != a->d_name) {
141  /* crm_trace("%s - wrong prefix", a->d_name); */
142 
143  } else if (!crm_ends_with(a->d_name, ".rng")) {
144  /* crm_trace("%s - wrong suffix", a->d_name); */
145 
146  } else if (sscanf(a->d_name, "pacemaker-%f.rng", &version) == 0) {
147  /* crm_trace("%s - wrong format", a->d_name); */
148 
149  } else if (strcmp(a->d_name, "pacemaker-1.1.rng") == 0) {
150  /* "-1.1" was used for what later became "-next" */
151  /* crm_trace("%s - hack", a->d_name); */
152 
153  } else {
154  /* crm_debug("%s - candidate", a->d_name); */
155  rc = 1;
156  }
157 
158  return rc;
159 }
160 
161 static int
162 schema_sort(const struct dirent **a, const struct dirent **b)
163 {
164  int rc = 0;
165  float a_version = 0.0;
166  float b_version = 0.0;
167 
168  sscanf(a[0]->d_name, "pacemaker-%f.rng", &a_version);
169  sscanf(b[0]->d_name, "pacemaker-%f.rng", &b_version);
170 
171  if (a_version > b_version) {
172  rc = 1;
173  } else if(a_version < b_version) {
174  rc = -1;
175  }
176 
177  /* crm_trace("%s (%f) vs. %s (%f) : %d", a[0]->d_name, a_version, b[0]->d_name, b_version, rc); */
178  return rc;
179 }
180 
181 static void
182 __xml_schema_add(int type, float version, const char *name,
183  const char *location, const char *transform,
184  int after_transform)
185 {
186  int last = xml_schema_max;
187 
188  xml_schema_max++;
189  known_schemas = realloc_safe(known_schemas,
190  xml_schema_max * sizeof(struct schema_s));
191  CRM_ASSERT(known_schemas != NULL);
192  memset(known_schemas+last, 0, sizeof(struct schema_s));
193  known_schemas[last].type = type;
194  known_schemas[last].after_transform = after_transform;
195 
196  if (version > 0.0) {
197  known_schemas[last].version = version;
198  known_schemas[last].name = crm_strdup_printf("pacemaker-%.1f", version);
199  known_schemas[last].location = crm_strdup_printf("%s.rng", known_schemas[last].name);
200 
201  } else {
202  char dummy[1024];
203  CRM_ASSERT(name);
204  CRM_ASSERT(location);
205  sscanf(name, "%[^-]-%f", dummy, &version);
206  known_schemas[last].version = version;
207  known_schemas[last].name = strdup(name);
208  known_schemas[last].location = strdup(location);
209  }
210 
211  if (transform) {
212  known_schemas[last].transform = strdup(transform);
213  }
214  if (after_transform == 0) {
215  after_transform = xml_schema_max; /* upgrade is a one-way */
216  }
217  known_schemas[last].after_transform = after_transform;
218 
219  if (known_schemas[last].after_transform < 0) {
220  crm_debug("Added supported schema %d: %s (%s)",
221  last, known_schemas[last].name, known_schemas[last].location);
222 
223  } else if (known_schemas[last].transform) {
224  crm_debug("Added supported schema %d: %s (%s upgrades to %d with %s)",
225  last, known_schemas[last].name, known_schemas[last].location,
226  known_schemas[last].after_transform,
227  known_schemas[last].transform);
228 
229  } else {
230  crm_debug("Added supported schema %d: %s (%s upgrades to %d)",
231  last, known_schemas[last].name, known_schemas[last].location,
232  known_schemas[last].after_transform);
233  }
234 }
235 
240 void
242 {
243  int lpc, max;
244  const char *base = get_schema_root();
245  struct dirent **namelist = NULL;
246 
247  max = scandir(base, &namelist, schema_filter, schema_sort);
248  __xml_schema_add(1, 0.0, "pacemaker-0.6", "crm.dtd", "upgrade06.xsl", 3);
249  __xml_schema_add(1, 0.0, "transitional-0.6", "crm-transitional.dtd",
250  "upgrade06.xsl", 3);
251  __xml_schema_add(2, 0.0, "pacemaker-0.7", "pacemaker-1.0.rng", NULL, 0);
252 
253  if (max < 0) {
254  crm_notice("scandir(%s) failed: %s (%d)", base, strerror(errno), errno);
255 
256  } else {
257  for (lpc = 0; lpc < max; lpc++) {
258  int next = 0;
259  float version = 0.0;
260  char *transform = NULL;
261 
262  sscanf(namelist[lpc]->d_name, "pacemaker-%f.rng", &version);
263  if ((lpc + 1) < max) {
264  float next_version = 0.0;
265 
266  sscanf(namelist[lpc+1]->d_name, "pacemaker-%f.rng",
267  &next_version);
268 
269  if (floor(version) < floor(next_version)) {
270  struct stat s;
271  char *xslt = NULL;
272 
273  transform = crm_strdup_printf("upgrade-%.1f.xsl", version);
274  xslt = get_schema_path(NULL, transform);
275  if (stat(xslt, &s) != 0) {
276  crm_err("Transform %s not found", xslt);
277  free(xslt);
278  __xml_schema_add(2, version, NULL, NULL, NULL, -1);
279  break;
280  } else {
281  free(xslt);
282  }
283  }
284 
285  } else {
286  next = -1;
287  }
288  __xml_schema_add(2, version, NULL, NULL, transform, next);
289  free(namelist[lpc]);
290  free(transform);
291  }
292  }
293 
294  /* 1.1 was the old name for -next */
295  __xml_schema_add(2, 0.0, "pacemaker-1.1", "pacemaker-next.rng", NULL, 0);
296  __xml_schema_add(2, 0.0, "pacemaker-next", "pacemaker-next.rng", NULL, -1);
297  __xml_schema_add(0, 0.0, "none", "N/A", NULL, -1);
298  free(namelist);
299 }
300 
301 static gboolean
302 validate_with_dtd(xmlDocPtr doc, gboolean to_logs, const char *dtd_file)
303 {
304  gboolean valid = TRUE;
305 
306  xmlDtdPtr dtd = NULL;
307  xmlValidCtxtPtr cvp = NULL;
308 
309  CRM_CHECK(doc != NULL, return FALSE);
310  CRM_CHECK(dtd_file != NULL, return FALSE);
311 
312  dtd = xmlParseDTD(NULL, (const xmlChar *)dtd_file);
313  if (dtd == NULL) {
314  crm_err("Could not locate/parse DTD: %s", dtd_file);
315  return TRUE;
316  }
317 
318  cvp = xmlNewValidCtxt();
319  if (cvp) {
320  if (to_logs) {
321  cvp->userData = (void *)LOG_ERR;
322  cvp->error = (xmlValidityErrorFunc) xml_log;
323  cvp->warning = (xmlValidityWarningFunc) xml_log;
324  } else {
325  cvp->userData = (void *)stderr;
326  cvp->error = (xmlValidityErrorFunc) fprintf;
327  cvp->warning = (xmlValidityWarningFunc) fprintf;
328  }
329 
330  if (!xmlValidateDtd(cvp, doc, dtd)) {
331  valid = FALSE;
332  }
333  xmlFreeValidCtxt(cvp);
334 
335  } else {
336  crm_err("Internal error: No valid context");
337  }
338 
339  xmlFreeDtd(dtd);
340  return valid;
341 }
342 
343 #if 0
344 static void
345 relaxng_invalid_stderr(void *userData, xmlErrorPtr error)
346 {
347  /*
348  Structure xmlError
349  struct _xmlError {
350  int domain : What part of the library raised this er
351  int code : The error code, e.g. an xmlParserError
352  char * message : human-readable informative error messag
353  xmlErrorLevel level : how consequent is the error
354  char * file : the filename
355  int line : the line number if available
356  char * str1 : extra string information
357  char * str2 : extra string information
358  char * str3 : extra string information
359  int int1 : extra number information
360  int int2 : column number of the error or 0 if N/A
361  void * ctxt : the parser context if available
362  void * node : the node in the tree
363  }
364  */
365  crm_err("Structured error: line=%d, level=%d %s", error->line, error->level, error->message);
366 }
367 #endif
368 
369 static gboolean
370 validate_with_relaxng(xmlDocPtr doc, gboolean to_logs, const char *relaxng_file,
371  relaxng_ctx_cache_t **cached_ctx)
372 {
373  int rc = 0;
374  gboolean valid = TRUE;
375  relaxng_ctx_cache_t *ctx = NULL;
376 
377  CRM_CHECK(doc != NULL, return FALSE);
378  CRM_CHECK(relaxng_file != NULL, return FALSE);
379 
380  if (cached_ctx && *cached_ctx) {
381  ctx = *cached_ctx;
382 
383  } else {
384  crm_info("Creating RNG parser context");
385  ctx = calloc(1, sizeof(relaxng_ctx_cache_t));
386 
387  xmlLoadExtDtdDefaultValue = 1;
388  ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file);
389  CRM_CHECK(ctx->parser != NULL, goto cleanup);
390 
391  if (to_logs) {
392  xmlRelaxNGSetParserErrors(ctx->parser,
393  (xmlRelaxNGValidityErrorFunc) xml_log,
394  (xmlRelaxNGValidityWarningFunc) xml_log,
395  GUINT_TO_POINTER(LOG_ERR));
396  } else {
397  xmlRelaxNGSetParserErrors(ctx->parser,
398  (xmlRelaxNGValidityErrorFunc) fprintf,
399  (xmlRelaxNGValidityWarningFunc) fprintf,
400  stderr);
401  }
402 
403  ctx->rng = xmlRelaxNGParse(ctx->parser);
404  CRM_CHECK(ctx->rng != NULL,
405  crm_err("Could not find/parse %s", relaxng_file);
406  goto cleanup);
407 
408  ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng);
409  CRM_CHECK(ctx->valid != NULL, goto cleanup);
410 
411  if (to_logs) {
412  xmlRelaxNGSetValidErrors(ctx->valid,
413  (xmlRelaxNGValidityErrorFunc) xml_log,
414  (xmlRelaxNGValidityWarningFunc) xml_log,
415  GUINT_TO_POINTER(LOG_ERR));
416  } else {
417  xmlRelaxNGSetValidErrors(ctx->valid,
418  (xmlRelaxNGValidityErrorFunc) fprintf,
419  (xmlRelaxNGValidityWarningFunc) fprintf,
420  stderr);
421  }
422  }
423 
424  /* xmlRelaxNGSetValidStructuredErrors( */
425  /* valid, relaxng_invalid_stderr, valid); */
426 
427  xmlLineNumbersDefault(1);
428  rc = xmlRelaxNGValidateDoc(ctx->valid, doc);
429  if (rc > 0) {
430  valid = FALSE;
431 
432  } else if (rc < 0) {
433  crm_err("Internal libxml error during validation");
434  }
435 
436  cleanup:
437 
438  if (cached_ctx) {
439  *cached_ctx = ctx;
440 
441  } else {
442  if (ctx->parser != NULL) {
443  xmlRelaxNGFreeParserCtxt(ctx->parser);
444  }
445  if (ctx->valid != NULL) {
446  xmlRelaxNGFreeValidCtxt(ctx->valid);
447  }
448  if (ctx->rng != NULL) {
449  xmlRelaxNGFree(ctx->rng);
450  }
451  free(ctx);
452  }
453 
454  return valid;
455 }
456 
461 void
463 {
464  int lpc;
465  relaxng_ctx_cache_t *ctx = NULL;
466 
467  for (lpc = 0; lpc < xml_schema_max; lpc++) {
468 
469  switch (known_schemas[lpc].type) {
470  case 0:
471  /* None */
472  break;
473  case 1:
474  /* DTD - Not cached */
475  break;
476  case 2:
477  /* RNG - Cached */
478  ctx = (relaxng_ctx_cache_t *) known_schemas[lpc].cache;
479  if (ctx == NULL) {
480  break;
481  }
482  if (ctx->parser != NULL) {
483  xmlRelaxNGFreeParserCtxt(ctx->parser);
484  }
485  if (ctx->valid != NULL) {
486  xmlRelaxNGFreeValidCtxt(ctx->valid);
487  }
488  if (ctx->rng != NULL) {
489  xmlRelaxNGFree(ctx->rng);
490  }
491  free(ctx);
492  known_schemas[lpc].cache = NULL;
493  break;
494  default:
495  break;
496  }
497  free(known_schemas[lpc].name);
498  free(known_schemas[lpc].location);
499  free(known_schemas[lpc].transform);
500  }
501  free(known_schemas);
502  known_schemas = NULL;
503 }
504 
505 static gboolean
506 validate_with(xmlNode *xml, int method, gboolean to_logs)
507 {
508  xmlDocPtr doc = NULL;
509  gboolean valid = FALSE;
510  int type = 0;
511  char *file = NULL;
512 
513  if (method < 0) {
514  return FALSE;
515  }
516 
517  type = known_schemas[method].type;
518  if(type == 0) {
519  return TRUE;
520  }
521 
522  CRM_CHECK(xml != NULL, return FALSE);
523  doc = getDocPtr(xml);
524  file = get_schema_path(known_schemas[method].name,
525  known_schemas[method].location);
526 
527  crm_trace("Validating with: %s (type=%d)", crm_str(file), type);
528  switch (type) {
529  case 1:
530  valid = validate_with_dtd(doc, to_logs, file);
531  break;
532  case 2:
533  valid =
534  validate_with_relaxng(doc, to_logs, file,
535  (relaxng_ctx_cache_t **) & (known_schemas[method].cache));
536  break;
537  default:
538  crm_err("Unknown validator type: %d", type);
539  break;
540  }
541 
542  free(file);
543  return valid;
544 }
545 
546 static void
547 dump_file(const char *filename)
548 {
549 
550  FILE *fp = NULL;
551  int ch, line = 0;
552 
553  CRM_CHECK(filename != NULL, return);
554 
555  fp = fopen(filename, "r");
556  if (fp == NULL) {
557  crm_perror(LOG_ERR, "Could not open %s for reading", filename);
558  return;
559  }
560 
561  fprintf(stderr, "%4d ", ++line);
562  do {
563  ch = getc(fp);
564  if (ch == EOF) {
565  putc('\n', stderr);
566  break;
567  } else if (ch == '\n') {
568  fprintf(stderr, "\n%4d ", ++line);
569  } else {
570  putc(ch, stderr);
571  }
572  } while (1);
573 
574  fclose(fp);
575 }
576 
577 gboolean
578 validate_xml_verbose(xmlNode *xml_blob)
579 {
580  int fd = 0;
581  xmlDoc *doc = NULL;
582  xmlNode *xml = NULL;
583  gboolean rc = FALSE;
584  char *filename = strdup(CRM_STATE_DIR "/cib-invalid.XXXXXX");
585 
586  CRM_CHECK(filename != NULL, return FALSE);
587 
588  umask(S_IWGRP | S_IWOTH | S_IROTH);
589  fd = mkstemp(filename);
590  write_xml_fd(xml_blob, filename, fd, FALSE);
591 
592  dump_file(filename);
593 
594  doc = xmlParseFile(filename);
595  xml = xmlDocGetRootElement(doc);
596  rc = validate_xml(xml, NULL, FALSE);
597  free_xml(xml);
598 
599  unlink(filename);
600  free(filename);
601 
602  return rc;
603 }
604 
605 gboolean
606 validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
607 {
608  int version = 0;
609 
610  if (validation == NULL) {
611  validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION);
612  }
613 
614  if (validation == NULL) {
615  int lpc = 0;
616  bool valid = FALSE;
617 
618  validation = crm_element_value(xml_blob, "ignore-dtd");
619  if (crm_is_true(validation)) {
620  /* Legacy compatibilty */
621  crm_xml_add(xml_blob, XML_ATTR_VALIDATION, "none");
622  return TRUE;
623  }
624 
625  /* Work it out */
626  for (lpc = 0; lpc < xml_schema_max; lpc++) {
627  if (validate_with(xml_blob, lpc, FALSE)) {
628  valid = TRUE;
630  known_schemas[lpc].name);
631  crm_info("XML validated against %s", known_schemas[lpc].name);
632  if(known_schemas[lpc].after_transform == 0) {
633  break;
634  }
635  }
636  }
637 
638  return valid;
639  }
640 
641  version = get_schema_version(validation);
642  if (strcmp(validation, "none") == 0) {
643  return TRUE;
644  } else if (version < xml_schema_max) {
645  return validate_with(xml_blob, version, to_logs);
646  }
647 
648  crm_err("Unknown validator: %s", validation);
649  return FALSE;
650 }
651 
652 #if HAVE_LIBXSLT
653 static xmlNode *
654 apply_transformation(xmlNode *xml, const char *transform)
655 {
656  char *xform = NULL;
657  xmlNode *out = NULL;
658  xmlDocPtr res = NULL;
659  xmlDocPtr doc = NULL;
660  xsltStylesheet *xslt = NULL;
661 
662  CRM_CHECK(xml != NULL, return FALSE);
663  doc = getDocPtr(xml);
664  xform = get_schema_path(NULL, transform);
665 
666  xmlLoadExtDtdDefaultValue = 1;
667  xmlSubstituteEntitiesDefault(1);
668 
669  xslt = xsltParseStylesheetFile((const xmlChar *)xform);
670  CRM_CHECK(xslt != NULL, goto cleanup);
671 
672  res = xsltApplyStylesheet(xslt, doc, NULL);
673  CRM_CHECK(res != NULL, goto cleanup);
674 
675  out = xmlDocGetRootElement(res);
676 
677  cleanup:
678  if (xslt) {
679  xsltFreeStylesheet(xslt);
680  }
681 
682  free(xform);
683 
684  return out;
685 }
686 #endif
687 
688 const char *
690 {
691  if (version < 0 || version >= xml_schema_max) {
692  return "unknown";
693  }
694  return known_schemas[version].name;
695 }
696 
697 int
698 get_schema_version(const char *name)
699 {
700  int lpc = 0;
701 
702  if (name == NULL) {
703  name = "none";
704  }
705  for (; lpc < xml_schema_max; lpc++) {
706  if (safe_str_eq(name, known_schemas[lpc].name)) {
707  return lpc;
708  }
709  }
710  return -1;
711 }
712 
713 /* set which validation to use */
714 int
715 update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform,
716  gboolean to_logs)
717 {
718  xmlNode *xml = NULL;
719  char *value = NULL;
720  int max_stable_schemas = xml_latest_schema_index();
721  int lpc = 0, match = -1, rc = pcmk_ok;
722  int next = -1; /* -1 denotes "inactive" value */
723 
724  CRM_CHECK(best != NULL, return -EINVAL);
725  *best = 0;
726 
727  CRM_CHECK(xml_blob != NULL, return -EINVAL);
728  CRM_CHECK(*xml_blob != NULL, return -EINVAL);
729 
730  xml = *xml_blob;
732 
733  if (value != NULL) {
734  match = get_schema_version(value);
735 
736  lpc = match;
737  if (lpc >= 0 && transform == FALSE) {
738  *best = lpc++;
739 
740  } else if (lpc < 0) {
741  crm_debug("Unknown validation type");
742  lpc = 0;
743  }
744  }
745 
746  if (match >= max_stable_schemas) {
747  /* nothing to do */
748  free(value);
749  *best = match;
750  return pcmk_ok;
751  }
752 
753  while (lpc <= max_stable_schemas) {
754  crm_debug("Testing '%s' validation (%d of %d)",
755  known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>",
756  lpc, max_stable_schemas);
757 
758  if (validate_with(xml, lpc, to_logs) == FALSE) {
759  if (next != -1) {
760  crm_info("Configuration not valid for schema: %s",
761  known_schemas[lpc].name);
762  next = -1;
763  } else {
764  crm_trace("%s validation failed",
765  known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>");
766  }
767  if (*best) {
768  /* we've satisfied the validation, no need to check further */
769  break;
770  }
772 
773  } else {
774  if (next != -1) {
775  crm_debug("Configuration valid for schema: %s",
776  known_schemas[next].name);
777  next = -1;
778  }
779  rc = pcmk_ok;
780  }
781 
782  if (rc == pcmk_ok) {
783  *best = lpc;
784  }
785 
786  if (rc == pcmk_ok && transform) {
787  xmlNode *upgrade = NULL;
788  next = known_schemas[lpc].after_transform;
789 
790  if (next <= lpc) {
791  /* There is no next version, or next would regress */
792  crm_trace("Stopping at %s", known_schemas[lpc].name);
793  break;
794 
795  } else if (max > 0 && (lpc == max || next > max)) {
796  crm_trace("Upgrade limit reached at %s (lpc=%d, next=%d, max=%d)",
797  known_schemas[lpc].name, lpc, next, max);
798  break;
799 
800  } else if (known_schemas[lpc].transform == NULL) {
801  crm_debug("%s-style configuration is also valid for %s",
802  known_schemas[lpc].name, known_schemas[next].name);
803 
804  lpc = next;
805 
806  } else {
807  crm_debug("Upgrading %s-style configuration to %s with %s",
808  known_schemas[lpc].name, known_schemas[next].name,
809  known_schemas[lpc].transform ? known_schemas[lpc].transform : "no-op");
810 
811 #if HAVE_LIBXSLT
812  upgrade = apply_transformation(xml, known_schemas[lpc].transform);
813 #endif
814  if (upgrade == NULL) {
815  crm_err("Transformation %s failed",
816  known_schemas[lpc].transform);
818 
819  } else if (validate_with(upgrade, next, to_logs)) {
820  crm_info("Transformation %s successful",
821  known_schemas[lpc].transform);
822  lpc = next;
823  *best = next;
824  free_xml(xml);
825  xml = upgrade;
826  rc = pcmk_ok;
827 
828  } else {
829  crm_err("Transformation %s did not produce a valid configuration",
830  known_schemas[lpc].transform);
831  crm_log_xml_info(upgrade, "transform:bad");
832  free_xml(upgrade);
834  }
835  next = -1;
836  }
837  }
838 
839  if (transform == FALSE || rc != pcmk_ok) {
840  /* we need some progress! */
841  lpc++;
842  }
843  }
844 
845  if (*best > match && *best) {
846  crm_info("%s the configuration from %s to %s",
847  transform?"Transformed":"Upgraded",
848  value ? value : "<none>", known_schemas[*best].name);
849  crm_xml_add(xml, XML_ATTR_VALIDATION, known_schemas[*best].name);
850  }
851 
852  *xml_blob = xml;
853  free(value);
854  return rc;
855 }
856 
857 gboolean
858 cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
859 {
860  gboolean rc = TRUE;
861  const char *value = crm_element_value(*xml, XML_ATTR_VALIDATION);
862  char *const orig_value = strdup(value == NULL ? "(none)" : value);
863 
864  int version = get_schema_version(value);
865  int orig_version = version;
866  int min_version = xml_minimum_schema_index();
867 
868  if (version < min_version) {
869  xmlNode *converted = NULL;
870 
871  converted = copy_xml(*xml);
872  update_validation(&converted, &version, 0, TRUE, to_logs);
873 
874  value = crm_element_value(converted, XML_ATTR_VALIDATION);
875  if (version < min_version) {
876  if (version < orig_version || orig_version == -1) {
877  if (to_logs) {
878  crm_config_err("Your current configuration %s could not"
879  " validate with any schema in range [%s, %s],"
880  " cannot upgrade to %s.",
881  orig_value,
882  get_schema_name(orig_version),
884  get_schema_name(min_version));
885  } else {
886  fprintf(stderr, "Your current configuration %s could not"
887  " validate with any schema in range [%s, %s],"
888  " cannot upgrade to %s.\n",
889  orig_value,
890  get_schema_name(orig_version),
892  get_schema_name(min_version));
893  }
894  } else if (to_logs) {
895  crm_config_err("Your current configuration could only be upgraded to %s... "
896  "the minimum requirement is %s.", crm_str(value),
897  get_schema_name(min_version));
898  } else {
899  fprintf(stderr, "Your current configuration could only be upgraded to %s... "
900  "the minimum requirement is %s.\n",
901  crm_str(value), get_schema_name(min_version));
902  }
903 
904  free_xml(converted);
905  converted = NULL;
906  rc = FALSE;
907 
908  } else {
909  free_xml(*xml);
910  *xml = converted;
911 
912  if (version < xml_latest_schema_index()) {
913  crm_config_warn("Your configuration was internally updated to %s... "
914  "which is acceptable but not the most recent",
915  get_schema_name(version));
916 
917  } else if (to_logs) {
918  crm_info("Your configuration was internally updated to the latest version (%s)",
919  get_schema_name(version));
920  }
921  }
922 
923  } else if (version >= get_schema_version("none")) {
924  if (to_logs) {
925  crm_config_warn("Configuration validation is currently disabled."
926  " It is highly encouraged and prevents many common cluster issues.");
927 
928  } else {
929  fprintf(stderr, "Configuration validation is currently disabled."
930  " It is highly encouraged and prevents many common cluster issues.\n");
931  }
932  }
933 
934  if (best_version) {
935  *best_version = version;
936  }
937 
938  free(orig_value);
939  return rc;
940 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:164
void crm_schema_init(void)
Definition: schemas.c:241
#define crm_notice(fmt, args...)
Definition: logging.h:250
void xml_log(int priority, const char *fmt,...) G_GNUC_PRINTF(2
Definition: schemas.c:64
#define crm_config_err(fmt...)
Definition: crm_internal.h:258
#define pcmk_ok
Definition: error.h:42
char * crm_element_value_copy(xmlNode *data, const char *name)
Definition: xml.c:3893
#define CRM_DTD_DIRECTORY
Definition: config.h:50
char * strerror(int errnum)
char version[256]
Definition: plugin.c:84
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:2388
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:2711
int get_schema_version(const char *name)
Definition: schemas.c:698
#define crm_debug(fmt, args...)
Definition: logging.h:253
#define pcmk_err_schema_validation
Definition: error.h:47
void crm_schema_cleanup(void)
Definition: schemas.c:462
#define crm_trace(fmt, args...)
Definition: logging.h:254
const char * get_schema_name(int version)
Definition: schemas.c:689
Wrappers for and extensions to libxml2.
const char * crm_element_value(xmlNode *data, const char *name)
Definition: xml.c:5134
#define XML_ATTR_VALIDATION
Definition: msg_xml.h:86
void free_xml(xmlNode *child)
Definition: xml.c:2705
gboolean validate_xml_verbose(xmlNode *xml_blob)
Definition: schemas.c:578
#define pcmk_err_transform_failed
Definition: error.h:48
gboolean crm_ends_with(const char *s, const char *match)
Definition: strings.c:242
#define crm_config_warn(fmt...)
Definition: crm_internal.h:259
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Definition: xml.c:2489
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
Definition: schemas.c:606
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:226
#define crm_err(fmt, args...)
Definition: logging.h:248
#define crm_log_xml_info(xml, text)
Definition: logging.h:260
#define CRM_ASSERT(expr)
Definition: error.h:35
#define crm_str(x)
Definition: logging.h:274
#define CRM_STATE_DIR
Definition: config.h:68
gboolean crm_is_true(const char *s)
Definition: strings.c:165
gboolean cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
Definition: schemas.c:858
int update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform, gboolean to_logs)
Try update CIB XML to the highest pacemaker&#39;s standard if feasible.
Definition: schemas.c:715
#define safe_str_eq(a, b)
Definition: util.h:64
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
Definition: xml.c:3167
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
#define crm_info(fmt, args...)
Definition: logging.h:251
const char * xml_latest_schema(void)
Definition: schemas.c:104
enum crm_ais_msg_types type
Definition: internal.h:51