29 # include <libxml/relaxng.h> 33 # include <libxslt/xslt.h> 34 # include <libxslt/transform.h> 42 xmlRelaxNGValidCtxtPtr valid;
43 xmlRelaxNGParserCtxtPtr parser;
44 } relaxng_ctx_cache_t;
56 static struct schema_s *known_schemas = NULL;
57 static int xml_schema_max = 0;
60 xml_log(
int priority,
const char *fmt, ...)
64 xml_log(
int priority, const
char *fmt, ...)
69 qb_log_from_external_source_va(__FUNCTION__, __FILE__, fmt, priority,
75 xml_latest_schema_index(
void)
77 return xml_schema_max - 4;
81 xml_minimum_schema_index(
void)
88 best = xml_latest_schema_index();
89 target = floor(known_schemas[best].
version);
91 for (lpc = best; lpc > 0; lpc--) {
92 if (known_schemas[lpc].version < target) {
98 best = xml_latest_schema_index();
110 get_schema_root(
void)
112 static const char *base = NULL;
115 base = getenv(
"PCMK_schema_directory");
117 if (base == NULL || strlen(base) == 0) {
124 get_schema_path(
const char *name,
const char *file)
126 const char *base = get_schema_root();
135 schema_filter(
const struct dirent *a)
140 if (strstr(a->d_name,
"pacemaker-") != a->d_name) {
146 }
else if (sscanf(a->d_name,
"pacemaker-%f.rng", &version) == 0) {
149 }
else if (strcmp(a->d_name,
"pacemaker-1.1.rng") == 0) {
162 schema_sort(
const struct dirent **a,
const struct dirent **b)
165 float a_version = 0.0;
166 float b_version = 0.0;
168 sscanf(a[0]->d_name,
"pacemaker-%f.rng", &a_version);
169 sscanf(b[0]->d_name,
"pacemaker-%f.rng", &b_version);
171 if (a_version > b_version) {
173 }
else if(a_version < b_version) {
182 __xml_schema_add(
int type,
float version,
const char *name,
183 const char *location,
const char *transform,
186 int last = xml_schema_max;
189 known_schemas = realloc_safe(known_schemas,
190 xml_schema_max *
sizeof(
struct schema_s));
192 memset(known_schemas+last, 0,
sizeof(
struct schema_s));
193 known_schemas[last].type =
type;
194 known_schemas[last].after_transform = after_transform;
197 known_schemas[last].version =
version;
199 known_schemas[last].location =
crm_strdup_printf(
"%s.rng", known_schemas[last].name);
205 sscanf(name,
"%[^-]-%f", dummy, &
version);
206 known_schemas[last].version =
version;
207 known_schemas[last].name = strdup(name);
208 known_schemas[last].location = strdup(location);
212 known_schemas[last].transform = strdup(transform);
214 if (after_transform == 0) {
215 after_transform = xml_schema_max;
217 known_schemas[last].after_transform = after_transform;
219 if (known_schemas[last].after_transform < 0) {
220 crm_debug(
"Added supported schema %d: %s (%s)",
221 last, known_schemas[last].name, known_schemas[last].location);
223 }
else if (known_schemas[last].transform) {
224 crm_debug(
"Added supported schema %d: %s (%s upgrades to %d with %s)",
225 last, known_schemas[last].name, known_schemas[last].location,
226 known_schemas[last].after_transform,
227 known_schemas[last].transform);
230 crm_debug(
"Added supported schema %d: %s (%s upgrades to %d)",
231 last, known_schemas[last].name, known_schemas[last].location,
232 known_schemas[last].after_transform);
244 const char *base = get_schema_root();
245 struct dirent **namelist = NULL;
247 max = scandir(base, &namelist, schema_filter, schema_sort);
248 __xml_schema_add(1, 0.0,
"pacemaker-0.6",
"crm.dtd",
"upgrade06.xsl", 3);
249 __xml_schema_add(1, 0.0,
"transitional-0.6",
"crm-transitional.dtd",
251 __xml_schema_add(2, 0.0,
"pacemaker-0.7",
"pacemaker-1.0.rng", NULL, 0);
257 for (lpc = 0; lpc < max; lpc++) {
260 char *transform = NULL;
262 sscanf(namelist[lpc]->d_name,
"pacemaker-%f.rng", &version);
263 if ((lpc + 1) < max) {
264 float next_version = 0.0;
266 sscanf(namelist[lpc+1]->d_name,
"pacemaker-%f.rng",
269 if (floor(version) < floor(next_version)) {
274 xslt = get_schema_path(NULL, transform);
275 if (stat(xslt, &s) != 0) {
276 crm_err(
"Transform %s not found", xslt);
278 __xml_schema_add(2, version, NULL, NULL, NULL, -1);
288 __xml_schema_add(2, version, NULL, NULL, transform, next);
295 __xml_schema_add(2, 0.0,
"pacemaker-1.1",
"pacemaker-next.rng", NULL, 0);
296 __xml_schema_add(2, 0.0,
"pacemaker-next",
"pacemaker-next.rng", NULL, -1);
297 __xml_schema_add(0, 0.0,
"none",
"N/A", NULL, -1);
302 validate_with_dtd(xmlDocPtr doc, gboolean to_logs,
const char *dtd_file)
304 gboolean valid = TRUE;
306 xmlDtdPtr dtd = NULL;
307 xmlValidCtxtPtr cvp = NULL;
310 CRM_CHECK(dtd_file != NULL,
return FALSE);
312 dtd = xmlParseDTD(NULL, (
const xmlChar *)dtd_file);
314 crm_err(
"Could not locate/parse DTD: %s", dtd_file);
318 cvp = xmlNewValidCtxt();
321 cvp->userData = (
void *)LOG_ERR;
322 cvp->error = (xmlValidityErrorFunc)
xml_log;
323 cvp->warning = (xmlValidityWarningFunc)
xml_log;
325 cvp->userData = (
void *)stderr;
326 cvp->error = (xmlValidityErrorFunc) fprintf;
327 cvp->warning = (xmlValidityWarningFunc) fprintf;
330 if (!xmlValidateDtd(cvp, doc, dtd)) {
333 xmlFreeValidCtxt(cvp);
336 crm_err(
"Internal error: No valid context");
345 relaxng_invalid_stderr(
void *userData, xmlErrorPtr error)
365 crm_err(
"Structured error: line=%d, level=%d %s", error->line, error->level, error->message);
370 validate_with_relaxng(xmlDocPtr doc, gboolean to_logs,
const char *relaxng_file,
371 relaxng_ctx_cache_t **cached_ctx)
374 gboolean valid = TRUE;
375 relaxng_ctx_cache_t *ctx = NULL;
378 CRM_CHECK(relaxng_file != NULL,
return FALSE);
380 if (cached_ctx && *cached_ctx) {
384 crm_info(
"Creating RNG parser context");
385 ctx = calloc(1,
sizeof(relaxng_ctx_cache_t));
387 xmlLoadExtDtdDefaultValue = 1;
388 ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file);
389 CRM_CHECK(ctx->parser != NULL,
goto cleanup);
392 xmlRelaxNGSetParserErrors(ctx->parser,
393 (xmlRelaxNGValidityErrorFunc)
xml_log,
394 (xmlRelaxNGValidityWarningFunc) xml_log,
395 GUINT_TO_POINTER(LOG_ERR));
397 xmlRelaxNGSetParserErrors(ctx->parser,
398 (xmlRelaxNGValidityErrorFunc) fprintf,
399 (xmlRelaxNGValidityWarningFunc) fprintf,
403 ctx->rng = xmlRelaxNGParse(ctx->parser);
405 crm_err(
"Could not find/parse %s", relaxng_file);
408 ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng);
409 CRM_CHECK(ctx->valid != NULL,
goto cleanup);
412 xmlRelaxNGSetValidErrors(ctx->valid,
413 (xmlRelaxNGValidityErrorFunc)
xml_log,
414 (xmlRelaxNGValidityWarningFunc) xml_log,
415 GUINT_TO_POINTER(LOG_ERR));
417 xmlRelaxNGSetValidErrors(ctx->valid,
418 (xmlRelaxNGValidityErrorFunc) fprintf,
419 (xmlRelaxNGValidityWarningFunc) fprintf,
427 xmlLineNumbersDefault(1);
428 rc = xmlRelaxNGValidateDoc(ctx->valid, doc);
433 crm_err(
"Internal libxml error during validation");
442 if (ctx->parser != NULL) {
443 xmlRelaxNGFreeParserCtxt(ctx->parser);
445 if (ctx->valid != NULL) {
446 xmlRelaxNGFreeValidCtxt(ctx->valid);
448 if (ctx->rng != NULL) {
449 xmlRelaxNGFree(ctx->rng);
465 relaxng_ctx_cache_t *ctx = NULL;
467 for (lpc = 0; lpc < xml_schema_max; lpc++) {
469 switch (known_schemas[lpc].
type) {
478 ctx = (relaxng_ctx_cache_t *) known_schemas[lpc].cache;
482 if (ctx->parser != NULL) {
483 xmlRelaxNGFreeParserCtxt(ctx->parser);
485 if (ctx->valid != NULL) {
486 xmlRelaxNGFreeValidCtxt(ctx->valid);
488 if (ctx->rng != NULL) {
489 xmlRelaxNGFree(ctx->rng);
492 known_schemas[lpc].cache = NULL;
497 free(known_schemas[lpc].name);
498 free(known_schemas[lpc].location);
499 free(known_schemas[lpc].transform);
502 known_schemas = NULL;
506 validate_with(xmlNode *xml,
int method, gboolean to_logs)
508 xmlDocPtr doc = NULL;
509 gboolean valid = FALSE;
517 type = known_schemas[method].type;
524 file = get_schema_path(known_schemas[method].name,
525 known_schemas[method].location);
530 valid = validate_with_dtd(doc, to_logs, file);
534 validate_with_relaxng(doc, to_logs, file,
535 (relaxng_ctx_cache_t **) & (known_schemas[method].cache));
538 crm_err(
"Unknown validator type: %d", type);
547 dump_file(
const char *filename)
555 fp = fopen(filename,
"r");
558 fprintf(stderr,
"%4d ", ++line);
564 }
else if (ch ==
'\n') {
565 fprintf(stderr,
"\n%4d ", ++line);
581 char *filename = strdup(
CRM_STATE_DIR "/cib-invalid.XXXXXX");
583 umask(S_IWGRP | S_IWOTH | S_IROTH);
584 fd = mkstemp(filename);
589 doc = xmlParseFile(filename);
590 xml = xmlDocGetRootElement(doc);
601 validate_xml(xmlNode *xml_blob,
const char *validation, gboolean to_logs)
605 if (validation == NULL) {
609 if (validation == NULL) {
621 for (lpc = 0; lpc < xml_schema_max; lpc++) {
622 if (validate_with(xml_blob, lpc, FALSE)) {
625 known_schemas[lpc].name);
626 crm_info(
"XML validated against %s", known_schemas[lpc].name);
627 if(known_schemas[lpc].after_transform == 0) {
637 if (strcmp(validation,
"none") == 0) {
639 }
else if (version < xml_schema_max) {
640 return validate_with(xml_blob, version, to_logs);
643 crm_err(
"Unknown validator: %s", validation);
649 apply_transformation(xmlNode *xml,
const char *transform)
653 xmlDocPtr res = NULL;
654 xmlDocPtr doc = NULL;
655 xsltStylesheet *xslt = NULL;
659 xform = get_schema_path(NULL, transform);
661 xmlLoadExtDtdDefaultValue = 1;
662 xmlSubstituteEntitiesDefault(1);
664 xslt = xsltParseStylesheetFile((
const xmlChar *)xform);
667 res = xsltApplyStylesheet(xslt, doc, NULL);
670 out = xmlDocGetRootElement(res);
674 xsltFreeStylesheet(xslt);
686 if (version < 0 || version >= xml_schema_max) {
689 return known_schemas[
version].name;
700 for (; lpc < xml_schema_max; lpc++) {
715 int max_stable_schemas = xml_latest_schema_index();
716 int lpc = 0, match = -1, rc =
pcmk_ok;
722 CRM_CHECK(xml_blob != NULL,
return -EINVAL);
723 CRM_CHECK(*xml_blob != NULL,
return -EINVAL);
732 if (lpc >= 0 && transform == FALSE) {
735 }
else if (lpc < 0) {
741 if (match >= max_stable_schemas) {
748 while (lpc <= max_stable_schemas) {
749 crm_debug(
"Testing '%s' validation (%d of %d)",
750 known_schemas[lpc].name ? known_schemas[lpc].name :
"<unset>",
751 lpc, max_stable_schemas);
753 if (validate_with(xml, lpc, to_logs) == FALSE) {
755 crm_info(
"Configuration not valid for schema: %s",
756 known_schemas[lpc].name);
760 known_schemas[lpc].name ? known_schemas[lpc].name :
"<unset>");
770 crm_debug(
"Configuration valid for schema: %s",
771 known_schemas[next].name);
781 if (rc ==
pcmk_ok && transform) {
782 xmlNode *upgrade = NULL;
783 next = known_schemas[lpc].after_transform;
787 crm_trace(
"Stopping at %s", known_schemas[lpc].name);
790 }
else if (max > 0 && (lpc == max || next > max)) {
791 crm_trace(
"Upgrade limit reached at %s (lpc=%d, next=%d, max=%d)",
792 known_schemas[lpc].name, lpc, next, max);
795 }
else if (known_schemas[lpc].transform == NULL) {
796 crm_debug(
"%s-style configuration is also valid for %s",
797 known_schemas[lpc].name, known_schemas[next].name);
802 crm_debug(
"Upgrading %s-style configuration to %s with %s",
803 known_schemas[lpc].name, known_schemas[next].name,
804 known_schemas[lpc].transform ? known_schemas[lpc].transform :
"no-op");
807 upgrade = apply_transformation(xml, known_schemas[lpc].transform);
809 if (upgrade == NULL) {
810 crm_err(
"Transformation %s failed",
811 known_schemas[lpc].transform);
814 }
else if (validate_with(upgrade, next, to_logs)) {
815 crm_info(
"Transformation %s successful",
816 known_schemas[lpc].transform);
824 crm_err(
"Transformation %s did not produce a valid configuration",
825 known_schemas[lpc].transform);
834 if (transform == FALSE || rc !=
pcmk_ok) {
840 if (*best > match && *best) {
841 crm_info(
"%s the configuration from %s to %s",
842 transform?
"Transformed":
"Upgraded",
843 value ? value :
"<none>", known_schemas[*best].name);
857 char *
const orig_value = strdup(value == NULL ?
"(none)" : value);
861 int min_version = xml_minimum_schema_index();
863 if (version < min_version) {
864 xmlNode *converted = NULL;
870 if (version < min_version) {
871 if (version < orig_version || orig_version == -1) {
874 " validate with any schema in range [%s, %s]," 875 " cannot upgrade to %s.",
881 fprintf(stderr,
"Your current configuration %s could not" 882 " validate with any schema in range [%s, %s]," 883 " cannot upgrade to %s.\n",
889 }
else if (to_logs) {
890 crm_config_err(
"Your current configuration could only be upgraded to %s... " 891 "the minimum requirement is %s.",
crm_str(value),
894 fprintf(stderr,
"Your current configuration could only be upgraded to %s... " 895 "the minimum requirement is %s.\n",
907 if (version < xml_latest_schema_index()) {
909 "which is acceptable but not the most recent",
912 }
else if (to_logs) {
913 crm_info(
"Your configuration was internally updated to the latest version (%s)",
921 " It is highly encouraged and prevents many common cluster issues.");
924 fprintf(stderr,
"Configuration validation is currently disabled." 925 " It is highly encouraged and prevents many common cluster issues.\n");
#define CRM_CHECK(expr, failure_action)
void crm_schema_init(void)
#define crm_notice(fmt, args...)
void xml_log(int priority, const char *fmt,...) G_GNUC_PRINTF(2
#define crm_config_err(fmt...)
char * crm_element_value_copy(xmlNode *data, const char *name)
#define CRM_DTD_DIRECTORY
char * strerror(int errnum)
xmlDoc * getDocPtr(xmlNode *node)
xmlNode * copy_xml(xmlNode *src_node)
int get_schema_version(const char *name)
#define crm_debug(fmt, args...)
#define pcmk_err_schema_validation
void crm_schema_cleanup(void)
#define crm_trace(fmt, args...)
const char * get_schema_name(int version)
Wrappers for and extensions to libxml2.
const char * crm_element_value(xmlNode *data, const char *name)
#define XML_ATTR_VALIDATION
void free_xml(xmlNode *child)
gboolean validate_xml_verbose(xmlNode *xml_blob)
#define pcmk_err_transform_failed
gboolean crm_ends_with(const char *s, const char *match)
#define crm_config_warn(fmt...)
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
#define crm_err(fmt, args...)
#define crm_log_xml_info(xml, text)
gboolean crm_is_true(const char *s)
gboolean cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
int update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform, gboolean to_logs)
Try update CIB XML to the highest pacemaker's standard if feasible.
#define safe_str_eq(a, b)
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
#define crm_info(fmt, args...)
const char * xml_latest_schema(void)
enum crm_ais_msg_types type