This source file includes following definitions.
- xml_log
- xml_latest_schema_index
- xml_minimum_schema_index
- xml_latest_schema
- version_from_filename
- schema_filter
- schema_sort
- add_schema
- add_schema_by_version
- wrap_libxslt
- crm_schema_init
- relaxng_invalid_stderr
- validate_with_relaxng
- crm_schema_cleanup
- validate_with
- validate_with_silent
- dump_file
- validate_xml_verbose
- validate_xml
- cib_upgrade_err
- apply_transformation
- apply_upgrade
- get_schema_name
- get_schema_version
- update_validation
- cli_config_update
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdio.h>
13 #include <string.h>
14 #include <dirent.h>
15 #include <errno.h>
16 #include <sys/stat.h>
17 #include <stdarg.h>
18
19 #include <libxml/relaxng.h>
20
21 #if HAVE_LIBXSLT
22 # include <libxslt/xslt.h>
23 # include <libxslt/transform.h>
24 # include <libxslt/security.h>
25 # include <libxslt/xsltutils.h>
26 #endif
27
28 #include <crm/msg_xml.h>
29 #include <crm/common/xml.h>
30 #include <crm/common/xml_internal.h>
31
32 typedef struct {
33 unsigned char v[2];
34 } schema_version_t;
35
36 #define SCHEMA_ZERO { .v = { 0, 0 } }
37
38 #define schema_scanf(s, prefix, version, suffix) \
39 sscanf((s), prefix "%hhu.%hhu" suffix, &((version).v[0]), &((version).v[1]))
40
41 #define schema_strdup_printf(prefix, version, suffix) \
42 crm_strdup_printf(prefix "%u.%u" suffix, (version).v[0], (version).v[1])
43
44 typedef struct {
45 xmlRelaxNGPtr rng;
46 xmlRelaxNGValidCtxtPtr valid;
47 xmlRelaxNGParserCtxtPtr parser;
48 } relaxng_ctx_cache_t;
49
50 enum schema_validator_e {
51 schema_validator_none,
52 schema_validator_rng
53 };
54
55 struct schema_s {
56 char *name;
57 char *transform;
58 void *cache;
59 enum schema_validator_e validator;
60 int after_transform;
61 schema_version_t version;
62 char *transform_enter;
63 bool transform_onleave;
64 };
65
66 static struct schema_s *known_schemas = NULL;
67 static int xml_schema_max = 0;
68 static bool silent_logging = FALSE;
69
70 static void
71 xml_log(int priority, const char *fmt, ...)
72 G_GNUC_PRINTF(2, 3);
73
74 static void
75 xml_log(int priority, const char *fmt, ...)
76 {
77 va_list ap;
78
79 va_start(ap, fmt);
80 if (silent_logging == FALSE) {
81
82 PCMK__XML_LOG_BASE(priority, FALSE, 0, NULL, fmt, ap);
83 }
84 va_end(ap);
85 }
86
87 static int
88 xml_latest_schema_index(void)
89 {
90 return xml_schema_max - 3;
91 }
92
93 static int
94 xml_minimum_schema_index(void)
95 {
96 static int best = 0;
97 if (best == 0) {
98 int lpc = 0;
99
100 best = xml_latest_schema_index();
101 for (lpc = best; lpc > 0; lpc--) {
102 if (known_schemas[lpc].version.v[0]
103 < known_schemas[best].version.v[0]) {
104 return best;
105 } else {
106 best = lpc;
107 }
108 }
109 best = xml_latest_schema_index();
110 }
111 return best;
112 }
113
114 const char *
115 xml_latest_schema(void)
116 {
117 return get_schema_name(xml_latest_schema_index());
118 }
119
120 static inline bool
121 version_from_filename(const char *filename, schema_version_t *version)
122 {
123 int rc = schema_scanf(filename, "pacemaker-", *version, ".rng");
124
125 return (rc == 2);
126 }
127
128 static int
129 schema_filter(const struct dirent *a)
130 {
131 int rc = 0;
132 schema_version_t version = SCHEMA_ZERO;
133
134 if (strstr(a->d_name, "pacemaker-") != a->d_name) {
135
136
137 } else if (!pcmk__ends_with_ext(a->d_name, ".rng")) {
138
139
140 } else if (!version_from_filename(a->d_name, &version)) {
141
142
143 } else {
144
145 rc = 1;
146 }
147
148 return rc;
149 }
150
151 static int
152 schema_sort(const struct dirent **a, const struct dirent **b)
153 {
154 schema_version_t a_version = SCHEMA_ZERO;
155 schema_version_t b_version = SCHEMA_ZERO;
156
157 if (!version_from_filename(a[0]->d_name, &a_version)
158 || !version_from_filename(b[0]->d_name, &b_version)) {
159
160 return 0;
161 }
162
163 for (int i = 0; i < 2; ++i) {
164 if (a_version.v[i] < b_version.v[i]) {
165 return -1;
166 } else if (a_version.v[i] > b_version.v[i]) {
167 return 1;
168 }
169 }
170 return 0;
171 }
172
173
174
175
176
177
178
179
180 static void
181 add_schema(enum schema_validator_e validator, const schema_version_t *version,
182 const char *name, const char *transform,
183 const char *transform_enter, bool transform_onleave,
184 int after_transform)
185 {
186 int last = xml_schema_max;
187 bool have_version = FALSE;
188
189 xml_schema_max++;
190 known_schemas = pcmk__realloc(known_schemas,
191 xml_schema_max * sizeof(struct schema_s));
192 CRM_ASSERT(known_schemas != NULL);
193 memset(known_schemas+last, 0, sizeof(struct schema_s));
194 known_schemas[last].validator = validator;
195 known_schemas[last].after_transform = after_transform;
196
197 for (int i = 0; i < 2; ++i) {
198 known_schemas[last].version.v[i] = version->v[i];
199 if (version->v[i]) {
200 have_version = TRUE;
201 }
202 }
203 if (have_version) {
204 known_schemas[last].name = schema_strdup_printf("pacemaker-", *version, "");
205 } else {
206 CRM_ASSERT(name);
207 schema_scanf(name, "%*[^-]-", known_schemas[last].version, "");
208 known_schemas[last].name = strdup(name);
209 }
210
211 if (transform) {
212 known_schemas[last].transform = strdup(transform);
213 }
214 if (transform_enter) {
215 known_schemas[last].transform_enter = strdup(transform_enter);
216 }
217 known_schemas[last].transform_onleave = transform_onleave;
218 if (after_transform == 0) {
219 after_transform = xml_schema_max;
220 }
221 known_schemas[last].after_transform = after_transform;
222
223 if (known_schemas[last].after_transform < 0) {
224 crm_debug("Added supported schema %d: %s",
225 last, known_schemas[last].name);
226
227 } else if (known_schemas[last].transform) {
228 crm_debug("Added supported schema %d: %s (upgrades to %d with %s.xsl)",
229 last, known_schemas[last].name,
230 known_schemas[last].after_transform,
231 known_schemas[last].transform);
232
233 } else {
234 crm_debug("Added supported schema %d: %s (upgrades to %d)",
235 last, known_schemas[last].name,
236 known_schemas[last].after_transform);
237 }
238 }
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268 static int
269 add_schema_by_version(const schema_version_t *version, int next,
270 bool transform_expected)
271 {
272 bool transform_onleave = FALSE;
273 int rc = pcmk_rc_ok;
274 struct stat s;
275 char *xslt = NULL,
276 *transform_upgrade = NULL,
277 *transform_enter = NULL;
278
279
280 if (transform_expected) {
281
282 transform_upgrade = schema_strdup_printf("upgrade-", *version, );
283 xslt = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
284 transform_upgrade);
285 }
286
287 if (!transform_expected) {
288
289
290 } else if (stat(xslt, &s) == 0) {
291
292 transform_enter = schema_strdup_printf("upgrade-", *version, "-enter");
293 free(xslt);
294 xslt = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
295 transform_enter);
296 if (stat(xslt, &s) != 0) {
297
298 crm_debug("Upgrade-enter transform %s.xsl not found", xslt);
299 free(xslt);
300 free(transform_enter);
301 transform_enter = strdup("upgrade-enter");
302 xslt = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
303 transform_enter);
304 if (stat(xslt, &s) != 0) {
305 crm_debug("Upgrade-enter transform %s.xsl not found, either", xslt);
306 free(xslt);
307 xslt = NULL;
308 }
309 }
310
311 if (xslt != NULL) {
312
313 memcpy(strrchr(xslt, '-') + 1, "leave", sizeof("leave") - 1);
314 transform_onleave = (stat(xslt, &s) == 0);
315 free(xslt);
316 } else {
317 free(transform_enter);
318 transform_enter = NULL;
319 }
320
321 } else {
322 crm_err("Upgrade transform %s not found", xslt);
323 free(xslt);
324 free(transform_upgrade);
325 transform_upgrade = NULL;
326 next = -1;
327 rc = ENOENT;
328 }
329
330 add_schema(schema_validator_rng, version, NULL,
331 transform_upgrade, transform_enter, transform_onleave, next);
332
333 free(transform_upgrade);
334 free(transform_enter);
335
336 return rc;
337 }
338
339 static void
340 wrap_libxslt(bool finalize)
341 {
342 static xsltSecurityPrefsPtr secprefs;
343 int ret = 0;
344
345
346 if (!finalize) {
347 CRM_ASSERT(secprefs == NULL);
348 secprefs = xsltNewSecurityPrefs();
349 ret = xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_WRITE_FILE,
350 xsltSecurityForbid)
351 | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_CREATE_DIRECTORY,
352 xsltSecurityForbid)
353 | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_READ_NETWORK,
354 xsltSecurityForbid)
355 | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_WRITE_NETWORK,
356 xsltSecurityForbid);
357 if (ret != 0) {
358 return;
359 }
360 } else {
361 xsltFreeSecurityPrefs(secprefs);
362 secprefs = NULL;
363 }
364
365
366 if (finalize) {
367 xsltCleanupGlobals();
368 }
369 }
370
371
372
373
374
375
376
377
378 void
379 crm_schema_init(void)
380 {
381 int lpc, max;
382 char *base = pcmk__xml_artefact_root(pcmk__xml_artefact_ns_legacy_rng);
383 struct dirent **namelist = NULL;
384 const schema_version_t zero = SCHEMA_ZERO;
385
386 wrap_libxslt(false);
387
388 max = scandir(base, &namelist, schema_filter, schema_sort);
389 if (max < 0) {
390 crm_notice("scandir(%s) failed: %s (%d)", base, strerror(errno), errno);
391 free(base);
392
393 } else {
394 free(base);
395 for (lpc = 0; lpc < max; lpc++) {
396 bool transform_expected = FALSE;
397 int next = 0;
398 schema_version_t version = SCHEMA_ZERO;
399
400 if (!version_from_filename(namelist[lpc]->d_name, &version)) {
401
402 crm_err("Skipping schema '%s': could not parse version",
403 namelist[lpc]->d_name);
404 continue;
405 }
406 if ((lpc + 1) < max) {
407 schema_version_t next_version = SCHEMA_ZERO;
408
409 if (version_from_filename(namelist[lpc+1]->d_name, &next_version)
410 && (version.v[0] < next_version.v[0])) {
411 transform_expected = TRUE;
412 }
413
414 } else {
415 next = -1;
416 }
417 if (add_schema_by_version(&version, next, transform_expected)
418 == ENOENT) {
419 break;
420 }
421 }
422
423 for (lpc = 0; lpc < max; lpc++) {
424 free(namelist[lpc]);
425 }
426 free(namelist);
427 }
428
429 add_schema(schema_validator_rng, &zero, "pacemaker-next",
430 NULL, NULL, FALSE, -1);
431
432 add_schema(schema_validator_none, &zero, "none", NULL, NULL, FALSE, -1);
433 }
434
435 #if 0
436 static void
437 relaxng_invalid_stderr(void *userData, xmlErrorPtr error)
438 {
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457 crm_err("Structured error: line=%d, level=%d %s", error->line, error->level, error->message);
458 }
459 #endif
460
461 static gboolean
462 validate_with_relaxng(xmlDocPtr doc, gboolean to_logs, const char *relaxng_file,
463 relaxng_ctx_cache_t **cached_ctx)
464 {
465 int rc = 0;
466 gboolean valid = TRUE;
467 relaxng_ctx_cache_t *ctx = NULL;
468
469 CRM_CHECK(doc != NULL, return FALSE);
470 CRM_CHECK(relaxng_file != NULL, return FALSE);
471
472 if (cached_ctx && *cached_ctx) {
473 ctx = *cached_ctx;
474
475 } else {
476 crm_debug("Creating RNG parser context");
477 ctx = calloc(1, sizeof(relaxng_ctx_cache_t));
478
479 xmlLoadExtDtdDefaultValue = 1;
480 ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file);
481 CRM_CHECK(ctx->parser != NULL, goto cleanup);
482
483 if (to_logs) {
484 xmlRelaxNGSetParserErrors(ctx->parser,
485 (xmlRelaxNGValidityErrorFunc) xml_log,
486 (xmlRelaxNGValidityWarningFunc) xml_log,
487 GUINT_TO_POINTER(LOG_ERR));
488 } else {
489 xmlRelaxNGSetParserErrors(ctx->parser,
490 (xmlRelaxNGValidityErrorFunc) fprintf,
491 (xmlRelaxNGValidityWarningFunc) fprintf,
492 stderr);
493 }
494
495 ctx->rng = xmlRelaxNGParse(ctx->parser);
496 CRM_CHECK(ctx->rng != NULL,
497 crm_err("Could not find/parse %s", relaxng_file);
498 goto cleanup);
499
500 ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng);
501 CRM_CHECK(ctx->valid != NULL, goto cleanup);
502
503 if (to_logs) {
504 xmlRelaxNGSetValidErrors(ctx->valid,
505 (xmlRelaxNGValidityErrorFunc) xml_log,
506 (xmlRelaxNGValidityWarningFunc) xml_log,
507 GUINT_TO_POINTER(LOG_ERR));
508 } else {
509 xmlRelaxNGSetValidErrors(ctx->valid,
510 (xmlRelaxNGValidityErrorFunc) fprintf,
511 (xmlRelaxNGValidityWarningFunc) fprintf,
512 stderr);
513 }
514 }
515
516
517
518
519 xmlLineNumbersDefault(1);
520 rc = xmlRelaxNGValidateDoc(ctx->valid, doc);
521 if (rc > 0) {
522 valid = FALSE;
523
524 } else if (rc < 0) {
525 crm_err("Internal libxml error during validation");
526 }
527
528 cleanup:
529
530 if (cached_ctx) {
531 *cached_ctx = ctx;
532
533 } else {
534 if (ctx->parser != NULL) {
535 xmlRelaxNGFreeParserCtxt(ctx->parser);
536 }
537 if (ctx->valid != NULL) {
538 xmlRelaxNGFreeValidCtxt(ctx->valid);
539 }
540 if (ctx->rng != NULL) {
541 xmlRelaxNGFree(ctx->rng);
542 }
543 free(ctx);
544 }
545
546 return valid;
547 }
548
549
550
551
552
553 void
554 crm_schema_cleanup(void)
555 {
556 int lpc;
557 relaxng_ctx_cache_t *ctx = NULL;
558
559 for (lpc = 0; lpc < xml_schema_max; lpc++) {
560
561 switch (known_schemas[lpc].validator) {
562 case schema_validator_none:
563 break;
564 case schema_validator_rng:
565 ctx = (relaxng_ctx_cache_t *) known_schemas[lpc].cache;
566 if (ctx == NULL) {
567 break;
568 }
569 if (ctx->parser != NULL) {
570 xmlRelaxNGFreeParserCtxt(ctx->parser);
571 }
572 if (ctx->valid != NULL) {
573 xmlRelaxNGFreeValidCtxt(ctx->valid);
574 }
575 if (ctx->rng != NULL) {
576 xmlRelaxNGFree(ctx->rng);
577 }
578 free(ctx);
579 known_schemas[lpc].cache = NULL;
580 break;
581 }
582 free(known_schemas[lpc].name);
583 free(known_schemas[lpc].transform);
584 free(known_schemas[lpc].transform_enter);
585 }
586 free(known_schemas);
587 known_schemas = NULL;
588
589 wrap_libxslt(true);
590 }
591
592 static gboolean
593 validate_with(xmlNode *xml, int method, gboolean to_logs)
594 {
595 xmlDocPtr doc = NULL;
596 gboolean valid = FALSE;
597 char *file = NULL;
598
599 if (method < 0) {
600 return FALSE;
601 }
602
603 if (known_schemas[method].validator == schema_validator_none) {
604 return TRUE;
605 }
606
607 CRM_CHECK(xml != NULL, return FALSE);
608 doc = getDocPtr(xml);
609 file = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_rng,
610 known_schemas[method].name);
611
612 crm_trace("Validating with: %s (type=%d)",
613 crm_str(file), known_schemas[method].validator);
614 switch (known_schemas[method].validator) {
615 case schema_validator_rng:
616 valid =
617 validate_with_relaxng(doc, to_logs, file,
618 (relaxng_ctx_cache_t **) & (known_schemas[method].cache));
619 break;
620 default:
621 crm_err("Unknown validator type: %d",
622 known_schemas[method].validator);
623 break;
624 }
625
626 free(file);
627 return valid;
628 }
629
630 static bool
631 validate_with_silent(xmlNode *xml, int method)
632 {
633 bool rc, sl_backup = silent_logging;
634 silent_logging = TRUE;
635 rc = validate_with(xml, method, TRUE);
636 silent_logging = sl_backup;
637 return rc;
638 }
639
640 static void
641 dump_file(const char *filename)
642 {
643
644 FILE *fp = NULL;
645 int ch, line = 0;
646
647 CRM_CHECK(filename != NULL, return);
648
649 fp = fopen(filename, "r");
650 if (fp == NULL) {
651 crm_perror(LOG_ERR, "Could not open %s for reading", filename);
652 return;
653 }
654
655 fprintf(stderr, "%4d ", ++line);
656 do {
657 ch = getc(fp);
658 if (ch == EOF) {
659 putc('\n', stderr);
660 break;
661 } else if (ch == '\n') {
662 fprintf(stderr, "\n%4d ", ++line);
663 } else {
664 putc(ch, stderr);
665 }
666 } while (1);
667
668 fclose(fp);
669 }
670
671 gboolean
672 validate_xml_verbose(xmlNode *xml_blob)
673 {
674 int fd = 0;
675 xmlDoc *doc = NULL;
676 xmlNode *xml = NULL;
677 gboolean rc = FALSE;
678 char *filename = NULL;
679
680 filename = crm_strdup_printf("%s/cib-invalid.XXXXXX", pcmk__get_tmpdir());
681
682 umask(S_IWGRP | S_IWOTH | S_IROTH);
683 fd = mkstemp(filename);
684 write_xml_fd(xml_blob, filename, fd, FALSE);
685
686 dump_file(filename);
687
688 doc = xmlParseFile(filename);
689 xml = xmlDocGetRootElement(doc);
690 rc = validate_xml(xml, NULL, FALSE);
691 free_xml(xml);
692
693 unlink(filename);
694 free(filename);
695
696 return rc;
697 }
698
699 gboolean
700 validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
701 {
702 int version = 0;
703
704 if (validation == NULL) {
705 validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION);
706 }
707
708 if (validation == NULL) {
709 int lpc = 0;
710 bool valid = FALSE;
711
712 for (lpc = 0; lpc < xml_schema_max; lpc++) {
713 if (validate_with(xml_blob, lpc, FALSE)) {
714 valid = TRUE;
715 crm_xml_add(xml_blob, XML_ATTR_VALIDATION,
716 known_schemas[lpc].name);
717 crm_info("XML validated against %s", known_schemas[lpc].name);
718 if(known_schemas[lpc].after_transform == 0) {
719 break;
720 }
721 }
722 }
723
724 return valid;
725 }
726
727 version = get_schema_version(validation);
728 if (strcmp(validation, "none") == 0) {
729 return TRUE;
730 } else if (version < xml_schema_max) {
731 return validate_with(xml_blob, version, to_logs);
732 }
733
734 crm_err("Unknown validator: %s", validation);
735 return FALSE;
736 }
737
738 #if HAVE_LIBXSLT
739
740 static void
741 cib_upgrade_err(void *ctx, const char *fmt, ...)
742 G_GNUC_PRINTF(2, 3);
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767 static void
768 cib_upgrade_err(void *ctx, const char *fmt, ...)
769 {
770 va_list ap, aq;
771 char *arg_cur;
772
773 bool found = FALSE;
774 const char *fmt_iter = fmt;
775 uint8_t msg_log_level = LOG_WARNING;
776 const unsigned * log_level = (const unsigned *) ctx;
777 enum {
778 escan_seennothing,
779 escan_seenpercent,
780 } scan_state = escan_seennothing;
781
782 va_start(ap, fmt);
783 va_copy(aq, ap);
784
785 while (!found && *fmt_iter != '\0') {
786
787 switch (*fmt_iter++) {
788 case '%':
789 if (scan_state == escan_seennothing) {
790 scan_state = escan_seenpercent;
791 } else if (scan_state == escan_seenpercent) {
792 scan_state = escan_seennothing;
793 }
794 break;
795 case 's':
796 if (scan_state == escan_seenpercent) {
797 scan_state = escan_seennothing;
798 arg_cur = va_arg(aq, char *);
799 if (arg_cur != NULL) {
800 switch (arg_cur[0]) {
801 case 'W':
802 if (!strncmp(arg_cur, "WARNING: ",
803 sizeof("WARNING: ") - 1)) {
804 msg_log_level = LOG_WARNING;
805 }
806 if (ctx == NULL) {
807 memmove(arg_cur, arg_cur + sizeof("WARNING: ") - 1,
808 strlen(arg_cur + sizeof("WARNING: ") - 1) + 1);
809 }
810 found = TRUE;
811 break;
812 case 'I':
813 if (!strncmp(arg_cur, "INFO: ",
814 sizeof("INFO: ") - 1)) {
815 msg_log_level = LOG_INFO;
816 }
817 if (ctx == NULL) {
818 memmove(arg_cur, arg_cur + sizeof("INFO: ") - 1,
819 strlen(arg_cur + sizeof("INFO: ") - 1) + 1);
820 }
821 found = TRUE;
822 break;
823 case 'D':
824 if (!strncmp(arg_cur, "DEBUG: ",
825 sizeof("DEBUG: ") - 1)) {
826 msg_log_level = LOG_DEBUG;
827 }
828 if (ctx == NULL) {
829 memmove(arg_cur, arg_cur + sizeof("DEBUG: ") - 1,
830 strlen(arg_cur + sizeof("DEBUG: ") - 1) + 1);
831 }
832 found = TRUE;
833 break;
834 }
835 }
836 }
837 break;
838 case '#': case '-': case ' ': case '+': case '\'': case 'I': case '.':
839 case '0': case '1': case '2': case '3': case '4':
840 case '5': case '6': case '7': case '8': case '9':
841 case '*':
842 break;
843 case 'l':
844 case 'z':
845 case 't':
846 case 'j':
847 case 'd': case 'i':
848 case 'o':
849 case 'u':
850 case 'x': case 'X':
851 case 'e': case 'E':
852 case 'f': case 'F':
853 case 'g': case 'G':
854 case 'a': case 'A':
855 case 'c':
856 case 'p':
857 if (scan_state == escan_seenpercent) {
858 (void) va_arg(aq, void *);
859 scan_state = escan_seennothing;
860 }
861 break;
862 default:
863 scan_state = escan_seennothing;
864 break;
865 }
866 }
867
868 if (log_level != NULL) {
869
870
871 if (*log_level + 4 >= msg_log_level) {
872 vfprintf(stderr, fmt, ap);
873 }
874 } else {
875 PCMK__XML_LOG_BASE(msg_log_level, TRUE, 0, "CIB upgrade: ", fmt, ap);
876 }
877
878 va_end(aq);
879 va_end(ap);
880 }
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897 #ifndef PCMK_SCHEMAS_EMERGENCY_XSLT
898 #define PCMK_SCHEMAS_EMERGENCY_XSLT 1
899 #endif
900
901 static xmlNode *
902 apply_transformation(xmlNode *xml, const char *transform, gboolean to_logs)
903 {
904 char *xform = NULL;
905 xmlNode *out = NULL;
906 xmlDocPtr res = NULL;
907 xmlDocPtr doc = NULL;
908 xsltStylesheet *xslt = NULL;
909 #if PCMK_SCHEMAS_EMERGENCY_XSLT != 0
910 xmlChar *emergency_result;
911 int emergency_txt_len;
912 int emergency_res;
913 #endif
914
915 CRM_CHECK(xml != NULL, return FALSE);
916 doc = getDocPtr(xml);
917 xform = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
918 transform);
919
920 xmlLoadExtDtdDefaultValue = 1;
921 xmlSubstituteEntitiesDefault(1);
922
923
924 if (to_logs) {
925 xsltSetGenericErrorFunc(NULL, cib_upgrade_err);
926 } else {
927 xsltSetGenericErrorFunc(&crm_log_level, cib_upgrade_err);
928 }
929
930 xslt = xsltParseStylesheetFile((pcmkXmlStr) xform);
931 CRM_CHECK(xslt != NULL, goto cleanup);
932
933 res = xsltApplyStylesheet(xslt, doc, NULL);
934 CRM_CHECK(res != NULL, goto cleanup);
935
936 xsltSetGenericErrorFunc(NULL, NULL);
937
938
939 #if PCMK_SCHEMAS_EMERGENCY_XSLT != 0
940 emergency_res = xsltSaveResultToString(&emergency_result,
941 &emergency_txt_len, res, xslt);
942 xmlFreeDoc(res);
943 CRM_CHECK(emergency_res == 0, goto cleanup);
944 out = string2xml((const char *) emergency_result);
945 free(emergency_result);
946 #else
947 out = xmlDocGetRootElement(res);
948 #endif
949
950 cleanup:
951 if (xslt) {
952 xsltFreeStylesheet(xslt);
953 }
954
955 free(xform);
956
957 return out;
958 }
959
960
961
962
963
964
965
966 static xmlNode *
967 apply_upgrade(xmlNode *xml, const struct schema_s *schema, gboolean to_logs)
968 {
969 bool transform_onleave = schema->transform_onleave;
970 char *transform_leave;
971 xmlNode *upgrade = NULL,
972 *final = NULL;
973
974 if (schema->transform_enter) {
975 crm_debug("Upgrading %s-style configuration, pre-upgrade phase with %s.xsl",
976 schema->name, schema->transform_enter);
977 upgrade = apply_transformation(xml, schema->transform_enter, to_logs);
978 if (upgrade == NULL) {
979 crm_warn("Upgrade-enter transformation %s.xsl failed",
980 schema->transform_enter);
981 transform_onleave = FALSE;
982 }
983 }
984 if (upgrade == NULL) {
985 upgrade = xml;
986 }
987
988 crm_debug("Upgrading %s-style configuration, main phase with %s.xsl",
989 schema->name, schema->transform);
990 final = apply_transformation(upgrade, schema->transform, to_logs);
991 if (upgrade != xml) {
992 free_xml(upgrade);
993 upgrade = NULL;
994 }
995
996 if (final != NULL && transform_onleave) {
997 upgrade = final;
998
999 CRM_ASSERT(schema->transform_enter != NULL);
1000 transform_leave = strdup(schema->transform_enter);
1001
1002 memcpy(strrchr(transform_leave, '-') + 1, "leave", sizeof("leave") - 1);
1003 crm_debug("Upgrading %s-style configuration, post-upgrade phase with %s.xsl",
1004 schema->name, transform_leave);
1005 final = apply_transformation(upgrade, transform_leave, to_logs);
1006 if (final == NULL) {
1007 crm_warn("Upgrade-leave transformation %s.xsl failed", transform_leave);
1008 final = upgrade;
1009 } else {
1010 free_xml(upgrade);
1011 }
1012 free(transform_leave);
1013 }
1014
1015 return final;
1016 }
1017
1018 #endif
1019
1020 const char *
1021 get_schema_name(int version)
1022 {
1023 if (version < 0 || version >= xml_schema_max) {
1024 return "unknown";
1025 }
1026 return known_schemas[version].name;
1027 }
1028
1029 int
1030 get_schema_version(const char *name)
1031 {
1032 int lpc = 0;
1033
1034 if (name == NULL) {
1035 name = "none";
1036 }
1037 for (; lpc < xml_schema_max; lpc++) {
1038 if (pcmk__str_eq(name, known_schemas[lpc].name, pcmk__str_casei)) {
1039 return lpc;
1040 }
1041 }
1042 return -1;
1043 }
1044
1045
1046 int
1047 update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform,
1048 gboolean to_logs)
1049 {
1050 xmlNode *xml = NULL;
1051 char *value = NULL;
1052 int max_stable_schemas = xml_latest_schema_index();
1053 int lpc = 0, match = -1, rc = pcmk_ok;
1054 int next = -1;
1055
1056 CRM_CHECK(best != NULL, return -EINVAL);
1057 *best = 0;
1058
1059 CRM_CHECK(xml_blob != NULL, return -EINVAL);
1060 CRM_CHECK(*xml_blob != NULL, return -EINVAL);
1061
1062 xml = *xml_blob;
1063 value = crm_element_value_copy(xml, XML_ATTR_VALIDATION);
1064
1065 if (value != NULL) {
1066 match = get_schema_version(value);
1067
1068 lpc = match;
1069 if (lpc >= 0 && transform == FALSE) {
1070 *best = lpc++;
1071
1072 } else if (lpc < 0) {
1073 crm_debug("Unknown validation schema");
1074 lpc = 0;
1075 }
1076 }
1077
1078 if (match >= max_stable_schemas) {
1079
1080 free(value);
1081 *best = match;
1082 return pcmk_ok;
1083 }
1084
1085 while (lpc <= max_stable_schemas) {
1086 crm_debug("Testing '%s' validation (%d of %d)",
1087 known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>",
1088 lpc, max_stable_schemas);
1089
1090 if (validate_with(xml, lpc, to_logs) == FALSE) {
1091 if (next != -1) {
1092 crm_info("Configuration not valid for schema: %s",
1093 known_schemas[lpc].name);
1094 next = -1;
1095 } else {
1096 crm_trace("%s validation failed",
1097 known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>");
1098 }
1099 if (*best) {
1100
1101 break;
1102 }
1103 rc = -pcmk_err_schema_validation;
1104
1105 } else {
1106 if (next != -1) {
1107 crm_debug("Configuration valid for schema: %s",
1108 known_schemas[next].name);
1109 next = -1;
1110 }
1111 rc = pcmk_ok;
1112 }
1113
1114 if (rc == pcmk_ok) {
1115 *best = lpc;
1116 }
1117
1118 if (rc == pcmk_ok && transform) {
1119 xmlNode *upgrade = NULL;
1120 next = known_schemas[lpc].after_transform;
1121
1122 if (next <= lpc) {
1123
1124 crm_trace("Stopping at %s", known_schemas[lpc].name);
1125 break;
1126
1127 } else if (max > 0 && (lpc == max || next > max)) {
1128 crm_trace("Upgrade limit reached at %s (lpc=%d, next=%d, max=%d)",
1129 known_schemas[lpc].name, lpc, next, max);
1130 break;
1131
1132 } else if (known_schemas[lpc].transform == NULL
1133
1134
1135
1136
1137
1138 || validate_with_silent(xml, next)) {
1139 crm_debug("%s-style configuration is also valid for %s",
1140 known_schemas[lpc].name, known_schemas[next].name);
1141
1142 lpc = next;
1143
1144 } else {
1145 crm_debug("Upgrading %s-style configuration to %s with %s.xsl",
1146 known_schemas[lpc].name, known_schemas[next].name,
1147 known_schemas[lpc].transform);
1148
1149 #if HAVE_LIBXSLT
1150 upgrade = apply_upgrade(xml, &known_schemas[lpc], to_logs);
1151 #endif
1152 if (upgrade == NULL) {
1153 crm_err("Transformation %s.xsl failed",
1154 known_schemas[lpc].transform);
1155 rc = -pcmk_err_transform_failed;
1156
1157 } else if (validate_with(upgrade, next, to_logs)) {
1158 crm_info("Transformation %s.xsl successful",
1159 known_schemas[lpc].transform);
1160 lpc = next;
1161 *best = next;
1162 free_xml(xml);
1163 xml = upgrade;
1164 rc = pcmk_ok;
1165
1166 } else {
1167 crm_err("Transformation %s.xsl did not produce a valid configuration",
1168 known_schemas[lpc].transform);
1169 crm_log_xml_info(upgrade, "transform:bad");
1170 free_xml(upgrade);
1171 rc = -pcmk_err_schema_validation;
1172 }
1173 next = -1;
1174 }
1175 }
1176
1177 if (transform == FALSE || rc != pcmk_ok) {
1178
1179 lpc++;
1180 }
1181 }
1182
1183 if (*best > match && *best) {
1184 crm_info("%s the configuration from %s to %s",
1185 transform?"Transformed":"Upgraded",
1186 value ? value : "<none>", known_schemas[*best].name);
1187 crm_xml_add(xml, XML_ATTR_VALIDATION, known_schemas[*best].name);
1188 }
1189
1190 *xml_blob = xml;
1191 free(value);
1192 return rc;
1193 }
1194
1195 gboolean
1196 cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
1197 {
1198 gboolean rc = TRUE;
1199 const char *value = crm_element_value(*xml, XML_ATTR_VALIDATION);
1200 char *const orig_value = strdup(value == NULL ? "(none)" : value);
1201
1202 int version = get_schema_version(value);
1203 int orig_version = version;
1204 int min_version = xml_minimum_schema_index();
1205
1206 if (version < min_version) {
1207
1208 xmlNode *converted = NULL;
1209
1210 converted = copy_xml(*xml);
1211 update_validation(&converted, &version, 0, TRUE, to_logs);
1212
1213 value = crm_element_value(converted, XML_ATTR_VALIDATION);
1214 if (version < min_version) {
1215
1216
1217 if (version < orig_version || orig_version == -1) {
1218
1219 if (to_logs) {
1220 pcmk__config_err("Cannot upgrade configuration (claiming "
1221 "schema %s) to at least %s because it "
1222 "does not validate with any schema from "
1223 "%s to %s",
1224 orig_value,
1225 get_schema_name(min_version),
1226 get_schema_name(orig_version),
1227 xml_latest_schema());
1228 } else {
1229 fprintf(stderr, "Cannot upgrade configuration (claiming "
1230 "schema %s) to at least %s because it "
1231 "does not validate with any schema from "
1232 "%s to %s\n",
1233 orig_value,
1234 get_schema_name(min_version),
1235 get_schema_name(orig_version),
1236 xml_latest_schema());
1237 }
1238 } else {
1239
1240 if (to_logs) {
1241 pcmk__config_err("Cannot upgrade configuration (claiming "
1242 "schema %s) to at least %s because it "
1243 "would not upgrade past %s",
1244 orig_value,
1245 get_schema_name(min_version),
1246 crm_str(value));
1247 } else {
1248 fprintf(stderr, "Cannot upgrade configuration (claiming "
1249 "schema %s) to at least %s because it "
1250 "would not upgrade past %s\n",
1251 orig_value,
1252 get_schema_name(min_version),
1253 crm_str(value));
1254 }
1255 }
1256
1257 free_xml(converted);
1258 converted = NULL;
1259 rc = FALSE;
1260
1261 } else {
1262
1263 free_xml(*xml);
1264 *xml = converted;
1265
1266 if (version < xml_latest_schema_index()) {
1267 if (to_logs) {
1268 pcmk__config_warn("Configuration with schema %s was "
1269 "internally upgraded to acceptable (but "
1270 "not most recent) %s",
1271 orig_value, get_schema_name(version));
1272 }
1273 } else {
1274 if (to_logs) {
1275 crm_info("Configuration with schema %s was internally "
1276 "upgraded to latest version %s",
1277 orig_value, get_schema_name(version));
1278 }
1279 }
1280 }
1281
1282 } else if (version >= get_schema_version("none")) {
1283
1284 if (to_logs) {
1285 pcmk__config_warn("Schema validation of configuration is disabled "
1286 "(enabling is encouraged and prevents common "
1287 "misconfigurations)");
1288
1289 } else {
1290 fprintf(stderr, "Schema validation of configuration is disabled "
1291 "(enabling is encouraged and prevents common "
1292 "misconfigurations)\n");
1293 }
1294 }
1295
1296 if (best_version) {
1297 *best_version = version;
1298 }
1299
1300 free(orig_value);
1301 return rc;
1302 }