pacemaker  1.1.16-94ff4df
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules
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  CRM_CHECK(fp != NULL, return);
557 
558  fprintf(stderr, "%4d ", ++line);
559  do {
560  ch = getc(fp);
561  if (ch == EOF) {
562  putc('\n', stderr);
563  break;
564  } else if (ch == '\n') {
565  fprintf(stderr, "\n%4d ", ++line);
566  } else {
567  putc(ch, stderr);
568  }
569  } while (1);
570 
571  fclose(fp);
572 }
573 
574 gboolean
575 validate_xml_verbose(xmlNode *xml_blob)
576 {
577  int fd = 0;
578  xmlDoc *doc = NULL;
579  xmlNode *xml = NULL;
580  gboolean rc = FALSE;
581  char *filename = strdup(CRM_STATE_DIR "/cib-invalid.XXXXXX");
582 
583  umask(S_IWGRP | S_IWOTH | S_IROTH);
584  fd = mkstemp(filename);
585  write_xml_fd(xml_blob, filename, fd, FALSE);
586 
587  dump_file(filename);
588 
589  doc = xmlParseFile(filename);
590  xml = xmlDocGetRootElement(doc);
591  rc = validate_xml(xml, NULL, FALSE);
592  free_xml(xml);
593 
594  unlink(filename);
595  free(filename);
596 
597  return rc;
598 }
599 
600 gboolean
601 validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
602 {
603  int version = 0;
604 
605  if (validation == NULL) {
606  validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION);
607  }
608 
609  if (validation == NULL) {
610  int lpc = 0;
611  bool valid = FALSE;
612 
613  validation = crm_element_value(xml_blob, "ignore-dtd");
614  if (crm_is_true(validation)) {
615  /* Legacy compatibilty */
616  crm_xml_add(xml_blob, XML_ATTR_VALIDATION, "none");
617  return TRUE;
618  }
619 
620  /* Work it out */
621  for (lpc = 0; lpc < xml_schema_max; lpc++) {
622  if (validate_with(xml_blob, lpc, FALSE)) {
623  valid = TRUE;
625  known_schemas[lpc].name);
626  crm_info("XML validated against %s", known_schemas[lpc].name);
627  if(known_schemas[lpc].after_transform == 0) {
628  break;
629  }
630  }
631  }
632 
633  return valid;
634  }
635 
636  version = get_schema_version(validation);
637  if (strcmp(validation, "none") == 0) {
638  return TRUE;
639  } else if (version < xml_schema_max) {
640  return validate_with(xml_blob, version, to_logs);
641  }
642 
643  crm_err("Unknown validator: %s", validation);
644  return FALSE;
645 }
646 
647 #if HAVE_LIBXSLT
648 static xmlNode *
649 apply_transformation(xmlNode *xml, const char *transform)
650 {
651  char *xform = NULL;
652  xmlNode *out = NULL;
653  xmlDocPtr res = NULL;
654  xmlDocPtr doc = NULL;
655  xsltStylesheet *xslt = NULL;
656 
657  CRM_CHECK(xml != NULL, return FALSE);
658  doc = getDocPtr(xml);
659  xform = get_schema_path(NULL, transform);
660 
661  xmlLoadExtDtdDefaultValue = 1;
662  xmlSubstituteEntitiesDefault(1);
663 
664  xslt = xsltParseStylesheetFile((const xmlChar *)xform);
665  CRM_CHECK(xslt != NULL, goto cleanup);
666 
667  res = xsltApplyStylesheet(xslt, doc, NULL);
668  CRM_CHECK(res != NULL, goto cleanup);
669 
670  out = xmlDocGetRootElement(res);
671 
672  cleanup:
673  if (xslt) {
674  xsltFreeStylesheet(xslt);
675  }
676 
677  free(xform);
678 
679  return out;
680 }
681 #endif
682 
683 const char *
684 get_schema_name(int version)
685 {
686  if (version < 0 || version >= xml_schema_max) {
687  return "unknown";
688  }
689  return known_schemas[version].name;
690 }
691 
692 int
693 get_schema_version(const char *name)
694 {
695  int lpc = 0;
696 
697  if (name == NULL) {
698  name = "none";
699  }
700  for (; lpc < xml_schema_max; lpc++) {
701  if (safe_str_eq(name, known_schemas[lpc].name)) {
702  return lpc;
703  }
704  }
705  return -1;
706 }
707 
708 /* set which validation to use */
709 int
710 update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform,
711  gboolean to_logs)
712 {
713  xmlNode *xml = NULL;
714  char *value = NULL;
715  int max_stable_schemas = xml_latest_schema_index();
716  int lpc = 0, match = -1, rc = pcmk_ok;
717  int next = -1; /* -1 denotes "inactive" value */
718 
719  CRM_CHECK(best != NULL, return -EINVAL);
720  *best = 0;
721 
722  CRM_CHECK(xml_blob != NULL, return -EINVAL);
723  CRM_CHECK(*xml_blob != NULL, return -EINVAL);
724 
725  xml = *xml_blob;
727 
728  if (value != NULL) {
729  match = get_schema_version(value);
730 
731  lpc = match;
732  if (lpc >= 0 && transform == FALSE) {
733  *best = lpc++;
734 
735  } else if (lpc < 0) {
736  crm_debug("Unknown validation type");
737  lpc = 0;
738  }
739  }
740 
741  if (match >= max_stable_schemas) {
742  /* nothing to do */
743  free(value);
744  *best = match;
745  return pcmk_ok;
746  }
747 
748  while (lpc <= max_stable_schemas) {
749  crm_debug("Testing '%s' validation (%d of %d)",
750  known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>",
751  lpc, max_stable_schemas);
752 
753  if (validate_with(xml, lpc, to_logs) == FALSE) {
754  if (next != -1) {
755  crm_info("Configuration not valid for schema: %s",
756  known_schemas[lpc].name);
757  next = -1;
758  } else {
759  crm_trace("%s validation failed",
760  known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>");
761  }
762  if (*best) {
763  /* we've satisfied the validation, no need to check further */
764  break;
765  }
767 
768  } else {
769  if (next != -1) {
770  crm_debug("Configuration valid for schema: %s",
771  known_schemas[next].name);
772  next = -1;
773  }
774  rc = pcmk_ok;
775  }
776 
777  if (rc == pcmk_ok) {
778  *best = lpc;
779  }
780 
781  if (rc == pcmk_ok && transform) {
782  xmlNode *upgrade = NULL;
783  next = known_schemas[lpc].after_transform;
784 
785  if (next <= lpc) {
786  /* There is no next version, or next would regress */
787  crm_trace("Stopping at %s", known_schemas[lpc].name);
788  break;
789 
790  } else if (max > 0 && (lpc == max || next > max)) {
791  crm_trace("Upgrade limit reached at %s (lpc=%d, next=%d, max=%d)",
792  known_schemas[lpc].name, lpc, next, max);
793  break;
794 
795  } else if (known_schemas[lpc].transform == NULL) {
796  crm_debug("%s-style configuration is also valid for %s",
797  known_schemas[lpc].name, known_schemas[next].name);
798 
799  lpc = next;
800 
801  } else {
802  crm_debug("Upgrading %s-style configuration to %s with %s",
803  known_schemas[lpc].name, known_schemas[next].name,
804  known_schemas[lpc].transform ? known_schemas[lpc].transform : "no-op");
805 
806 #if HAVE_LIBXSLT
807  upgrade = apply_transformation(xml, known_schemas[lpc].transform);
808 #endif
809  if (upgrade == NULL) {
810  crm_err("Transformation %s failed",
811  known_schemas[lpc].transform);
813 
814  } else if (validate_with(upgrade, next, to_logs)) {
815  crm_info("Transformation %s successful",
816  known_schemas[lpc].transform);
817  lpc = next;
818  *best = next;
819  free_xml(xml);
820  xml = upgrade;
821  rc = pcmk_ok;
822 
823  } else {
824  crm_err("Transformation %s did not produce a valid configuration",
825  known_schemas[lpc].transform);
826  crm_log_xml_info(upgrade, "transform:bad");
827  free_xml(upgrade);
829  }
830  next = -1;
831  }
832  }
833 
834  if (transform == FALSE || rc != pcmk_ok) {
835  /* we need some progress! */
836  lpc++;
837  }
838  }
839 
840  if (*best > match && *best) {
841  crm_info("%s the configuration from %s to %s",
842  transform?"Transformed":"Upgraded",
843  value ? value : "<none>", known_schemas[*best].name);
844  crm_xml_add(xml, XML_ATTR_VALIDATION, known_schemas[*best].name);
845  }
846 
847  *xml_blob = xml;
848  free(value);
849  return rc;
850 }
851 
852 gboolean
853 cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
854 {
855  gboolean rc = TRUE;
856  const char *value = crm_element_value(*xml, XML_ATTR_VALIDATION);
857  char *const orig_value = strdup(value == NULL ? "(none)" : value);
858 
859  int version = get_schema_version(value);
860  int orig_version = version;
861  int min_version = xml_minimum_schema_index();
862 
863  if (version < min_version) {
864  xmlNode *converted = NULL;
865 
866  converted = copy_xml(*xml);
867  update_validation(&converted, &version, 0, TRUE, to_logs);
868 
869  value = crm_element_value(converted, XML_ATTR_VALIDATION);
870  if (version < min_version) {
871  if (version < orig_version || orig_version == -1) {
872  if (to_logs) {
873  crm_config_err("Your current configuration %s could not"
874  " validate with any schema in range [%s, %s],"
875  " cannot upgrade to %s.",
876  orig_value,
877  get_schema_name(orig_version),
879  get_schema_name(min_version));
880  } else {
881  fprintf(stderr, "Your current configuration %s could not"
882  " validate with any schema in range [%s, %s],"
883  " cannot upgrade to %s.\n",
884  orig_value,
885  get_schema_name(orig_version),
887  get_schema_name(min_version));
888  }
889  } else if (to_logs) {
890  crm_config_err("Your current configuration could only be upgraded to %s... "
891  "the minimum requirement is %s.", crm_str(value),
892  get_schema_name(min_version));
893  } else {
894  fprintf(stderr, "Your current configuration could only be upgraded to %s... "
895  "the minimum requirement is %s.\n",
896  crm_str(value), get_schema_name(min_version));
897  }
898 
899  free_xml(converted);
900  converted = NULL;
901  rc = FALSE;
902 
903  } else {
904  free_xml(*xml);
905  *xml = converted;
906 
907  if (version < xml_latest_schema_index()) {
908  crm_config_warn("Your configuration was internally updated to %s... "
909  "which is acceptable but not the most recent",
910  get_schema_name(version));
911 
912  } else if (to_logs) {
913  crm_info("Your configuration was internally updated to the latest version (%s)",
914  get_schema_name(version));
915  }
916  }
917 
918  } else if (version >= get_schema_version("none")) {
919  if (to_logs) {
920  crm_config_warn("Configuration validation is currently disabled."
921  " It is highly encouraged and prevents many common cluster issues.");
922 
923  } else {
924  fprintf(stderr, "Configuration validation is currently disabled."
925  " It is highly encouraged and prevents many common cluster issues.\n");
926  }
927  }
928 
929  if (best_version) {
930  *best_version = version;
931  }
932 
933  free(orig_value);
934  return rc;
935 }
#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:256
#define pcmk_ok
Definition: error.h:42
char * crm_element_value_copy(xmlNode *data, const char *name)
Definition: xml.c:3770
#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:2333
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:2633
int get_schema_version(const char *name)
Definition: schemas.c:693
#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:684
Wrappers for and extensions to libxml2.
const char * crm_element_value(xmlNode *data, const char *name)
Definition: xml.c:4987
#define XML_ATTR_VALIDATION
Definition: msg_xml.h:85
void free_xml(xmlNode *child)
Definition: xml.c:2587
gboolean validate_xml_verbose(xmlNode *xml_blob)
Definition: schemas.c:575
#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:257
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Definition: xml.c:2434
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
Definition: schemas.c:601
#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:62
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:853
int update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform, gboolean to_logs)
Try update CIB XML to the highest pacemaker's standard if feasible.
Definition: schemas.c:710
#define safe_str_eq(a, b)
Definition: util.h:63
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
Definition: xml.c:3044
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