pacemaker 2.1.8-2.1.8~rc4
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
schemas.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2024 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
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> /* PCMK__XML_LOG_BASE */
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
35typedef struct {
36 xmlRelaxNGPtr rng;
37 xmlRelaxNGValidCtxtPtr valid;
38 xmlRelaxNGParserCtxtPtr parser;
39} relaxng_ctx_cache_t;
40
41static GList *known_schemas = NULL;
42static bool initialized = false;
43static bool silent_logging = FALSE;
44
45static void G_GNUC_PRINTF(2, 3)
46xml_log(int priority, const char *fmt, ...)
47{
48 va_list ap;
49
50 va_start(ap, fmt);
51 if (silent_logging == FALSE) {
52 /* XXX should not this enable dechunking as well? */
53 PCMK__XML_LOG_BASE(priority, FALSE, 0, NULL, fmt, ap);
54 }
55 va_end(ap);
56}
57
58static int
59xml_latest_schema_index(void)
60{
61 /* This function assumes that crm_schema_init() has been called beforehand,
62 * so we have at least three schemas (one real schema, the "pacemaker-next"
63 * schema, and the "none" schema).
64 *
65 * @COMPAT: pacemaker-next is deprecated since 2.1.5 and none since 2.1.8.
66 * Update this when we drop those.
67 */
68 return g_list_length(known_schemas) - 3;
69}
70
77static GList *
78get_highest_schema(void)
79{
80 /* The highest numerically versioned schema is the one before pacemaker-next
81 *
82 * @COMPAT pacemaker-next is deprecated since 2.1.5
83 */
84 GList *entry = pcmk__get_schema("pacemaker-next");
85
86 CRM_ASSERT((entry != NULL) && (entry->prev != NULL));
87 return entry->prev;
88}
89
96const char *
98{
99 GList *entry = get_highest_schema();
100
101 return ((pcmk__schema_t *)(entry->data))->name;
102}
103
110GList *
112{
113#if defined(PCMK__UNIT_TESTING)
114 /* If we're unit testing, this can't be static because it'll stick
115 * around from one test run to the next. It needs to be cleared out
116 * every time.
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 /* We've found a schema in an older major version series. Return
135 * the index of the first one in the same major version series as
136 * the highest schema.
137 */
138 if (schema->version.v[0] < highest_schema->version.v[0]) {
139 x_0_entry = iter->next;
140 break;
141 }
142
143 /* We're out of list to examine. This probably means there was only
144 * one major version series, so return the first schema entry.
145 */
146 if (iter->prev == NULL) {
147 x_0_entry = known_schemas->data;
148 break;
149 }
150 }
151 return x_0_entry;
152}
153
154static inline bool
155version_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
164static int
165schema_filter(const struct dirent *a)
166{
167 int rc = 0;
169
170 if (strstr(a->d_name, "pacemaker-") != a->d_name) {
171 /* crm_trace("%s - wrong prefix", a->d_name); */
172
173 } else if (!pcmk__ends_with_ext(a->d_name, ".rng")) {
174 /* crm_trace("%s - wrong suffix", a->d_name); */
175
176 } else if (!version_from_filename(a->d_name, &version)) {
177 /* crm_trace("%s - wrong format", a->d_name); */
178
179 } else {
180 /* crm_debug("%s - candidate", a->d_name); */
181 rc = 1;
182 }
183
184 return rc;
185}
186
187static int
188schema_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
200static int
201schema_cmp_directory(const struct dirent **a, const struct dirent **b)
202{
205
206 if (!version_from_filename(a[0]->d_name, &a_version)
207 || !version_from_filename(b[0]->d_name, &b_version)) {
208 // Shouldn't be possible, but makes static analysis happy
209 return 0;
210 }
211
212 return schema_cmp(a_version, b_version);
213}
214
222static void
223add_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 // schema->schema_index is set after all schemas are loaded and sorted
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
282static int
283add_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 /* prologue for further transform_expected handling */
293 if (transform_expected) {
294 /* check if there's suitable "upgrade" stylesheet */
295 transform_upgrade = schema_strdup_printf("upgrade-", *version, );
297 transform_upgrade);
298 }
299
300 if (!transform_expected) {
301 /* jump directly to the end */
302
303 } else if (stat(xslt, &s) == 0) {
304 /* perhaps there's also a targeted "upgrade-enter" stylesheet */
305 transform_enter = schema_strdup_printf("upgrade-", *version, "-enter");
306 free(xslt);
308 transform_enter);
309 if (stat(xslt, &s) != 0) {
310 /* or initially, at least a generic one */
311 crm_debug("Upgrade-enter transform %s.xsl not found", xslt);
312 free(xslt);
313 free(transform_enter);
314 transform_enter = strdup("upgrade-enter");
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 /* xslt contains full path to "upgrade-enter" stylesheet */
324 if (xslt != NULL) {
325 /* then there should be "upgrade-leave" counterpart (enter->leave) */
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
351static void
352wrap_libxslt(bool finalize)
353{
354 static xsltSecurityPrefsPtr secprefs;
355 int ret = 0;
356
357 /* security framework preferences */
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 /* cleanup only */
378 if (finalize) {
379 xsltCleanupGlobals();
380 }
381}
382
383void
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;
398
399 if (!version_from_filename(namelist[lpc]->d_name, &version)) {
400 // Shouldn't be possible, but makes static analysis happy
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
426static gint
427schema_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 // @COMPAT pacemaker-next is deprecated since 2.1.5 and none since 2.1.8
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
456void
458{
459 known_schemas = g_list_sort(known_schemas, schema_sort_GCompareFunc);
460}
461
469void
471{
472 if (!initialized) {
473 const char *remote_schema_dir = pcmk__remote_schema_dir();
476 int schema_index = 0;
477
478 initialized = true;
479
480 wrap_libxslt(false);
481
483 pcmk__load_schemas_from_dir(remote_schema_dir);
484 free(base);
485
486 // @COMPAT: Deprecated since 2.1.5
487 add_schema(pcmk__schema_validator_rng, &zero, "pacemaker-next", NULL,
488 NULL, FALSE);
489
490 // @COMPAT Deprecated since 2.1.8
491 add_schema(pcmk__schema_validator_none, &zero, PCMK_VALUE_NONE, NULL,
492 NULL, FALSE);
493
494 /* add_schema() prepends items to the list, so in the simple case, this
495 * just reverses the list. However if there were any remote schemas,
496 * sorting is necessary.
497 */
499
500 // Now set the schema indexes and log the final result
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
515static bool
516validate_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
599static void
600free_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: // not cached
607 break;
608
609 case pcmk__schema_validator_rng: // cached
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
642void
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
662GList *
664{
665 // @COMPAT Not specifying a schema name is deprecated since 2.1.8
666 if (name == NULL) {
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
690int
691pcmk__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
710static bool
711validate_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
728 schema->name);
729
730 crm_trace("Validating with %s (type=%d)",
731 pcmk__s(file, "missing schema"), schema->validator);
732 switch (schema->validator) {
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
746static bool
747validate_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
756bool
757pcmk__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 }
770
771 // @COMPAT Not specifying a schema name is deprecated since 2.1.8
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
808bool
810{
811 return pcmk__validate_xml(xml, NULL,
812 (xmlRelaxNGValidityErrorFunc) xml_log,
813 GUINT_TO_POINTER(LOG_ERR));
814}
815
816/* With this arrangement, an attempt to identify the message severity
817 as explicitly signalled directly from XSLT is performed in rather
818 a smart way (no reliance on formatting string + arguments being
819 always specified as ["%s", purposeful_string], as it can also be
820 ["%s: %s", some_prefix, purposeful_string] etc. so every argument
821 pertaining %s specifier is investigated), and if such a mark found,
822 the respective level is determined and, when the messages are to go
823 to the native logs, the mark itself gets dropped
824 (by the means of string shift).
825
826 NOTE: whether the native logging is the right sink is decided per
827 the ctx parameter -- NULL denotes this case, otherwise it
828 carries a pointer to the numeric expression of the desired
829 target logging level (messages with higher level will be
830 suppressed)
831
832 NOTE: on some architectures, this string shift may not have any
833 effect, but that's an acceptable tradeoff
834
835 The logging level for not explicitly designated messages
836 (suspicious, likely internal errors or some runaways) is
837 LOG_WARNING.
838 */
839static void G_GNUC_PRINTF(2, 3)
840cib_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; /* default for runaway messages */
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 /* while casing schema borrowed from libqb:qb_vsnprintf_serialize */
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 *); /* skip forward */
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 /* intention of the following offset is:
942 cibadmin -V -> start showing INFO labelled messages */
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
965static xmlNode *
966apply_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
975 transform);
976
977 /* for capturing, e.g., what's emitted via <xsl:message> */
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); /* restore default one */
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
1020static xmlNode *
1021apply_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 /* following condition ensured in add_schema_by_version */
1068 CRM_ASSERT(schema->transform_enter != NULL);
1069 transform_leave = strdup(schema->transform_enter);
1070 /* enter -> leave */
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 // Ensure result validates with its new schema
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
1114static GList *
1115get_configured_schema(const xmlNode *xml)
1116{
1117 const char *schema_name = crm_element_value(xml, PCMK_XA_VALIDATE_WITH);
1118
1120 if (schema_name == NULL) {
1121 return NULL;
1122 }
1123 return pcmk__get_schema(schema_name);
1124}
1125
1140int
1141pcmk__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 // @COMPAT Not specifying a schema name is deprecated since 2.1.8
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 /* we've satisfied the validation, no need to check further */
1193 break;
1194 }
1196 continue; // Try again with the next higher schema
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; // No further transformations possible
1204 }
1205
1206 if (!transform || (current_schema->transform == NULL)
1207 || validate_with_silent(*xml, entry->next->data)) {
1208 /* The next schema either doesn't require a transform or validates
1209 * successfully even without the transform. Skip the transform and
1210 * try the next schema with the same XML.
1211 */
1212 continue;
1213 }
1214
1215 upgrade = apply_upgrade(*xml, current_schema->schema_index, to_logs);
1216 if (upgrade == NULL) {
1217 /* The transform failed, so this schema can't be used. Later
1218 * schemas are unlikely to validate, but try anyway until we
1219 * run out of options.
1220 */
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
1250int
1251pcmk_update_configured_schema(xmlNode **xml, bool to_logs)
1252{
1253 int rc = pcmk_rc_ok;
1254 char *original_schema_name = NULL;
1255
1256 // @COMPAT Not specifying a schema name is deprecated since 2.1.8
1257 const char *effective_original_name = "the first";
1258
1259 int orig_version = -1;
1260 pcmk__schema_t *x_0_schema = pcmk__find_x_0_schema()->data;
1261 GList *entry = NULL;
1262
1263 CRM_CHECK(xml != NULL, return EINVAL);
1264
1265 original_schema_name = crm_element_value_copy(*xml, PCMK_XA_VALIDATE_WITH);
1266 pcmk__warn_if_schema_deprecated(original_schema_name);
1267 entry = pcmk__get_schema(original_schema_name);
1268 if (entry != NULL) {
1269 pcmk__schema_t *original_schema = entry->data;
1270
1271 effective_original_name = original_schema->name;
1272 orig_version = original_schema->schema_index;
1273 }
1274
1275 if (orig_version < x_0_schema->schema_index) {
1276 // Current configuration schema is not acceptable, try to update
1277 xmlNode *converted = NULL;
1278 const char *new_schema_name = NULL;
1279 pcmk__schema_t *schema = NULL;
1280
1281 entry = NULL;
1282 converted = pcmk__xml_copy(NULL, *xml);
1283 if (pcmk__update_schema(&converted, NULL, true, to_logs) == pcmk_rc_ok) {
1284 new_schema_name = crm_element_value(converted,
1286 entry = pcmk__get_schema(new_schema_name);
1287 }
1288 schema = (entry == NULL)? NULL : entry->data;
1289
1290 if ((schema == NULL)
1291 || (schema->schema_index < x_0_schema->schema_index)) {
1292 // Updated configuration schema is still not acceptable
1293
1294 if ((orig_version == -1) || (schema == NULL)
1295 || (schema->schema_index < orig_version)) {
1296 // We couldn't validate any schema at all
1297 if (to_logs) {
1298 pcmk__config_err("Cannot upgrade configuration (claiming "
1299 "%s schema) to at least %s because it "
1300 "does not validate with any schema from "
1301 "%s to the latest",
1302 pcmk__s(original_schema_name, "no"),
1303 x_0_schema->name, effective_original_name);
1304 } else {
1305 fprintf(stderr, "Cannot upgrade configuration (claiming "
1306 "%s schema) to at least %s because it "
1307 "does not validate with any schema from "
1308 "%s to the latest\n",
1309 pcmk__s(original_schema_name, "no"),
1310 x_0_schema->name, effective_original_name);
1311 }
1312 } else {
1313 // We updated configuration successfully, but still too low
1314 if (to_logs) {
1315 pcmk__config_err("Cannot upgrade configuration (claiming "
1316 "%s schema) to at least %s because it "
1317 "would not upgrade past %s",
1318 pcmk__s(original_schema_name, "no"),
1319 x_0_schema->name,
1320 pcmk__s(new_schema_name, "unspecified version"));
1321 } else {
1322 fprintf(stderr, "Cannot upgrade configuration (claiming "
1323 "%s schema) to at least %s because it "
1324 "would not upgrade past %s\n",
1325 pcmk__s(original_schema_name, "no"),
1326 x_0_schema->name,
1327 pcmk__s(new_schema_name, "unspecified version"));
1328 }
1329 }
1330
1331 free_xml(converted);
1332 converted = NULL;
1334
1335 } else {
1336 // Updated configuration schema is acceptable
1337 free_xml(*xml);
1338 *xml = converted;
1339
1340 if (schema->schema_index < xml_latest_schema_index()) {
1341 if (to_logs) {
1342 pcmk__config_warn("Configuration with %s schema was "
1343 "internally upgraded to acceptable (but "
1344 "not most recent) %s",
1345 pcmk__s(original_schema_name, "no"),
1346 schema->name);
1347 }
1348 } else if (to_logs) {
1349 crm_info("Configuration with %s schema was internally "
1350 "upgraded to latest version %s",
1351 pcmk__s(original_schema_name, "no"),
1352 schema->name);
1353 }
1354 }
1355
1356 } else {
1357 // @COMPAT the none schema is deprecated since 2.1.8
1358 pcmk__schema_t *none_schema = NULL;
1359
1361 CRM_ASSERT((entry != NULL) && (entry->data != NULL));
1362
1363 none_schema = entry->data;
1364 if (!to_logs && (orig_version >= none_schema->schema_index)) {
1365 fprintf(stderr, "Schema validation of configuration is "
1366 "disabled (support for " PCMK_XA_VALIDATE_WITH
1367 " set to \"" PCMK_VALUE_NONE "\" is deprecated"
1368 " and will be removed in a future release)\n");
1369 }
1370 }
1371
1372 free(original_schema_name);
1373 return rc;
1374}
1375
1388GList *
1390{
1391 GList *lst = NULL;
1393
1394 if (!version_from_filename(name, &ver)) {
1395 return lst;
1396 }
1397
1398 for (GList *iter = g_list_nth(known_schemas, xml_latest_schema_index());
1399 iter != NULL; iter = iter->prev) {
1400 pcmk__schema_t *schema = iter->data;
1401 char *s = NULL;
1402
1403 if (schema_cmp(ver, schema->version) != -1) {
1404 continue;
1405 }
1406
1407 s = crm_strdup_printf("%s.rng", schema->name);
1408 lst = g_list_prepend(lst, s);
1409
1410 if (schema->transform != NULL) {
1411 char *xform = crm_strdup_printf("%s.xsl", schema->transform);
1412 lst = g_list_prepend(lst, xform);
1413 }
1414
1415 if (schema->transform_enter != NULL) {
1416 char *enter = crm_strdup_printf("%s.xsl", schema->transform_enter);
1417
1418 lst = g_list_prepend(lst, enter);
1419
1420 if (schema->transform_onleave) {
1421 int last_dash = strrchr(enter, '-') - enter;
1422 char *leave = crm_strdup_printf("%.*s-leave.xsl", last_dash, enter);
1423
1424 lst = g_list_prepend(lst, leave);
1425 }
1426 }
1427 }
1428
1429 return lst;
1430}
1431
1432static void
1433append_href(xmlNode *xml, void *user_data)
1434{
1435 GList **list = user_data;
1436 char *href = crm_element_value_copy(xml, "href");
1437
1438 if (href == NULL) {
1439 return;
1440 }
1441 *list = g_list_prepend(*list, href);
1442}
1443
1444static void
1445external_refs_in_schema(GList **list, const char *contents)
1446{
1447 /* local-name()= is needed to ignore the xmlns= setting at the top of
1448 * the XML file. Otherwise, the xpath query will always return nothing.
1449 */
1450 const char *search = "//*[local-name()='externalRef'] | //*[local-name()='include']";
1451 xmlNode *xml = pcmk__xml_parse(contents);
1452
1453 crm_foreach_xpath_result(xml, search, append_href, list);
1454 free_xml(xml);
1455}
1456
1457static int
1458read_file_contents(const char *file, char **contents)
1459{
1460 int rc = pcmk_rc_ok;
1461 char *path = NULL;
1462
1463 if (pcmk__ends_with(file, ".rng")) {
1465 } else {
1467 }
1468
1469 rc = pcmk__file_contents(path, contents);
1470
1471 free(path);
1472 return rc;
1473}
1474
1475static void
1476add_schema_file_to_xml(xmlNode *parent, const char *file, GList **already_included)
1477{
1478 char *contents = NULL;
1479 char *path = NULL;
1480 xmlNode *file_node = NULL;
1481 GList *includes = NULL;
1482 int rc = pcmk_rc_ok;
1483
1484 /* If we already included this file, don't do so again. */
1485 if (g_list_find_custom(*already_included, file, (GCompareFunc) strcmp) != NULL) {
1486 return;
1487 }
1488
1489 /* Ensure whatever file we were given has a suffix we know about. If not,
1490 * just assume it's an RNG file.
1491 */
1492 if (!pcmk__ends_with(file, ".rng") && !pcmk__ends_with(file, ".xsl")) {
1493 path = crm_strdup_printf("%s.rng", file);
1494 } else {
1495 path = pcmk__str_copy(file);
1496 }
1497
1498 rc = read_file_contents(path, &contents);
1499 if (rc != pcmk_rc_ok || contents == NULL) {
1500 crm_warn("Could not read schema file %s: %s", file, pcmk_rc_str(rc));
1501 free(path);
1502 return;
1503 }
1504
1505 /* Create a new <file path="..."> node with the contents of the file
1506 * as a CDATA block underneath it.
1507 */
1508 file_node = pcmk__xe_create(parent, PCMK_XA_FILE);
1509 crm_xml_add(file_node, PCMK_XA_PATH, path);
1510 *already_included = g_list_prepend(*already_included, path);
1511
1512 xmlAddChild(file_node, xmlNewCDataBlock(parent->doc, (pcmkXmlStr) contents,
1513 strlen(contents)));
1514
1515 /* Scan the file for any <externalRef> or <include> nodes and build up
1516 * a list of the files they reference.
1517 */
1518 external_refs_in_schema(&includes, contents);
1519
1520 /* For each referenced file, recurse to add it (and potentially anything it
1521 * references, ...) to the XML.
1522 */
1523 for (GList *iter = includes; iter != NULL; iter = iter->next) {
1524 add_schema_file_to_xml(parent, iter->data, already_included);
1525 }
1526
1527 free(contents);
1528 g_list_free_full(includes, free);
1529}
1530
1545void
1546pcmk__build_schema_xml_node(xmlNode *parent, const char *name, GList **already_included)
1547{
1548 xmlNode *schema_node = pcmk__xe_create(parent, PCMK__XA_SCHEMA);
1549
1550 crm_xml_add(schema_node, PCMK_XA_VERSION, name);
1551 add_schema_file_to_xml(schema_node, name, already_included);
1552
1553 if (schema_node->children == NULL) {
1554 // Not needed if empty. May happen if name was invalid, for example.
1555 free_xml(schema_node);
1556 }
1557}
1558
1564const char *
1566{
1568
1569 if (pcmk__str_empty(dir)) {
1571 }
1572
1573 return dir;
1574}
1575
1582void
1584{
1585 if ((schema == NULL) ||
1586 pcmk__strcase_any_of(schema, "pacemaker-next", PCMK_VALUE_NONE, NULL)) {
1587 pcmk__config_warn("Support for " PCMK_XA_VALIDATE_WITH "='%s' is "
1588 "deprecated and will be removed in a future release "
1589 "without the possibility of upgrades (manually edit "
1590 "to use a supported schema)", pcmk__s(schema, ""));
1591 }
1592}
1593
1594// Deprecated functions kept only for backward API compatibility
1595// LCOV_EXCL_START
1596
1597#include <crm/common/xml_compat.h>
1598
1599const char *
1601{
1603}
1604
1605const char *
1607{
1608 pcmk__schema_t *schema = g_list_nth_data(known_schemas, version);
1609
1610 return (schema != NULL)? schema->name : "unknown";
1611}
1612
1613int
1615{
1616 int lpc = 0;
1617
1618 if (name == NULL) {
1620 }
1621
1622 for (GList *iter = known_schemas; iter != NULL; iter = iter->next) {
1623 pcmk__schema_t *schema = iter->data;
1624
1625 if (pcmk__str_eq(name, schema->name, pcmk__str_casei)) {
1626 return lpc;
1627 }
1628
1629 lpc++;
1630 }
1631
1632 return -1;
1633}
1634
1635int
1636update_validation(xmlNode **xml, int *best, int max, gboolean transform,
1637 gboolean to_logs)
1638{
1639 int rc = pcmk__update_schema(xml, get_schema_name(max), transform, to_logs);
1640
1641 if ((best != NULL) && (xml != NULL) && (rc == pcmk_rc_ok)) {
1642 const char *schema_name = crm_element_value(*xml,
1644 GList *schema_entry = pcmk__get_schema(schema_name);
1645
1646 if (schema_entry != NULL) {
1647 *best = ((pcmk__schema_t *)(schema_entry->data))->schema_index;
1648 }
1649 }
1650
1651 return pcmk_rc2legacy(rc);
1652}
1653
1654gboolean
1655validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
1656{
1657 bool rc = pcmk__validate_xml(xml_blob, validation,
1658 to_logs? (xmlRelaxNGValidityErrorFunc) xml_log : NULL,
1659 GUINT_TO_POINTER(LOG_ERR));
1660 return rc? TRUE : FALSE;
1661}
1662
1663static void
1664dump_file(const char *filename)
1665{
1666
1667 FILE *fp = NULL;
1668 int ch, line = 0;
1669
1670 CRM_CHECK(filename != NULL, return);
1671
1672 fp = fopen(filename, "r");
1673 if (fp == NULL) {
1674 crm_perror(LOG_ERR, "Could not open %s for reading", filename);
1675 return;
1676 }
1677
1678 fprintf(stderr, "%4d ", ++line);
1679 do {
1680 ch = getc(fp);
1681 if (ch == EOF) {
1682 putc('\n', stderr);
1683 break;
1684 } else if (ch == '\n') {
1685 fprintf(stderr, "\n%4d ", ++line);
1686 } else {
1687 putc(ch, stderr);
1688 }
1689 } while (1);
1690
1691 fclose(fp);
1692}
1693
1694gboolean
1695validate_xml_verbose(const xmlNode *xml_blob)
1696{
1697 int fd = 0;
1698 xmlDoc *doc = NULL;
1699 xmlNode *xml = NULL;
1700 gboolean rc = FALSE;
1701 char *filename = NULL;
1702
1703 filename = crm_strdup_printf("%s/cib-invalid.XXXXXX", pcmk__get_tmpdir());
1704
1705 umask(S_IWGRP | S_IWOTH | S_IROTH);
1706 fd = mkstemp(filename);
1707 pcmk__xml_write_fd(xml_blob, filename, fd, false, NULL);
1708
1709 dump_file(filename);
1710
1711 doc = xmlReadFile(filename, NULL, 0);
1712 xml = xmlDocGetRootElement(doc);
1713 rc = pcmk__validate_xml(xml, NULL, NULL, NULL);
1714 free_xml(xml);
1715
1716 unlink(filename);
1717 free(filename);
1718
1719 return rc? TRUE : FALSE;
1720}
1721
1722gboolean
1723cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
1724{
1725 int rc = pcmk_update_configured_schema(xml, to_logs);
1726
1727 if (best_version != NULL) {
1728 const char *name = crm_element_value(*xml, PCMK_XA_VALIDATE_WITH);
1729
1730 if (name == NULL) {
1731 *best_version = -1;
1732 } else {
1733 GList *entry = pcmk__get_schema(name);
1734 pcmk__schema_t *schema = (entry == NULL)? NULL : entry->data;
1735
1736 *best_version = (schema == NULL)? -1 : schema->schema_index;
1737 }
1738 }
1739 return (rc == pcmk_rc_ok)? TRUE: FALSE;
1740}
1741
1742// LCOV_EXCL_STOP
1743// End deprecated API
const char * parent
Definition cib.c:27
const char * path
Definition cib.c:28
const char * name
Definition cib.c:26
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:297
uint32_t version
Definition remote.c:1
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define PCMK__REMOTE_SCHEMA_DIR
Definition config.h:565
char data[0]
Definition cpg.c:10
pcmk__schema_validator
@ pcmk__schema_validator_rng
@ pcmk__schema_validator_none
int pcmk__file_contents(const char *filename, char **contents)
Definition io.c:432
const char * pcmk__get_tmpdir(void)
Definition io.c:547
#define crm_info(fmt, args...)
Definition logging.h:397
#define crm_warn(fmt, args...)
Definition logging.h:392
#define crm_log_xml_debug(xml, text)
Definition logging.h:409
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:331
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_debug(fmt, args...)
Definition logging.h:400
#define crm_err(fmt, args...)
Definition logging.h:389
unsigned int crm_log_level
Definition logging.c:45
#define crm_trace(fmt, args...)
Definition logging.h:402
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition nvpair.c:674
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition nvpair.c:301
#define PCMK_VALUE_NONE
Definition options.h:178
#define PCMK__ENV_REMOTE_SCHEMA_DIRECTORY
const char * pcmk__env_option(const char *option)
Definition options.c:1088
#define CRM_ASSERT(expr)
Definition results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:501
@ pcmk_rc_transform_failed
Definition results.h:143
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_rc_schema_validation
Definition results.h:141
int pcmk_rc2legacy(int rc)
Definition results.c:546
const char * get_schema_name(int version)
Definition schemas.c:1606
int get_schema_version(const char *name)
Definition schemas.c:1614
#define SCHEMA_ZERO
Definition schemas.c:30
#define schema_strdup_printf(prefix, version, suffix)
Definition schemas.c:32
int pcmk_update_configured_schema(xmlNode **xml, bool to_logs)
Update XML from its configured schema to the latest major series.
Definition schemas.c:1251
GList * pcmk__schema_files_later_than(const char *name)
Definition schemas.c:1389
bool pcmk__validate_xml(xmlNode *xml_blob, const char *validation, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context)
Definition schemas.c:757
void pcmk__load_schemas_from_dir(const char *dir)
Definition schemas.c:384
GList * pcmk__get_schema(const char *name)
Definition schemas.c:663
void pcmk__sort_schemas(void)
Definition schemas.c:457
const char * pcmk__highest_schema_name(void)
Definition schemas.c:97
const char * xml_latest_schema(void)
Definition schemas.c:1600
GList * pcmk__find_x_0_schema(void)
Definition schemas.c:111
gboolean cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
Definition schemas.c:1723
const char * pcmk__remote_schema_dir(void)
Definition schemas.c:1565
bool pcmk__configured_schema_validates(xmlNode *xml)
Definition schemas.c:809
void pcmk__build_schema_xml_node(xmlNode *parent, const char *name, GList **already_included)
Definition schemas.c:1546
int update_validation(xmlNode **xml, int *best, int max, gboolean transform, gboolean to_logs)
Definition schemas.c:1636
gboolean validate_xml_verbose(const xmlNode *xml_blob)
Definition schemas.c:1695
void pcmk__warn_if_schema_deprecated(const char *schema)
Definition schemas.c:1583
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
Definition schemas.c:1655
void crm_schema_cleanup(void)
Definition schemas.c:643
int pcmk__cmp_schemas_by_name(const char *schema1_name, const char *schema2_name)
Definition schemas.c:691
int pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, bool to_logs)
Update CIB XML to latest schema that validates it.
Definition schemas.c:1141
void crm_schema_init(void)
Definition schemas.c:470
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition strings.c:635
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1026
@ pcmk__str_none
@ pcmk__str_casei
bool pcmk__ends_with(const char *s, const char *match)
Definition strings.c:608
#define pcmk__str_copy(str)
pcmk__schema_version_t version
enum pcmk__schema_validator validator
Wrappers for and extensions to libxml2.
void crm_foreach_xpath_result(xmlNode *xml, const char *xpath, void(*helper)(xmlNode *, void *), void *user_data)
Run a supplied function for each result of an xpath search.
Definition xpath.c:170
const xmlChar * pcmkXmlStr
Definition xml.h:41
void free_xml(xmlNode *child)
Definition xml.c:867
Deprecated Pacemaker XML API.
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:883
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
Definition xml.c:2184
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition xml.c:720
#define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)
Base for directing lib{xml2,xslt} log into standard libqb backend.
@ pcmk__xml_artefact_ns_legacy_xslt
@ pcmk__xml_artefact_ns_legacy_rng
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
Definition xml.c:2241
int pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd, bool compress, unsigned int *nbytes)
Definition xml_io.c:660
xmlNode * pcmk__xml_parse(const char *input)
Definition xml_io.c:244
#define PCMK_XA_VALIDATE_WITH
Definition xml_names.h:436
#define PCMK_XA_PATH
Definition xml_names.h:350
#define PCMK_XA_VERSION
Definition xml_names.h:439
#define PCMK_XA_FILE
Definition xml_names.h:282
#define PCMK__XA_SCHEMA