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