chiark / gitweb /
rtc in localtime: use settimeofday(NULL, tz) instead of hwclock(8)
authorKay Sievers <kay.sievers@vrfy.org>
Tue, 24 May 2011 18:23:07 +0000 (20:23 +0200)
committerKay Sievers <kay.sievers@vrfy.org>
Tue, 24 May 2011 18:23:07 +0000 (20:23 +0200)
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
src/main.c
src/util.c
src/util.h
units/hwclock-load.service [deleted file]

index 120c6034e23dece1a7456aeda700731073a4db5b..1fc91639c687696b501b4640b4022396b76adb87 100644 (file)
@@ -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 \
index 52b3031fbe30ebb080affb08636f425bd317e33c..68328b76d3161317500773455e1fc39b4ea0cd3e 100644 (file)
@@ -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);
index a271708ba291979b17e6c4881389ad85b921ad4f..4046938fcdbef876c9dfdee5f3bfc8d3faf52cfa 100644 (file)
@@ -51,6 +51,8 @@
 #include <dlfcn.h>
 #include <sys/wait.h>
 #include <sys/capability.h>
+#include <sys/time.h>
+#include <linux/rtc.h>
 
 #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;
+}
index 04049f7d9277d802a9beb3e4a5715c8016d7b2e5..79d634bb6343b02dc0ea2dc0261740667daa4e96 100644 (file)
@@ -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 (file)
index f278a67..0000000
+++ /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