Richard W.M. Jones
2017-Mar-21 16:48 UTC
[Libguestfs] [PATCH] p2v: Calculate offset of the Real Time Clock from UTC.
Unlike the <cpu> node (see the other thread on the libguestfs ML), reading the Real Time Clock doesn't require libvirt and does work :-) For reference, read: https://libvirt.org/formatdomain.html#elementsTime https://en.wikipedia.org/wiki/Time_zone To test this you can run virt-p2v under qemu with a RTC offset: make -C p2v \ run-virt-p2v-in-a-vm \ QEMU_OPTIONS="-rtc base=`date +%Y-%m-%dT12:%M:%S`" ^^ Adjust the hour (marked with ^^) to simulate selecting a different offset. Rich.
Richard W.M. Jones
2017-Mar-21 16:48 UTC
[Libguestfs] [PATCH] p2v: Calculate offset of the Real Time Clock from UTC.
Calculate the offset of the physical host's Real Time Clock (RTC) from UTC and pass this to virt-v2v through the libvirt XML description of the physical machine. The libvirt XML is modified to add one of the following: (no <clock/> element) - if the RTC could not be read or there was some other time calculation error. <clock offset='utc' /> - if the RTC is the same as UTC. <clock offset='localtime' /> - if the RTC is in local time. It's not possible to distinguish between UTC and localtime in timezones that lie along the Greenwich Meridian (obviously including the UK), when daylight savings time is not in effect. In that case, UTC is chosen. Necessarily in timezones that use DST, this depends on when virt-p2v is run, so in the unusual case where a physical machine is switched off for a long time and then booted directly into virt-p2v when DST has changed, the calculation can be wrong. <clock offset='variable' basis='utc' adjustment='<seconds>' /> - if the RTC is some other offset to UTC, other than equal to UTC or localtime. --- m4/guestfs_libraries.m4 | 1 + p2v/Makefile.am | 4 +- p2v/config.c | 13 ++++ p2v/conversion.c | 23 +++++++ p2v/main.c | 1 + p2v/p2v.h | 13 ++++ p2v/rtc.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 p2v/rtc.c diff --git a/m4/guestfs_libraries.m4 b/m4/guestfs_libraries.m4 index 60f89d44b..b8fa1446a 100644 --- a/m4/guestfs_libraries.m4 +++ b/m4/guestfs_libraries.m4 @@ -43,6 +43,7 @@ AC_CHECK_HEADERS([\ errno.h \ linux/fs.h \ linux/raid/md_u.h \ + linux/rtc.h \ printf.h \ sys/inotify.h \ sys/resource.h \ diff --git a/p2v/Makefile.am b/p2v/Makefile.am index 42483dabe..5f90aa728 100644 --- a/p2v/Makefile.am +++ b/p2v/Makefile.am @@ -84,6 +84,7 @@ virt_p2v_SOURCES = \ main.c \ nbd.c \ p2v.h \ + rtc.c \ ssh.c \ utils.c \ whole-file.c @@ -112,7 +113,8 @@ virt_p2v_LDADD = \ $(LIBXML2_LIBS) \ $(GTK_LIBS) \ $(DBUS_LIBS) \ - ../gnulib/lib/libgnu.la + ../gnulib/lib/libgnu.la \ + -lm # Scripts to build the disk image, USB key, or kickstart. bin_SCRIPTS = virt-p2v-make-disk virt-p2v-make-kickstart virt-p2v-make-kiwi diff --git a/p2v/config.c b/p2v/config.c index 982017a6d..2b376c117 100644 --- a/p2v/config.c +++ b/p2v/config.c @@ -152,6 +152,19 @@ print_config (struct config *config, FILE *fp) config->cpu.acpi ? " acpi" : "", config->cpu.apic ? " apic" : "", config->cpu.pae ? " pae" : ""); + fprintf (fp, "rtc offset . . . "); + switch (config->rtc.basis) { + case BASIS_UNKNOWN: + fprintf (fp, "unknown"); + break; + case BASIS_UTC: + fprintf (fp, "%d seconds from UTC", config->rtc.offset); + break; + case BASIS_LOCALTIME: + fprintf (fp, "%d seconds from localtime", config->rtc.offset); + break; + } + fprintf (fp, "\n"); fprintf (fp, "disks . . . . . "); if (config->disks != NULL) { for (i = 0; config->disks[i] != NULL; ++i) diff --git a/p2v/conversion.c b/p2v/conversion.c index 55fbfb11d..e7e7ce039 100644 --- a/p2v/conversion.c +++ b/p2v/conversion.c @@ -641,6 +641,29 @@ generate_libvirt_xml (struct config *config, struct data_conn *data_conns, } end_element (); } + switch (config->rtc.basis) { + case BASIS_UNKNOWN: + /* Don't emit any <clock> element. */ + break; + case BASIS_UTC: + start_element ("clock") { + if (config->rtc.offset == 0) + attribute ("offset", "utc"); + else { + attribute ("offset", "variable"); + attribute ("basis", "utc"); + attribute_format ("adjustment", "%d", config->rtc.offset); + } + } end_element (); + break; + case BASIS_LOCALTIME: + start_element ("clock") { + attribute ("offset", "localtime"); + /* config->rtc.offset is always 0 in this case */ + } end_element (); + break; + } + start_element ("os") { start_element ("type") { attribute ("arch", host_cpu); diff --git a/p2v/main.c b/p2v/main.c index 7f1e1c065..8921184b4 100644 --- a/p2v/main.c +++ b/p2v/main.c @@ -340,6 +340,7 @@ set_config_defaults (struct config *config) config->memory++; get_cpu_config (&config->cpu); + get_rtc_config (&config->rtc); /* Find all block devices in the system. */ if (!test_disk) diff --git a/p2v/p2v.h b/p2v/p2v.h index 6c794f027..38ebe7916 100644 --- a/p2v/p2v.h +++ b/p2v/p2v.h @@ -71,6 +71,15 @@ struct cpu_config { bool pae; }; +struct rtc_config { + enum { + BASIS_UNKNOWN, /* RTC could not be read. */ + BASIS_UTC, /* RTC is either UTC or an offset from UTC. */ + BASIS_LOCALTIME, /* RTC is localtime. */ + } basis; + int offset; /* RTC seconds offset from basis. */ +}; + struct config { char *server; int port; @@ -84,6 +93,7 @@ struct config { int vcpus; uint64_t memory; struct cpu_config cpu; + struct rtc_config rtc; char **disks; char **removable; char **interfaces; @@ -107,6 +117,9 @@ extern void print_config (struct config *, FILE *); /* cpuid.c */ extern void get_cpu_config (struct cpu_config *); +/* rtc.c */ +extern void get_rtc_config (struct rtc_config *); + /* kernel-cmdline.c */ extern char **parse_cmdline_string (const char *cmdline); extern char **parse_proc_cmdline (void); diff --git a/p2v/rtc.c b/p2v/rtc.c new file mode 100644 index 000000000..28884fcc0 --- /dev/null +++ b/p2v/rtc.c @@ -0,0 +1,165 @@ +/* virt-p2v + * Copyright (C) 2017 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * Try to calculate Real Time Clock (RTC) offset from UTC in seconds. + * For example if the RTC is 1 hour ahead of UTC, this will return + * C<3600>. This is stored in C<config-E<gt>rtc_offset>. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <fcntl.h> +#include <errno.h> +#include <libintl.h> +#include <time.h> +#include <sys/ioctl.h> + +#include <math.h> + +#ifdef HAVE_LINUX_RTC_H +#include <linux/rtc.h> +#endif + +#include "getprogname.h" +#include "ignore-value.h" + +#include "p2v.h" + +#ifndef HAVE_LINUX_RTC_H +void +get_rtc_config (struct rtc_config *rtc) +{ + fprintf (stderr, "%s: RTC: compiled without support for /dev/rtc\n", + getprogname ()); + + rtc->offset = 0; + rtc->basis = BASIS_UTC; +} + +#else /* HAVE_LINUX_RTC_H */ + +/** + * Return RTC offset from UTC in seconds, positive numbers meaning + * that the RTC is running ahead of UTC. + * + * In the error case, C<rtcE<gt>offset> is updated with 0 and + * C<rtcE<gt>basis> is set to C<BASIS_UNKNOWN>. + */ +void +get_rtc_config (struct rtc_config *rtc) +{ + int fd; + struct rtc_time rtm; + struct tm tm; + time_t rtc_time; + time_t system_time; + double rf; + + rtc->basis = BASIS_UNKNOWN; + rtc->offset = 0; + + fd = open ("/dev/rtc", O_RDONLY); + if (fd == -1) { + perror ("/dev/rtc"); + return; + } + + if (ioctl (fd, RTC_RD_TIME, &rtm) == -1) { + perror ("ioctl: RTC_RD_TIME"); + close (fd); + return; + } + + close (fd); + +#ifdef DEBUG_STDERR + fprintf (stderr, "%s: RTC: %04d-%02d-%02d %02d:%02d:%02d\n", + getprogname (), + rtm.tm_year + 1900, rtm.tm_mon + 1, rtm.tm_mday, + rtm.tm_hour, rtm.tm_min, rtm.tm_sec); +#endif + + /* Convert this to seconds since the epoch. */ + tm.tm_sec = rtm.tm_sec; + tm.tm_min = rtm.tm_min; + tm.tm_hour = rtm.tm_hour; + tm.tm_mday = rtm.tm_mday; + tm.tm_mon = rtm.tm_mon; + tm.tm_year = rtm.tm_year; + tm.tm_isdst = 0; /* Ignore DST when calculating. */ + rtc_time = timegm (&tm); + if (rtc_time == -1) + return; /* Not representable as a Unix time. */ + + /* Get system time in UTC. */ + system_time = time (NULL); + + /* Calculate the difference, rounded to the nearest 15 minutes. */ + rf = rtc_time - system_time; + +#ifdef DEBUG_STDERR + fprintf (stderr, "%s: RTC: %ld system time: %ld difference: %g\n", + getprogname (), + (long) rtc_time, (long) system_time, rf); +#endif + + rf /= 15*60; + rf = round (rf); + rf *= 15*60; + + /* If it's obviously out of range then print an error and return. */ + if (rf < -12*60*60 || rf > 14*60*60) { + fprintf (stderr, + "%s: RTC: offset of RTC from UTC is out of range (%g).\n", + getprogname (), rf); + return; + } + + rtc->offset = (int) rf; + +#ifdef DEBUG_STDERR + fprintf (stderr, "%s: RTC: offset of RTC from UTC = %d secs\n", + getprogname (), rtc->offset); +#endif + + /* Is the hardware clock set to localtime? + * + * Unfortunately it's not possible to distinguish between UTC and + * localtime in timezones that lie along the Greenwich Meridian + * (obviously including the UK), when daylight savings time is not + * in effect. In that case, prefer UTC. + */ + localtime_r (&system_time, &tm); + if (tm.tm_gmtoff != 0 && tm.tm_gmtoff != rtc->offset) + rtc->basis = BASIS_UTC; + else { + rtc->basis = BASIS_LOCALTIME; + rtc->offset = 0; +#ifdef DEBUG_STDERR + fprintf (stderr, "%s: RTC time is localtime\n", getprogname ()); +#endif + } + + return; +} + +#endif /* HAVE_LINUX_RTC_H */ -- 2.12.0
Pino Toscano
2017-Mar-24 11:08 UTC
Re: [Libguestfs] [PATCH] p2v: Calculate offset of the Real Time Clock from UTC.
On Tuesday, 21 March 2017 17:48:50 CET Richard W.M. Jones wrote:> Calculate the offset of the physical host's Real Time Clock (RTC) from > UTC and pass this to virt-v2v through the libvirt XML description of > the physical machine. > > The libvirt XML is modified to add one of the following: > > (no <clock/> element) > > - if the RTC could not be read or there was some other time > calculation error. > > <clock offset='utc' /> > > - if the RTC is the same as UTC. > > <clock offset='localtime' /> > > - if the RTC is in local time. > > It's not possible to distinguish between UTC and localtime in > timezones that lie along the Greenwich Meridian (obviously > including the UK), when daylight savings time is not in effect. In > that case, UTC is chosen. > > Necessarily in timezones that use DST, this depends on when > virt-p2v is run, so in the unusual case where a physical machine is > switched off for a long time and then booted directly into virt-p2v > when DST has changed, the calculation can be wrong. > > <clock offset='variable' basis='utc' adjustment='<seconds>' /> > > - if the RTC is some other offset to UTC, other than equal to UTC or > localtime. > ---LGTM. Thanks, -- Pino Toscano