One caveat though, qemu-ga is expecting time and returning time
in nanoseconds. With all the buffering and propagation delay, the
time is already wrong once it gets to the qemu-ga, but there's
nothing we can do about it.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/qemu/qemu_agent.c | 95 +++++++++++++++++++++++++++++++++
src/qemu/qemu_agent.h | 6 +++
src/qemu/qemu_driver.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 240 insertions(+)
diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c
index 4c83d7d..cde67cc 100644
--- a/src/qemu/qemu_agent.c
+++ b/src/qemu/qemu_agent.c
@@ -1647,3 +1647,98 @@ qemuAgentUpdateCPUInfo(unsigned int nvcpus,
return 0;
}
+
+
+int
+qemuAgentGetTime(qemuAgentPtr mon,
+ long long *getTime)
+{
+ int ret = -1;
+ unsigned long long json_time;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+
+ cmd = qemuAgentMakeCommand("guest-get-time",
+ NULL);
+ if (!cmd)
+ return ret;
+
+ if (qemuAgentCommand(mon, cmd, &reply,
+ VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
+ goto cleanup;
+
+ if (!reply || qemuAgentCheckError(cmd, reply) < 0)
+ goto cleanup;
+
+ if (virJSONValueObjectGetNumberUlong(reply, "return", &json_time) <
0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed return value"));
+ goto cleanup;
+ }
+
+ /* guest agent returns time in nanoseconds,
+ * we need it in seconds here */
+ *getTime = json_time / 1000000000LL;
+ ret = 0;
+
+ cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+
+/**
+ * qemuAgentSetTime:
+ * @setTime: time to set
+ * @sync: let guest agent to read domain's RTC (@setTime is ignored)
+ */
+int
+qemuAgentSetTime(qemuAgentPtr mon,
+ long long setTime,
+ bool sync)
+{
+ int ret = -1;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+
+ if (sync) {
+ cmd = qemuAgentMakeCommand("guest-set-time", NULL);
+ } else {
+ /* guest agent expect time with nanosecond granularity.
+ * Impressing. */
+ long long json_time;
+
+ /* Check if we overflow. For some reason qemu doesn't handle unsigned
+ * long long on the monitor well as it silently truncates numbers to
+ * signed long long. Therefore we must check overflow against LLONG_MAX
+ * not ULLONG_MAX. */
+ if (setTime > LLONG_MAX / 1000000000LL) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("Time '%lld' is too big for guest
agent"),
+ setTime);
+ return ret;
+ }
+
+ json_time = setTime * 1000000000LL;
+ cmd = qemuAgentMakeCommand("guest-set-time",
+ "I:time", json_time,
+ NULL);
+ }
+
+ if (!cmd)
+ return ret;
+
+ if (qemuAgentCommand(mon, cmd, &reply,
+ VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
+ goto cleanup;
+
+ if (!reply || qemuAgentCheckError(cmd, reply) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h
index 5fbacdb..d711acf 100644
--- a/src/qemu/qemu_agent.h
+++ b/src/qemu/qemu_agent.h
@@ -97,4 +97,10 @@ int qemuAgentSetVCPUs(qemuAgentPtr mon, qemuAgentCPUInfoPtr cpus,
size_t ncpus);
int qemuAgentUpdateCPUInfo(unsigned int nvcpus,
qemuAgentCPUInfoPtr cpuinfo,
int ncpuinfo);
+
+int qemuAgentGetTime(qemuAgentPtr mon,
+ long long *getTime);
+int qemuAgentSetTime(qemuAgentPtr mon,
+ long long setTime,
+ bool sync);
#endif /* __QEMU_AGENT_H__ */
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 9a2de12..98b7220 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -16662,6 +16662,143 @@ qemuConnectGetCPUModelNames(virConnectPtr conn,
return cpuGetModels(arch, models);
}
+static int
+qemuDomainGetTime(virDomainPtr dom,
+ virTypedParameterPtr *params,
+ int *nparams,
+ unsigned int flags)
+{
+ virQEMUDriverPtr driver = dom->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ qemuDomainObjPrivatePtr priv;
+ virTypedParameterPtr par = NULL;
+ int maxpar = 0;
+ int npar = 0;
+ int ret = -1;
+ int rv;
+ long long guest_time;
+
+ virCheckFlags(0, ret);
+
+ if (!(vm = qemuDomObjFromDomain(dom)))
+ return ret;
+
+ if (virDomainGetTimeEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ priv = vm->privateData;
+
+ if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto endjob;
+ }
+
+ if (!qemuDomainAgentAvailable(priv, true))
+ goto endjob;
+
+ qemuDomainObjEnterAgent(vm);
+ rv = qemuAgentGetTime(priv->agent, &guest_time);
+ qemuDomainObjExitAgent(vm);
+
+ if (rv < 0)
+ goto endjob;
+
+ if (virTypedParamsAddLLong(&par, &npar, &maxpar,
+ VIR_DOMAIN_TIME_SECONDS, guest_time) < 0)
+ goto endjob;
+
+ *params = par;
+ *nparams = npar;
+ ret = 0;
+
+ endjob:
+ if (!qemuDomainObjEndJob(driver, vm))
+ vm = NULL;
+
+ cleanup:
+ if (vm)
+ virObjectUnlock(vm);
+ return ret;
+}
+
+static int
+qemuDomainSetTime(virDomainPtr dom,
+ virTypedParameterPtr params,
+ int nparams,
+ unsigned int flags)
+{
+ virQEMUDriverPtr driver = dom->conn->privateData;
+ qemuDomainObjPrivatePtr priv;
+ virDomainObjPtr vm;
+ long long guest_time;
+ bool sync = flags & VIR_DOMAIN_TIME_SYNC;
+ int ret = -1;
+ int rv;
+
+ virCheckFlags(VIR_DOMAIN_TIME_SYNC, ret);
+
+ if (virTypedParamsValidate(params, nparams,
+ VIR_DOMAIN_TIME_SECONDS,
+ VIR_TYPED_PARAM_ULLONG,
+ NULL) < 0)
+ return ret;
+
+ if (!(vm = qemuDomObjFromDomain(dom)))
+ return ret;
+
+ if (virDomainSetTimeEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ priv = vm->privateData;
+
+ if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto endjob;
+ }
+
+ if (!qemuDomainAgentAvailable(priv, true))
+ goto endjob;
+
+ if (!sync) {
+ rv = virTypedParamsGetLLong(params, nparams,
+ VIR_DOMAIN_TIME_SECONDS, &guest_time);
+ if (rv < 0)
+ goto endjob;
+
+ if (rv == 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("no time specified"));
+ goto endjob;
+ }
+ }
+
+ qemuDomainObjEnterAgent(vm);
+ rv = qemuAgentSetTime(priv->agent, guest_time, sync);
+ qemuDomainObjExitAgent(vm);
+
+ if (rv < 0)
+ goto endjob;
+
+ ret = 0;
+
+ endjob:
+ if (!qemuDomainObjEndJob(driver, vm))
+ vm = NULL;
+
+ cleanup:
+ if (vm)
+ virObjectUnlock(vm);
+ return ret;
+}
+
static virDriver qemuDriver = {
.no = VIR_DRV_QEMU,
@@ -16853,6 +16990,8 @@ static virDriver qemuDriver = {
.domainMigrateFinish3Params = qemuDomainMigrateFinish3Params, /* 1.1.0 */
.domainMigrateConfirm3Params = qemuDomainMigrateConfirm3Params, /* 1.1.0 */
.connectGetCPUModelNames = qemuConnectGetCPUModelNames, /* 1.1.3 */
+ .domainGetTime = qemuDomainGetTime, /* 1.2.4 */
+ .domainSetTime = qemuDomainSetTime, /* 1.2.4 */
};
--
1.9.0