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