From f2f5d7f0cf1b93b397fe62b66f445d10b30104cd Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Thu, 3 Apr 2014 18:29:40 +0200 Subject: [PATCH 24/30] qga: Don't require 'time' argument in guest-set-time command RH-Author: Laszlo Ersek Message-id: <1396549780-7169-1-git-send-email-lersek@redhat.com> Patchwork-id: 58333 O-Subject: [RHEL-6.6 qemu-kvm PATCH] qga: Don't require 'time' argument in guest-set-time command Bugzilla: 1066191 RH-Acked-by: Amos Kong RH-Acked-by: Marcel Apfelbaum RH-Acked-by: Luiz Capitulino From: Michal Privoznik Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1066191 Brew: https://brewweb.devel.redhat.com/taskinfo?taskID=7299711 Testing: https://bugzilla.redhat.com/show_bug.cgi?id=1066191#c3 As the description to the guest-set-time states, the command is there to ease time synchronization after resume. If guest was suspended for longer period of time, its system time can go off so badly, that even NTP refuses to set it. That's why the command was invented: to give users chance to set the time (not necessarily 100% correct). However, there's is no real need for us to require users to pass an arbitrary time. Especially if we can read the correct value from RTC (boiling down to reading host's time). Hence this commit enables logic: guest-set-time() == guest-set-time($now_from_rtc) Signed-off-by: Michal Privoznik Reviewed-by: Eric Blake Signed-off-by: Michael Roth (cherry picked from commit 2c958923bc09b1faf2505a988b4b1c458580e9ac) Signed-off-by: Laszlo Ersek --- qapi-schema-guest.json | 9 +++++---- qga/commands-posix.c | 37 +++++++++++++++++++++++-------------- qga/commands-win32.c | 34 +++++++++++++++++++++++----------- 3 files changed, 51 insertions(+), 29 deletions(-) Signed-off-by: Miroslav Rezanina --- qapi-schema-guest.json | 9 +++++---- qga/commands-posix.c | 41 +++++++++++++++++++++++++---------------- qga/commands-win32.c | 34 +++++++++++++++++++++++----------- 3 files changed, 53 insertions(+), 31 deletions(-) diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json index 7155b7a..1fe7517 100644 --- a/qapi-schema-guest.json +++ b/qapi-schema-guest.json @@ -120,17 +120,18 @@ # This command tries to set guest time to the given value, # then sets the Hardware Clock to the current System Time. # This will make it easier for a guest to resynchronize -# without waiting for NTP. +# without waiting for NTP. If no @time is specified, then +# the time to set is read from RTC. # -# @time: time of nanoseconds, relative to the Epoch of -# 1970-01-01 in UTC. +# @time: #optional time of nanoseconds, relative to the Epoch +# of 1970-01-01 in UTC. # # Returns: Nothing on success. # # Since: 1.5 ## { 'command': 'guest-set-time', - 'data': { 'time': 'int' } } + 'data': { '*time': 'int' } } ## # @GuestAgentCommandInfo: diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 234f794..d31213c 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -143,7 +143,7 @@ int64_t qmp_guest_get_time(Error **errp) return time_ns; } -void qmp_guest_set_time(int64_t time_ns, Error **errp) +void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) { int ret; int status; @@ -151,22 +151,28 @@ void qmp_guest_set_time(int64_t time_ns, Error **errp) Error *local_err = NULL; struct timeval tv; - /* year-2038 will overflow in case time_t is 32bit */ - if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) { - error_setg(errp, "Time %" PRId64 " is too large", time_ns); - return; - } + /* If user has passed a time, validate and set it. */ + if (has_time) { + /* year-2038 will overflow in case time_t is 32bit */ + if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) { + error_setg(errp, "Time %" PRId64 " is too large", time_ns); + return; + } - tv.tv_sec = time_ns / 1000000000; - tv.tv_usec = (time_ns % 1000000000) / 1000; + tv.tv_sec = time_ns / 1000000000; + tv.tv_usec = (time_ns % 1000000000) / 1000; - ret = settimeofday(&tv, NULL); - if (ret < 0) { - error_setg_errno(errp, errno, "Failed to set time to guest"); - return; + ret = settimeofday(&tv, NULL); + if (ret < 0) { + error_setg_errno(errp, errno, "Failed to set time to guest"); + return; + } } - /* Set the Hardware Clock to the current System Time. */ + /* Now, if user has passed a time to set and the system time is set, we + * just need to synchronize the hardware clock. However, if no time was + * passed, user is requesting the opposite: set the system time from the + * hardware clock. */ pid = fork(); if (pid == 0) { setsid(); @@ -174,7 +180,10 @@ void qmp_guest_set_time(int64_t time_ns, Error **errp) reopen_fd_to_null(1); reopen_fd_to_null(2); - execle("/sbin/hwclock", "hwclock", "-w", NULL, environ); + /* Use '/sbin/hwclock -w' to set RTC from the system time, + * or '/sbin/hwclock -s' to set the system time from RTC. */ + execle("/sbin/hwclock", "hwclock", has_time ? "-w" : "-s", + NULL, environ); _exit(EXIT_FAILURE); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 59cc645..d77dbe9 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -307,25 +307,37 @@ int64_t qmp_guest_get_time(Error **errp) return time_ns; } -void qmp_guest_set_time(int64_t time_ns, Error **errp) +void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) { SYSTEMTIME ts; FILETIME tf; LONGLONG time; - if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) { - error_setg(errp, "Time %" PRId64 "is invalid", time_ns); - return; - } + if (has_time) { + /* Okay, user passed a time to set. Validate it. */ + if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) { + error_setg(errp, "Time %" PRId64 "is invalid", time_ns); + return; + } - time = time_ns / 100 + W32_FT_OFFSET; + time = time_ns / 100 + W32_FT_OFFSET; - tf.dwLowDateTime = (DWORD) time; - tf.dwHighDateTime = (DWORD) (time >> 32); + tf.dwLowDateTime = (DWORD) time; + tf.dwHighDateTime = (DWORD) (time >> 32); - if (!FileTimeToSystemTime(&tf, &ts)) { - error_setg(errp, "Failed to convert system time %d", (int)GetLastError()); - return; + if (!FileTimeToSystemTime(&tf, &ts)) { + error_setg(errp, "Failed to convert system time %d", + (int)GetLastError()); + return; + } + } else { + /* Otherwise read the time from RTC which contains the correct value. + * Hopefully. */ + GetSystemTime(&ts); + if (ts.wYear < 1601 || ts.wYear > 30827) { + error_setg(errp, "Failed to get time"); + return; + } } acquire_privilege(SE_SYSTEMTIME_NAME, errp); -- 1.7.1