pacemaker 2.1.8-2.1.8
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
1241int
1243{
1244 return pcmk__update_configured_schema(xml, true);
1245}
1246
1256int
1257pcmk__update_configured_schema(xmlNode **xml, bool to_logs)
1258{
1259 int rc = pcmk_rc_ok;
1260 char *original_schema_name = NULL;
1261
1262 // @COMPAT Not specifying a schema name is deprecated since 2.1.8
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 // Current configuration schema is not acceptable, try to update
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,
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 // Updated configuration schema is still not acceptable
1299
1300 if ((orig_version == -1) || (schema == NULL)
1301 || (schema->schema_index < orig_version)) {
1302 // We couldn't validate any schema at all
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 // We updated configuration successfully, but still too low
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;
1340
1341 } else {
1342 // Updated configuration schema is acceptable
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 // @COMPAT the none schema is deprecated since 2.1.8
1364 pcmk__schema_t *none_schema = NULL;
1365
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
1394GList *
1396{
1397 GList *lst = NULL;
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
1438static void
1439append_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
1450static void
1451external_refs_in_schema(GList **list, const char *contents)
1452{
1453 /* local-name()= is needed to ignore the xmlns= setting at the top of
1454 * the XML file. Otherwise, the xpath query will always return nothing.
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
1463static int
1464read_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")) {
1471 } else {
1473 }
1474
1475 rc = pcmk__file_contents(path, contents);
1476
1477 free(path);
1478 return rc;
1479}
1480
1481static void
1482add_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 /* If we already included this file, don't do so again. */
1491 if (g_list_find_custom(*already_included, file, (GCompareFunc) strcmp) != NULL) {
1492 return;
1493 }
1494
1495 /* Ensure whatever file we were given has a suffix we know about. If not,
1496 * just assume it's an RNG file.
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 /* Create a new <file path="..."> node with the contents of the file
1512 * as a CDATA block underneath it.
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 /* Scan the file for any <externalRef> or <include> nodes and build up
1522 * a list of the files they reference.
1523 */
1524 external_refs_in_schema(&includes, contents);
1525
1526 /* For each referenced file, recurse to add it (and potentially anything it
1527 * references, ...) to the XML.
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
1551void
1552pcmk__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 // Not needed if empty. May happen if name was invalid, for example.
1561 free_xml(schema_node);
1562 }
1563}
1564
1570const char *
1572{
1574
1575 if (pcmk__str_empty(dir)) {
1577 }
1578
1579 return dir;
1580}
1581
1588void
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// Deprecated functions kept only for backward API compatibility
1601// LCOV_EXCL_START
1602
1603#include <crm/common/xml_compat.h>
1604
1605const char *
1607{
1609}
1610
1611const char *
1613{
1614 pcmk__schema_t *schema = g_list_nth_data(known_schemas, version);
1615
1616 return (schema != NULL)? schema->name : "unknown";
1617}
1618
1619int
1621{
1622 int lpc = 0;
1623
1624 if (name == NULL) {
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
1641int
1642update_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,
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
1660gboolean
1661validate_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
1669static void
1670dump_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
1700gboolean
1701validate_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
1728gboolean
1729cli_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// LCOV_EXCL_STOP
1749// 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:1612
int get_schema_version(const char *name)
Definition schemas.c:1620
#define SCHEMA_ZERO
Definition schemas.c:30
#define schema_strdup_printf(prefix, version, suffix)
Definition schemas.c:32
GList * pcmk__schema_files_later_than(const char *name)
Definition schemas.c:1395
bool pcmk__validate_xml(xmlNode *xml_blob, const char *validation, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context)
Definition schemas.c:757
int pcmk_update_configured_schema(xmlNode **xml)
Definition schemas.c:1242
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:1606
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:1729
const char * pcmk__remote_schema_dir(void)
Definition schemas.c:1571
int pcmk__update_configured_schema(xmlNode **xml, bool to_logs)
Update XML from its configured schema to the latest major series.
Definition schemas.c:1257
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:1552
int update_validation(xmlNode **xml, int *best, int max, gboolean transform, gboolean to_logs)
Definition schemas.c:1642
gboolean validate_xml_verbose(const xmlNode *xml_blob)
Definition schemas.c:1701
void pcmk__warn_if_schema_deprecated(const char *schema)
Definition schemas.c:1589
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
Definition schemas.c:1661
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