The XML from libxml2 we use in libvirt has in many cases very bad error
messages, which make it very difficult to point to the specific problem
with the validated XML.
This patch adds infrastructure which will allow users to specify a
custom XML validator program which will be used to validate the XML
before we invoke libxml's validator.
The configuration is done via a global variable as normally the daemon
config is not accessible from inside the runtime.
Signed-off-by: Peter Krempa <pkrempa(a)redhat.com>
---
src/libvirt_private.syms | 1 +
src/util/virxml.c | 93 +++++++++++++++++++++++++++++++++++++++-
src/util/virxml.h | 3 ++
3 files changed, 95 insertions(+), 2 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 25794bc2f4..3e90cd4599 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -3678,6 +3678,7 @@ virXMLNodeNameEqual;
virXMLNodeSanitizeNamespaces;
virXMLNodeToString;
virXMLParseHelper;
+virXMLParseSetCustomValidator;
virXMLPickShellSafeComment;
virXMLPropEnum;
virXMLPropEnumDefault;
diff --git a/src/util/virxml.c b/src/util/virxml.c
index d6e2e5dd91..aea9b3aac3 100644
--- a/src/util/virxml.c
+++ b/src/util/virxml.c
@@ -33,6 +33,7 @@
#include "virfile.h"
#include "virstring.h"
#include "virutil.h"
+#include "vircommand.h"
#include "configmake.h"
#define VIR_FROM_THIS VIR_FROM_XML
@@ -41,6 +42,8 @@
virReportErrorHelper(from, code, __FILE__, \
__FUNCTION__, __LINE__, __VA_ARGS__)
+static char *virXMLCustomValidatorPath;
+
/* Internal data to be passed to SAX parser and used by error handler. */
struct virParserData {
int domcode;
@@ -1029,6 +1032,68 @@ catchXMLError(void *ctx, const char *msg G_GNUC_UNUSED, ...)
}
}
+
+/**
+ * virXMLParseHelperValidateCustom:
+ *
+ * Invokes an external validator program configured in the
+ * 'virXMLCustomValidatorPath' global variable and captures the validation
+ * output. The program is invoked as:
+ *
+ * /path/to/program /path/to/schema
+ *
+ * The XML to validate is fed on stdin of the program. The program is expected
+ * to return 0 on successful validation and non-zero on failure to validate,
+ * in which case STDOUT of the program is used in the error message reported
+ * by libvirt.
+ */
+static int
+virXMLParseHelperValidateCustom(const char *schema,
+ const char *filename,
+ const char *xmlStr)
+{
+ g_autoptr(virCommand) cmd = virCommandNewArgList(virXMLCustomValidatorPath,
+ schema, NULL);
+ g_autofree char *filebuf = NULL;
+ g_autofree char *outbuf = NULL;
+ int exitstatus = 0;
+
+ if (filename) {
+ /* virsh uses 10 MiB as max XML size */
+ if (virFileReadAll(filename, 10 * 1024 * 1024, &filebuf) < 0)
+ return -1;
+
+ xmlStr = filebuf;
+ }
+
+ if (!virFileIsExecutable(virXMLCustomValidatorPath)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("custom XML validator program '%s' is not
executable"),
+ virXMLCustomValidatorPath);
+ return -1;
+ }
+
+ virCommandSetInputBuffer(cmd, xmlStr);
+ virCommandSetOutputBuffer(cmd, &outbuf);
+ virCommandDoAsyncIO(cmd);
+
+ if (virCommandRunAsync(cmd, NULL) < 0)
+ return -1;
+
+ if (virCommandWait(cmd, &exitstatus) < 0)
+ return -1;
+
+ if (exitstatus != 0) {
+ virReportError(VIR_ERR_XML_INVALID_SCHEMA,
+ _("Unable to validate doc against %s\n%s"),
+ schema, outbuf);
+ return -1;
+ }
+
+ return 0;
+}
+
+
/**
* virXMLParseHelper:
* @domcode: error domain of the caller, usually VIR_FROM_THIS
@@ -1126,8 +1191,14 @@ virXMLParseHelper(int domcode,
g_autofree char *schema = virFileFindResource(schemafile,
abs_top_srcdir
"/src/conf/schemas",
PKGDATADIR "/schemas");
- if (!schema ||
- (virXMLValidateAgainstSchema(schema, xml) < 0))
+ if (!schema)
+ return NULL;
+
+ if (virXMLCustomValidatorPath &&
+ virXMLParseHelperValidateCustom(schema, filename, xmlStr) < 0)
+ return NULL;
+
+ if (virXMLValidateAgainstSchema(schema, xml) < 0)
return NULL;
}
@@ -1884,3 +1955,21 @@ virXMLNewNode(xmlNsPtr ns,
return ret;
}
+
+
+/**
+ * virXMLParseSetCustomValidator:
+ * @path: path to the custom validator binary
+ *
+ * Set the path to the custom XML validator helper program (see
+ * virXMLParseHelperValidateCustom). This function must be called only
+ * during daemon initialization.
+ */
+void
+virXMLParseSetCustomValidator(const char *path)
+{
+ if (virXMLCustomValidatorPath)
+ return;
+
+ virXMLCustomValidatorPath = g_strdup(path);
+}
diff --git a/src/util/virxml.h b/src/util/virxml.h
index 539228a9ba..41a27a5722 100644
--- a/src/util/virxml.h
+++ b/src/util/virxml.h
@@ -165,6 +165,9 @@ virXMLPropEnumDefault(xmlNodePtr node,
ATTRIBUTE_NONNULL(5);
+void
+virXMLParseSetCustomValidator(const char *path);
+
/* Internal function; prefer the macros below. */
xmlDocPtr
virXMLParseHelper(int domcode,
--
2.37.1