Signed-off-by: Chris Lalancette <clalance@xxxxxxxxxx> --- src/qemu/qemu_conf.c | 28 ++++++ src/qemu/qemu_conf.h | 6 ++ src/qemu/qemu_driver.c | 190 ++++++++++++++++++++++++++++++++++++------ src/qemu/qemu_monitor.c | 14 +++ src/qemu/qemu_monitor.h | 2 + src/qemu/qemu_monitor_json.c | 46 ++++++++++ src/qemu/qemu_monitor_json.h | 4 + src/qemu/qemu_monitor_text.c | 11 +++ src/qemu/qemu_monitor_text.h | 2 + 9 files changed, 278 insertions(+), 25 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 48252a5..3093583 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -3363,6 +3363,9 @@ qemuBuildSmpArgStr(const virDomainDefPtr def, return virBufferContentAndReset(&buf); } +static int qemuStringToArgvEnv(const char *args, + const char ***retenv, + const char ***retargv); /* * Constructs a argv suitable for launching qemu with config defined @@ -4593,6 +4596,31 @@ int qemudBuildCommandLine(virConnectPtr conn, ADD_ARG_LIT(current_snapshot->def->name); } + if (def->namespaceData) { + qemuDomainAdvancedDefPtr adv; + + adv = def->namespaceData; + if (adv->cmdline_extra) { + const char **progenv = NULL; + const char **progargv = NULL; + + if (qemuStringToArgvEnv(adv->cmdline_extra, &progenv, &progargv) < 0) + goto error; + + for (i = 0 ; progargv && progargv[i] ; i++) { + ADD_ARG_LIT(progargv[i]); + VIR_FREE(progargv[i]); + } + VIR_FREE(progargv); + + for (i = 0 ; progenv && progenv[i] ; i++) { + ADD_ENV_LIT(progenv[i]); + VIR_FREE(progenv[i]); + } + VIR_FREE(progenv); + } + } + ADD_ARG(NULL); ADD_ENV(NULL); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e0666cb..eef88c4 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -158,6 +158,12 @@ struct qemud_driver { typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet; typedef qemuDomainPCIAddressSet *qemuDomainPCIAddressSetPtr; +typedef struct _qemuDomainAdvancedDef qemuDomainAdvancedDef; +typedef qemuDomainAdvancedDef *qemuDomainAdvancedDefPtr; +struct _qemuDomainAdvancedDef { + char *cmdline_extra; +}; + /* Port numbers used for KVM migration. */ # define QEMUD_MIGRATION_FIRST_PORT 49152 # define QEMUD_MIGRATION_NUM_PORTS 64 diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 74b200b..e0c17aa 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -47,6 +47,8 @@ #include <sys/ioctl.h> #include <sys/un.h> +#include <libxml/xpathInternals.h> + #ifdef __linux__ # include <sys/vfs.h> # ifndef NFS_SUPER_MAGIC @@ -89,6 +91,9 @@ #define VIR_FROM_THIS VIR_FROM_QEMU +#define QEMU_NAMESPACE_HREF "http://libvirt.org/schemas/domain/qemu/1.0" + + /* Only 1 job is allowed at any time * A job includes *all* monitor commands, even those just querying * information, not merely actions */ @@ -527,6 +532,103 @@ static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virD } } +static void *qemuDomainDefNamespaceParse(xmlDocPtr xml, + xmlNodePtr root, + xmlXPathContextPtr ctxt) +{ + qemuDomainAdvancedDefPtr adv = NULL; + xmlNsPtr ns; + + ns = xmlSearchNs(xml, root, BAD_CAST "qemu"); + if (!ns) + /* this is fine; it just means there was no qemu namespace listed */ + return NULL; + + if (STRNEQ((const char *)ns->href, QEMU_NAMESPACE_HREF)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Found namespace '%s' doesn't match expected '%s'"), + ns->href, QEMU_NAMESPACE_HREF); + return NULL; + } + + if (VIR_ALLOC(adv) < 0) { + virReportOOMError(); + return NULL; + } + + xmlXPathRegisterNs(ctxt, ns->prefix, ns->href); + + adv->cmdline_extra = virXPathString("string(./qemu:advanced/qemu:commandline/qemu:extra[1])", + ctxt); + + return adv; +} + +static void qemuDomainDefNamespaceFree(void *nsdata) +{ + qemuDomainAdvancedDefPtr adv = nsdata; + + if (!adv) + return; + + VIR_FREE(adv->cmdline_extra); + + VIR_FREE(adv); +} + +static int qemuDomainDefNamespaceFormatXML(virBufferPtr buf, + void *nsdata) +{ + qemuDomainAdvancedDefPtr adv = nsdata; + + virBufferAddLit(buf, " <qemu:advanced>\n"); + if (adv->cmdline_extra) { + virBufferAddLit(buf, " <qemu:commandline>\n"); + virBufferVSprintf(buf, " <qemu:extra>%s</qemu:extra>\n", + adv->cmdline_extra); + virBufferAddLit(buf, " </qemu:commandline>\n"); + } + virBufferAddLit(buf, " </qemu:advanced>\n"); + + return 0; +} + +static const char *qemuDomainDefNamespaceHref(void) +{ + return "xmlns:qemu='" QEMU_NAMESPACE_HREF "'"; +} + +static int qemuDomainLoadAllConfigs(virCapsPtr caps, + virDomainObjListPtr doms, + const char *configDir, + const char *autostartDir, + int liveStatus, + virDomainLoadConfigNotify notify, + void *opaque) +{ + struct xmlNamespace ns; + + ns.parse = qemuDomainDefNamespaceParse; + ns.free = qemuDomainDefNamespaceFree; + ns.format = qemuDomainDefNamespaceFormatXML; + ns.href = qemuDomainDefNamespaceHref; + + return virDomainLoadAllConfigs(caps, doms, configDir, autostartDir, + liveStatus, notify, &ns, opaque); +} + +static virDomainDefPtr qemuDomainDefParseString(virCapsPtr caps, + const char *xml, int flags) +{ + struct xmlNamespace ns; + + ns.parse = qemuDomainDefNamespaceParse; + ns.free = qemuDomainDefNamespaceFree; + ns.format = qemuDomainDefNamespaceFormatXML; + ns.href = qemuDomainDefNamespaceHref; + + return virDomainDefParseString(caps, xml, &ns, flags); +} static int qemuCgroupControllerActive(struct qemud_driver *driver, int controller) @@ -1647,21 +1749,21 @@ qemudStartup(int privileged) { } /* Get all the running persistent or transient configs first */ - if (virDomainLoadAllConfigs(qemu_driver->caps, - &qemu_driver->domains, - qemu_driver->stateDir, - NULL, - 1, NULL, NULL) < 0) + if (qemuDomainLoadAllConfigs(qemu_driver->caps, + &qemu_driver->domains, + qemu_driver->stateDir, + NULL, + 1, NULL, NULL) < 0) goto error; qemuReconnectDomains(qemu_driver); /* Then inactive persistent configs */ - if (virDomainLoadAllConfigs(qemu_driver->caps, - &qemu_driver->domains, - qemu_driver->configDir, - qemu_driver->autostartDir, - 0, NULL, NULL) < 0) + if (qemuDomainLoadAllConfigs(qemu_driver->caps, + &qemu_driver->domains, + qemu_driver->configDir, + qemu_driver->autostartDir, + 0, NULL, NULL) < 0) goto error; @@ -1711,11 +1813,11 @@ qemudReload(void) { return 0; qemuDriverLock(qemu_driver); - virDomainLoadAllConfigs(qemu_driver->caps, - &qemu_driver->domains, - qemu_driver->configDir, - qemu_driver->autostartDir, - 0, qemudNotifyLoadDomain, qemu_driver); + qemuDomainLoadAllConfigs(qemu_driver->caps, + &qemu_driver->domains, + qemu_driver->configDir, + qemu_driver->autostartDir, + 0, qemudNotifyLoadDomain, qemu_driver); qemuDriverUnlock(qemu_driver); qemudAutostartConfigs(qemu_driver); @@ -4027,8 +4129,8 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char *xml, virDomainEventPtr event = NULL; qemuDriverLock(driver); - if (!(def = virDomainDefParseString(driver->caps, xml, - VIR_DOMAIN_XML_INACTIVE))) + if (!(def = qemuDomainDefParseString(driver->caps, xml, + VIR_DOMAIN_XML_INACTIVE))) goto cleanup; if (virSecurityDriverVerify(def) < 0) @@ -5861,8 +5963,8 @@ static int qemudDomainRestore(virConnectPtr conn, } /* Create a domain from this XML */ - if (!(def = virDomainDefParseString(driver->caps, xml, - VIR_DOMAIN_XML_INACTIVE))) { + if (!(def = qemuDomainDefParseString(driver->caps, xml, + VIR_DOMAIN_XML_INACTIVE))) { qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", _("failed to parse XML")); goto cleanup; @@ -6146,7 +6248,7 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, goto cleanup; } - def = virDomainDefParseString(driver->caps, xmlData, 0); + def = qemuDomainDefParseString(driver->caps, xmlData, 0); if (!def) goto cleanup; @@ -6463,7 +6565,7 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char *xml) { int dupVM; qemuDriverLock(driver); - if (!(def = virDomainDefParseString(driver->caps, xml, + if (!(def = qemuDomainDefParseString(driver->caps, xml, VIR_DOMAIN_XML_INACTIVE))) goto cleanup; @@ -9335,7 +9437,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, } /* Parse the domain XML. */ - if (!(def = virDomainDefParseString(driver->caps, dom_xml, + if (!(def = qemuDomainDefParseString(driver->caps, dom_xml, VIR_DOMAIN_XML_INACTIVE))) { qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", _("failed to parse XML")); @@ -9566,8 +9668,8 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, VIR_DEBUG("Generated uri_out=%s", *uri_out); /* Parse the domain XML. */ - if (!(def = virDomainDefParseString(driver->caps, dom_xml, - VIR_DOMAIN_XML_INACTIVE))) { + if (!(def = qemuDomainDefParseString(driver->caps, dom_xml, + VIR_DOMAIN_XML_INACTIVE))) { qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", _("failed to parse XML")); goto cleanup; @@ -11249,6 +11351,44 @@ cleanup: return ret; } +static int qemuMonitorCommand(virDomainPtr domain, const char *cmd, + char **result, unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + VIR_WARN(_("Qemu monitor command '%s' executed; libvirt results may be unpredictable!"), cmd); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorArbitraryCommand(priv->mon, cmd, result); + qemuDomainObjExitMonitorWithDriver(driver, vm); + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + static virDriver qemuDriver = { VIR_DRV_QEMU, "QEMU", @@ -11346,7 +11486,7 @@ static virDriver qemuDriver = { qemuDomainSnapshotCurrent, /* domainSnapshotCurrent */ qemuDomainRevertToSnapshot, /* domainRevertToSnapshot */ qemuDomainSnapshotDelete, /* domainSnapshotDelete */ - NULL, /* qemuMonitorCommand */ + qemuMonitorCommand, /* qemuMonitorCommand */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 01e3a46..a47932d 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1530,3 +1530,17 @@ int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name) ret = qemuMonitorTextDeleteSnapshot(mon, name); return ret; } + +int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply) +{ + int ret; + + DEBUG("mon=%p, cmd=%s, reply=%p", mon, cmd, reply); + + if (mon->json) + ret = qemuMonitorJSONArbitraryCommand(mon, cmd, reply); + else + ret = qemuMonitorTextArbitraryCommand(mon, cmd, reply); + return ret; +} + diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 21b8989..74b0f60 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -348,4 +348,6 @@ int qemuMonitorCreateSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply); + #endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 2904201..92f9dd0 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2222,3 +2222,49 @@ int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name) virJSONValueFree(reply); return ret; } + +int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, + const char *cmd, + char **reply) +{ + int ret = -1; + qemuMonitorMessage msg; + + *reply = NULL; + + memset(&msg, 0, sizeof msg); + + if (virAsprintf(&msg.txBuffer, "%s\r\n", cmd) < 0) { + virReportOOMError(); + goto cleanup; + } + msg.txLength = strlen(msg.txBuffer); + msg.txFD = -1; + + ret = qemuMonitorSend(mon, &msg); + + /* If we got ret==0, but not reply data something rather bad + * went wrong, so lets fake an EIO error */ + if (!msg.rxBuffer && ret == 0) { + msg.lastErrno = EIO; + ret = -1; + } + + if (ret == 0) { + *reply = strdup(msg.rxBuffer); + if (*reply == NULL) { + ret = -1; + virReportOOMError(); + goto cleanup; + } + } + else + virReportSystemError(msg.lastErrno, + _("cannot send monitor command '%s'"), cmd); + +cleanup: + VIR_FREE(msg.txBuffer); + VIR_FREE(msg.rxBuffer); + + return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index e7baf84..a0814e8 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -179,4 +179,8 @@ int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, + const char *cmd, + char **reply); + #endif /* QEMU_MONITOR_JSON_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 9942768..65760f5 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2438,3 +2438,14 @@ cleanup: VIR_FREE(reply); return ret; } + +int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply) +{ + if (qemuMonitorCommand(mon, cmd, reply)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to run cmd '%s'"), cmd); + return -1; + } + + return 0; +} diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index fb7d08b..27a9500 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -181,4 +181,6 @@ int qemuMonitorTextCreateSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorTextLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply); + #endif /* QEMU_MONITOR_TEXT_H */ -- 1.6.6.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list