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?= <[email protected]>
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_priva...@version@ {
# buf.h
virBufferVSprintf;
+ virBufferEscapeString;
virBufferAdd;
virBufferAddChar;
virBufferContentAndReset;
@@ -360,6 +361,7 @@ libvirt_priva...@version@ {
virDomainObjFree;
virDomainObjListFree;
virDomainRemoveInactive;
+ virDomainSaveXML;
virDomainSaveConfig;
virDomainSoundDefFree;
virDomainSoundModelTypeFromString;
@@ -601,6 +603,7 @@ libvirt_priva...@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
--
Libvir-list mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/libvir-list