This source file includes following definitions.
- G_GNUC_PRINTF
- xml_latest_schema_index
- get_highest_schema
- pcmk__highest_schema_name
- pcmk__find_x_0_schema
- version_from_filename
- schema_filter
- schema_cmp
- schema_cmp_directory
- add_schema
- add_schema_by_version
- wrap_libxslt
- pcmk__load_schemas_from_dir
- schema_sort_GCompareFunc
- pcmk__sort_schemas
- crm_schema_init
- validate_with_relaxng
- free_schema
- crm_schema_cleanup
- pcmk__get_schema
- pcmk__cmp_schemas_by_name
- validate_with
- validate_with_silent
- pcmk__validate_xml
- pcmk__configured_schema_validates
- G_GNUC_PRINTF
- apply_transformation
- apply_upgrade
- get_configured_schema
- pcmk__update_schema
- pcmk_update_configured_schema
- pcmk__update_configured_schema
- pcmk__schema_files_later_than
- append_href
- external_refs_in_schema
- read_file_contents
- add_schema_file_to_xml
- pcmk__build_schema_xml_node
- pcmk__remote_schema_dir
- pcmk__warn_if_schema_deprecated
- xml_latest_schema
- get_schema_name
- get_schema_version
- update_validation
- validate_xml
- dump_file
- validate_xml_verbose
- 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/common/xml.h>
26 #include <crm/common/xml_internal.h>
27
28 #include "crmcommon_private.h"
29
30 #define SCHEMA_ZERO { .v = { 0, 0 } }
31
32 #define schema_strdup_printf(prefix, version, suffix) \
33 crm_strdup_printf(prefix "%u.%u" suffix, (version).v[0], (version).v[1])
34
35 typedef struct {
36 xmlRelaxNGPtr rng;
37 xmlRelaxNGValidCtxtPtr valid;
38 xmlRelaxNGParserCtxtPtr parser;
39 } relaxng_ctx_cache_t;
40
41 static GList *known_schemas = NULL;
42 static bool initialized = false;
43 static bool silent_logging = FALSE;
44
45 static void G_GNUC_PRINTF(2, 3)
46 xml_log(int priority, const char *fmt, ...)
47 {
48 va_list ap;
49
50 va_start(ap, fmt);
51 if (silent_logging == FALSE) {
52
53 PCMK__XML_LOG_BASE(priority, FALSE, 0, NULL, fmt, ap);
54 }
55 va_end(ap);
56 }
57
58 static int
59 xml_latest_schema_index(void)
60 {
61
62
63
64
65
66
67
68 return g_list_length(known_schemas) - 3;
69 }
70
71
72
73
74
75
76
77 static GList *
78 get_highest_schema(void)
79 {
80
81
82
83
84 GList *entry = pcmk__get_schema("pacemaker-next");
85
86 CRM_ASSERT((entry != NULL) && (entry->prev != NULL));
87 return entry->prev;
88 }
89
90
91
92
93
94
95
96 const char *
97 pcmk__highest_schema_name(void)
98 {
99 GList *entry = get_highest_schema();
100
101 return ((pcmk__schema_t *)(entry->data))->name;
102 }
103
104
105
106
107
108
109
110 GList *
111 pcmk__find_x_0_schema(void)
112 {
113 #if defined(PCMK__UNIT_TESTING)
114
115
116
117
118 GList *x_0_entry = NULL;
119 #else
120 static GList *x_0_entry = NULL;
121 #endif
122
123 pcmk__schema_t *highest_schema = NULL;
124
125 if (x_0_entry != NULL) {
126 return x_0_entry;
127 }
128 x_0_entry = get_highest_schema();
129 highest_schema = x_0_entry->data;
130
131 for (GList *iter = x_0_entry->prev; iter != NULL; iter = iter->prev) {
132 pcmk__schema_t *schema = iter->data;
133
134
135
136
137
138 if (schema->version.v[0] < highest_schema->version.v[0]) {
139 x_0_entry = iter->next;
140 break;
141 }
142
143
144
145
146 if (iter->prev == NULL) {
147 x_0_entry = known_schemas->data;
148 break;
149 }
150 }
151 return x_0_entry;
152 }
153
154 static inline bool
155 version_from_filename(const char *filename, pcmk__schema_version_t *version)
156 {
157 if (pcmk__ends_with(filename, ".rng")) {
158 return sscanf(filename, "pacemaker-%hhu.%hhu.rng", &(version->v[0]), &(version->v[1])) == 2;
159 } else {
160 return sscanf(filename, "pacemaker-%hhu.%hhu", &(version->v[0]), &(version->v[1])) == 2;
161 }
162 }
163
164 static int
165 schema_filter(const struct dirent *a)
166 {
167 int rc = 0;
168 pcmk__schema_version_t version = SCHEMA_ZERO;
169
170 if (strstr(a->d_name, "pacemaker-") != a->d_name) {
171
172
173 } else if (!pcmk__ends_with_ext(a->d_name, ".rng")) {
174
175
176 } else if (!version_from_filename(a->d_name, &version)) {
177
178
179 } else {
180
181 rc = 1;
182 }
183
184 return rc;
185 }
186
187 static int
188 schema_cmp(pcmk__schema_version_t a_version, pcmk__schema_version_t b_version)
189 {
190 for (int i = 0; i < 2; ++i) {
191 if (a_version.v[i] < b_version.v[i]) {
192 return -1;
193 } else if (a_version.v[i] > b_version.v[i]) {
194 return 1;
195 }
196 }
197 return 0;
198 }
199
200 static int
201 schema_cmp_directory(const struct dirent **a, const struct dirent **b)
202 {
203 pcmk__schema_version_t a_version = SCHEMA_ZERO;
204 pcmk__schema_version_t b_version = SCHEMA_ZERO;
205
206 if (!version_from_filename(a[0]->d_name, &a_version)
207 || !version_from_filename(b[0]->d_name, &b_version)) {
208
209 return 0;
210 }
211
212 return schema_cmp(a_version, b_version);
213 }
214
215
216
217
218
219
220
221
222 static void
223 add_schema(enum pcmk__schema_validator validator, const pcmk__schema_version_t *version,
224 const char *name, const char *transform,
225 const char *transform_enter, bool transform_onleave)
226 {
227 pcmk__schema_t *schema = NULL;
228
229 schema = pcmk__assert_alloc(1, sizeof(pcmk__schema_t));
230
231 schema->validator = validator;
232 schema->version.v[0] = version->v[0];
233 schema->version.v[1] = version->v[1];
234 schema->transform_onleave = transform_onleave;
235
236
237 if (version->v[0] || version->v[1]) {
238 schema->name = schema_strdup_printf("pacemaker-", *version, "");
239 } else {
240 schema->name = pcmk__str_copy(name);
241 }
242
243 if (transform) {
244 schema->transform = pcmk__str_copy(transform);
245 }
246
247 if (transform_enter) {
248 schema->transform_enter = pcmk__str_copy(transform_enter);
249 }
250
251 known_schemas = g_list_prepend(known_schemas, schema);
252 }
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282 static int
283 add_schema_by_version(const pcmk__schema_version_t *version, bool transform_expected)
284 {
285 bool transform_onleave = FALSE;
286 int rc = pcmk_rc_ok;
287 struct stat s;
288 char *xslt = NULL,
289 *transform_upgrade = NULL,
290 *transform_enter = NULL;
291
292
293 if (transform_expected) {
294
295 transform_upgrade = schema_strdup_printf("upgrade-", *version, );
296 xslt = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
297 transform_upgrade);
298 }
299
300 if (!transform_expected) {
301
302
303 } else if (stat(xslt, &s) == 0) {
304
305 transform_enter = schema_strdup_printf("upgrade-", *version, "-enter");
306 free(xslt);
307 xslt = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
308 transform_enter);
309 if (stat(xslt, &s) != 0) {
310
311 crm_debug("Upgrade-enter transform %s.xsl not found", xslt);
312 free(xslt);
313 free(transform_enter);
314 transform_enter = strdup("upgrade-enter");
315 xslt = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
316 transform_enter);
317 if (stat(xslt, &s) != 0) {
318 crm_debug("Upgrade-enter transform %s.xsl not found, either", xslt);
319 free(xslt);
320 xslt = NULL;
321 }
322 }
323
324 if (xslt != NULL) {
325
326 memcpy(strrchr(xslt, '-') + 1, "leave", sizeof("leave") - 1);
327 transform_onleave = (stat(xslt, &s) == 0);
328 free(xslt);
329 } else {
330 free(transform_enter);
331 transform_enter = NULL;
332 }
333
334 } else {
335 crm_err("Upgrade transform %s not found", xslt);
336 free(xslt);
337 free(transform_upgrade);
338 transform_upgrade = NULL;
339 rc = ENOENT;
340 }
341
342 add_schema(pcmk__schema_validator_rng, version, NULL,
343 transform_upgrade, transform_enter, transform_onleave);
344
345 free(transform_upgrade);
346 free(transform_enter);
347
348 return rc;
349 }
350
351 static void
352 wrap_libxslt(bool finalize)
353 {
354 static xsltSecurityPrefsPtr secprefs;
355 int ret = 0;
356
357
358 if (!finalize) {
359 CRM_ASSERT(secprefs == NULL);
360 secprefs = xsltNewSecurityPrefs();
361 ret = xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_WRITE_FILE,
362 xsltSecurityForbid)
363 | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_CREATE_DIRECTORY,
364 xsltSecurityForbid)
365 | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_READ_NETWORK,
366 xsltSecurityForbid)
367 | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_WRITE_NETWORK,
368 xsltSecurityForbid);
369 if (ret != 0) {
370 return;
371 }
372 } else {
373 xsltFreeSecurityPrefs(secprefs);
374 secprefs = NULL;
375 }
376
377
378 if (finalize) {
379 xsltCleanupGlobals();
380 }
381 }
382
383 void
384 pcmk__load_schemas_from_dir(const char *dir)
385 {
386 int lpc, max;
387 struct dirent **namelist = NULL;
388
389 max = scandir(dir, &namelist, schema_filter, schema_cmp_directory);
390 if (max < 0) {
391 crm_warn("Could not load schemas from %s: %s", dir, strerror(errno));
392 return;
393 }
394
395 for (lpc = 0; lpc < max; lpc++) {
396 bool transform_expected = false;
397 pcmk__schema_version_t version = SCHEMA_ZERO;
398
399 if (!version_from_filename(namelist[lpc]->d_name, &version)) {
400
401 crm_warn("Skipping schema '%s': could not parse version",
402 namelist[lpc]->d_name);
403 continue;
404 }
405 if ((lpc + 1) < max) {
406 pcmk__schema_version_t next_version = SCHEMA_ZERO;
407
408 if (version_from_filename(namelist[lpc+1]->d_name, &next_version)
409 && (version.v[0] < next_version.v[0])) {
410 transform_expected = true;
411 }
412 }
413
414 if (add_schema_by_version(&version, transform_expected) != pcmk_rc_ok) {
415 break;
416 }
417 }
418
419 for (lpc = 0; lpc < max; lpc++) {
420 free(namelist[lpc]);
421 }
422
423 free(namelist);
424 }
425
426 static gint
427 schema_sort_GCompareFunc(gconstpointer a, gconstpointer b)
428 {
429 const pcmk__schema_t *schema_a = a;
430 const pcmk__schema_t *schema_b = b;
431
432
433 if (pcmk__str_eq(schema_a->name, "pacemaker-next", pcmk__str_none)) {
434 if (pcmk__str_eq(schema_b->name, PCMK_VALUE_NONE, pcmk__str_none)) {
435 return -1;
436 } else {
437 return 1;
438 }
439 } else if (pcmk__str_eq(schema_a->name, PCMK_VALUE_NONE, pcmk__str_none)) {
440 return 1;
441 } else if (pcmk__str_eq(schema_b->name, "pacemaker-next", pcmk__str_none)) {
442 return -1;
443 } else {
444 return schema_cmp(schema_a->version, schema_b->version);
445 }
446 }
447
448
449
450
451
452
453
454
455
456 void
457 pcmk__sort_schemas(void)
458 {
459 known_schemas = g_list_sort(known_schemas, schema_sort_GCompareFunc);
460 }
461
462
463
464
465
466
467
468
469 void
470 crm_schema_init(void)
471 {
472 if (!initialized) {
473 const char *remote_schema_dir = pcmk__remote_schema_dir();
474 char *base = pcmk__xml_artefact_root(pcmk__xml_artefact_ns_legacy_rng);
475 const pcmk__schema_version_t zero = SCHEMA_ZERO;
476 int schema_index = 0;
477
478 initialized = true;
479
480 wrap_libxslt(false);
481
482 pcmk__load_schemas_from_dir(base);
483 pcmk__load_schemas_from_dir(remote_schema_dir);
484 free(base);
485
486
487 add_schema(pcmk__schema_validator_rng, &zero, "pacemaker-next", NULL,
488 NULL, FALSE);
489
490
491 add_schema(pcmk__schema_validator_none, &zero, PCMK_VALUE_NONE, NULL,
492 NULL, FALSE);
493
494
495
496
497
498 pcmk__sort_schemas();
499
500
501 for (GList *iter = known_schemas; iter != NULL; iter = iter->next) {
502 pcmk__schema_t *schema = iter->data;
503
504 if (schema->transform == NULL) {
505 crm_debug("Loaded schema %d: %s", schema_index, schema->name);
506 } else {
507 crm_debug("Loaded schema %d: %s (upgrades with %s.xsl)",
508 schema_index, schema->name, schema->transform);
509 }
510 schema->schema_index = schema_index++;
511 }
512 }
513 }
514
515 static bool
516 validate_with_relaxng(xmlDocPtr doc, xmlRelaxNGValidityErrorFunc error_handler,
517 void *error_handler_context, const char *relaxng_file,
518 relaxng_ctx_cache_t **cached_ctx)
519 {
520 int rc = 0;
521 bool valid = true;
522 relaxng_ctx_cache_t *ctx = NULL;
523
524 CRM_CHECK(doc != NULL, return false);
525 CRM_CHECK(relaxng_file != NULL, return false);
526
527 if (cached_ctx && *cached_ctx) {
528 ctx = *cached_ctx;
529
530 } else {
531 crm_debug("Creating RNG parser context");
532 ctx = pcmk__assert_alloc(1, sizeof(relaxng_ctx_cache_t));
533
534 ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file);
535 CRM_CHECK(ctx->parser != NULL, goto cleanup);
536
537 if (error_handler) {
538 xmlRelaxNGSetParserErrors(ctx->parser,
539 (xmlRelaxNGValidityErrorFunc) error_handler,
540 (xmlRelaxNGValidityWarningFunc) error_handler,
541 error_handler_context);
542 } else {
543 xmlRelaxNGSetParserErrors(ctx->parser,
544 (xmlRelaxNGValidityErrorFunc) fprintf,
545 (xmlRelaxNGValidityWarningFunc) fprintf,
546 stderr);
547 }
548
549 ctx->rng = xmlRelaxNGParse(ctx->parser);
550 CRM_CHECK(ctx->rng != NULL,
551 crm_err("Could not find/parse %s", relaxng_file);
552 goto cleanup);
553
554 ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng);
555 CRM_CHECK(ctx->valid != NULL, goto cleanup);
556
557 if (error_handler) {
558 xmlRelaxNGSetValidErrors(ctx->valid,
559 (xmlRelaxNGValidityErrorFunc) error_handler,
560 (xmlRelaxNGValidityWarningFunc) error_handler,
561 error_handler_context);
562 } else {
563 xmlRelaxNGSetValidErrors(ctx->valid,
564 (xmlRelaxNGValidityErrorFunc) fprintf,
565 (xmlRelaxNGValidityWarningFunc) fprintf,
566 stderr);
567 }
568 }
569
570 rc = xmlRelaxNGValidateDoc(ctx->valid, doc);
571 if (rc > 0) {
572 valid = false;
573
574 } else if (rc < 0) {
575 crm_err("Internal libxml error during validation");
576 }
577
578 cleanup:
579
580 if (cached_ctx) {
581 *cached_ctx = ctx;
582
583 } else {
584 if (ctx->parser != NULL) {
585 xmlRelaxNGFreeParserCtxt(ctx->parser);
586 }
587 if (ctx->valid != NULL) {
588 xmlRelaxNGFreeValidCtxt(ctx->valid);
589 }
590 if (ctx->rng != NULL) {
591 xmlRelaxNGFree(ctx->rng);
592 }
593 free(ctx);
594 }
595
596 return valid;
597 }
598
599 static void
600 free_schema(gpointer data)
601 {
602 pcmk__schema_t *schema = data;
603 relaxng_ctx_cache_t *ctx = NULL;
604
605 switch (schema->validator) {
606 case pcmk__schema_validator_none:
607 break;
608
609 case pcmk__schema_validator_rng:
610 ctx = (relaxng_ctx_cache_t *) schema->cache;
611 if (ctx == NULL) {
612 break;
613 }
614
615 if (ctx->parser != NULL) {
616 xmlRelaxNGFreeParserCtxt(ctx->parser);
617 }
618
619 if (ctx->valid != NULL) {
620 xmlRelaxNGFreeValidCtxt(ctx->valid);
621 }
622
623 if (ctx->rng != NULL) {
624 xmlRelaxNGFree(ctx->rng);
625 }
626
627 free(ctx);
628 schema->cache = NULL;
629 break;
630 }
631
632 free(schema->name);
633 free(schema->transform);
634 free(schema->transform_enter);
635 free(schema);
636 }
637
638
639
640
641
642 void
643 crm_schema_cleanup(void)
644 {
645 if (known_schemas != NULL) {
646 g_list_free_full(known_schemas, free_schema);
647 known_schemas = NULL;
648 }
649 initialized = false;
650
651 wrap_libxslt(true);
652 }
653
654
655
656
657
658
659
660
661
662 GList *
663 pcmk__get_schema(const char *name)
664 {
665
666 if (name == NULL) {
667 name = PCMK_VALUE_NONE;
668 }
669 for (GList *iter = known_schemas; iter != NULL; iter = iter->next) {
670 pcmk__schema_t *schema = iter->data;
671
672 if (pcmk__str_eq(name, schema->name, pcmk__str_casei)) {
673 return iter;
674 }
675 }
676 return NULL;
677 }
678
679
680
681
682
683
684
685
686
687
688
689
690 int
691 pcmk__cmp_schemas_by_name(const char *schema1_name, const char *schema2_name)
692 {
693 GList *entry1 = pcmk__get_schema(schema1_name);
694 GList *entry2 = pcmk__get_schema(schema2_name);
695
696 if (entry1 == NULL) {
697 return (entry2 == NULL)? 0 : -1;
698
699 } else if (entry2 == NULL) {
700 return 1;
701
702 } else {
703 pcmk__schema_t *schema1 = entry1->data;
704 pcmk__schema_t *schema2 = entry2->data;
705
706 return schema1->schema_index - schema2->schema_index;
707 }
708 }
709
710 static bool
711 validate_with(xmlNode *xml, pcmk__schema_t *schema,
712 xmlRelaxNGValidityErrorFunc error_handler,
713 void *error_handler_context)
714 {
715 bool valid = false;
716 char *file = NULL;
717 relaxng_ctx_cache_t **cache = NULL;
718
719 if (schema == NULL) {
720 return false;
721 }
722
723 if (schema->validator == pcmk__schema_validator_none) {
724 return true;
725 }
726
727 file = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_rng,
728 schema->name);
729
730 crm_trace("Validating with %s (type=%d)",
731 pcmk__s(file, "missing schema"), schema->validator);
732 switch (schema->validator) {
733 case pcmk__schema_validator_rng:
734 cache = (relaxng_ctx_cache_t **) &(schema->cache);
735 valid = validate_with_relaxng(xml->doc, error_handler, error_handler_context, file, cache);
736 break;
737 default:
738 crm_err("Unknown validator type: %d", schema->validator);
739 break;
740 }
741
742 free(file);
743 return valid;
744 }
745
746 static bool
747 validate_with_silent(xmlNode *xml, pcmk__schema_t *schema)
748 {
749 bool rc, sl_backup = silent_logging;
750 silent_logging = TRUE;
751 rc = validate_with(xml, schema, (xmlRelaxNGValidityErrorFunc) xml_log, GUINT_TO_POINTER(LOG_ERR));
752 silent_logging = sl_backup;
753 return rc;
754 }
755
756 bool
757 pcmk__validate_xml(xmlNode *xml_blob, const char *validation,
758 xmlRelaxNGValidityErrorFunc error_handler,
759 void *error_handler_context)
760 {
761 GList *entry = NULL;
762 pcmk__schema_t *schema = NULL;
763
764 CRM_CHECK((xml_blob != NULL) && (xml_blob->doc != NULL), return false);
765
766 if (validation == NULL) {
767 validation = crm_element_value(xml_blob, PCMK_XA_VALIDATE_WITH);
768 }
769 pcmk__warn_if_schema_deprecated(validation);
770
771
772 if (validation == NULL) {
773 bool valid = false;
774
775 for (entry = known_schemas; entry != NULL; entry = entry->next) {
776 schema = entry->data;
777 if (validate_with(xml_blob, schema, NULL, NULL)) {
778 valid = true;
779 crm_xml_add(xml_blob, PCMK_XA_VALIDATE_WITH, schema->name);
780 crm_info("XML validated against %s", schema->name);
781 }
782 }
783 return valid;
784 }
785
786 entry = pcmk__get_schema(validation);
787 if (entry == NULL) {
788 pcmk__config_err("Cannot validate CIB with " PCMK_XA_VALIDATE_WITH
789 " set to an unknown schema such as '%s' (manually"
790 " edit to use a known schema)",
791 validation);
792 return false;
793 }
794
795 schema = entry->data;
796 return validate_with(xml_blob, schema, error_handler,
797 error_handler_context);
798 }
799
800
801
802
803
804
805
806
807
808 bool
809 pcmk__configured_schema_validates(xmlNode *xml)
810 {
811 return pcmk__validate_xml(xml, NULL,
812 (xmlRelaxNGValidityErrorFunc) xml_log,
813 GUINT_TO_POINTER(LOG_ERR));
814 }
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839 static void G_GNUC_PRINTF(2, 3)
840 cib_upgrade_err(void *ctx, const char *fmt, ...)
841 {
842 va_list ap, aq;
843 char *arg_cur;
844
845 bool found = FALSE;
846 const char *fmt_iter = fmt;
847 uint8_t msg_log_level = LOG_WARNING;
848 const unsigned * log_level = (const unsigned *) ctx;
849 enum {
850 escan_seennothing,
851 escan_seenpercent,
852 } scan_state = escan_seennothing;
853
854 va_start(ap, fmt);
855 va_copy(aq, ap);
856
857 while (!found && *fmt_iter != '\0') {
858
859 switch (*fmt_iter++) {
860 case '%':
861 if (scan_state == escan_seennothing) {
862 scan_state = escan_seenpercent;
863 } else if (scan_state == escan_seenpercent) {
864 scan_state = escan_seennothing;
865 }
866 break;
867 case 's':
868 if (scan_state == escan_seenpercent) {
869 scan_state = escan_seennothing;
870 arg_cur = va_arg(aq, char *);
871 if (arg_cur != NULL) {
872 switch (arg_cur[0]) {
873 case 'W':
874 if (!strncmp(arg_cur, "WARNING: ",
875 sizeof("WARNING: ") - 1)) {
876 msg_log_level = LOG_WARNING;
877 }
878 if (ctx == NULL) {
879 memmove(arg_cur, arg_cur + sizeof("WARNING: ") - 1,
880 strlen(arg_cur + sizeof("WARNING: ") - 1) + 1);
881 }
882 found = TRUE;
883 break;
884 case 'I':
885 if (!strncmp(arg_cur, "INFO: ",
886 sizeof("INFO: ") - 1)) {
887 msg_log_level = LOG_INFO;
888 }
889 if (ctx == NULL) {
890 memmove(arg_cur, arg_cur + sizeof("INFO: ") - 1,
891 strlen(arg_cur + sizeof("INFO: ") - 1) + 1);
892 }
893 found = TRUE;
894 break;
895 case 'D':
896 if (!strncmp(arg_cur, "DEBUG: ",
897 sizeof("DEBUG: ") - 1)) {
898 msg_log_level = LOG_DEBUG;
899 }
900 if (ctx == NULL) {
901 memmove(arg_cur, arg_cur + sizeof("DEBUG: ") - 1,
902 strlen(arg_cur + sizeof("DEBUG: ") - 1) + 1);
903 }
904 found = TRUE;
905 break;
906 }
907 }
908 }
909 break;
910 case '#': case '-': case ' ': case '+': case '\'': case 'I': case '.':
911 case '0': case '1': case '2': case '3': case '4':
912 case '5': case '6': case '7': case '8': case '9':
913 case '*':
914 break;
915 case 'l':
916 case 'z':
917 case 't':
918 case 'j':
919 case 'd': case 'i':
920 case 'o':
921 case 'u':
922 case 'x': case 'X':
923 case 'e': case 'E':
924 case 'f': case 'F':
925 case 'g': case 'G':
926 case 'a': case 'A':
927 case 'c':
928 case 'p':
929 if (scan_state == escan_seenpercent) {
930 (void) va_arg(aq, void *);
931 scan_state = escan_seennothing;
932 }
933 break;
934 default:
935 scan_state = escan_seennothing;
936 break;
937 }
938 }
939
940 if (log_level != NULL) {
941
942
943 if (*log_level + 4 >= msg_log_level) {
944 vfprintf(stderr, fmt, ap);
945 }
946 } else {
947 PCMK__XML_LOG_BASE(msg_log_level, TRUE, 0, "CIB upgrade: ", fmt, ap);
948 }
949
950 va_end(aq);
951 va_end(ap);
952 }
953
954
955
956
957
958
959
960
961
962
963
964
965 static xmlNode *
966 apply_transformation(const xmlNode *xml, const char *transform,
967 gboolean to_logs)
968 {
969 char *xform = NULL;
970 xmlNode *out = NULL;
971 xmlDocPtr res = NULL;
972 xsltStylesheet *xslt = NULL;
973
974 xform = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
975 transform);
976
977
978 if (to_logs) {
979 xsltSetGenericErrorFunc(NULL, cib_upgrade_err);
980 } else {
981 xsltSetGenericErrorFunc(&crm_log_level, cib_upgrade_err);
982 }
983
984 xslt = xsltParseStylesheetFile((pcmkXmlStr) xform);
985 CRM_CHECK(xslt != NULL, goto cleanup);
986
987 res = xsltApplyStylesheet(xslt, xml->doc, NULL);
988 CRM_CHECK(res != NULL, goto cleanup);
989
990 xsltSetGenericErrorFunc(NULL, NULL);
991
992 out = xmlDocGetRootElement(res);
993
994 cleanup:
995 if (xslt) {
996 xsltFreeStylesheet(xslt);
997 }
998
999 free(xform);
1000
1001 return out;
1002 }
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020 static xmlNode *
1021 apply_upgrade(const xmlNode *original_xml, int schema_index, gboolean to_logs)
1022 {
1023 pcmk__schema_t *schema = g_list_nth_data(known_schemas, schema_index);
1024 pcmk__schema_t *upgraded_schema = g_list_nth_data(known_schemas,
1025 schema_index + 1);
1026 bool transform_onleave = false;
1027 char *transform_leave;
1028 const xmlNode *xml = original_xml;
1029 xmlNode *upgrade = NULL;
1030 xmlNode *final = NULL;
1031 xmlRelaxNGValidityErrorFunc error_handler = NULL;
1032
1033 CRM_ASSERT((schema != NULL) && (upgraded_schema != NULL));
1034
1035 if (to_logs) {
1036 error_handler = (xmlRelaxNGValidityErrorFunc) xml_log;
1037 }
1038
1039 transform_onleave = schema->transform_onleave;
1040 if (schema->transform_enter != NULL) {
1041 crm_debug("Upgrading schema from %s to %s: "
1042 "applying pre-upgrade XSL transform %s",
1043 schema->name, upgraded_schema->name, schema->transform_enter);
1044 upgrade = apply_transformation(xml, schema->transform_enter, to_logs);
1045 if (upgrade == NULL) {
1046 crm_warn("Pre-upgrade XSL transform %s failed, "
1047 "will skip post-upgrade transform",
1048 schema->transform_enter);
1049 transform_onleave = FALSE;
1050 } else {
1051 xml = upgrade;
1052 }
1053 }
1054
1055
1056 crm_debug("Upgrading schema from %s to %s: "
1057 "applying upgrade XSL transform %s",
1058 schema->name, upgraded_schema->name, schema->transform);
1059 final = apply_transformation(xml, schema->transform, to_logs);
1060 if (upgrade != xml) {
1061 free_xml(upgrade);
1062 upgrade = NULL;
1063 }
1064
1065 if ((final != NULL) && transform_onleave) {
1066 upgrade = final;
1067
1068 CRM_ASSERT(schema->transform_enter != NULL);
1069 transform_leave = strdup(schema->transform_enter);
1070
1071 memcpy(strrchr(transform_leave, '-') + 1, "leave", sizeof("leave") - 1);
1072 crm_debug("Upgrading schema from %s to %s: "
1073 "applying post-upgrade XSL transform %s",
1074 schema->name, upgraded_schema->name, transform_leave);
1075 final = apply_transformation(upgrade, transform_leave, to_logs);
1076 if (final == NULL) {
1077 crm_warn("Ignoring failure of post-upgrade XSL transform %s",
1078 transform_leave);
1079 final = upgrade;
1080 } else {
1081 free_xml(upgrade);
1082 }
1083 free(transform_leave);
1084 }
1085
1086 if (final == NULL) {
1087 return NULL;
1088 }
1089
1090
1091 if (!validate_with(final, upgraded_schema, error_handler,
1092 GUINT_TO_POINTER(LOG_ERR))) {
1093 crm_err("Schema upgrade from %s to %s failed: "
1094 "XSL transform %s produced an invalid configuration",
1095 schema->name, upgraded_schema->name, schema->transform);
1096 crm_log_xml_debug(final, "bad-transform-result");
1097 free_xml(final);
1098 return NULL;
1099 }
1100
1101 crm_info("Schema upgrade from %s to %s succeeded",
1102 schema->name, upgraded_schema->name);
1103 return final;
1104 }
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114 static GList *
1115 get_configured_schema(const xmlNode *xml)
1116 {
1117 const char *schema_name = crm_element_value(xml, PCMK_XA_VALIDATE_WITH);
1118
1119 pcmk__warn_if_schema_deprecated(schema_name);
1120 if (schema_name == NULL) {
1121 return NULL;
1122 }
1123 return pcmk__get_schema(schema_name);
1124 }
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140 int
1141 pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform,
1142 bool to_logs)
1143 {
1144 int max_stable_schemas = xml_latest_schema_index();
1145 int max_schema_index = 0;
1146 int rc = pcmk_rc_ok;
1147 GList *entry = NULL;
1148 pcmk__schema_t *best_schema = NULL;
1149 pcmk__schema_t *original_schema = NULL;
1150 xmlRelaxNGValidityErrorFunc error_handler =
1151 to_logs ? (xmlRelaxNGValidityErrorFunc) xml_log : NULL;
1152
1153 CRM_CHECK((xml != NULL) && (*xml != NULL) && ((*xml)->doc != NULL),
1154 return EINVAL);
1155
1156 if (max_schema_name != NULL) {
1157 GList *max_entry = pcmk__get_schema(max_schema_name);
1158
1159 if (max_entry != NULL) {
1160 pcmk__schema_t *max_schema = max_entry->data;
1161
1162 max_schema_index = max_schema->schema_index;
1163 }
1164 }
1165 if ((max_schema_index < 1) || (max_schema_index > max_stable_schemas)) {
1166 max_schema_index = max_stable_schemas;
1167 }
1168
1169 entry = get_configured_schema(*xml);
1170 if (entry == NULL) {
1171
1172 entry = known_schemas;
1173 } else {
1174 original_schema = entry->data;
1175 if (original_schema->schema_index >= max_schema_index) {
1176 return pcmk_rc_ok;
1177 }
1178 }
1179
1180 for (; entry != NULL; entry = entry->next) {
1181 pcmk__schema_t *current_schema = entry->data;
1182 xmlNode *upgrade = NULL;
1183
1184 if (current_schema->schema_index > max_schema_index) {
1185 break;
1186 }
1187
1188 if (!validate_with(*xml, current_schema, error_handler,
1189 GUINT_TO_POINTER(LOG_ERR))) {
1190 crm_debug("Schema %s does not validate", current_schema->name);
1191 if (best_schema != NULL) {
1192
1193 break;
1194 }
1195 rc = pcmk_rc_schema_validation;
1196 continue;
1197 }
1198
1199 crm_debug("Schema %s validates", current_schema->name);
1200 rc = pcmk_rc_ok;
1201 best_schema = current_schema;
1202 if (current_schema->schema_index == max_schema_index) {
1203 break;
1204 }
1205
1206 if (!transform || (current_schema->transform == NULL)
1207 || validate_with_silent(*xml, entry->next->data)) {
1208
1209
1210
1211
1212 continue;
1213 }
1214
1215 upgrade = apply_upgrade(*xml, current_schema->schema_index, to_logs);
1216 if (upgrade == NULL) {
1217
1218
1219
1220
1221 rc = pcmk_rc_transform_failed;
1222 } else {
1223 best_schema = current_schema;
1224 free_xml(*xml);
1225 *xml = upgrade;
1226 }
1227 }
1228
1229 if (best_schema != NULL) {
1230 if ((original_schema == NULL)
1231 || (best_schema->schema_index > original_schema->schema_index)) {
1232 crm_info("%s the configuration schema to %s",
1233 (transform? "Transformed" : "Upgraded"),
1234 best_schema->name);
1235 crm_xml_add(*xml, PCMK_XA_VALIDATE_WITH, best_schema->name);
1236 }
1237 }
1238 return rc;
1239 }
1240
1241 int
1242 pcmk_update_configured_schema(xmlNode **xml)
1243 {
1244 return pcmk__update_configured_schema(xml, true);
1245 }
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256 int
1257 pcmk__update_configured_schema(xmlNode **xml, bool to_logs)
1258 {
1259 int rc = pcmk_rc_ok;
1260 char *original_schema_name = NULL;
1261
1262
1263 const char *effective_original_name = "the first";
1264
1265 int orig_version = -1;
1266 pcmk__schema_t *x_0_schema = pcmk__find_x_0_schema()->data;
1267 GList *entry = NULL;
1268
1269 CRM_CHECK(xml != NULL, return EINVAL);
1270
1271 original_schema_name = crm_element_value_copy(*xml, PCMK_XA_VALIDATE_WITH);
1272 pcmk__warn_if_schema_deprecated(original_schema_name);
1273 entry = pcmk__get_schema(original_schema_name);
1274 if (entry != NULL) {
1275 pcmk__schema_t *original_schema = entry->data;
1276
1277 effective_original_name = original_schema->name;
1278 orig_version = original_schema->schema_index;
1279 }
1280
1281 if (orig_version < x_0_schema->schema_index) {
1282
1283 xmlNode *converted = NULL;
1284 const char *new_schema_name = NULL;
1285 pcmk__schema_t *schema = NULL;
1286
1287 entry = NULL;
1288 converted = pcmk__xml_copy(NULL, *xml);
1289 if (pcmk__update_schema(&converted, NULL, true, to_logs) == pcmk_rc_ok) {
1290 new_schema_name = crm_element_value(converted,
1291 PCMK_XA_VALIDATE_WITH);
1292 entry = pcmk__get_schema(new_schema_name);
1293 }
1294 schema = (entry == NULL)? NULL : entry->data;
1295
1296 if ((schema == NULL)
1297 || (schema->schema_index < x_0_schema->schema_index)) {
1298
1299
1300 if ((orig_version == -1) || (schema == NULL)
1301 || (schema->schema_index < orig_version)) {
1302
1303 if (to_logs) {
1304 pcmk__config_err("Cannot upgrade configuration (claiming "
1305 "%s schema) to at least %s because it "
1306 "does not validate with any schema from "
1307 "%s to the latest",
1308 pcmk__s(original_schema_name, "no"),
1309 x_0_schema->name, effective_original_name);
1310 } else {
1311 fprintf(stderr, "Cannot upgrade configuration (claiming "
1312 "%s schema) to at least %s because it "
1313 "does not validate with any schema from "
1314 "%s to the latest\n",
1315 pcmk__s(original_schema_name, "no"),
1316 x_0_schema->name, effective_original_name);
1317 }
1318 } else {
1319
1320 if (to_logs) {
1321 pcmk__config_err("Cannot upgrade configuration (claiming "
1322 "%s schema) to at least %s because it "
1323 "would not upgrade past %s",
1324 pcmk__s(original_schema_name, "no"),
1325 x_0_schema->name,
1326 pcmk__s(new_schema_name, "unspecified version"));
1327 } else {
1328 fprintf(stderr, "Cannot upgrade configuration (claiming "
1329 "%s schema) to at least %s because it "
1330 "would not upgrade past %s\n",
1331 pcmk__s(original_schema_name, "no"),
1332 x_0_schema->name,
1333 pcmk__s(new_schema_name, "unspecified version"));
1334 }
1335 }
1336
1337 free_xml(converted);
1338 converted = NULL;
1339 rc = pcmk_rc_transform_failed;
1340
1341 } else {
1342
1343 free_xml(*xml);
1344 *xml = converted;
1345
1346 if (schema->schema_index < xml_latest_schema_index()) {
1347 if (to_logs) {
1348 pcmk__config_warn("Configuration with %s schema was "
1349 "internally upgraded to acceptable (but "
1350 "not most recent) %s",
1351 pcmk__s(original_schema_name, "no"),
1352 schema->name);
1353 }
1354 } else if (to_logs) {
1355 crm_info("Configuration with %s schema was internally "
1356 "upgraded to latest version %s",
1357 pcmk__s(original_schema_name, "no"),
1358 schema->name);
1359 }
1360 }
1361
1362 } else {
1363
1364 pcmk__schema_t *none_schema = NULL;
1365
1366 entry = pcmk__get_schema(PCMK_VALUE_NONE);
1367 CRM_ASSERT((entry != NULL) && (entry->data != NULL));
1368
1369 none_schema = entry->data;
1370 if (!to_logs && (orig_version >= none_schema->schema_index)) {
1371 fprintf(stderr, "Schema validation of configuration is "
1372 "disabled (support for " PCMK_XA_VALIDATE_WITH
1373 " set to \"" PCMK_VALUE_NONE "\" is deprecated"
1374 " and will be removed in a future release)\n");
1375 }
1376 }
1377
1378 free(original_schema_name);
1379 return rc;
1380 }
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394 GList *
1395 pcmk__schema_files_later_than(const char *name)
1396 {
1397 GList *lst = NULL;
1398 pcmk__schema_version_t ver;
1399
1400 if (!version_from_filename(name, &ver)) {
1401 return lst;
1402 }
1403
1404 for (GList *iter = g_list_nth(known_schemas, xml_latest_schema_index());
1405 iter != NULL; iter = iter->prev) {
1406 pcmk__schema_t *schema = iter->data;
1407 char *s = NULL;
1408
1409 if (schema_cmp(ver, schema->version) != -1) {
1410 continue;
1411 }
1412
1413 s = crm_strdup_printf("%s.rng", schema->name);
1414 lst = g_list_prepend(lst, s);
1415
1416 if (schema->transform != NULL) {
1417 char *xform = crm_strdup_printf("%s.xsl", schema->transform);
1418 lst = g_list_prepend(lst, xform);
1419 }
1420
1421 if (schema->transform_enter != NULL) {
1422 char *enter = crm_strdup_printf("%s.xsl", schema->transform_enter);
1423
1424 lst = g_list_prepend(lst, enter);
1425
1426 if (schema->transform_onleave) {
1427 int last_dash = strrchr(enter, '-') - enter;
1428 char *leave = crm_strdup_printf("%.*s-leave.xsl", last_dash, enter);
1429
1430 lst = g_list_prepend(lst, leave);
1431 }
1432 }
1433 }
1434
1435 return lst;
1436 }
1437
1438 static void
1439 append_href(xmlNode *xml, void *user_data)
1440 {
1441 GList **list = user_data;
1442 char *href = crm_element_value_copy(xml, "href");
1443
1444 if (href == NULL) {
1445 return;
1446 }
1447 *list = g_list_prepend(*list, href);
1448 }
1449
1450 static void
1451 external_refs_in_schema(GList **list, const char *contents)
1452 {
1453
1454
1455
1456 const char *search = "//*[local-name()='externalRef'] | //*[local-name()='include']";
1457 xmlNode *xml = pcmk__xml_parse(contents);
1458
1459 crm_foreach_xpath_result(xml, search, append_href, list);
1460 free_xml(xml);
1461 }
1462
1463 static int
1464 read_file_contents(const char *file, char **contents)
1465 {
1466 int rc = pcmk_rc_ok;
1467 char *path = NULL;
1468
1469 if (pcmk__ends_with(file, ".rng")) {
1470 path = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_rng, file);
1471 } else {
1472 path = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt, file);
1473 }
1474
1475 rc = pcmk__file_contents(path, contents);
1476
1477 free(path);
1478 return rc;
1479 }
1480
1481 static void
1482 add_schema_file_to_xml(xmlNode *parent, const char *file, GList **already_included)
1483 {
1484 char *contents = NULL;
1485 char *path = NULL;
1486 xmlNode *file_node = NULL;
1487 GList *includes = NULL;
1488 int rc = pcmk_rc_ok;
1489
1490
1491 if (g_list_find_custom(*already_included, file, (GCompareFunc) strcmp) != NULL) {
1492 return;
1493 }
1494
1495
1496
1497
1498 if (!pcmk__ends_with(file, ".rng") && !pcmk__ends_with(file, ".xsl")) {
1499 path = crm_strdup_printf("%s.rng", file);
1500 } else {
1501 path = pcmk__str_copy(file);
1502 }
1503
1504 rc = read_file_contents(path, &contents);
1505 if (rc != pcmk_rc_ok || contents == NULL) {
1506 crm_warn("Could not read schema file %s: %s", file, pcmk_rc_str(rc));
1507 free(path);
1508 return;
1509 }
1510
1511
1512
1513
1514 file_node = pcmk__xe_create(parent, PCMK_XA_FILE);
1515 crm_xml_add(file_node, PCMK_XA_PATH, path);
1516 *already_included = g_list_prepend(*already_included, path);
1517
1518 xmlAddChild(file_node, xmlNewCDataBlock(parent->doc, (pcmkXmlStr) contents,
1519 strlen(contents)));
1520
1521
1522
1523
1524 external_refs_in_schema(&includes, contents);
1525
1526
1527
1528
1529 for (GList *iter = includes; iter != NULL; iter = iter->next) {
1530 add_schema_file_to_xml(parent, iter->data, already_included);
1531 }
1532
1533 free(contents);
1534 g_list_free_full(includes, free);
1535 }
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551 void
1552 pcmk__build_schema_xml_node(xmlNode *parent, const char *name, GList **already_included)
1553 {
1554 xmlNode *schema_node = pcmk__xe_create(parent, PCMK__XA_SCHEMA);
1555
1556 crm_xml_add(schema_node, PCMK_XA_VERSION, name);
1557 add_schema_file_to_xml(schema_node, name, already_included);
1558
1559 if (schema_node->children == NULL) {
1560
1561 free_xml(schema_node);
1562 }
1563 }
1564
1565
1566
1567
1568
1569
1570 const char *
1571 pcmk__remote_schema_dir(void)
1572 {
1573 const char *dir = pcmk__env_option(PCMK__ENV_REMOTE_SCHEMA_DIRECTORY);
1574
1575 if (pcmk__str_empty(dir)) {
1576 return PCMK__REMOTE_SCHEMA_DIR;
1577 }
1578
1579 return dir;
1580 }
1581
1582
1583
1584
1585
1586
1587
1588 void
1589 pcmk__warn_if_schema_deprecated(const char *schema)
1590 {
1591 if ((schema == NULL) ||
1592 pcmk__strcase_any_of(schema, "pacemaker-next", PCMK_VALUE_NONE, NULL)) {
1593 pcmk__config_warn("Support for " PCMK_XA_VALIDATE_WITH "='%s' is "
1594 "deprecated and will be removed in a future release "
1595 "without the possibility of upgrades (manually edit "
1596 "to use a supported schema)", pcmk__s(schema, ""));
1597 }
1598 }
1599
1600
1601
1602
1603 #include <crm/common/xml_compat.h>
1604
1605 const char *
1606 xml_latest_schema(void)
1607 {
1608 return pcmk__highest_schema_name();
1609 }
1610
1611 const char *
1612 get_schema_name(int version)
1613 {
1614 pcmk__schema_t *schema = g_list_nth_data(known_schemas, version);
1615
1616 return (schema != NULL)? schema->name : "unknown";
1617 }
1618
1619 int
1620 get_schema_version(const char *name)
1621 {
1622 int lpc = 0;
1623
1624 if (name == NULL) {
1625 name = PCMK_VALUE_NONE;
1626 }
1627
1628 for (GList *iter = known_schemas; iter != NULL; iter = iter->next) {
1629 pcmk__schema_t *schema = iter->data;
1630
1631 if (pcmk__str_eq(name, schema->name, pcmk__str_casei)) {
1632 return lpc;
1633 }
1634
1635 lpc++;
1636 }
1637
1638 return -1;
1639 }
1640
1641 int
1642 update_validation(xmlNode **xml, int *best, int max, gboolean transform,
1643 gboolean to_logs)
1644 {
1645 int rc = pcmk__update_schema(xml, get_schema_name(max), transform, to_logs);
1646
1647 if ((best != NULL) && (xml != NULL) && (rc == pcmk_rc_ok)) {
1648 const char *schema_name = crm_element_value(*xml,
1649 PCMK_XA_VALIDATE_WITH);
1650 GList *schema_entry = pcmk__get_schema(schema_name);
1651
1652 if (schema_entry != NULL) {
1653 *best = ((pcmk__schema_t *)(schema_entry->data))->schema_index;
1654 }
1655 }
1656
1657 return pcmk_rc2legacy(rc);
1658 }
1659
1660 gboolean
1661 validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
1662 {
1663 bool rc = pcmk__validate_xml(xml_blob, validation,
1664 to_logs? (xmlRelaxNGValidityErrorFunc) xml_log : NULL,
1665 GUINT_TO_POINTER(LOG_ERR));
1666 return rc? TRUE : FALSE;
1667 }
1668
1669 static void
1670 dump_file(const char *filename)
1671 {
1672
1673 FILE *fp = NULL;
1674 int ch, line = 0;
1675
1676 CRM_CHECK(filename != NULL, return);
1677
1678 fp = fopen(filename, "r");
1679 if (fp == NULL) {
1680 crm_perror(LOG_ERR, "Could not open %s for reading", filename);
1681 return;
1682 }
1683
1684 fprintf(stderr, "%4d ", ++line);
1685 do {
1686 ch = getc(fp);
1687 if (ch == EOF) {
1688 putc('\n', stderr);
1689 break;
1690 } else if (ch == '\n') {
1691 fprintf(stderr, "\n%4d ", ++line);
1692 } else {
1693 putc(ch, stderr);
1694 }
1695 } while (1);
1696
1697 fclose(fp);
1698 }
1699
1700 gboolean
1701 validate_xml_verbose(const xmlNode *xml_blob)
1702 {
1703 int fd = 0;
1704 xmlDoc *doc = NULL;
1705 xmlNode *xml = NULL;
1706 gboolean rc = FALSE;
1707 char *filename = NULL;
1708
1709 filename = crm_strdup_printf("%s/cib-invalid.XXXXXX", pcmk__get_tmpdir());
1710
1711 umask(S_IWGRP | S_IWOTH | S_IROTH);
1712 fd = mkstemp(filename);
1713 pcmk__xml_write_fd(xml_blob, filename, fd, false, NULL);
1714
1715 dump_file(filename);
1716
1717 doc = xmlReadFile(filename, NULL, 0);
1718 xml = xmlDocGetRootElement(doc);
1719 rc = pcmk__validate_xml(xml, NULL, NULL, NULL);
1720 free_xml(xml);
1721
1722 unlink(filename);
1723 free(filename);
1724
1725 return rc? TRUE : FALSE;
1726 }
1727
1728 gboolean
1729 cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
1730 {
1731 int rc = pcmk__update_configured_schema(xml, to_logs);
1732
1733 if (best_version != NULL) {
1734 const char *name = crm_element_value(*xml, PCMK_XA_VALIDATE_WITH);
1735
1736 if (name == NULL) {
1737 *best_version = -1;
1738 } else {
1739 GList *entry = pcmk__get_schema(name);
1740 pcmk__schema_t *schema = (entry == NULL)? NULL : entry->data;
1741
1742 *best_version = (schema == NULL)? -1 : schema->schema_index;
1743 }
1744 }
1745 return (rc == pcmk_rc_ok)? TRUE: FALSE;
1746 }
1747
1748
1749