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 = { -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list