From 7948c4dfbea73ac21250b588089039aa17a90386 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 24 May 2011 20:23:07 +0200 Subject: [PATCH] rtc in localtime: use settimeofday(NULL, tz) instead of hwclock(8) We check for LOCAL in /etc/adjtime and if needed, ask the kernel to apply the timezone delta to the system clock. The very first call of settimeofday() without a time, but a timezone warps the system clock, so that it properly runs in UTC. --- Makefile.am | 4 -- src/main.c | 7 ++++ src/util.c | 80 ++++++++++++++++++++++++++++++++++++++ src/util.h | 9 +++++ units/hwclock-load.service | 34 ---------------- 5 files changed, 96 insertions(+), 38 deletions(-) delete mode 100644 units/hwclock-load.service diff --git a/Makefile.am b/Makefile.am index 120c6034e..1fc91639c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -274,7 +274,6 @@ dist_systemunit_DATA = \ units/sys-kernel-security.mount \ units/var-run.mount \ units/media.mount \ - units/hwclock-load.service \ units/hwclock-save.service \ units/remount-rootfs.service \ units/printer.target \ @@ -1430,9 +1429,6 @@ endif ( cd $(DESTDIR)$(pkgsysconfdir)/system/multi-user.target.wants && \ rm -f remote-fs.target && \ $(LN_S) $(systemunitdir)/remote-fs.target remote-fs.target ) - ( cd $(DESTDIR)$(pkgsysconfdir)/system/sysinit.target.wants && \ - rm -f hwclock-load.service && \ - $(LN_S) $(systemunitdir)/hwclock-load.service hwclock-load.service ) ( cd $(DESTDIR)$(systemunitdir)/sysinit.target.wants && \ rm -f dev-hugepages.automount \ dev-mqueue.automount \ diff --git a/src/main.c b/src/main.c index 52b3031fb..68328b76d 100644 --- a/src/main.c +++ b/src/main.c @@ -1049,6 +1049,13 @@ int main(int argc, char *argv[]) { if (label_init() < 0) goto finish; + + if (hwclock_is_localtime()) { + int min; + + min = hwclock_apply_localtime_delta(); + log_info("Hwclock configured in localtime, applying delta of %i minutes to system time", min); + } } else { arg_running_as = MANAGER_USER; log_set_target(LOG_TARGET_CONSOLE); diff --git a/src/util.c b/src/util.c index a271708ba..4046938fc 100644 --- a/src/util.c +++ b/src/util.c @@ -51,6 +51,8 @@ #include #include #include +#include +#include #include "macro.h" #include "util.h" @@ -4761,3 +4763,81 @@ finish: *strv = files; return r; } + +bool hwclock_is_localtime(void) { + FILE *f; + char line[LINE_MAX]; + bool local = false; + + /* + * The third line of adjtime is "UTC" or "LOCAL" or nothing. + * # /etc/adjtime + * 0.0 0 0.0 + * 0 + * UTC + */ + f = fopen("/etc/adjtime", "re"); + if (f) { + if (fgets(line, sizeof(line), f) && + fgets(line, sizeof(line), f) && + fgets(line, sizeof(line), f) ) { + if (!strcmp(line, "LOCAL\n")) + local = true; + } + fclose(f); + } + return local; +} + +int hwclock_apply_localtime_delta(void) { + const struct timeval *tv_null = NULL; + struct timeval tv; + struct tm *tm; + int minuteswest; + struct timezone tz; + + gettimeofday(&tv, NULL); + tm = localtime(&tv.tv_sec); + minuteswest = tm->tm_gmtoff / 60; + + tz.tz_minuteswest = -minuteswest; + tz.tz_dsttime = 0; /* DST_NONE*/ + + /* + * If the hardware clock does not run in UTC, but in local time: + * The very first time we set the kernel's timezone, it will warp + * the clock so that it runs in UTC instead of local time. + */ + if (settimeofday(tv_null, &tz) < 0) + return -errno; + else + return minuteswest; +} + +int hwclock_get_time(struct tm *tm) { + int fd; + int err = 0; + + fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC); + if (fd < 0) + return -errno; + if (ioctl(fd, RTC_RD_TIME, tm) < 0) + err = -errno; + close(fd); + + return err; +} + +int hwclock_set_time(const struct tm *tm) { + int fd; + int err = 0; + + fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC); + if (fd < 0) + return -errno; + if (ioctl(fd, RTC_SET_TIME, tm) < 0) + err = -errno; + close(fd); + + return err; +} diff --git a/src/util.h b/src/util.h index 04049f7d9..79d634bb6 100644 --- a/src/util.h +++ b/src/util.h @@ -451,4 +451,13 @@ int signal_from_string(const char *s); int signal_from_string_try_harder(const char *s); int conf_files_list(char ***strv, const char *suffix, const char *dir, ...); + +bool hwclock_is_localtime(void); + +int hwclock_apply_localtime_delta(void); + +int hwclock_get_time(struct tm *tm); + +int hwclock_set_time(const struct tm *tm); + #endif diff --git a/units/hwclock-load.service b/units/hwclock-load.service deleted file mode 100644 index f278a671a..000000000 --- a/units/hwclock-load.service +++ /dev/null @@ -1,34 +0,0 @@ -# This file is part of systemd. -# -# systemd 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. - -[Unit] -Description=Apply System Clock UTC Offset -DefaultDependencies=no -Wants=time-sync.target -Conflicts=shutdown.target -After=systemd-readahead-collect.service systemd-readahead-replay.service -Before=sysinit.target shutdown.target udev.service time-sync.target - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=/sbin/hwclock --systz -StandardOutput=syslog - -# Note the weird semantics of hwclock and the kernel here: the first -# settimeofday() invocation from userspace is special and may be used -# to set the offset from UTC of the system clock. It is independent -# of any specific RTC device. This is mostly a crufty hack to support -# legacy operating systems which insist on storing local time in the -# RTC. - -# Note that we do not run --hctosys here, we assume the kernel -# includes a compiled in RTC module which is used to initialize the -# system time as part of kernel setup. - -[Install] -WantedBy=sysinit.target -- 2.30.2