Signed-off-by: Lei Li <li...@linux.vnet.ibm.com> --- qga/commands-posix.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ qga/qapi-schema.json | 42 +++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 0 deletions(-)
diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 2fef2b6..5424c50 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -149,6 +149,82 @@ struct TimeInfo *qmp_guest_get_time(Error **errp) return time_info; } +void qmp_guest_set_time(bool has_seconds, int64_t seconds, + bool has_microseconds, int64_t microseconds, + bool has_utc_offset, int64_t utc_offset, Error **errp) +{ + int ret; + int status; + pid_t pid, rpid; + struct timeval tv; + TimeInfo *time_info; + + /* year-2038 will overflow in case time_t is 32bit */ + if (sizeof(time_t) == 4) { + return; + } + + if (!has_seconds) { + if (has_microseconds || has_utc_offset) { + error_setg(errp, "field 'seconds' missing"); + return; + } else { + /* Grab time from the host if no arguments given. */ + time_info = get_host_time(errp); + if (!time_info) { + return; + } + + tv.tv_sec = time_info->seconds; + tv.tv_usec = time_info->microseconds; + } + } else { + if (abs(utc_offset) > (24 * 60)) { + error_setg(errp, "'utc_offset' shoud be less than 24 hours"); + return; + } + + if (microseconds > 1000000) { + error_setg(errp, "'microseconds' overflow"); + return; + } + + tv.tv_sec = (time_t) seconds + (has_utc_offset ? utc_offset * 60 : 0); + tv.tv_usec = has_microseconds ? (time_t) microseconds : 0; + } + + + ret = settimeofday(&tv, NULL); + if (ret < 0) { + error_set(errp, QERR_QGA_COMMAND_FAILED, strerror(errno)); + return; + } + + /* Set the Hardware Clock to the current System Time. */ + pid = fork(); + if (pid == 0) { + setsid(); + reopen_fd_to_null(0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + + execle("/sbin/hwclock", "hwclock", "-w", NULL, environ); + _exit(EXIT_FAILURE); + } else if (pid < 0) { + goto exit_err; + } + + do { + rpid = waitpid(pid, &status, 0); + } while (rpid == -1 && errno == EINTR); + if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) { + return; + } + +exit_err: + error_set(errp, QERR_UNDEFINED_ERROR); +} + typedef struct GuestFileHandle { uint64_t id; FILE *fh; diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index d067fa5..6eba625 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -121,6 +121,48 @@ 'returns': 'TimeInfo' } ## +# @guest-set-time: +# +# Set guest time. If no arguments were given, will set +# host time to guest. +# +# Right now, when a guest is paused or migrated to a file +# then loaded from that file, the guest OS has no idea that +# there was a big gap in the time. Depending on how long +# the gap was, NTP might not be able to resynchronize the +# guest. +# +# This command tries to set guest time based on the information +# from host or an absolute value given by management app, and +# set the Hardware Clock to the current System Time. This +# will make it easier for a guest to resynchronize without +# waiting for NTP. +# +# @seconds: #optional "seconds" time. +# +# @microseconds: #optional "microseconds" time. +# +# @utc-offset: #optional utc offset. +# +# Returns: Nothing on success. +# +# Notes: If no arguments were given, will grab time from +# the host. +# +# @microseconds and @utc-offset must not be provided +# unless @seconds is present. +# +# If just @seconds provided, other fields would be +# set to zero. +# +# present +# Since: 1.4 +## +{ 'command': 'guest-set-time', + 'data': { '*seconds': 'int', '*microseconds': 'int', + '*utc-offset': 'int' } } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- 1.7.7.6