Signed-off-by: Chris Lalancette <clalance(a)redhat.com>
---
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