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.
This commit is contained in:
Kay Sievers 2011-05-24 20:23:07 +02:00
parent 359306dfe5
commit 7948c4dfbe
5 changed files with 96 additions and 38 deletions

View 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 \

View 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);

View 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;
}

View 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

View file

@ -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