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 pcmk__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 pcmk__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
988
989
990 res = xsltApplyStylesheet(xslt, xml->doc, NULL);
991 CRM_CHECK(res != NULL, goto cleanup);
992
993 xsltSetGenericErrorFunc(NULL, NULL);
994
995 out = xmlDocGetRootElement(res);
996
997 cleanup:
998 if (xslt) {
999 xsltFreeStylesheet(xslt);
1000 }
1001
1002 free(xform);
1003
1004 return out;
1005 }
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023 static xmlNode *
1024 apply_upgrade(const xmlNode *original_xml, int schema_index, gboolean to_logs)
1025 {
1026 pcmk__schema_t *schema = g_list_nth_data(known_schemas, schema_index);
1027 pcmk__schema_t *upgraded_schema = g_list_nth_data(known_schemas,
1028 schema_index + 1);
1029 bool transform_onleave = false;
1030 char *transform_leave;
1031 const xmlNode *xml = original_xml;
1032 xmlNode *upgrade = NULL;
1033 xmlNode *final = NULL;
1034 xmlRelaxNGValidityErrorFunc error_handler = NULL;
1035
1036 pcmk__assert((schema != NULL) && (upgraded_schema != NULL));
1037
1038 if (to_logs) {
1039 error_handler = (xmlRelaxNGValidityErrorFunc) xml_log;
1040 }
1041
1042 transform_onleave = schema->transform_onleave;
1043 if (schema->transform_enter != NULL) {
1044 crm_debug("Upgrading schema from %s to %s: "
1045 "applying pre-upgrade XSL transform %s",
1046 schema->name, upgraded_schema->name, schema->transform_enter);
1047 upgrade = apply_transformation(xml, schema->transform_enter, to_logs);
1048 if (upgrade == NULL) {
1049 crm_warn("Pre-upgrade XSL transform %s failed, "
1050 "will skip post-upgrade transform",
1051 schema->transform_enter);
1052 transform_onleave = FALSE;
1053 } else {
1054 xml = upgrade;
1055 }
1056 }
1057
1058
1059 crm_debug("Upgrading schema from %s to %s: "
1060 "applying upgrade XSL transform %s",
1061 schema->name, upgraded_schema->name, schema->transform);
1062 final = apply_transformation(xml, schema->transform, to_logs);
1063 if (upgrade != xml) {
1064 free_xml(upgrade);
1065
1066 }
1067
1068 if ((final != NULL) && transform_onleave) {
1069 upgrade = final;
1070
1071 pcmk__assert(schema->transform_enter != NULL);
1072 transform_leave = strdup(schema->transform_enter);
1073
1074 memcpy(strrchr(transform_leave, '-') + 1, "leave", sizeof("leave") - 1);
1075 crm_debug("Upgrading schema from %s to %s: "
1076 "applying post-upgrade XSL transform %s",
1077 schema->name, upgraded_schema->name, transform_leave);
1078 final = apply_transformation(upgrade, transform_leave, to_logs);
1079 if (final == NULL) {
1080 crm_warn("Ignoring failure of post-upgrade XSL transform %s",
1081 transform_leave);
1082 final = upgrade;
1083 } else {
1084 free_xml(upgrade);
1085 }
1086 free(transform_leave);
1087 }
1088
1089 if (final == NULL) {
1090 return NULL;
1091 }
1092
1093
1094 pcmk__xml_new_private_data((xmlNode *) final->doc);
1095
1096
1097 if (!validate_with(final, upgraded_schema, error_handler,
1098 GUINT_TO_POINTER(LOG_ERR))) {
1099 crm_err("Schema upgrade from %s to %s failed: "
1100 "XSL transform %s produced an invalid configuration",
1101 schema->name, upgraded_schema->name, schema->transform);
1102 crm_log_xml_debug(final, "bad-transform-result");
1103 free_xml(final);
1104 return NULL;
1105 }
1106
1107 crm_info("Schema upgrade from %s to %s succeeded",
1108 schema->name, upgraded_schema->name);
1109 return final;
1110 }
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120 static GList *
1121 get_configured_schema(const xmlNode *xml)
1122 {
1123 const char *schema_name = crm_element_value(xml, PCMK_XA_VALIDATE_WITH);
1124
1125 pcmk__warn_if_schema_deprecated(schema_name);
1126 if (schema_name == NULL) {
1127 return NULL;
1128 }
1129 return pcmk__get_schema(schema_name);
1130 }
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146 int
1147 pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform,
1148 bool to_logs)
1149 {
1150 int max_stable_schemas = xml_latest_schema_index();
1151 int max_schema_index = 0;
1152 int rc = pcmk_rc_ok;
1153 GList *entry = NULL;
1154 pcmk__schema_t *best_schema = NULL;
1155 pcmk__schema_t *original_schema = NULL;
1156 xmlRelaxNGValidityErrorFunc error_handler =
1157 to_logs ? (xmlRelaxNGValidityErrorFunc) xml_log : NULL;
1158
1159 CRM_CHECK((xml != NULL) && (*xml != NULL) && ((*xml)->doc != NULL),
1160 return EINVAL);
1161
1162 if (max_schema_name != NULL) {
1163 GList *max_entry = pcmk__get_schema(max_schema_name);
1164
1165 if (max_entry != NULL) {
1166 pcmk__schema_t *max_schema = max_entry->data;
1167
1168 max_schema_index = max_schema->schema_index;
1169 }
1170 }
1171 if ((max_schema_index < 1) || (max_schema_index > max_stable_schemas)) {
1172 max_schema_index = max_stable_schemas;
1173 }
1174
1175 entry = get_configured_schema(*xml);
1176 if (entry == NULL) {
1177
1178 entry = known_schemas;
1179 } else {
1180 original_schema = entry->data;
1181 if (original_schema->schema_index >= max_schema_index) {
1182 return pcmk_rc_ok;
1183 }
1184 }
1185
1186 for (; entry != NULL; entry = entry->next) {
1187 pcmk__schema_t *current_schema = entry->data;
1188 xmlNode *upgrade = NULL;
1189
1190 if (current_schema->schema_index > max_schema_index) {
1191 break;
1192 }
1193
1194 if (!validate_with(*xml, current_schema, error_handler,
1195 GUINT_TO_POINTER(LOG_ERR))) {
1196 crm_debug("Schema %s does not validate", current_schema->name);
1197 if (best_schema != NULL) {
1198
1199 break;
1200 }
1201 rc = pcmk_rc_schema_validation;
1202 continue;
1203 }
1204
1205 crm_debug("Schema %s validates", current_schema->name);
1206 rc = pcmk_rc_ok;
1207 best_schema = current_schema;
1208 if (current_schema->schema_index == max_schema_index) {
1209 break;
1210 }
1211
1212 if (!transform || (current_schema->transform == NULL)
1213 || validate_with_silent(*xml, entry->next->data)) {
1214
1215
1216
1217
1218 continue;
1219 }
1220
1221 upgrade = apply_upgrade(*xml, current_schema->schema_index, to_logs);
1222 if (upgrade == NULL) {
1223
1224
1225
1226
1227 rc = pcmk_rc_transform_failed;
1228 } else {
1229 best_schema = current_schema;
1230 free_xml(*xml);
1231 *xml = upgrade;
1232 }
1233 }
1234
1235 if (best_schema != NULL) {
1236 if ((original_schema == NULL)
1237 || (best_schema->schema_index > original_schema->schema_index)) {
1238 crm_info("%s the configuration schema to %s",
1239 (transform? "Transformed" : "Upgraded"),
1240 best_schema->name);
1241 crm_xml_add(*xml, PCMK_XA_VALIDATE_WITH, best_schema->name);
1242 }
1243 }
1244 return rc;
1245 }
1246
1247 int
1248 pcmk_update_configured_schema(xmlNode **xml)
1249 {
1250 return pcmk__update_configured_schema(xml, true);
1251 }
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262 int
1263 pcmk__update_configured_schema(xmlNode **xml, bool to_logs)
1264 {
1265 int rc = pcmk_rc_ok;
1266 char *original_schema_name = NULL;
1267
1268
1269 const char *effective_original_name = "the first";
1270
1271 int orig_version = -1;
1272 pcmk__schema_t *x_0_schema = pcmk__find_x_0_schema()->data;
1273 GList *entry = NULL;
1274
1275 CRM_CHECK(xml != NULL, return EINVAL);
1276
1277 original_schema_name = crm_element_value_copy(*xml, PCMK_XA_VALIDATE_WITH);
1278 pcmk__warn_if_schema_deprecated(original_schema_name);
1279 entry = pcmk__get_schema(original_schema_name);
1280 if (entry != NULL) {
1281 pcmk__schema_t *original_schema = entry->data;
1282
1283 effective_original_name = original_schema->name;
1284 orig_version = original_schema->schema_index;
1285 }
1286
1287 if (orig_version < x_0_schema->schema_index) {
1288
1289 xmlNode *converted = NULL;
1290 const char *new_schema_name = NULL;
1291 pcmk__schema_t *schema = NULL;
1292
1293 entry = NULL;
1294 converted = pcmk__xml_copy(NULL, *xml);
1295 if (pcmk__update_schema(&converted, NULL, true, to_logs) == pcmk_rc_ok) {
1296 new_schema_name = crm_element_value(converted,
1297 PCMK_XA_VALIDATE_WITH);
1298 entry = pcmk__get_schema(new_schema_name);
1299 }
1300 schema = (entry == NULL)? NULL : entry->data;
1301
1302 if ((schema == NULL)
1303 || (schema->schema_index < x_0_schema->schema_index)) {
1304
1305
1306 if ((orig_version == -1) || (schema == NULL)
1307 || (schema->schema_index < orig_version)) {
1308
1309 if (to_logs) {
1310 pcmk__config_err("Cannot upgrade configuration (claiming "
1311 "%s schema) to at least %s because it "
1312 "does not validate with any schema from "
1313 "%s to the latest",
1314 pcmk__s(original_schema_name, "no"),
1315 x_0_schema->name, effective_original_name);
1316 } else {
1317 fprintf(stderr, "Cannot upgrade configuration (claiming "
1318 "%s schema) to at least %s because it "
1319 "does not validate with any schema from "
1320 "%s to the latest\n",
1321 pcmk__s(original_schema_name, "no"),
1322 x_0_schema->name, effective_original_name);
1323 }
1324 } else {
1325
1326 if (to_logs) {
1327 pcmk__config_err("Cannot upgrade configuration (claiming "
1328 "%s schema) to at least %s because it "
1329 "would not upgrade past %s",
1330 pcmk__s(original_schema_name, "no"),
1331 x_0_schema->name,
1332 pcmk__s(new_schema_name, "unspecified version"));
1333 } else {
1334 fprintf(stderr, "Cannot upgrade configuration (claiming "
1335 "%s schema) to at least %s because it "
1336 "would not upgrade past %s\n",
1337 pcmk__s(original_schema_name, "no"),
1338 x_0_schema->name,
1339 pcmk__s(new_schema_name, "unspecified version"));
1340 }
1341 }
1342
1343 free_xml(converted);
1344 converted = NULL;
1345 rc = pcmk_rc_transform_failed;
1346
1347 } else {
1348
1349 free_xml(*xml);
1350 *xml = converted;
1351
1352 if (schema->schema_index < xml_latest_schema_index()) {
1353 if (to_logs) {
1354 pcmk__config_warn("Configuration with %s schema was "
1355 "internally upgraded to acceptable (but "
1356 "not most recent) %s",
1357 pcmk__s(original_schema_name, "no"),
1358 schema->name);
1359 }
1360 } else if (to_logs) {
1361 crm_info("Configuration with %s schema was internally "
1362 "upgraded to latest version %s",
1363 pcmk__s(original_schema_name, "no"),
1364 schema->name);
1365 }
1366 }
1367
1368 } else {
1369
1370 pcmk__schema_t *none_schema = NULL;
1371
1372 entry = pcmk__get_schema(PCMK_VALUE_NONE);
1373 pcmk__assert((entry != NULL) && (entry->data != NULL));
1374
1375 none_schema = entry->data;
1376 if (!to_logs && (orig_version >= none_schema->schema_index)) {
1377 fprintf(stderr, "Schema validation of configuration is "
1378 "disabled (support for " PCMK_XA_VALIDATE_WITH
1379 " set to \"" PCMK_VALUE_NONE "\" is deprecated"
1380 " and will be removed in a future release)\n");
1381 }
1382 }
1383
1384 free(original_schema_name);
1385 return rc;
1386 }
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400 GList *
1401 pcmk__schema_files_later_than(const char *name)
1402 {
1403 GList *lst = NULL;
1404 pcmk__schema_version_t ver;
1405
1406 if (!version_from_filename(name, &ver)) {
1407 return lst;
1408 }
1409
1410 for (GList *iter = g_list_nth(known_schemas, xml_latest_schema_index());
1411 iter != NULL; iter = iter->prev) {
1412 pcmk__schema_t *schema = iter->data;
1413 char *s = NULL;
1414
1415 if (schema_cmp(ver, schema->version) != -1) {
1416 continue;
1417 }
1418
1419 s = crm_strdup_printf("%s.rng", schema->name);
1420 lst = g_list_prepend(lst, s);
1421
1422 if (schema->transform != NULL) {
1423 char *xform = crm_strdup_printf("%s.xsl", schema->transform);
1424 lst = g_list_prepend(lst, xform);
1425 }
1426
1427 if (schema->transform_enter != NULL) {
1428 char *enter = crm_strdup_printf("%s.xsl", schema->transform_enter);
1429
1430 lst = g_list_prepend(lst, enter);
1431
1432 if (schema->transform_onleave) {
1433 int last_dash = strrchr(enter, '-') - enter;
1434 char *leave = crm_strdup_printf("%.*s-leave.xsl", last_dash, enter);
1435
1436 lst = g_list_prepend(lst, leave);
1437 }
1438 }
1439 }
1440
1441 return lst;
1442 }
1443
1444 static void
1445 append_href(xmlNode *xml, void *user_data)
1446 {
1447 GList **list = user_data;
1448 char *href = crm_element_value_copy(xml, "href");
1449
1450 if (href == NULL) {
1451 return;
1452 }
1453 *list = g_list_prepend(*list, href);
1454 }
1455
1456 static void
1457 external_refs_in_schema(GList **list, const char *contents)
1458 {
1459
1460
1461
1462 const char *search = "//*[local-name()='externalRef'] | //*[local-name()='include']";
1463 xmlNode *xml = pcmk__xml_parse(contents);
1464
1465 crm_foreach_xpath_result(xml, search, append_href, list);
1466 free_xml(xml);
1467 }
1468
1469 static int
1470 read_file_contents(const char *file, char **contents)
1471 {
1472 int rc = pcmk_rc_ok;
1473 char *path = NULL;
1474
1475 if (pcmk__ends_with(file, ".rng")) {
1476 path = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_rng, file);
1477 } else {
1478 path = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt, file);
1479 }
1480
1481 rc = pcmk__file_contents(path, contents);
1482
1483 free(path);
1484 return rc;
1485 }
1486
1487 static void
1488 add_schema_file_to_xml(xmlNode *parent, const char *file, GList **already_included)
1489 {
1490 char *contents = NULL;
1491 char *path = NULL;
1492 xmlNode *file_node = NULL;
1493 GList *includes = NULL;
1494 int rc = pcmk_rc_ok;
1495
1496
1497 if (g_list_find_custom(*already_included, file, (GCompareFunc) strcmp) != NULL) {
1498 return;
1499 }
1500
1501
1502
1503
1504 if (!pcmk__ends_with(file, ".rng") && !pcmk__ends_with(file, ".xsl")) {
1505 path = crm_strdup_printf("%s.rng", file);
1506 } else {
1507 path = pcmk__str_copy(file);
1508 }
1509
1510 rc = read_file_contents(path, &contents);
1511 if (rc != pcmk_rc_ok || contents == NULL) {
1512 crm_warn("Could not read schema file %s: %s", file, pcmk_rc_str(rc));
1513 free(path);
1514 return;
1515 }
1516
1517
1518
1519
1520 file_node = pcmk__xe_create(parent, PCMK_XA_FILE);
1521 crm_xml_add(file_node, PCMK_XA_PATH, path);
1522 *already_included = g_list_prepend(*already_included, path);
1523
1524 xmlAddChild(file_node, xmlNewCDataBlock(parent->doc, (pcmkXmlStr) contents,
1525 strlen(contents)));
1526
1527
1528
1529
1530 external_refs_in_schema(&includes, contents);
1531
1532
1533
1534
1535 for (GList *iter = includes; iter != NULL; iter = iter->next) {
1536 add_schema_file_to_xml(parent, iter->data, already_included);
1537 }
1538
1539 free(contents);
1540 g_list_free_full(includes, free);
1541 }
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557 void
1558 pcmk__build_schema_xml_node(xmlNode *parent, const char *name, GList **already_included)
1559 {
1560 xmlNode *schema_node = pcmk__xe_create(parent, PCMK__XA_SCHEMA);
1561
1562 crm_xml_add(schema_node, PCMK_XA_VERSION, name);
1563 add_schema_file_to_xml(schema_node, name, already_included);
1564
1565 if (schema_node->children == NULL) {
1566
1567 free_xml(schema_node);
1568 }
1569 }
1570
1571
1572
1573
1574
1575
1576 const char *
1577 pcmk__remote_schema_dir(void)
1578 {
1579 const char *dir = pcmk__env_option(PCMK__ENV_REMOTE_SCHEMA_DIRECTORY);
1580
1581 if (pcmk__str_empty(dir)) {
1582 return PCMK__REMOTE_SCHEMA_DIR;
1583 }
1584
1585 return dir;
1586 }
1587
1588
1589
1590
1591
1592
1593
1594 void
1595 pcmk__warn_if_schema_deprecated(const char *schema)
1596 {
1597 if ((schema == NULL) ||
1598 pcmk__strcase_any_of(schema, "pacemaker-next", PCMK_VALUE_NONE, NULL)) {
1599 pcmk__config_warn("Support for " PCMK_XA_VALIDATE_WITH "='%s' is "
1600 "deprecated and will be removed in a future release "
1601 "without the possibility of upgrades (manually edit "
1602 "to use a supported schema)", pcmk__s(schema, ""));
1603 }
1604 }
1605
1606
1607
1608
1609 #include <crm/common/xml_compat.h>
1610
1611 const char *
1612 xml_latest_schema(void)
1613 {
1614 return pcmk__highest_schema_name();
1615 }
1616
1617 const char *
1618 get_schema_name(int version)
1619 {
1620 pcmk__schema_t *schema = g_list_nth_data(known_schemas, version);
1621
1622 return (schema != NULL)? schema->name : "unknown";
1623 }
1624
1625 int
1626 get_schema_version(const char *name)
1627 {
1628 int lpc = 0;
1629
1630 if (name == NULL) {
1631 name = PCMK_VALUE_NONE;
1632 }
1633
1634 for (GList *iter = known_schemas; iter != NULL; iter = iter->next) {
1635 pcmk__schema_t *schema = iter->data;
1636
1637 if (pcmk__str_eq(name, schema->name, pcmk__str_casei)) {
1638 return lpc;
1639 }
1640
1641 lpc++;
1642 }
1643
1644 return -1;
1645 }
1646
1647 int
1648 update_validation(xmlNode **xml, int *best, int max, gboolean transform,
1649 gboolean to_logs)
1650 {
1651 int rc = pcmk__update_schema(xml, get_schema_name(max), transform, to_logs);
1652
1653 if ((best != NULL) && (xml != NULL) && (rc == pcmk_rc_ok)) {
1654 const char *schema_name = crm_element_value(*xml,
1655 PCMK_XA_VALIDATE_WITH);
1656 GList *schema_entry = pcmk__get_schema(schema_name);
1657
1658 if (schema_entry != NULL) {
1659 *best = ((pcmk__schema_t *)(schema_entry->data))->schema_index;
1660 }
1661 }
1662
1663 return pcmk_rc2legacy(rc);
1664 }
1665
1666 gboolean
1667 validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
1668 {
1669 bool rc = pcmk__validate_xml(xml_blob, validation,
1670 to_logs? (xmlRelaxNGValidityErrorFunc) xml_log : NULL,
1671 GUINT_TO_POINTER(LOG_ERR));
1672 return rc? TRUE : FALSE;
1673 }
1674
1675 static void
1676 dump_file(const char *filename)
1677 {
1678
1679 FILE *fp = NULL;
1680 int ch, line = 0;
1681
1682 CRM_CHECK(filename != NULL, return);
1683
1684 fp = fopen(filename, "r");
1685 if (fp == NULL) {
1686 crm_perror(LOG_ERR, "Could not open %s for reading", filename);
1687 return;
1688 }
1689
1690 fprintf(stderr, "%4d ", ++line);
1691 do {
1692 ch = getc(fp);
1693 if (ch == EOF) {
1694 putc('\n', stderr);
1695 break;
1696 } else if (ch == '\n') {
1697 fprintf(stderr, "\n%4d ", ++line);
1698 } else {
1699 putc(ch, stderr);
1700 }
1701 } while (1);
1702
1703 fclose(fp);
1704 }
1705
1706 gboolean
1707 validate_xml_verbose(const xmlNode *xml_blob)
1708 {
1709 int fd = 0;
1710 xmlDoc *doc = NULL;
1711 xmlNode *xml = NULL;
1712 gboolean rc = FALSE;
1713 char *filename = NULL;
1714
1715 filename = crm_strdup_printf("%s/cib-invalid.XXXXXX", pcmk__get_tmpdir());
1716
1717 umask(S_IWGRP | S_IWOTH | S_IROTH);
1718 fd = mkstemp(filename);
1719 pcmk__xml_write_fd(xml_blob, filename, fd, false, NULL);
1720
1721 dump_file(filename);
1722
1723 doc = xmlReadFile(filename, NULL, 0);
1724 xml = xmlDocGetRootElement(doc);
1725 rc = pcmk__validate_xml(xml, NULL, NULL, NULL);
1726 free_xml(xml);
1727
1728 unlink(filename);
1729 free(filename);
1730
1731 return rc? TRUE : FALSE;
1732 }
1733
1734 gboolean
1735 cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
1736 {
1737 int rc = pcmk__update_configured_schema(xml, to_logs);
1738
1739 if (best_version != NULL) {
1740 const char *name = crm_element_value(*xml, PCMK_XA_VALIDATE_WITH);
1741
1742 if (name == NULL) {
1743 *best_version = -1;
1744 } else {
1745 GList *entry = pcmk__get_schema(name);
1746 pcmk__schema_t *schema = (entry == NULL)? NULL : entry->data;
1747
1748 *best_version = (schema == NULL)? -1 : schema->schema_index;
1749 }
1750 }
1751 return (rc == pcmk_rc_ok)? TRUE: FALSE;
1752 }
1753
1754
1755