Commit 3918ae33 authored by Michael Biebl's avatar Michael Biebl

Only set the kernel's timezone when the RTC runs in local time

Otherwise, every daylight saving time change or time zone change by
travelling will make the time jump, and the local time might jump
backwards which creates unsolvable problems with file timestamps.

Closes: #759319
parent 17fa2939
......@@ -3,6 +3,10 @@ systemd (215-17+deb8u3) UNRELEASED; urgency=medium
* Fix namespace breakage due to incorrect path sorting. (Closes: #787758)
* Don't timeout after 90 seconds when no password was entered for cryptsetup
devices. (Closes: #802897)
* Only set the kernel's timezone when the RTC runs in local time. Otherwise,
every daylight saving time change or time zone change by travelling will
make the time jump, and the local time might jump backwards which creates
unsolvable problems with file timestamps. (Closes: #759319)
-- Michael Biebl <biebl@debian.org> Mon, 16 Nov 2015 17:49:20 +0100
......
From: Kay Sievers <kay@vrfy.org>
Date: Sun, 27 Jul 2014 00:11:08 +0200
Subject: core: only set the kernel's timezone when the RTC runs in local time
We can not reliably manage any notion of local time. Every daylight
saving time change or time zone change by traveling will make the
time jump, and the local time might jump backwards which creates
unsolvable problems with file timestamps.
We will no longer tell the kernel our local time zone and leave
everything set to UTC. This will effectively turn FAT timestamps
into UTC timestamps.
If and only if the machine is configured to read the RTC in local
time mode, the kernel's time zone will be configured, but
systemd-timesysnc will disable the kernel's system time to RTC
syncing. In this mode, the RTC will not be managed, and external
tools like Windows bootups are expected to manage the RTC's time.
https://bugs.freedesktop.org/show_bug.cgi?id=81538
(cherry-picked from commit c264aeab4b0e7b69f469e12e78d4a48b3ed7a66e)
---
src/core/main.c | 23 +++++++++++++++--------
src/shared/clock-util.c | 15 ++++++++-------
src/shared/clock-util.h | 2 +-
src/timedate/timedatectl.c | 7 ++++---
src/timesync/timesyncd.c | 24 +++++++++++++++++++-----
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 a732c69..904cdb8 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1347,7 +1347,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));
@@ -1355,19 +1362,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 e66db63..fc49393 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 4deeac7..7ed371a 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 a8769e4..d87e183 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 0fb92f1..69f6e50 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -396,9 +396,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;
@@ -409,7 +406,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;
@@ -425,6 +422,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;
@@ -447,7 +455,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,
@@ -1270,6 +1278,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 bcd14f7..3ae7ccb 100644
--- a/src/timesync/timesyncd.h
+++ b/src/timesync/timesyncd.h
@@ -95,6 +95,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);
......@@ -153,6 +153,7 @@ manager-pass-correct-errno-to-strerror.patch
shared-add-path_compare-an-ordering-path-comparison.patch
core-namespace-fix-path-sorting.patch
cryptsetup-Fix-timeout-on-dm-device.patch
core-only-set-the-kernel-s-timezone-when-the-RTC-runs-in-.patch
## Debian specific patches:
Add-back-support-for-Debian-specific-config-files.patch
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment