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