[libvirt] [PATCH 2/2] Detailed XML errors

Richard W.M. Jones rjones at redhat.com
Wed Jul 30 16:12:53 UTC 2008


This implements the XML errors using thread-local storage to
store the connection.

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v
-------------- next part --------------
Index: src/domain_conf.c
===================================================================
RCS file: /data/cvs/libvirt/src/domain_conf.c,v
retrieving revision 1.10
diff -u -r1.10 domain_conf.c
--- src/domain_conf.c	28 Jul 2008 14:06:54 -0000	1.10
+++ src/domain_conf.c	30 Jul 2008 16:07:07 -0000
@@ -38,6 +38,7 @@
 #include "util.h"
 #include "buf.h"
 #include "c-ctype.h"
+#include "threads.h"
 
 VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST,
               "qemu",
@@ -1838,32 +1839,66 @@
     return NULL;
 }
 
+/* Called from SAX on parsing errors in the XML. */
+THREAD_LOCAL(xml_conn)
+
+static void
+catchXMLError (void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
+{
+    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
+
+    if (ctxt &&
+        ctxt->lastError.level == XML_ERR_FATAL &&
+        ctxt->lastError.message != NULL) {
+        virDomainReportError (THREAD_LOCAL_GET (xml_conn), VIR_ERR_XML_DETAIL,
+                              ctxt->lastError.message, ctxt->lastError.line);
+    }
+}
 
 virDomainDefPtr virDomainDefParseString(virConnectPtr conn,
                                         virCapsPtr caps,
                                         const char *xmlStr)
 {
-    xmlDocPtr xml;
+    xmlParserCtxtPtr pctxt;
+    xmlDocPtr xml = NULL;
     xmlNodePtr root;
     virDomainDefPtr def = NULL;
 
-    if (!(xml = xmlReadDoc(BAD_CAST xmlStr, "domain.xml", NULL,
-                           XML_PARSE_NOENT | XML_PARSE_NONET |
-                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
-        virDomainReportError(conn, VIR_ERR_XML_ERROR, NULL);
-        return NULL;
+    THREAD_LOCAL_INIT (xml_conn);
+    THREAD_LOCAL_SET (xml_conn, conn);
+
+    /* Set up a parser context so we can catch the details of XML errors. */
+    pctxt = xmlNewParserCtxt ();
+    if (!pctxt || !pctxt->sax)
+        goto cleanup;
+    pctxt->sax->error = catchXMLError;
+
+    /* It'd be nice to do this, but seems ->userData field is used
+     * by something else.
+     */
+    /*pctxt->userData = conn;*/
+
+    xml = xmlCtxtReadDoc (pctxt, BAD_CAST xmlStr, "domain.xml", NULL,
+                          XML_PARSE_NOENT | XML_PARSE_NONET |
+                          XML_PARSE_NOWARNING);
+    if (!xml) {
+        /* virDomainReportError has already been called by the
+         * callback function (hopefully).
+         */
+        goto cleanup;
     }
 
     if ((root = xmlDocGetRootElement(xml)) == NULL) {
         virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
                               "%s", _("missing root element"));
-        xmlFreeDoc(xml);
-        return NULL;
+        goto cleanup;
     }
 
     def = virDomainDefParseNode(conn, caps, xml, root);
 
-    xmlFreeDoc(xml);
+cleanup:
+    xmlFreeParserCtxt (pctxt);
+    xmlFreeDoc (xml);
     return def;
 }
 
@@ -1871,27 +1906,46 @@
                                       virCapsPtr caps,
                                       const char *filename)
 {
-    xmlDocPtr xml;
+    xmlParserCtxtPtr pctxt;
+    xmlDocPtr xml = NULL;
     xmlNodePtr root;
     virDomainDefPtr def = NULL;
 
-    if (!(xml = xmlReadFile(filename, NULL,
-                            XML_PARSE_NOENT | XML_PARSE_NONET |
-                            XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
-        virDomainReportError(conn, VIR_ERR_XML_ERROR, NULL);
-        return NULL;
+    THREAD_LOCAL_INIT (xml_conn);
+    THREAD_LOCAL_SET (xml_conn, conn);
+
+    /* Set up a parser context so we can catch the details of XML errors. */
+    pctxt = xmlNewParserCtxt ();
+    if (!pctxt || !pctxt->sax)
+        goto cleanup;
+    pctxt->sax->error = catchXMLError;
+
+    /* It'd be nice to do this, but seems ->userData field is used
+     * by something else.
+     */
+    /*pctxt->userData = conn;*/
+
+    xml = xmlCtxtReadFile (pctxt, filename, NULL,
+                           XML_PARSE_NOENT | XML_PARSE_NONET |
+                           XML_PARSE_NOWARNING);
+    if (!xml) {
+        /* virDomainReportError has already been called by the
+         * callback function (hopefully).
+         */
+        goto cleanup;
     }
 
     if ((root = xmlDocGetRootElement(xml)) == NULL) {
         virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
                               "%s", _("missing root element"));
-        xmlFreeDoc(xml);
-        return NULL;
+        goto cleanup;
     }
 
     def = virDomainDefParseNode(conn, caps, xml, root);
 
-    xmlFreeDoc(xml);
+cleanup:
+    xmlFreeParserCtxt (pctxt);
+    xmlFreeDoc (xml);
     return def;
 }
 
Index: src/network_conf.c
===================================================================
RCS file: /data/cvs/libvirt/src/network_conf.c,v
retrieving revision 1.3
diff -u -r1.3 network_conf.c
--- src/network_conf.c	25 Jul 2008 14:27:25 -0000	1.3
+++ src/network_conf.c	30 Jul 2008 16:07:07 -0000
@@ -39,6 +39,7 @@
 #include "uuid.h"
 #include "util.h"
 #include "buf.h"
+#include "threads.h"
 
 VIR_ENUM_DECL(virNetworkForward)
 
@@ -348,57 +349,111 @@
     return NULL;
 }
 
+/* Called from SAX on parsing errors in the XML. */
+THREAD_LOCAL (xml_conn)
+
+static void
+catchXMLError (void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
+{
+    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
+
+    if (ctxt &&
+        ctxt->lastError.level == XML_ERR_FATAL &&
+        ctxt->lastError.message != NULL) {
+        virNetworkReportError (THREAD_LOCAL_GET (xml_conn), VIR_ERR_XML_DETAIL,
+                               ctxt->lastError.message, ctxt->lastError.line);
+    }
+}
+
 virNetworkDefPtr virNetworkDefParseString(virConnectPtr conn,
                                           const char *xmlStr)
 {
-    xmlDocPtr xml;
+    xmlParserCtxtPtr pctxt;
+    xmlDocPtr xml = NULL;
     xmlNodePtr root;
-    virNetworkDefPtr def;
+    virNetworkDefPtr def = NULL;
 
-    if (!(xml = xmlReadDoc(BAD_CAST xmlStr, "network.xml", NULL,
-                           XML_PARSE_NOENT | XML_PARSE_NONET |
-                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
-        virNetworkReportError(conn, VIR_ERR_XML_ERROR, NULL);
-        return NULL;
+    THREAD_LOCAL_INIT (xml_conn);
+    THREAD_LOCAL_SET (xml_conn, conn);
+
+    /* Set up a parser context so we can catch the details of XML errors. */
+    pctxt = xmlNewParserCtxt ();
+    if (!pctxt || !pctxt->sax)
+        goto cleanup;
+    pctxt->sax->error = catchXMLError;
+
+    /* It'd be nice to do this, but seems ->userData field is used
+     * by something else.
+     */
+    /*pctxt->userData = conn;*/
+
+    xml = xmlCtxtReadDoc (pctxt, BAD_CAST xmlStr, "network.xml", NULL,
+                          XML_PARSE_NOENT | XML_PARSE_NONET |
+                          XML_PARSE_NOWARNING);
+    if (!xml) {
+        /* virNetworkReportError has already been called by the
+         * callback function (hopefully).
+         */
+        goto cleanup;
     }
 
     if ((root = xmlDocGetRootElement(xml)) == NULL) {
         virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                               "%s", _("missing root element"));
-        xmlFreeDoc(xml);
-        return NULL;
+        goto cleanup;
     }
 
     def = virNetworkDefParseNode(conn, xml, root);
 
-    xmlFreeDoc(xml);
+cleanup:
+    xmlFreeParserCtxt (pctxt);
+    xmlFreeDoc (xml);
     return def;
 }
 
 virNetworkDefPtr virNetworkDefParseFile(virConnectPtr conn,
                                         const char *filename)
 {
-    xmlDocPtr xml;
+    xmlParserCtxtPtr pctxt;
+    xmlDocPtr xml = NULL;
     xmlNodePtr root;
-    virNetworkDefPtr def;
+    virNetworkDefPtr def = NULL;
 
-    if (!(xml = xmlReadFile(filename, NULL,
-                            XML_PARSE_NOENT | XML_PARSE_NONET |
-                            XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
-        virNetworkReportError(conn, VIR_ERR_XML_ERROR, NULL);
-        return NULL;
+    THREAD_LOCAL_INIT (xml_conn);
+    THREAD_LOCAL_SET (xml_conn, conn);
+
+    /* Set up a parser context so we can catch the details of XML errors. */
+    pctxt = xmlNewParserCtxt ();
+    if (!pctxt || !pctxt->sax)
+        goto cleanup;
+    pctxt->sax->error = catchXMLError;
+
+    /* It'd be nice to do this, but seems ->userData field is used
+     * by something else.
+     */
+    /*pctxt->userData = conn;*/
+
+    xml = xmlCtxtReadFile (pctxt, filename, NULL,
+                           XML_PARSE_NOENT | XML_PARSE_NONET |
+                           XML_PARSE_NOWARNING);
+    if (!xml) {
+        /* virNetworkReportError has already been called by the
+         * callback function (hopefully).
+         */
+        goto cleanup;
     }
 
     if ((root = xmlDocGetRootElement(xml)) == NULL) {
         virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                               "%s", _("missing root element"));
-        xmlFreeDoc(xml);
-        return NULL;
+        goto cleanup;
     }
 
     def = virNetworkDefParseNode(conn, xml, root);
 
-    xmlFreeDoc(xml);
+cleanup:
+    xmlFreeParserCtxt (pctxt);
+    xmlFreeDoc (xml);
     return def;
 }
 
Index: src/storage_conf.c
===================================================================
RCS file: /data/cvs/libvirt/src/storage_conf.c,v
retrieving revision 1.10
diff -u -r1.10 storage_conf.c
--- src/storage_conf.c	25 Jul 2008 14:27:25 -0000	1.10
+++ src/storage_conf.c	30 Jul 2008 16:07:08 -0000
@@ -46,6 +46,7 @@
 #include "buf.h"
 #include "util.h"
 #include "memory.h"
+#include "threads.h"
 
 #define virStorageLog(msg...) fprintf(stderr, msg)
 
@@ -361,22 +362,55 @@
     return NULL;
 }
 
+/* Called from SAX on parsing errors in the XML. */
+THREAD_LOCAL (xml_conn)
+
+static void
+catchXMLError (void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
+{
+    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
+
+    if (ctxt &&
+        ctxt->lastError.level == XML_ERR_FATAL &&
+        ctxt->lastError.message != NULL) {
+        virStorageReportError (THREAD_LOCAL_GET (xml_conn), VIR_ERR_XML_DETAIL,
+                              ctxt->lastError.message, ctxt->lastError.line);
+    }
+}
+
 virStoragePoolDefPtr
 virStoragePoolDefParse(virConnectPtr conn,
                        const char *xmlStr,
                        const char *filename) {
     virStoragePoolDefPtr ret = NULL;
+    xmlParserCtxtPtr pctxt;
     xmlDocPtr xml = NULL;
     xmlNodePtr node = NULL;
     xmlXPathContextPtr ctxt = NULL;
 
-    if (!(xml = xmlReadDoc(BAD_CAST xmlStr,
-                           filename ? filename : "storage.xml",
-                           NULL,
-                           XML_PARSE_NOENT | XML_PARSE_NONET |
-                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
-        virStorageReportError(conn, VIR_ERR_XML_ERROR,
-                              "%s", _("malformed xml document"));
+    THREAD_LOCAL_INIT (xml_conn);
+    THREAD_LOCAL_SET (xml_conn, conn);
+
+    /* Set up a parser context so we can catch the details of XML errors. */
+    pctxt = xmlNewParserCtxt ();
+    if (!pctxt || !pctxt->sax)
+        goto cleanup;
+    pctxt->sax->error = catchXMLError;
+
+    /* It'd be nice to do this, but seems ->userData field is used
+     * by something else.
+     */
+    /*pctxt->userData = conn;*/
+
+    xml = xmlCtxtReadDoc (pctxt, BAD_CAST xmlStr,
+                          filename ? filename : "storage.xml",
+                          NULL,
+                          XML_PARSE_NOENT | XML_PARSE_NONET |
+                          XML_PARSE_NOWARNING);
+    if (!xml) {
+        /* virStorageReportError has already been called by the
+         * callback function (hopefully).
+         */
         goto cleanup;
     }
 
@@ -396,12 +430,14 @@
 
     ret = virStoragePoolDefParseDoc(conn, ctxt, node);
 
+    xmlFreeParserCtxt (pctxt);
     xmlXPathFreeContext(ctxt);
     xmlFreeDoc(xml);
 
     return ret;
 
  cleanup:
+    xmlFreeParserCtxt (pctxt);
     xmlXPathFreeContext(ctxt);
     xmlFreeDoc(xml);
     return NULL;
@@ -725,15 +761,33 @@
                       const char *xmlStr,
                       const char *filename) {
     virStorageVolDefPtr ret = NULL;
+    xmlParserCtxtPtr pctxt;
     xmlDocPtr xml = NULL;
     xmlNodePtr node = NULL;
     xmlXPathContextPtr ctxt = NULL;
 
-    if (!(xml = xmlReadDoc(BAD_CAST xmlStr, filename ? filename : "storage.xml", NULL,
-                           XML_PARSE_NOENT | XML_PARSE_NONET |
-                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
-        virStorageReportError(conn, VIR_ERR_XML_ERROR,
-                              "%s", _("malformed xml document"));
+    THREAD_LOCAL_INIT (xml_conn);
+    THREAD_LOCAL_SET (xml_conn, conn);
+
+    /* Set up a parser context so we can catch the details of XML errors. */
+    pctxt = xmlNewParserCtxt ();
+    if (!pctxt || !pctxt->sax)
+        goto cleanup;
+    pctxt->sax->error = catchXMLError;
+
+    /* It'd be nice to do this, but seems ->userData field is used
+     * by something else.
+     */
+    /*pctxt->userData = conn;*/
+
+    xml = xmlCtxtReadDoc (pctxt, BAD_CAST xmlStr,
+                          filename ? filename : "storage.xml", NULL,
+                          XML_PARSE_NOENT | XML_PARSE_NONET |
+                          XML_PARSE_NOWARNING);
+    if (!xml) {
+        /* virStorageReportError has already been called by the
+         * callback function (hopefully).
+         */
         goto cleanup;
     }
 
@@ -753,12 +807,14 @@
 
     ret = virStorageVolDefParseDoc(conn, pool, ctxt, node);
 
+    xmlFreeParserCtxt (pctxt);
     xmlXPathFreeContext(ctxt);
     xmlFreeDoc(xml);
 
     return ret;
 
  cleanup:
+    xmlFreeParserCtxt (pctxt);
     xmlXPathFreeContext(ctxt);
     xmlFreeDoc(xml);
     return NULL;


More information about the libvir-list mailing list