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
- validate_with_relaxng
- crm_schema_cleanup
- validate_with
- validate_with_silent
- dump_file
- validate_xml_verbose
- validate_xml
- pcmk__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 static gboolean
436 validate_with_relaxng(xmlDocPtr doc, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context, const char *relaxng_file,
437 relaxng_ctx_cache_t **cached_ctx)
438 {
439 int rc = 0;
440 gboolean valid = TRUE;
441 relaxng_ctx_cache_t *ctx = NULL;
442
443 CRM_CHECK(doc != NULL, return FALSE);
444 CRM_CHECK(relaxng_file != NULL, return FALSE);
445
446 if (cached_ctx && *cached_ctx) {
447 ctx = *cached_ctx;
448
449 } else {
450 crm_debug("Creating RNG parser context");
451 ctx = calloc(1, sizeof(relaxng_ctx_cache_t));
452
453 ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file);
454 CRM_CHECK(ctx->parser != NULL, goto cleanup);
455
456 if (error_handler) {
457 xmlRelaxNGSetParserErrors(ctx->parser,
458 (xmlRelaxNGValidityErrorFunc) error_handler,
459 (xmlRelaxNGValidityWarningFunc) error_handler,
460 error_handler_context);
461 } else {
462 xmlRelaxNGSetParserErrors(ctx->parser,
463 (xmlRelaxNGValidityErrorFunc) fprintf,
464 (xmlRelaxNGValidityWarningFunc) fprintf,
465 stderr);
466 }
467
468 ctx->rng = xmlRelaxNGParse(ctx->parser);
469 CRM_CHECK(ctx->rng != NULL,
470 crm_err("Could not find/parse %s", relaxng_file);
471 goto cleanup);
472
473 ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng);
474 CRM_CHECK(ctx->valid != NULL, goto cleanup);
475
476 if (error_handler) {
477 xmlRelaxNGSetValidErrors(ctx->valid,
478 (xmlRelaxNGValidityErrorFunc) error_handler,
479 (xmlRelaxNGValidityWarningFunc) error_handler,
480 error_handler_context);
481 } else {
482 xmlRelaxNGSetValidErrors(ctx->valid,
483 (xmlRelaxNGValidityErrorFunc) fprintf,
484 (xmlRelaxNGValidityWarningFunc) fprintf,
485 stderr);
486 }
487 }
488
489 rc = xmlRelaxNGValidateDoc(ctx->valid, doc);
490 if (rc > 0) {
491 valid = FALSE;
492
493 } else if (rc < 0) {
494 crm_err("Internal libxml error during validation");
495 }
496
497 cleanup:
498
499 if (cached_ctx) {
500 *cached_ctx = ctx;
501
502 } else {
503 if (ctx->parser != NULL) {
504 xmlRelaxNGFreeParserCtxt(ctx->parser);
505 }
506 if (ctx->valid != NULL) {
507 xmlRelaxNGFreeValidCtxt(ctx->valid);
508 }
509 if (ctx->rng != NULL) {
510 xmlRelaxNGFree(ctx->rng);
511 }
512 free(ctx);
513 }
514
515 return valid;
516 }
517
518
519
520
521
522 void
523 crm_schema_cleanup(void)
524 {
525 int lpc;
526 relaxng_ctx_cache_t *ctx = NULL;
527
528 for (lpc = 0; lpc < xml_schema_max; lpc++) {
529
530 switch (known_schemas[lpc].validator) {
531 case schema_validator_none:
532 break;
533 case schema_validator_rng:
534 ctx = (relaxng_ctx_cache_t *) known_schemas[lpc].cache;
535 if (ctx == NULL) {
536 break;
537 }
538 if (ctx->parser != NULL) {
539 xmlRelaxNGFreeParserCtxt(ctx->parser);
540 }
541 if (ctx->valid != NULL) {
542 xmlRelaxNGFreeValidCtxt(ctx->valid);
543 }
544 if (ctx->rng != NULL) {
545 xmlRelaxNGFree(ctx->rng);
546 }
547 free(ctx);
548 known_schemas[lpc].cache = NULL;
549 break;
550 }
551 free(known_schemas[lpc].name);
552 free(known_schemas[lpc].transform);
553 free(known_schemas[lpc].transform_enter);
554 }
555 free(known_schemas);
556 known_schemas = NULL;
557
558 wrap_libxslt(true);
559 }
560
561 static gboolean
562 validate_with(xmlNode *xml, int method, xmlRelaxNGValidityErrorFunc error_handler, void* error_handler_context)
563 {
564 gboolean valid = FALSE;
565 char *file = NULL;
566 struct schema_s *schema = NULL;
567 relaxng_ctx_cache_t **cache = NULL;
568
569 if (method < 0) {
570 return FALSE;
571 }
572
573 schema = &(known_schemas[method]);
574 if (schema->validator == schema_validator_none) {
575 return TRUE;
576 }
577
578 if (pcmk__str_eq(schema->name, "pacemaker-next", pcmk__str_none)) {
579 crm_warn("The pacemaker-next schema is deprecated and will be removed "
580 "in a future release.");
581 }
582
583 file = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_rng,
584 schema->name);
585
586 crm_trace("Validating with %s (type=%d)",
587 pcmk__s(file, "missing schema"), schema->validator);
588 switch (schema->validator) {
589 case schema_validator_rng:
590 cache = (relaxng_ctx_cache_t **) &(schema->cache);
591 valid = validate_with_relaxng(xml->doc, error_handler, error_handler_context, file, cache);
592 break;
593 default:
594 crm_err("Unknown validator type: %d",
595 known_schemas[method].validator);
596 break;
597 }
598
599 free(file);
600 return valid;
601 }
602
603 static bool
604 validate_with_silent(xmlNode *xml, int method)
605 {
606 bool rc, sl_backup = silent_logging;
607 silent_logging = TRUE;
608 rc = validate_with(xml, method, (xmlRelaxNGValidityErrorFunc) xml_log, GUINT_TO_POINTER(LOG_ERR));
609 silent_logging = sl_backup;
610 return rc;
611 }
612
613 static void
614 dump_file(const char *filename)
615 {
616
617 FILE *fp = NULL;
618 int ch, line = 0;
619
620 CRM_CHECK(filename != NULL, return);
621
622 fp = fopen(filename, "r");
623 if (fp == NULL) {
624 crm_perror(LOG_ERR, "Could not open %s for reading", filename);
625 return;
626 }
627
628 fprintf(stderr, "%4d ", ++line);
629 do {
630 ch = getc(fp);
631 if (ch == EOF) {
632 putc('\n', stderr);
633 break;
634 } else if (ch == '\n') {
635 fprintf(stderr, "\n%4d ", ++line);
636 } else {
637 putc(ch, stderr);
638 }
639 } while (1);
640
641 fclose(fp);
642 }
643
644 gboolean
645 validate_xml_verbose(const xmlNode *xml_blob)
646 {
647 int fd = 0;
648 xmlDoc *doc = NULL;
649 xmlNode *xml = NULL;
650 gboolean rc = FALSE;
651 char *filename = NULL;
652
653 filename = crm_strdup_printf("%s/cib-invalid.XXXXXX", pcmk__get_tmpdir());
654
655 umask(S_IWGRP | S_IWOTH | S_IROTH);
656 fd = mkstemp(filename);
657 write_xml_fd(xml_blob, filename, fd, FALSE);
658
659 dump_file(filename);
660
661 doc = xmlReadFile(filename, NULL, 0);
662 xml = xmlDocGetRootElement(doc);
663 rc = validate_xml(xml, NULL, FALSE);
664 free_xml(xml);
665
666 unlink(filename);
667 free(filename);
668
669 return rc;
670 }
671
672 gboolean
673 validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
674 {
675 return pcmk__validate_xml(xml_blob, validation, to_logs ? (xmlRelaxNGValidityErrorFunc) xml_log : NULL, GUINT_TO_POINTER(LOG_ERR));
676 }
677
678 gboolean
679 pcmk__validate_xml(xmlNode *xml_blob, const char *validation, xmlRelaxNGValidityErrorFunc error_handler, void* error_handler_context)
680 {
681 int version = 0;
682
683 CRM_CHECK((xml_blob != NULL) && (xml_blob->doc != NULL), return FALSE);
684
685 if (validation == NULL) {
686 validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION);
687 }
688
689 if (validation == NULL) {
690 int lpc = 0;
691 bool valid = FALSE;
692
693 for (lpc = 0; lpc < xml_schema_max; lpc++) {
694 if (validate_with(xml_blob, lpc, NULL, NULL)) {
695 valid = TRUE;
696 crm_xml_add(xml_blob, XML_ATTR_VALIDATION,
697 known_schemas[lpc].name);
698 crm_info("XML validated against %s", known_schemas[lpc].name);
699 if(known_schemas[lpc].after_transform == 0) {
700 break;
701 }
702 }
703 }
704
705 return valid;
706 }
707
708 version = get_schema_version(validation);
709 if (strcmp(validation, PCMK__VALUE_NONE) == 0) {
710 return TRUE;
711 } else if (version < xml_schema_max) {
712 return validate_with(xml_blob, version, error_handler, error_handler_context);
713 }
714
715 crm_err("Unknown validator: %s", validation);
716 return FALSE;
717 }
718
719 static void
720 cib_upgrade_err(void *ctx, const char *fmt, ...)
721 G_GNUC_PRINTF(2, 3);
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746 static void
747 cib_upgrade_err(void *ctx, const char *fmt, ...)
748 {
749 va_list ap, aq;
750 char *arg_cur;
751
752 bool found = FALSE;
753 const char *fmt_iter = fmt;
754 uint8_t msg_log_level = LOG_WARNING;
755 const unsigned * log_level = (const unsigned *) ctx;
756 enum {
757 escan_seennothing,
758 escan_seenpercent,
759 } scan_state = escan_seennothing;
760
761 va_start(ap, fmt);
762 va_copy(aq, ap);
763
764 while (!found && *fmt_iter != '\0') {
765
766 switch (*fmt_iter++) {
767 case '%':
768 if (scan_state == escan_seennothing) {
769 scan_state = escan_seenpercent;
770 } else if (scan_state == escan_seenpercent) {
771 scan_state = escan_seennothing;
772 }
773 break;
774 case 's':
775 if (scan_state == escan_seenpercent) {
776 scan_state = escan_seennothing;
777 arg_cur = va_arg(aq, char *);
778 if (arg_cur != NULL) {
779 switch (arg_cur[0]) {
780 case 'W':
781 if (!strncmp(arg_cur, "WARNING: ",
782 sizeof("WARNING: ") - 1)) {
783 msg_log_level = LOG_WARNING;
784 }
785 if (ctx == NULL) {
786 memmove(arg_cur, arg_cur + sizeof("WARNING: ") - 1,
787 strlen(arg_cur + sizeof("WARNING: ") - 1) + 1);
788 }
789 found = TRUE;
790 break;
791 case 'I':
792 if (!strncmp(arg_cur, "INFO: ",
793 sizeof("INFO: ") - 1)) {
794 msg_log_level = LOG_INFO;
795 }
796 if (ctx == NULL) {
797 memmove(arg_cur, arg_cur + sizeof("INFO: ") - 1,
798 strlen(arg_cur + sizeof("INFO: ") - 1) + 1);
799 }
800 found = TRUE;
801 break;
802 case 'D':
803 if (!strncmp(arg_cur, "DEBUG: ",
804 sizeof("DEBUG: ") - 1)) {
805 msg_log_level = LOG_DEBUG;
806 }
807 if (ctx == NULL) {
808 memmove(arg_cur, arg_cur + sizeof("DEBUG: ") - 1,
809 strlen(arg_cur + sizeof("DEBUG: ") - 1) + 1);
810 }
811 found = TRUE;
812 break;
813 }
814 }
815 }
816 break;
817 case '#': case '-': case ' ': case '+': case '\'': case 'I': case '.':
818 case '0': case '1': case '2': case '3': case '4':
819 case '5': case '6': case '7': case '8': case '9':
820 case '*':
821 break;
822 case 'l':
823 case 'z':
824 case 't':
825 case 'j':
826 case 'd': case 'i':
827 case 'o':
828 case 'u':
829 case 'x': case 'X':
830 case 'e': case 'E':
831 case 'f': case 'F':
832 case 'g': case 'G':
833 case 'a': case 'A':
834 case 'c':
835 case 'p':
836 if (scan_state == escan_seenpercent) {
837 (void) va_arg(aq, void *);
838 scan_state = escan_seennothing;
839 }
840 break;
841 default:
842 scan_state = escan_seennothing;
843 break;
844 }
845 }
846
847 if (log_level != NULL) {
848
849
850 if (*log_level + 4 >= msg_log_level) {
851 vfprintf(stderr, fmt, ap);
852 }
853 } else {
854 PCMK__XML_LOG_BASE(msg_log_level, TRUE, 0, "CIB upgrade: ", fmt, ap);
855 }
856
857 va_end(aq);
858 va_end(ap);
859 }
860
861 static xmlNode *
862 apply_transformation(xmlNode *xml, const char *transform, gboolean to_logs)
863 {
864 char *xform = NULL;
865 xmlNode *out = NULL;
866 xmlDocPtr res = NULL;
867 xsltStylesheet *xslt = NULL;
868
869 xform = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
870 transform);
871
872
873 if (to_logs) {
874 xsltSetGenericErrorFunc(NULL, cib_upgrade_err);
875 } else {
876 xsltSetGenericErrorFunc(&crm_log_level, cib_upgrade_err);
877 }
878
879 xslt = xsltParseStylesheetFile((pcmkXmlStr) xform);
880 CRM_CHECK(xslt != NULL, goto cleanup);
881
882 res = xsltApplyStylesheet(xslt, xml->doc, NULL);
883 CRM_CHECK(res != NULL, goto cleanup);
884
885 xsltSetGenericErrorFunc(NULL, NULL);
886
887 out = xmlDocGetRootElement(res);
888
889 cleanup:
890 if (xslt) {
891 xsltFreeStylesheet(xslt);
892 }
893
894 free(xform);
895
896 return out;
897 }
898
899
900
901
902
903
904
905 static xmlNode *
906 apply_upgrade(xmlNode *xml, const struct schema_s *schema, gboolean to_logs)
907 {
908 bool transform_onleave = schema->transform_onleave;
909 char *transform_leave;
910 xmlNode *upgrade = NULL,
911 *final = NULL;
912
913 if (schema->transform_enter) {
914 crm_debug("Upgrading %s-style configuration, pre-upgrade phase with %s.xsl",
915 schema->name, schema->transform_enter);
916 upgrade = apply_transformation(xml, schema->transform_enter, to_logs);
917 if (upgrade == NULL) {
918 crm_warn("Upgrade-enter transformation %s.xsl failed",
919 schema->transform_enter);
920 transform_onleave = FALSE;
921 }
922 }
923 if (upgrade == NULL) {
924 upgrade = xml;
925 }
926
927 crm_debug("Upgrading %s-style configuration, main phase with %s.xsl",
928 schema->name, schema->transform);
929 final = apply_transformation(upgrade, schema->transform, to_logs);
930 if (upgrade != xml) {
931 free_xml(upgrade);
932 upgrade = NULL;
933 }
934
935 if (final != NULL && transform_onleave) {
936 upgrade = final;
937
938 CRM_ASSERT(schema->transform_enter != NULL);
939 transform_leave = strdup(schema->transform_enter);
940
941 memcpy(strrchr(transform_leave, '-') + 1, "leave", sizeof("leave") - 1);
942 crm_debug("Upgrading %s-style configuration, post-upgrade phase with %s.xsl",
943 schema->name, transform_leave);
944 final = apply_transformation(upgrade, transform_leave, to_logs);
945 if (final == NULL) {
946 crm_warn("Upgrade-leave transformation %s.xsl failed", transform_leave);
947 final = upgrade;
948 } else {
949 free_xml(upgrade);
950 }
951 free(transform_leave);
952 }
953
954 return final;
955 }
956
957 const char *
958 get_schema_name(int version)
959 {
960 if (version < 0 || version >= xml_schema_max) {
961 return "unknown";
962 }
963 return known_schemas[version].name;
964 }
965
966 int
967 get_schema_version(const char *name)
968 {
969 int lpc = 0;
970
971 if (name == NULL) {
972 name = PCMK__VALUE_NONE;
973 }
974 for (; lpc < xml_schema_max; lpc++) {
975 if (pcmk__str_eq(name, known_schemas[lpc].name, pcmk__str_casei)) {
976 return lpc;
977 }
978 }
979 return -1;
980 }
981
982
983 int
984 update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform,
985 gboolean to_logs)
986 {
987 xmlNode *xml = NULL;
988 char *value = NULL;
989 int max_stable_schemas = xml_latest_schema_index();
990 int lpc = 0, match = -1, rc = pcmk_ok;
991 int next = -1;
992 xmlRelaxNGValidityErrorFunc error_handler =
993 to_logs ? (xmlRelaxNGValidityErrorFunc) xml_log : NULL;
994
995 CRM_CHECK(best != NULL, return -EINVAL);
996 *best = 0;
997
998 CRM_CHECK((xml_blob != NULL) && (*xml_blob != NULL)
999 && ((*xml_blob)->doc != NULL),
1000 return -EINVAL);
1001
1002 xml = *xml_blob;
1003 value = crm_element_value_copy(xml, XML_ATTR_VALIDATION);
1004
1005 if (value != NULL) {
1006 match = get_schema_version(value);
1007
1008 lpc = match;
1009 if (lpc >= 0 && transform == FALSE) {
1010 *best = lpc++;
1011
1012 } else if (lpc < 0) {
1013 crm_debug("Unknown validation schema");
1014 lpc = 0;
1015 }
1016 }
1017
1018 if (match >= max_stable_schemas) {
1019
1020 free(value);
1021 *best = match;
1022 return pcmk_ok;
1023 }
1024
1025 while (lpc <= max_stable_schemas) {
1026 crm_debug("Testing '%s' validation (%d of %d)",
1027 known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>",
1028 lpc, max_stable_schemas);
1029
1030 if (validate_with(xml, lpc, error_handler, GUINT_TO_POINTER(LOG_ERR)) == FALSE) {
1031 if (next != -1) {
1032 crm_info("Configuration not valid for schema: %s",
1033 known_schemas[lpc].name);
1034 next = -1;
1035 } else {
1036 crm_trace("%s validation failed",
1037 known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>");
1038 }
1039 if (*best) {
1040
1041 break;
1042 }
1043 rc = -pcmk_err_schema_validation;
1044
1045 } else {
1046 if (next != -1) {
1047 crm_debug("Configuration valid for schema: %s",
1048 known_schemas[next].name);
1049 next = -1;
1050 }
1051 rc = pcmk_ok;
1052 }
1053
1054 if (rc == pcmk_ok) {
1055 *best = lpc;
1056 }
1057
1058 if (rc == pcmk_ok && transform) {
1059 xmlNode *upgrade = NULL;
1060 next = known_schemas[lpc].after_transform;
1061
1062 if (next <= lpc) {
1063
1064 crm_trace("Stopping at %s", known_schemas[lpc].name);
1065 break;
1066
1067 } else if (max > 0 && (lpc == max || next > max)) {
1068 crm_trace("Upgrade limit reached at %s (lpc=%d, next=%d, max=%d)",
1069 known_schemas[lpc].name, lpc, next, max);
1070 break;
1071
1072 } else if (known_schemas[lpc].transform == NULL
1073
1074
1075
1076
1077
1078 || validate_with_silent(xml, next)) {
1079 crm_debug("%s-style configuration is also valid for %s",
1080 known_schemas[lpc].name, known_schemas[next].name);
1081
1082 lpc = next;
1083
1084 } else {
1085 crm_debug("Upgrading %s-style configuration to %s with %s.xsl",
1086 known_schemas[lpc].name, known_schemas[next].name,
1087 known_schemas[lpc].transform);
1088
1089 upgrade = apply_upgrade(xml, &known_schemas[lpc], to_logs);
1090 if (upgrade == NULL) {
1091 crm_err("Transformation %s.xsl failed",
1092 known_schemas[lpc].transform);
1093 rc = -pcmk_err_transform_failed;
1094
1095 } else if (validate_with(upgrade, next, error_handler, GUINT_TO_POINTER(LOG_ERR))) {
1096 crm_info("Transformation %s.xsl successful",
1097 known_schemas[lpc].transform);
1098 lpc = next;
1099 *best = next;
1100 free_xml(xml);
1101 xml = upgrade;
1102 rc = pcmk_ok;
1103
1104 } else {
1105 crm_err("Transformation %s.xsl did not produce a valid configuration",
1106 known_schemas[lpc].transform);
1107 crm_log_xml_info(upgrade, "transform:bad");
1108 free_xml(upgrade);
1109 rc = -pcmk_err_schema_validation;
1110 }
1111 next = -1;
1112 }
1113 }
1114
1115 if (transform == FALSE || rc != pcmk_ok) {
1116
1117 lpc++;
1118 }
1119 }
1120
1121 if (*best > match && *best) {
1122 crm_info("%s the configuration from %s to %s",
1123 transform?"Transformed":"Upgraded",
1124 value ? value : "<none>", known_schemas[*best].name);
1125 crm_xml_add(xml, XML_ATTR_VALIDATION, known_schemas[*best].name);
1126 }
1127
1128 *xml_blob = xml;
1129 free(value);
1130 return rc;
1131 }
1132
1133 gboolean
1134 cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
1135 {
1136 gboolean rc = TRUE;
1137 const char *value = crm_element_value(*xml, XML_ATTR_VALIDATION);
1138 char *const orig_value = strdup(value == NULL ? "(none)" : value);
1139
1140 int version = get_schema_version(value);
1141 int orig_version = version;
1142 int min_version = xml_minimum_schema_index();
1143
1144 if (version < min_version) {
1145
1146 xmlNode *converted = NULL;
1147
1148 converted = copy_xml(*xml);
1149 update_validation(&converted, &version, 0, TRUE, to_logs);
1150
1151 value = crm_element_value(converted, XML_ATTR_VALIDATION);
1152 if (version < min_version) {
1153
1154
1155 if (version < orig_version || orig_version == -1) {
1156
1157 if (to_logs) {
1158 pcmk__config_err("Cannot upgrade configuration (claiming "
1159 "schema %s) to at least %s because it "
1160 "does not validate with any schema from "
1161 "%s to %s",
1162 orig_value,
1163 get_schema_name(min_version),
1164 get_schema_name(orig_version),
1165 xml_latest_schema());
1166 } else {
1167 fprintf(stderr, "Cannot upgrade configuration (claiming "
1168 "schema %s) to at least %s because it "
1169 "does not validate with any schema from "
1170 "%s to %s\n",
1171 orig_value,
1172 get_schema_name(min_version),
1173 get_schema_name(orig_version),
1174 xml_latest_schema());
1175 }
1176 } else {
1177
1178 if (to_logs) {
1179 pcmk__config_err("Cannot upgrade configuration (claiming "
1180 "schema %s) to at least %s because it "
1181 "would not upgrade past %s",
1182 orig_value,
1183 get_schema_name(min_version),
1184 pcmk__s(value, "unspecified version"));
1185 } else {
1186 fprintf(stderr, "Cannot upgrade configuration (claiming "
1187 "schema %s) to at least %s because it "
1188 "would not upgrade past %s\n",
1189 orig_value,
1190 get_schema_name(min_version),
1191 pcmk__s(value, "unspecified version"));
1192 }
1193 }
1194
1195 free_xml(converted);
1196 converted = NULL;
1197 rc = FALSE;
1198
1199 } else {
1200
1201 free_xml(*xml);
1202 *xml = converted;
1203
1204 if (version < xml_latest_schema_index()) {
1205 if (to_logs) {
1206 pcmk__config_warn("Configuration with schema %s was "
1207 "internally upgraded to acceptable (but "
1208 "not most recent) %s",
1209 orig_value, get_schema_name(version));
1210 }
1211 } else {
1212 if (to_logs) {
1213 crm_info("Configuration with schema %s was internally "
1214 "upgraded to latest version %s",
1215 orig_value, get_schema_name(version));
1216 }
1217 }
1218 }
1219
1220 } else if (version >= get_schema_version(PCMK__VALUE_NONE)) {
1221
1222 if (to_logs) {
1223 pcmk__config_warn("Schema validation of configuration is disabled "
1224 "(enabling is encouraged and prevents common "
1225 "misconfigurations)");
1226
1227 } else {
1228 fprintf(stderr, "Schema validation of configuration is disabled "
1229 "(enabling is encouraged and prevents common "
1230 "misconfigurations)\n");
1231 }
1232 }
1233
1234 if (best_version) {
1235 *best_version = version;
1236 }
1237
1238 free(orig_value);
1239 return rc;
1240 }