Add virDomainQemuAgentCommand() to qemu driver and remote driver
to support qemuAgentCommand().
daemon/remote.c | 35 ++++++++++++++++++++
include/libvirt/libvirt-qemu.h | 3 +
src/driver.h | 6 +++
src/libvirt-qemu.c | 58 +++++++++++++++++++++++++++++++++
src/libvirt_qemu.syms | 5 ++
src/qemu/qemu_driver.c | 71 +++++++++++++++++++++++++++++++++++++++++
src/qemu_protocol-structs | 10 +++++
src/remote/qemu_protocol.x | 14 +++++++-
src/remote/remote_driver.c | 41 +++++++++++++++++++++++
9 files changed, 242 insertions(+), 1 deletion(-)
diff --git a/daemon/remote.c b/daemon/remote.c
index 832307e..4fd5c9b 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -3948,6 +3948,41 @@ cleanup:
return rv;
}
+static int
+qemuDispatchAgentCommand(virNetServerPtr server ATTRIBUTE_UNUSED,
+ virNetServerClientPtr client ATTRIBUTE_UNUSED,
+ virNetMessagePtr msg ATTRIBUTE_UNUSED,
+ virNetMessageErrorPtr rerr,
+ qemu_agent_command_args *args,
+ qemu_agent_command_ret *ret)
+{
+ virDomainPtr dom = NULL;
+ int rv = -1;
+ struct daemonClientPrivate *priv =
+ virNetServerClientGetPrivateData(client);
+
+ if (!priv->conn) {
+ virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not
open"));
+ goto cleanup;
+ }
+
+ if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+ goto cleanup;
+
+ if (virDomainQemuAgentCommand(dom, args->cmd, &ret->result,
+ args->timeout, args->flags) < 0) {
+ goto cleanup;
+ }
+
+ rv = 0;
+cleanup:
+ if (rv < 0)
+ virNetMessageSaveError(rerr);
+ if (dom)
+ virDomainFree(dom);
+ return rv;
+}
+
/*----- Helpers. -----*/
/* get_nonnull_domain and get_nonnull_network turn an on-wire
diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h
index ffc5ae5..aed1259 100644
--- a/include/libvirt/libvirt-qemu.h
+++ b/include/libvirt/libvirt-qemu.h
@@ -49,6 +49,9 @@ typedef enum {
VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT = 0,
} virDomainQemuAgentCommandTimeoutFlags;
+int virDomainQemuAgentCommand(virDomainPtr domain, const char *cmd,
+ char **result, int timeout, unsigned int flags);
+
# ifdef __cplusplus
}
# endif
diff --git a/src/driver.h b/src/driver.h
index aab9766..c368273 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -689,6 +689,11 @@ typedef int
(*virDrvDomainQemuMonitorCommand)(virDomainPtr domain, const char *cmd,
char **result, unsigned int flags);
+typedef int
+ (*virDrvDomainQemuAgentCommand)(virDomainPtr domain, const char *cmd,
+ char **result, int timeout,
+ unsigned int flags);
+
/* Choice of unsigned int rather than pid_t is intentional. */
typedef virDomainPtr
(*virDrvDomainQemuAttach)(virConnectPtr conn,
@@ -1048,6 +1053,7 @@ struct _virDriver {
virDrvDomainGetDiskErrors domainGetDiskErrors;
virDrvDomainSetMetadata domainSetMetadata;
virDrvDomainGetMetadata domainGetMetadata;
+ virDrvDomainQemuAgentCommand qemuDomainQemuAgentCommand;
};
typedef int
diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c
index 78480bb..0e12425 100644
--- a/src/libvirt-qemu.c
+++ b/src/libvirt-qemu.c
@@ -185,3 +185,61 @@ error:
virDispatchError(conn);
return NULL;
}
+
+/**
+ * virDomainQemuAgentCommand:
+ * @domain: a domain object
+ * @cmd: the guest agent command string
+ * @result: a string returned by @cmd
+ * @timeout: timeout seconds
+ * @flags: execution flags
+ *
+ * Execution Guest Agent Command
+ *
+ * Issue @cmd to the guest agent running in @domain.
+ * If @result is NULL, then don't wait for a result (and @timeout
+ * must be 0). Otherwise, wait for @timeout seconds for a
+ * successful result to be populated into *@result, with
+ * VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK (-1) meaning to block
+ * forever waiting for a result.
+ *
+ * Returns 0 if succeeded, -1 in failing.
+ */
+int
+virDomainQemuAgentCommand(virDomainPtr domain,
+ const char *cmd,
+ char **result,
+ int timeout,
+ unsigned int flags)
+{
+ virConnectPtr conn;
+
+ VIR_DEBUG("domain=%p, cmd=%s, result=%p, timeout=%d, flags=%x",
+ domain, cmd, result, timeout, flags);
+
+ if (!VIR_IS_CONNECTED_DOMAIN(domain)) {
+ virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ virDispatchError(NULL);
+ return -1;
+ }
+
+ conn = domain->conn;
+
+ virCheckNonNullArgGoto(result, error);
+
+ if (conn->driver->qemuDomainQemuAgentCommand) {
+ int ret;
+ ret = conn->driver->qemuDomainQemuAgentCommand(domain, cmd, result,
+ timeout, flags);
+ if (ret < 0)
+ goto error;
+ return ret;
+ }
+
+ virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+ /* Copy to connection error object for back compatability */
+ virDispatchError(domain->conn);
+ return -1;
+}
diff --git a/src/libvirt_qemu.syms b/src/libvirt_qemu.syms
index 8447730..f968d91 100644
--- a/src/libvirt_qemu.syms
+++ b/src/libvirt_qemu.syms
@@ -19,3 +19,8 @@ LIBVIRT_QEMU_0.9.4 {
global:
virDomainQemuAttach;
} LIBVIRT_QEMU_0.8.3;
+
+LIBVIRT_QEMU_0.10.0 {
+ global:
+ virDomainQemuAgentCommand;
+} LIBVIRT_QEMU_0.9.4;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index dee1268..750577e 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -13204,6 +13204,76 @@ qemuListAllDomains(virConnectPtr conn,
return ret;
}
+static int
+qemuDomainQemuAgentCommand(virDomainPtr domain,
+ const char *cmd,
+ char **result,
+ int timeout,
+ unsigned int flags)
+{
+ struct qemud_driver *driver = domain->conn->privateData;
+ virDomainObjPtr vm;
+ int ret = -1;
+ qemuDomainObjPrivatePtr priv;
+
+ virCheckFlags(0, -1);
+
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, domain->uuid);
+ qemuDriverUnlock(driver);
+
+ 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;
+ }
+
+ priv = vm->privateData;
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto cleanup;
+ }
+
+ if (priv->agentError) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("QEMU guest agent is not available due to an
error"));
+ goto cleanup;
+ }
+
+ if (!priv->agent) {
+ qemuReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+ _("QEMU guest agent is not configured"));
+ goto cleanup;
+ }
+
+ if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto endjob;
+ }
+
+ qemuDomainObjEnterAgent(driver, vm);
+ ret = qemuAgentQemuAgentCommand(priv->agent, cmd, result, timeout);
+ qemuDomainObjExitAgent(driver, vm);
+
+endjob:
+ if (qemuDomainObjEndJob(driver, vm) == 0) {
+ vm = NULL;
+ }
+
+cleanup:
+ if (vm)
+ virDomainObjUnlock(vm);
+ return ret;
+}
+
static virDriver qemuDriver = {
.no = VIR_DRV_QEMU,
.name = QEMU_DRIVER_NAME,
@@ -13369,6 +13439,7 @@ static virDriver qemuDriver = {
.domainPMSuspendForDuration = qemuDomainPMSuspendForDuration, /* 0.9.11 */
.domainPMWakeup = qemuDomainPMWakeup, /* 0.9.11 */
.domainGetCPUStats = qemuDomainGetCPUStats, /* 0.9.11 */
+ .qemuDomainQemuAgentCommand = qemuDomainQemuAgentCommand, /* 0.10.0 */
};
diff --git a/src/qemu_protocol-structs b/src/qemu_protocol-structs
index 67968eb..624fa7b 100644
--- a/src/qemu_protocol-structs
+++ b/src/qemu_protocol-structs
@@ -19,7 +19,17 @@ struct qemu_domain_attach_args {
struct qemu_domain_attach_ret {
remote_nonnull_domain dom;
};
+struct qemu_agent_command_args {
+ remote_nonnull_domain dom;
+ remote_nonnull_string cmd;
+ int timeout;
+ u_int flags;
+};
+struct qemu_agent_command_ret {
+ remote_nonnull_string result;
+};
enum qemu_procedure {
QEMU_PROC_MONITOR_COMMAND = 1,
QEMU_PROC_DOMAIN_ATTACH = 2,
+ QEMU_PROC_AGENT_COMMAND = 3,
};
diff --git a/src/remote/qemu_protocol.x b/src/remote/qemu_protocol.x
index c06339c..67c6493 100644
--- a/src/remote/qemu_protocol.x
+++ b/src/remote/qemu_protocol.x
@@ -47,6 +47,17 @@ struct qemu_domain_attach_ret {
remote_nonnull_domain dom;
};
+struct qemu_agent_command_args {
+ remote_nonnull_domain dom;
+ remote_nonnull_string cmd;
+ int timeout;
+ unsigned int flags;
+};
+
+struct qemu_agent_command_ret {
+ remote_nonnull_string result;
+};
+
/* Define the program number, protocol version and procedure numbers here. */
const QEMU_PROGRAM = 0x20008087;
const QEMU_PROTOCOL_VERSION = 1;
@@ -61,5 +72,6 @@ enum qemu_procedure {
* are some exceptions to this rule, e.g. domainDestroy. Other APIs MAY
* be marked as high priority. If in doubt, it's safe to choose low. */
QEMU_PROC_MONITOR_COMMAND = 1, /* skipgen skipgen priority:low */
- QEMU_PROC_DOMAIN_ATTACH = 2 /* autogen autogen priority:low */
+ QEMU_PROC_DOMAIN_ATTACH = 2, /* autogen autogen priority:low */
+ QEMU_PROC_AGENT_COMMAND = 3 /* skipgen skipgen priority:low */
};
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 353a153..6f27f59 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -5191,6 +5191,46 @@ make_nonnull_domain_snapshot (remote_nonnull_domain_snapshot
*snapshot_dst, virD
make_nonnull_domain(&snapshot_dst->dom, snapshot_src->domain);
}
+static int
+remoteQemuDomainQemuAgentCommand (virDomainPtr domain, const char *cmd,
+ char **result, int timeout,
+ unsigned int flags)
+{
+ int rv = -1;
+ qemu_agent_command_args args;
+ qemu_agent_command_ret ret;
+ struct private_data *priv = domain->conn->privateData;
+
+ remoteDriverLock(priv);
+
+ make_nonnull_domain(&args.dom, domain);
+ args.cmd = (char *)cmd;
+ args.timeout = timeout;
+ args.flags = flags;
+
+ memset (&ret, 0, sizeof(ret));
+
+ if (call (domain->conn, priv, REMOTE_CALL_QEMU, QEMU_PROC_AGENT_COMMAND,
+ (xdrproc_t) xdr_qemu_agent_command_args, (char *) &args,
+ (xdrproc_t) xdr_qemu_agent_command_ret, (char *) &ret) == -1)
+ goto done;
+
+ *result = strdup(ret.result);
+ if (*result == NULL) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ rv = 0;
+
+cleanup:
+ xdr_free ((xdrproc_t) xdr_qemu_agent_command_ret, (char *) &ret);
+
+done:
+ remoteDriverUnlock(priv);
+ return rv;
+}
+
/*----------------------------------------------------------------------*/
unsigned long remoteVersion(void)
@@ -5368,6 +5408,7 @@ static virDriver remote_driver = {
.domainSetMetadata = remoteDomainSetMetadata, /* 0.9.10 */
.domainGetMetadata = remoteDomainGetMetadata, /* 0.9.10 */
.domainGetHostname = remoteDomainGetHostname, /* 0.10.0 */
+ .qemuDomainQemuAgentCommand = remoteQemuDomainQemuAgentCommand, /* 0.10.0 */
};
static virNetworkDriver network_driver = {