This source file includes following definitions.
- xml_log
- xml_latest_schema_index
- xml_minimum_schema_index
- xml_latest_schema
- get_schema_root
- get_schema_path
- version_from_filename
- schema_filter
- schema_sort
- add_schema
- crm_schema_init
- validate_with_dtd
- relaxng_invalid_stderr
- validate_with_relaxng
- crm_schema_cleanup
- validate_with
- dump_file
- validate_xml_verbose
- validate_xml
- cib_upgrade_err
- apply_transformation
- get_schema_name
- get_schema_version
- update_validation
- cli_config_update
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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>
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
60 enum schema_validator_e {
61 schema_validator_none,
62 schema_validator_dtd,
63 schema_validator_rng
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
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 *
122 xml_latest_schema(void)
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
168
169 } else if (!crm_ends_with_ext(a->d_name, ".rng")) {
170
171
172 } else if (!version_from_filename(a->d_name, &version)) {
173
174
175 } else if (strcmp(a->d_name, "pacemaker-1.1.rng") == 0) {
176
177
178
179 } else {
180
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
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;
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
268
269
270
271
272 void
273 crm_schema_init(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
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
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
399
400
401
402
403
404
405
406
407
408
409
410
411
412
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
475
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
507
508
509
510
511 void
512 crm_schema_cleanup(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) {
520 case schema_validator_none:
521 case schema_validator_dtd:
522 break;
523 case schema_validator_rng:
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) {
572 case schema_validator_dtd:
573 valid = validate_with_dtd(doc, to_logs, file);
574 break;
575 case schema_validator_rng:
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
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
670 for (lpc = 0; lpc < xml_schema_max; lpc++) {
671 if (validate_with(xml_blob, lpc, FALSE)) {
672 valid = TRUE;
673 crm_xml_add(xml_blob, XML_ATTR_VALIDATION,
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
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);
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
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;
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;
799 value = crm_element_value_copy(xml, XML_ATTR_VALIDATION);
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
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
837 break;
838 }
839 rc = -pcmk_err_schema_validation;
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
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);
885 rc = -pcmk_err_transform_failed;
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);
901 rc = -pcmk_err_schema_validation;
902 }
903 next = -1;
904 }
905 }
906
907 if (transform == FALSE || rc != pcmk_ok) {
908
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),
951 xml_latest_schema(),
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),
959 xml_latest_schema(),
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 }