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>
---
diff to v4:
- s/1.2.4/1.2.5/
- drop explicit agent error check since it's done in qemuAgentCommand now
src/qemu/qemu_agent.c | 93 +++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_agent.h | 8 ++++
src/qemu/qemu_driver.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 210 insertions(+)
diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c
index 57c7cc5..10e2b8d 100644
--- a/src/qemu/qemu_agent.c
+++ b/src/qemu/qemu_agent.c
@@ -1685,3 +1685,96 @@ qemuAgentUpdateCPUInfo(unsigned int nvcpus,
return 0;
}
+
+
+int
+qemuAgentGetTime(qemuAgentPtr mon,
+ long long *seconds,
+ unsigned int *nseconds)
+{
+ 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, true,
+ VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 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 */
+ *seconds = json_time / 1000000000LL;
+ *nseconds = 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 seconds,
+ unsigned int nseconds,
+ 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 (seconds > LLONG_MAX / 1000000000LL) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("Time '%lld' is too big for guest
agent"),
+ seconds);
+ return ret;
+ }
+
+ json_time = seconds * 1000000000LL;
+ json_time += nseconds;
+ cmd = qemuAgentMakeCommand("guest-set-time",
+ "I:time", json_time,
+ NULL);
+ }
+
+ if (!cmd)
+ return ret;
+
+ if (qemuAgentCommand(mon, cmd, &reply, true,
+ VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 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 58531d5..6cd6b49 100644
--- a/src/qemu/qemu_agent.h
+++ b/src/qemu/qemu_agent.h
@@ -98,4 +98,12 @@ int qemuAgentSetVCPUs(qemuAgentPtr mon, qemuAgentCPUInfoPtr cpus,
size_t ncpus);
int qemuAgentUpdateCPUInfo(unsigned int nvcpus,
qemuAgentCPUInfoPtr cpuinfo,
int ncpuinfo);
+
+int qemuAgentGetTime(qemuAgentPtr mon,
+ long long *seconds,
+ unsigned int *nseconds);
+int qemuAgentSetTime(qemuAgentPtr mon,
+ long long seconds,
+ unsigned int nseconds,
+ bool sync);
#endif /* __QEMU_AGENT_H__ */
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 52ca47c..cab653b 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -16536,6 +16536,113 @@ qemuConnectGetCPUModelNames(virConnectPtr conn,
return cpuGetModels(arch, models);
}
+static int
+qemuDomainGetTime(virDomainPtr dom,
+ long long *seconds,
+ unsigned int *nseconds,
+ unsigned int flags)
+{
+ virQEMUDriverPtr driver = dom->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ qemuDomainObjPrivatePtr priv;
+ int ret = -1;
+ int rv;
+
+ 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, seconds, nseconds);
+ qemuDomainObjExitAgent(vm);
+
+ if (rv < 0)
+ goto endjob;
+
+ ret = 0;
+
+ endjob:
+ if (!qemuDomainObjEndJob(driver, vm))
+ vm = NULL;
+
+ cleanup:
+ if (vm)
+ virObjectUnlock(vm);
+ return ret;
+}
+
+static int
+qemuDomainSetTime(virDomainPtr dom,
+ long long seconds,
+ unsigned int nseconds,
+ unsigned int flags)
+{
+ virQEMUDriverPtr driver = dom->conn->privateData;
+ qemuDomainObjPrivatePtr priv;
+ virDomainObjPtr vm;
+ bool sync = flags & VIR_DOMAIN_TIME_SYNC;
+ int ret = -1;
+ int rv;
+
+ virCheckFlags(VIR_DOMAIN_TIME_SYNC, 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;
+
+ qemuDomainObjEnterAgent(vm);
+ rv = qemuAgentSetTime(priv->agent, seconds, nseconds, 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 int
qemuDomainFSFreeze(virDomainPtr dom,
@@ -16819,6 +16926,8 @@ static virDriver qemuDriver = {
.connectGetCPUModelNames = qemuConnectGetCPUModelNames, /* 1.1.3 */
.domainFSFreeze = qemuDomainFSFreeze, /* 1.2.5 */
.domainFSThaw = qemuDomainFSThaw, /* 1.2.5 */
+ .domainGetTime = qemuDomainGetTime, /* 1.2.5 */
+ .domainSetTime = qemuDomainSetTime, /* 1.2.5 */
};
--
1.9.3