diff -uNrp libvirt-0.9.13.orig/daemon/remote.c libvirt-0.9.13/daemon/remote.c --- libvirt-0.9.13.orig/daemon/remote.c 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/daemon/remote.c 2012-07-06 09:18:12.494454191 +0900 @@ -3923,6 +3923,42 @@ 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->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 -uNrp libvirt-0.9.13.orig/docs/hvsupport.pl libvirt-0.9.13/docs/hvsupport.pl --- libvirt-0.9.13.orig/docs/hvsupport.pl 2011-06-10 15:50:11.000000000 +0900 +++ libvirt-0.9.13/docs/hvsupport.pl 2012-07-06 09:18:36.363454752 +0900 @@ -129,6 +129,7 @@ $apis{virDomainMigratePerform3} = "0.9.2 $apis{virDomainMigrateFinish3} = "0.9.2"; $apis{virDomainMigrateConfirm3} = "0.9.2"; +$apis{virDomainQemuSupportCommand} = "0.9.14"; # Now we want to get the mapping between public APIs diff -uNrp libvirt-0.9.13.orig/include/libvirt/libvirt-qemu.h libvirt-0.9.13/include/libvirt/libvirt-qemu.h --- libvirt-0.9.13.orig/include/libvirt/libvirt-qemu.h 2012-03-06 22:59:21.000000000 +0900 +++ libvirt-0.9.13/include/libvirt/libvirt-qemu.h 2012-07-06 09:18:12.494454191 +0900 @@ -32,6 +32,9 @@ virDomainPtr virDomainQemuAttach(virConn unsigned int pid_value, unsigned int flags); +int virDomainQemuAgentCommand(virDomainPtr domain, const char *cmd, + char **result, unsigned int flags); + # ifdef __cplusplus } # endif diff -uNrp libvirt-0.9.13.orig/python/generator.py libvirt-0.9.13/python/generator.py --- libvirt-0.9.13.orig/python/generator.py 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/python/generator.py 2012-07-06 09:18:12.495454550 +0900 @@ -429,6 +429,7 @@ skip_impl = ( qemu_skip_impl = ( 'virDomainQemuMonitorCommand', + 'virDomainQemuAgentCommand', ) diff -uNrp libvirt-0.9.13.orig/python/libvirt-qemu-override-api.xml libvirt-0.9.13/python/libvirt-qemu-override-api.xml --- libvirt-0.9.13.orig/python/libvirt-qemu-override-api.xml 2011-09-14 15:24:44.000000000 +0900 +++ libvirt-0.9.13/python/libvirt-qemu-override-api.xml 2012-07-06 09:18:12.496455183 +0900 @@ -8,5 +8,12 @@ + + Send a Guest Agent command to domain + + + + + diff -uNrp libvirt-0.9.13.orig/python/libvirt-qemu-override.c libvirt-0.9.13/python/libvirt-qemu-override.c --- libvirt-0.9.13.orig/python/libvirt-qemu-override.c 2012-03-30 11:45:28.000000000 +0900 +++ libvirt-0.9.13/python/libvirt-qemu-override.c 2012-07-06 09:18:12.496455183 +0900 @@ -82,6 +82,36 @@ libvirt_qemu_virDomainQemuMonitorCommand return py_retval; } +static PyObject * +libvirt_qemu_virDomainQemuAgentCommand(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *py_retval; + char *result = NULL; + virDomainPtr domain; + PyObject *pyobj_domain; + unsigned int flags; + char *cmd; + int c_retval; + + if (!PyArg_ParseTuple(args, (char *)"Ozi:virDomainQemuAgentCommand", + &pyobj_domain, &cmd, &flags)) + return NULL; + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (domain == NULL) + return VIR_PY_NONE; + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virDomainQemuAgentCommand(domain, cmd, &result, flags); + LIBVIRT_END_ALLOW_THREADS; + + if (c_retval < 0) + return VIR_PY_NONE; + + py_retval = PyString_FromString(result); + return py_retval; +} + /************************************************************************ * * * The registration stuff * @@ -90,6 +120,7 @@ libvirt_qemu_virDomainQemuMonitorCommand static PyMethodDef libvirtQemuMethods[] = { #include "libvirt-qemu-export.c" {(char *) "virDomainQemuMonitorCommand", libvirt_qemu_virDomainQemuMonitorCommand, METH_VARARGS, NULL}, + {(char *) "virDomainQemuAgentCommand", libvirt_qemu_virDomainQemuAgentCommand, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; diff -uNrp libvirt-0.9.13.orig/src/driver.h libvirt-0.9.13/src/driver.h --- libvirt-0.9.13.orig/src/driver.h 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/driver.h 2012-07-06 09:18:12.497454257 +0900 @@ -680,7 +680,7 @@ typedef int unsigned int flags); typedef int - (*virDrvDomainQemuMonitorCommand)(virDomainPtr domain, const char *cmd, + (*virDrvDomainQemuSupportCommand)(virDomainPtr domain, const char *cmd, char **result, unsigned int flags); /* Choice of unsigned int rather than pid_t is intentional. */ @@ -1015,7 +1015,7 @@ struct _virDriver { virDrvDomainSnapshotHasMetadata domainSnapshotHasMetadata; virDrvDomainRevertToSnapshot domainRevertToSnapshot; virDrvDomainSnapshotDelete domainSnapshotDelete; - virDrvDomainQemuMonitorCommand qemuDomainMonitorCommand; + virDrvDomainQemuSupportCommand qemuDomainMonitorCommand; virDrvDomainQemuAttach qemuDomainAttach; virDrvDomainOpenConsole domainOpenConsole; virDrvDomainOpenGraphics domainOpenGraphics; @@ -1041,6 +1041,7 @@ struct _virDriver { virDrvDomainGetDiskErrors domainGetDiskErrors; virDrvDomainSetMetadata domainSetMetadata; virDrvDomainGetMetadata domainGetMetadata; + virDrvDomainQemuSupportCommand qemuDomainQemuAgentCommand; }; typedef int diff -uNrp libvirt-0.9.13.orig/src/libvirt-qemu.c libvirt-0.9.13/src/libvirt-qemu.c --- libvirt-0.9.13.orig/src/libvirt-qemu.c 2012-05-31 23:23:22.000000000 +0900 +++ libvirt-0.9.13/src/libvirt-qemu.c 2012-07-06 09:18:12.497454257 +0900 @@ -185,3 +185,52 @@ error: virDispatchError(conn); return NULL; } + +/** + * virDomainQemuAgentCommand: + * @domain: a domain object + * @cmd: the guest agent command string + * @result: a string returned by @cmd + * @flags: execution flags + * + * Execution Guest Agent Command + * + * Returns 0 if succeeded, -1 in failing. + */ +int +virDomainQemuAgentCommand(virDomainPtr domain, + const char *cmd, + char **result, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DEBUG("domain=%p, cmd=%s, result=%p, flags=%x", + domain, cmd, result, 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, + 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 -uNrp libvirt-0.9.13.orig/src/libvirt_qemu.syms libvirt-0.9.13/src/libvirt_qemu.syms --- libvirt-0.9.13.orig/src/libvirt_qemu.syms 2011-07-14 12:00:39.000000000 +0900 +++ libvirt-0.9.13/src/libvirt_qemu.syms 2012-07-06 09:18:12.498454377 +0900 @@ -19,3 +19,8 @@ LIBVIRT_QEMU_0.9.4 { global: virDomainQemuAttach; } LIBVIRT_QEMU_0.8.3; + +LIBVIRT_QEMU_0.9.14 { + global: + virDomainQemuAgentCommand; +} LIBVIRT_QEMU_0.9.4; diff -uNrp libvirt-0.9.13.orig/src/qemu/qemu_agent.c libvirt-0.9.13/src/qemu/qemu_agent.c --- libvirt-0.9.13.orig/src/qemu/qemu_agent.c 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/qemu/qemu_agent.c 2012-07-06 09:18:12.498454377 +0900 @@ -1410,3 +1410,29 @@ qemuAgentSuspend(qemuAgentPtr mon, virJSONValueFree(reply); return ret; } + +int qemuAgentQemuAgentCommand(qemuAgentPtr mon, + const char *cmd_str, + char **result) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + cmd = virJSONValueFromString(cmd_str); + if (!cmd) + return ret; + + ret = qemuAgentCommand(mon, cmd, &reply); + + if (ret == 0) { + ret = qemuAgentCheckError(cmd, reply); + *result = virJSONValueToString(reply); + } else { + *result = NULL; + } + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} diff -uNrp libvirt-0.9.13.orig/src/qemu/qemu_agent.h libvirt-0.9.13/src/qemu/qemu_agent.h --- libvirt-0.9.13.orig/src/qemu/qemu_agent.h 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/qemu/qemu_agent.h 2012-07-06 09:18:12.499545367 +0900 @@ -80,4 +80,8 @@ int qemuAgentFSThaw(qemuAgentPtr mon); int qemuAgentSuspend(qemuAgentPtr mon, unsigned int target); + +int qemuAgentQemuAgentCommand(qemuAgentPtr mon, + const char *cmd, + char **result); #endif /* __QEMU_AGENT_H__ */ diff -uNrp libvirt-0.9.13.orig/src/qemu/qemu_driver.c libvirt-0.9.13/src/qemu/qemu_driver.c --- libvirt-0.9.13.orig/src/qemu/qemu_driver.c 2012-06-27 10:44:39.000000000 +0900 +++ libvirt-0.9.13/src/qemu/qemu_driver.c 2012-07-06 09:18:12.503579712 +0900 @@ -13158,6 +13158,78 @@ qemuListAllDomains(virConnectPtr conn, return ret; } +static int +qemuDomainQemuAgentCommand(virDomainPtr domain, + const char *cmd, + char **result, + 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); + qemuDomainObjExitAgent(driver, vm); + + VIR_DEBUG ("qemu-agent-command ret: '%d' domain: '%s' string: %s", + ret, vm->def->name, *result); + +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, @@ -13323,6 +13395,7 @@ static virDriver qemuDriver = { .domainPMSuspendForDuration = qemuDomainPMSuspendForDuration, /* 0.9.11 */ .domainPMWakeup = qemuDomainPMWakeup, /* 0.9.11 */ .domainGetCPUStats = qemuDomainGetCPUStats, /* 0.9.11 */ + .qemuDomainQemuAgentCommand = qemuDomainQemuAgentCommand, /* 0.9.14 */ }; diff -uNrp libvirt-0.9.13.orig/src/qemu_protocol-structs libvirt-0.9.13/src/qemu_protocol-structs --- libvirt-0.9.13.orig/src/qemu_protocol-structs 2012-03-06 22:59:21.000000000 +0900 +++ libvirt-0.9.13/src/qemu_protocol-structs 2012-07-06 09:18:12.505455079 +0900 @@ -19,7 +19,16 @@ 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; + 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 -uNrp libvirt-0.9.13.orig/src/remote/qemu_protocol.x libvirt-0.9.13/src/remote/qemu_protocol.x --- libvirt-0.9.13.orig/src/remote/qemu_protocol.x 2012-03-06 22:59:21.000000000 +0900 +++ libvirt-0.9.13/src/remote/qemu_protocol.x 2012-07-06 09:18:12.506618309 +0900 @@ -47,6 +47,16 @@ struct qemu_domain_attach_ret { remote_nonnull_domain dom; }; +struct qemu_agent_command_args { + remote_nonnull_domain dom; + remote_nonnull_string cmd; + u_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 +71,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 -uNrp libvirt-0.9.13.orig/src/remote/remote_driver.c libvirt-0.9.13/src/remote/remote_driver.c --- libvirt-0.9.13.orig/src/remote/remote_driver.c 2012-06-25 16:06:18.000000000 +0900 +++ libvirt-0.9.13/src/remote/remote_driver.c 2012-07-06 09:18:12.508579685 +0900 @@ -5127,6 +5127,44 @@ make_nonnull_domain_snapshot (remote_non make_nonnull_domain(&snapshot_dst->dom, snapshot_src->domain); } +static int +remoteQemuDomainQemuAgentCommand (virDomainPtr domain, const char *cmd, + char **result, 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.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) @@ -5303,6 +5341,7 @@ static virDriver remote_driver = { .domainGetDiskErrors = remoteDomainGetDiskErrors, /* 0.9.10 */ .domainSetMetadata = remoteDomainSetMetadata, /* 0.9.10 */ .domainGetMetadata = remoteDomainGetMetadata, /* 0.9.10 */ + .qemuDomainQemuAgentCommand = remoteQemuDomainQemuAgentCommand, /* 0.9.14 */ }; static virNetworkDriver network_driver = { diff -uNrp libvirt-0.9.13.orig/tools/virsh.c libvirt-0.9.13/tools/virsh.c --- libvirt-0.9.13.orig/tools/virsh.c 2012-07-02 10:45:49.000000000 +0900 +++ libvirt-0.9.13/tools/virsh.c 2012-07-06 09:18:12.514579757 +0900 @@ -18138,6 +18138,68 @@ cleanup: return ret; } +/* + * "qemu-agent-command" command + */ +static const vshCmdInfo info_qemu_agent_command[] = { + {"help", N_("Qemu Guest Agent Command")}, + {"desc", N_("Qemu Guest Agent Command")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_qemu_agent_command[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + bool ret = false; + char *guest_agent_cmd = NULL; + char *result = NULL; + unsigned int flags = 0; + const vshCmdOpt *opt = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + bool pad = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto cleanup; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (pad) + virBufferAddChar(&buf, ' '); + pad = true; + virBufferAdd(&buf, opt->data, -1); + } + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to collect command")); + goto cleanup; + } + guest_agent_cmd = virBufferContentAndReset(&buf); + + if (virDomainQemuAgentCommand(dom, guest_agent_cmd, &result, flags) < 0) + goto cleanup; + + printf("%s\n", result); + + ret = true; + +cleanup: + VIR_FREE(result); + VIR_FREE(guest_agent_cmd); + if (dom) + virDomainFree(dom); + + return ret; +} + static const vshCmdDef domManagementCmds[] = { {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device, 0}, @@ -18447,6 +18509,8 @@ static const vshCmdDef hostAndHypervisor {"qemu-attach", cmdQemuAttach, opts_qemu_attach, info_qemu_attach, 0}, {"qemu-monitor-command", cmdQemuMonitorCommand, opts_qemu_monitor_command, info_qemu_monitor_command, 0}, + {"qemu-agent-command", cmdQemuAgentCommand, opts_qemu_agent_command, + info_qemu_agent_command, 0}, {"sysinfo", cmdSysinfo, NULL, info_sysinfo, 0}, {"uri", cmdURI, NULL, info_uri, 0}, {"version", cmdVersion, opts_version, info_version, 0},