[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [libvirt] [PATCH 3/5] add XML parsing for vm status file



On Thu, Dec 18, 2008 at 10:23:39AM +0000, Daniel P. Berrange wrote:
> No, not guarenteed to be safe because the 'config_xml' string could 
> contain '%' sequences that'll be interpreted by sprintf. In any case
> why not just use
> 
>    virBufferAdd(&buf, config_xml)
Updated version using virBufferAdd attached.
 -- Guido
>From 8e749c346ab336172ade3cf93035a040e209518b Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Guido=20G=C3=BCnther?= <agx sigxcpu org>
Date: Fri, 12 Dec 2008 15:59:04 +0100
Subject: [PATCH] add XML parsing for vm status file

Functions to read and write vm status in $(statedir)/qemu/<domain>.xml. Keeps
the necessary state in within <domstate>...</domstate>.
---
 src/domain_conf.c          |   36 ++++++---
 src/domain_conf.h          |    6 ++
 src/libvirt_sym.version.in |    3 +
 src/qemu_conf.c            |  191 ++++++++++++++++++++++++++++++++++++++++++++
 src/qemu_conf.h            |   17 ++++
 5 files changed, 242 insertions(+), 11 deletions(-)

diff --git a/src/domain_conf.c b/src/domain_conf.c
index eef5226..5374e17 100644
--- a/src/domain_conf.c
+++ b/src/domain_conf.c
@@ -430,6 +430,7 @@ void virDomainObjFree(virDomainObjPtr dom)
     virDomainDefFree(dom->def);
     virDomainDefFree(dom->newDef);
 
+    VIR_FREE(dom->monitorpath);
     VIR_FREE(dom->vcpupids);
 
     VIR_FREE(dom);
@@ -3243,11 +3244,11 @@ char *virDomainDefFormat(virConnectPtr conn,
 
 #ifndef PROXY
 
-int virDomainSaveConfig(virConnectPtr conn,
-                        const char *configDir,
-                        virDomainDefPtr def)
+int virDomainSaveXML(virConnectPtr conn,
+                     const char *configDir,
+                     virDomainDefPtr def,
+                     const char *xml)
 {
-    char *xml;
     char *configFile = NULL;
     int fd = -1, ret = -1;
     size_t towrite;
@@ -3256,11 +3257,6 @@ int virDomainSaveConfig(virConnectPtr conn,
     if ((configFile = virDomainConfigFile(conn, configDir, def->name)) == NULL)
         goto cleanup;
 
-    if (!(xml = virDomainDefFormat(conn,
-                                   def,
-                                   VIR_DOMAIN_XML_SECURE)))
-        goto cleanup;
-
     if ((err = virFileMakePath(configDir))) {
         virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
                               _("cannot create config directory %s: %s"),
@@ -3293,12 +3289,30 @@ int virDomainSaveConfig(virConnectPtr conn,
     }
 
     ret = 0;
-
  cleanup:
-    VIR_FREE(xml);
     if (fd != -1)
         close(fd);
+    return ret;
+}
+
+int virDomainSaveConfig(virConnectPtr conn,
+                        const char *configDir,
+                        virDomainDefPtr def)
+{
+    int ret = -1;
+    char *xml;
 
+    if (!(xml = virDomainDefFormat(conn,
+                                   def,
+                                   VIR_DOMAIN_XML_SECURE)))
+        goto cleanup;
+
+    if (virDomainSaveXML(conn, configDir, def, xml))
+        goto cleanup;
+
+    ret = 0;
+cleanup:
+    VIR_FREE(xml);
     return ret;
 }
 
diff --git a/src/domain_conf.h b/src/domain_conf.h
index d8a3174..3ad518b 100644
--- a/src/domain_conf.h
+++ b/src/domain_conf.h
@@ -464,6 +464,7 @@ struct _virDomainObj {
     int stderr_fd;
     int stderr_watch;
     int monitor;
+    char *monitorpath;
     int monitorWatch;
     int logfile;
     int pid;
@@ -555,6 +556,11 @@ int virDomainDiskQSort(const void *a, const void *b);
 int virDomainDiskCompare(virDomainDiskDefPtr a,
                          virDomainDiskDefPtr b);
 
+int virDomainSaveXML(virConnectPtr conn,
+                     const char *configDir,
+                     virDomainDefPtr def,
+                     const char *xml);
+
 int virDomainSaveConfig(virConnectPtr conn,
                         const char *configDir,
                         virDomainDefPtr def);
diff --git a/src/libvirt_sym.version.in b/src/libvirt_sym.version.in
index fa9bc5a..b3812b6 100644
--- a/src/libvirt_sym.version.in
+++ b/src/libvirt_sym.version.in
@@ -281,6 +281,7 @@ LIBVIRT_PRIVATE_ VERSION@ {
 
 	# buf.h
 	virBufferVSprintf;
+	virBufferEscapeString;
 	virBufferAdd;
 	virBufferAddChar;
 	virBufferContentAndReset;
@@ -360,6 +361,7 @@ LIBVIRT_PRIVATE_ VERSION@ {
 	virDomainObjFree;
 	virDomainObjListFree;
 	virDomainRemoveInactive;
+	virDomainSaveXML;
 	virDomainSaveConfig;
 	virDomainSoundDefFree;
 	virDomainSoundModelTypeFromString;
@@ -601,6 +603,7 @@ LIBVIRT_PRIVATE_ VERSION@ {
 
 	# xml.h
 	virXPathLong;
+	virXPathNode;
 	virXPathNodeSet;
 	virXPathString;
 	virXMLPropString;
diff --git a/src/qemu_conf.c b/src/qemu_conf.c
index c973adb..4704b99 100644
--- a/src/qemu_conf.c
+++ b/src/qemu_conf.c
@@ -49,6 +49,8 @@
 #include "util.h"
 #include "memory.h"
 #include "verify.h"
+#include "datatypes.h"
+#include "xml.h"
 
 VIR_ENUM_DECL(virDomainDiskQEMUBus)
 VIR_ENUM_IMPL(virDomainDiskQEMUBus, VIR_DOMAIN_DISK_BUS_LAST,
@@ -1334,3 +1336,192 @@ int qemudBuildCommandLine(virConnectPtr conn,
 #undef ADD_ENV_LIT
 #undef ADD_ENV_SPACE
 }
+
+
+/* Called from SAX on parsing errors in the XML. */
+static void
+catchXMLError (void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
+{
+    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
+
+    if (ctxt) {
+        virConnectPtr conn = ctxt->_private;
+
+        if (ctxt->lastError.level == XML_ERR_FATAL &&
+            ctxt->lastError.message != NULL) {
+            qemudReportError (conn, NULL, NULL, VIR_ERR_XML_DETAIL,
+                                  _("at line %d: %s"),
+                                  ctxt->lastError.line,
+                                  ctxt->lastError.message);
+        }
+    }
+}
+
+
+/**
+ * qemudDomainStatusParseFile
+ *
+ * read the last known status of a domain
+ *
+ * Returns 0 on success
+ */
+qemudDomainStatusPtr
+qemudDomainStatusParseFile(virConnectPtr conn,
+                           virCapsPtr caps,
+                           const char *filename, int flags)
+{
+    xmlParserCtxtPtr pctxt = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlDocPtr xml = NULL;
+    xmlNodePtr root, config_root;
+    virDomainDefPtr def = NULL;
+    char *tmp = NULL;
+    long val;
+    qemudDomainStatusPtr status = NULL;
+
+    if (VIR_ALLOC(status) < 0) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
+                         "%s", _("failed to allocate space for vm status"));
+        goto error;
+    }
+
+    /* Set up a parser context so we can catch the details of XML errors. */
+    pctxt = xmlNewParserCtxt ();
+    if (!pctxt || !pctxt->sax)
+        goto error;
+    pctxt->sax->error = catchXMLError;
+    pctxt->_private = conn;
+
+    if (conn) virResetError (&conn->err);
+    xml = xmlCtxtReadFile (pctxt, filename, NULL,
+                           XML_PARSE_NOENT | XML_PARSE_NONET |
+                           XML_PARSE_NOWARNING);
+    if (!xml) {
+        if (conn && conn->err.code == VIR_ERR_NONE)
+              qemudReportError(conn, NULL, NULL, VIR_ERR_XML_ERROR,
+                                   "%s", _("failed to parse xml document"));
+        goto error;
+    }
+
+    if ((root = xmlDocGetRootElement(xml)) == NULL) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                              "%s", _("missing root element"));
+        goto error;
+    }
+
+    ctxt = xmlXPathNewContext(xml);
+    if (ctxt == NULL) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+        goto error;
+    }
+
+    if (!xmlStrEqual(root->name, BAD_CAST "domstatus")) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("incorrect root element"));
+        goto error;
+    }
+
+    ctxt->node = root;
+    if((virXPathLong(conn, "string(./@state)", ctxt, &val)) < 0) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("invalid domain state"));
+        goto error;
+    } else
+        status->state = (int)val;
+
+    if((virXPathLong(conn, "string(./@pid)", ctxt, &val)) < 0) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("invalid pid"));
+        goto error;
+    } else
+        status->pid = (pid_t)val;
+
+    if(!(tmp = virXPathString(conn, "string(./monitor[1]/@path)", ctxt))) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("no monitor path"));
+        goto error;
+    } else
+        status->monitorpath = tmp;
+
+    if(!(config_root = virXPathNode(conn, "./domain", ctxt))) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("no domain config"));
+        goto error;
+    }
+    if(!(def = virDomainDefParseNode(conn, caps, xml, config_root, flags)))
+        goto error;
+    else
+        status->def = def;
+
+cleanup:
+    xmlFreeParserCtxt (pctxt);
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc (xml);
+    return status;
+
+error:
+    VIR_FREE(tmp);
+    VIR_FREE(status);
+    goto cleanup;
+}
+
+
+/**
+ * qemudDomainStatusFormat
+ *
+ * Get the state of a running domain as XML
+ *
+ * Returns xml on success
+ */
+static char*
+qemudDomainStatusFormat(virConnectPtr conn,
+                        virDomainObjPtr vm)
+{
+    char *config_xml = NULL, *xml = NULL;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferVSprintf(&buf, "<domstatus state='%d' pid='%d'>\n", vm->state, vm->pid);
+    virBufferEscapeString(&buf, "  <monitor path='%s'/>\n", vm->monitorpath);
+
+    if (!(config_xml = virDomainDefFormat(conn,
+                                          vm->def,
+                                          VIR_DOMAIN_XML_SECURE)))
+        goto cleanup;
+
+    virBufferAdd(&buf, config_xml, strlen(config_xml));
+    virBufferVSprintf(&buf, "</domstatus>\n");
+
+    xml = virBufferContentAndReset(&buf);
+cleanup:
+    VIR_FREE(config_xml);
+    return xml;
+}
+
+
+/**
+ * qemudSaveDomainStatus
+ *
+ * Save the current status of a running domain
+ *
+ * Returns 0 on success
+ */
+int
+qemudSaveDomainStatus(virConnectPtr conn,
+                      struct qemud_driver *driver,
+                      virDomainObjPtr vm)
+{
+    int ret = -1;
+    char *xml = NULL;
+
+    if (!(xml = qemudDomainStatusFormat(conn, vm)))
+        goto cleanup;
+
+    if ((ret = virDomainSaveXML(conn, driver->stateDir, vm->def, xml)))
+        goto cleanup;
+
+    ret = 0;
+cleanup:
+    VIR_FREE(xml);
+    return ret;
+}
+
diff --git a/src/qemu_conf.h b/src/qemu_conf.h
index ffbd0e7..70d9394 100644
--- a/src/qemu_conf.h
+++ b/src/qemu_conf.h
@@ -77,6 +77,16 @@ struct qemud_driver {
     int domainEventDispatching;
 };
 
+/* Status needed to reconenct to running VMs */
+typedef struct _qemudDomainStatus qemudDomainStatus;
+typedef qemudDomainStatus *qemudDomainStatusPtr;
+struct _qemudDomainStatus {
+    char *monitorpath;
+    pid_t pid;
+    int state;
+    virDomainDefPtr def;
+};
+
 /* Port numbers used for KVM migration. */
 #define QEMUD_MIGRATION_FIRST_PORT 49152
 #define QEMUD_MIGRATION_NUM_PORTS 64
@@ -108,5 +118,12 @@ int         qemudBuildCommandLine       (virConnectPtr conn,
                                          const char *migrateFrom);
 
 const char *qemudVirtTypeToString       (int type);
+qemudDomainStatusPtr qemudDomainStatusParseFile(virConnectPtr conn,
+                                                virCapsPtr caps,
+                                                const char *filename,
+                                                int flags);
+int qemudSaveDomainStatus(virConnectPtr conn,
+                          struct qemud_driver *driver,
+                          virDomainObjPtr vm);
 
 #endif /* __QEMUD_CONF_H */
-- 
1.6.0.2


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]