summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/main.c23
-rw-r--r--src/shared/clock-util.c15
-rw-r--r--src/shared/clock-util.h2
-rw-r--r--src/timedate/timedatectl.c7
-rw-r--r--src/timesync/timesyncd.c24
-rw-r--r--src/timesync/timesyncd.h3
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);