diff options
-rw-r--r-- | src/core/main.c | 23 | ||||
-rw-r--r-- | src/shared/clock-util.c | 15 | ||||
-rw-r--r-- | src/shared/clock-util.h | 2 | ||||
-rw-r--r-- | src/timedate/timedatectl.c | 7 | ||||
-rw-r--r-- | src/timesync/timesyncd.c | 24 | ||||
-rw-r--r-- | src/timesync/timesyncd.h | 3 |
6 files changed, 50 insertions, 24 deletions
diff --git a/src/core/main.c b/src/core/main.c index 29f97364a4..f67c003941 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1358,7 +1358,14 @@ int main(int argc, char *argv[]) { if (clock_is_localtime() > 0) { int min; - /* The first-time call to settimeofday() does a time warp in the kernel */ + /* + * The very first call of settimeofday() also does a time warp in the kernel. + * + * In the rtc-in-local time mode, we set the kernel's timezone, and rely on + * external tools to take care of maintaining the RTC and do all adjustments. + * This matches the behavior of Windows, which leaves the RTC alone if the + * registry tells that the RTC runs in UTC. + */ r = clock_set_timezone(&min); if (r < 0) log_error("Failed to apply local time delta, ignoring: %s", strerror(-r)); @@ -1366,19 +1373,19 @@ int main(int argc, char *argv[]) { log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min); } else if (!in_initrd()) { /* - * Do dummy first-time call to seal the kernel's time warp magic + * Do a dummy very first call to seal the kernel's time warp magic. * * Do not call this this from inside the initrd. The initrd might not * carry /etc/adjtime with LOCAL, but the real system could be set up * that way. In such case, we need to delay the time-warp or the sealing * until we reach the real system. + * + * Do no set the kernel's timezone. The concept of local time cannot + * be supported reliably, the time will jump or be incorrect at every daylight + * saving time change. All kernel local time concepts will be treated + * as UTC that way. */ - clock_reset_timezone(); - - /* Tell the kernel our timezone */ - r = clock_set_timezone(NULL); - if (r < 0) - log_error("Failed to set the kernel's timezone, ignoring: %s", strerror(-r)); + clock_reset_timewarp(); } } diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c index e66db63ba0..fc49393c72 100644 --- a/src/shared/clock-util.c +++ b/src/shared/clock-util.c @@ -124,9 +124,10 @@ int clock_set_timezone(int *min) { 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 the RTC does not run in UTC but in local time, the very first + * call to settimeofday() will set the kernel's timezone and will warp the + * system clock, so that it runs in UTC instead of the local time we + * have read from the RTC. */ if (settimeofday(tv_null, &tz) < 0) return -errno; @@ -135,7 +136,7 @@ int clock_set_timezone(int *min) { return 0; } -int clock_reset_timezone(void) { +int clock_reset_timewarp(void) { const struct timeval *tv_null = NULL; struct timezone tz; @@ -143,9 +144,9 @@ int clock_reset_timezone(void) { tz.tz_dsttime = 0; /* DST_NONE*/ /* - * The very first time we set the kernel's timezone, it will warp - * the clock. Do a dummy call here, so the time warping is sealed - * and we set only the timezone with the next call. + * The very first call to settimeofday() does time warp magic. Do a + * dummy call here, so the time warping is sealed and all later calls + * behave as expected. */ if (settimeofday(tv_null, &tz) < 0) return -errno; diff --git a/src/shared/clock-util.h b/src/shared/clock-util.h index 4deeac7736..7ed371a958 100644 --- a/src/shared/clock-util.h +++ b/src/shared/clock-util.h @@ -23,6 +23,6 @@ int clock_is_localtime(void); int clock_set_timezone(int *min); -int clock_reset_timezone(void); +int clock_reset_timewarp(void); int clock_get_hwclock(struct tm *tm); int clock_set_hwclock(const struct tm *tm); diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index 203b5be6dd..53123154db 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -203,9 +203,10 @@ static void print_status_info(const StatusInfo *i) { if (i->rtc_local) fputs("\n" ANSI_HIGHLIGHT_ON - "Warning: The RTC is configured to maintain time in the local time zone. This\n" - " mode is not fully supported and will create various problems with time\n" - " zone changes and daylight saving time adjustments. If at all possible, use\n" + "Warning: The system is configured to read the RTC time in the local time zone. This\n" + " mode can not be fully supported. It will create various problems with time\n" + " zone changes and daylight saving time adjustments. The RTC time is never updated,\n" + " it relies on external facilities to maintain it. If at all possible, use\n" " RTC in UTC by calling 'timedatectl set-local-rtc 0'" ANSI_HIGHLIGHT_OFF ".\n", stdout); } diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index 732f5bf6c2..b80e03b349 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -386,9 +386,6 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { /* * For small deltas, tell the kernel to gradually adjust the system * clock to the NTP time, larger deltas are just directly set. - * - * Clear STA_UNSYNC, it will enable the kernel's 11-minute mode, which - * syncs the system time periodically to the hardware clock. */ if (fabs(offset) < NTP_MAX_ADJUST) { tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR; @@ -399,7 +396,7 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { tmx.esterror = 0; log_debug(" adjust (slew): %+.3f sec\n", offset); } else { - tmx.modes = ADJ_SETOFFSET | ADJ_NANO; + tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET; /* ADJ_NANO uses nanoseconds in the microseconds field */ tmx.time.tv_sec = (long)offset; @@ -415,6 +412,17 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { log_debug(" adjust (jump): %+.3f sec\n", offset); } + /* + * An unset STA_UNSYNC will enable the kernel's 11-minute mode, + * which syncs the system time periodically to the RTC. + * + * In case the RTC runs in local time, never touch the RTC, + * we have no way to properly handle daylight saving changes and + * mobile devices moving between time zones. + */ + if (m->rtc_local_time) + tmx.status |= STA_UNSYNC; + switch (leap_sec) { case 1: tmx.status |= STA_INS; @@ -437,7 +445,7 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { " constant : %li\n" " offset : %+.3f sec\n" " freq offset : %+li (%i ppm)\n", - tmx.status, tmx.status & STA_UNSYNC ? "" : "sync", + tmx.status, tmx.status & STA_UNSYNC ? "unsync" : "sync", tmx.time.tv_sec, (unsigned long long) (tmx.time.tv_usec / NSEC_PER_MSEC), tmx.constant, (double)tmx.offset / NSEC_PER_SEC, @@ -1218,6 +1226,12 @@ int main(int argc, char *argv[]) { goto out; } + if (clock_is_localtime() > 0) { + log_info("The system is configured to read the RTC time in the local time zone. " + "This mode can not be fully supported. All system time to RTC updates are disabled."); + m->rtc_local_time = true; + } + manager_add_server_string(m, NTP_SERVERS); manager_parse_config_file(m); diff --git a/src/timesync/timesyncd.h b/src/timesync/timesyncd.h index 52cca6d1ed..e723404d82 100644 --- a/src/timesync/timesyncd.h +++ b/src/timesync/timesyncd.h @@ -97,6 +97,9 @@ struct Manager { /* Retry connections */ sd_event_source *event_retry; + + /* RTC runs in local time, leave it alone */ + bool rtc_local_time; }; const struct ConfigPerfItem* timesyncd_gperf_lookup(const char *key, unsigned length); |