From 7948c4dfbea73ac21250b588089039aa17a90386 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 24 May 2011 20:23:07 +0200 Subject: 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. --- src/main.c | 7 ++++++ src/util.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 9 +++++++ 3 files changed, 96 insertions(+) (limited to 'src') diff --git a/src/main.c b/src/main.c index 52b3031fbe..68328b76d3 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 a271708ba2..4046938fcd 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 04049f7d92..79d634bb63 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 -- cgit v1.2.3-54-g00ecf