diff --git a/ChangeLog b/ChangeLog index 082207e2f7..4353fa1e0e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +Sat Dec 20 13:53:14 CET 2008 Guido Günther + + add XML parsing for qemu/kvm status files + * src/domain_conf.c, src/domain_conf.h (virDomainSaveXML): new function + * src/domain_conf.c (virDomainSaveConfig): split out XML writing into + virDomainSaveXML + * src/qemu_conf.c (qemudDomainStatusParseFile): new function to parse + status XML + (qemudDomainStatusFormat): new function to format status XML + (qemudSaveDomainStatus): new function to write status XML, uses + virDomainSaveXML + * src/libvirt_sym.version.in: add virBufferEscapeString, virDomainSaveXML, + virXPathNode symbols + Fri Dec 19 10:41:00 UTC 2008 Richard W.M. Jones * docs/formatdomain.html.in: Fix documentation typo diff --git a/src/domain_conf.c b/src/domain_conf.c index eef5226da7..5374e1790b 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 d8a31743c0..3ad518b091 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 fa9bc5a778..b3812b6fe1 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 c973adb770..469bf1feb5 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, "\n", vm->state, vm->pid); + virBufferEscapeString(&buf, " \n", vm->monitorpath); + + if (!(config_xml = virDomainDefFormat(conn, + vm->def, + VIR_DOMAIN_XML_SECURE))) + goto cleanup; + + virBufferAdd(&buf, config_xml, strlen(config_xml)); + virBufferAddLit(&buf, "\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 ffbd0e7921..70d9394e38 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 */