summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMike Yuan <me@yhndnzj.com>2024-06-16 17:42:47 +0200
committerGitHub <noreply@github.com>2024-06-16 17:42:47 +0200
commitd4d90ef900cc0eab474d942a3d4ab778c28ef3d9 (patch)
tree974a71ce3515da193d1bf22fd29686851e0fc552 /src
parentMerge pull request #33352 from YHNdnzj/freeconp-void (diff)
parentman: describe setting of the clock by systemd and systemd-timesyncd (diff)
downloadsystemd-d4d90ef900cc0eab474d942a3d4ab778c28ef3d9.tar.xz
systemd-d4d90ef900cc0eab474d942a3d4ab778c28ef3d9.zip
Merge pull request #33214 from keszybz/system-clock-epoch
Rework the setting and description of system clock to the epoch
Diffstat (limited to 'src')
-rw-r--r--src/core/clock-warp.c89
-rw-r--r--src/core/clock-warp.h7
-rw-r--r--src/core/main.c26
-rw-r--r--src/core/meson.build1
-rw-r--r--src/shared/clock-util.c130
-rw-r--r--src/shared/clock-util.h21
-rw-r--r--src/timedate/hwclock-util.c44
-rw-r--r--src/timedate/hwclock-util.h7
-rw-r--r--src/timedate/meson.build6
-rw-r--r--src/timedate/timedated.c11
-rw-r--r--src/timesync/timesyncd-manager.c5
-rw-r--r--src/timesync/timesyncd-manager.h3
-rw-r--r--src/timesync/timesyncd.c66
13 files changed, 226 insertions, 190 deletions
diff --git a/src/core/clock-warp.c b/src/core/clock-warp.c
new file mode 100644
index 0000000000..49d57afa9a
--- /dev/null
+++ b/src/core/clock-warp.c
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "sd-messages.h"
+
+#include "clock-util.h"
+#include "clock-warp.h"
+#include "errno-util.h"
+
+int clock_reset_timewarp(void) {
+ static const struct timezone tz = {
+ .tz_minuteswest = 0,
+ .tz_dsttime = 0, /* DST_NONE */
+ };
+
+ /* 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. */
+ return RET_NERRNO(settimeofday(NULL, &tz));
+}
+
+void clock_apply_epoch(bool allow_backwards) {
+ usec_t epoch_usec = 0, timesyncd_usec = 0;
+ struct stat st;
+ int r;
+
+ r = RET_NERRNO(stat(TIMESYNCD_CLOCK_FILE, &st));
+ if (r >= 0)
+ epoch_usec = timespec_load(&st.st_mtim);
+ else if (r != -ENOENT)
+ log_warning_errno(r, "Could not stat %s, ignoring: %m", TIMESYNCD_CLOCK_FILE);
+
+ r = RET_NERRNO(stat(EPOCH_CLOCK_FILE, &st));
+ if (r >= 0)
+ timesyncd_usec = timespec_load(&st.st_mtim);
+ else if (r != -ENOENT)
+ log_warning_errno(r, "Could not stat %s, ignoring: %m", EPOCH_CLOCK_FILE);
+
+ epoch_usec = MAX3(epoch_usec,
+ timesyncd_usec,
+ (usec_t) TIME_EPOCH * USEC_PER_SEC);
+
+ if (epoch_usec == 0) /* Weird, but may happen if mtimes were reset to 0 during compilation. */
+ return log_debug("Clock epoch is 0, skipping clock adjustment.");
+
+ usec_t now_usec = now(CLOCK_REALTIME);
+ bool advance;
+
+ if (now_usec < epoch_usec)
+ advance = true;
+ else if (CLOCK_VALID_RANGE_USEC_MAX > 0 &&
+ now_usec > usec_add(epoch_usec, CLOCK_VALID_RANGE_USEC_MAX) &&
+ allow_backwards)
+ advance = false;
+ else
+ return; /* Nothing to do. */
+
+ r = RET_NERRNO(clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(epoch_usec)));
+ if (r < 0) {
+ if (advance)
+ return (void) log_error_errno(r, "Current system time is before epoch, but cannot correct: %m");
+ else
+ return (void) log_error_errno(r, "Current system time is further ahead than %s after epoch, but cannot correct: %m",
+ FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY));
+ }
+
+ const char *from =
+ epoch_usec == (usec_t) TIME_EPOCH * USEC_PER_SEC ? "built-in epoch" :
+ epoch_usec == timesyncd_usec ? "timestamp on "TIMESYNCD_CLOCK_FILE :
+ "timestamp on "EPOCH_CLOCK_FILE;
+ if (advance)
+ log_struct(LOG_INFO,
+ "MESSAGE_ID=" SD_MESSAGE_TIME_BUMP_STR,
+ "REALTIME_USEC=" USEC_FMT, epoch_usec,
+ "DIRECTION=forwards",
+ LOG_MESSAGE("System time advanced to %s: %s",
+ from,
+ FORMAT_TIMESTAMP(epoch_usec)));
+ else
+ log_struct(LOG_INFO,
+ "MESSAGE_ID=" SD_MESSAGE_TIME_BUMP_STR,
+ "REALTIME_USEC=" USEC_FMT, epoch_usec,
+ "DIRECTION=backwards",
+ LOG_MESSAGE("System time was further ahead than %s after %s, clock reset to %s",
+ FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY),
+ from,
+ FORMAT_TIMESTAMP(epoch_usec)));
+}
diff --git a/src/core/clock-warp.h b/src/core/clock-warp.h
new file mode 100644
index 0000000000..402c7eee04
--- /dev/null
+++ b/src/core/clock-warp.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+
+int clock_reset_timewarp(void);
+void clock_apply_epoch(bool allow_backwards);
diff --git a/src/core/main.c b/src/core/main.c
index e49252b310..6792a79c1f 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -30,6 +30,7 @@
#include "cgroup-util.h"
#include "chase.h"
#include "clock-util.h"
+#include "clock-warp.h"
#include "conf-parser.h"
#include "confidential-virt.h"
#include "copy.h"
@@ -1670,7 +1671,7 @@ static int become_shutdown(int objective, int retval) {
return -errno;
}
-static void initialize_clock(void) {
+static void initialize_clock_timewarp(void) {
int r;
/* This is called very early on, before we parse the kernel command line or otherwise figure out why
@@ -1682,7 +1683,7 @@ static void initialize_clock(void) {
/* 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
+ * 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);
@@ -1704,24 +1705,11 @@ static void initialize_clock(void) {
* time concepts will be treated as UTC that way.
*/
(void) clock_reset_timewarp();
-
- ClockChangeDirection change_dir;
- r = clock_apply_epoch(&change_dir);
- if (r > 0 && change_dir == CLOCK_CHANGE_FORWARD)
- log_info("System time before build time, advancing clock.");
- else if (r > 0 && change_dir == CLOCK_CHANGE_BACKWARD)
- log_info("System time is further ahead than %s after build time, resetting clock to build time.",
- FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY));
- else if (r < 0 && change_dir == CLOCK_CHANGE_FORWARD)
- log_error_errno(r, "Current system time is before build time, but cannot correct: %m");
- else if (r < 0 && change_dir == CLOCK_CHANGE_BACKWARD)
- log_error_errno(r, "Current system time is further ahead %s after build time, but cannot correct: %m",
- FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY));
}
static void apply_clock_update(void) {
- /* This is called later than initialize_clock(), i.e. after we parsed configuration files/kernel
- * command line and such. */
+ /* This is called later than clock_apply_epoch(), i.e. after we have parsed
+ * configuration files/kernel command line and such. */
if (arg_clock_usec == 0)
return;
@@ -3048,7 +3036,9 @@ int main(int argc, char *argv[]) {
}
if (!skip_setup)
- initialize_clock();
+ initialize_clock_timewarp();
+
+ clock_apply_epoch(/* allow_backwards= */ !skip_setup);
/* Set the default for later on, but don't actually open the logs like this for
* now. Note that if we are transitioning from the initrd there might still be
diff --git a/src/core/meson.build b/src/core/meson.build
index 7a2012a372..af9928a599 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -143,6 +143,7 @@ core_includes = [includes, include_directories('.')]
systemd_sources = files(
'main.c',
'crash-handler.c',
+ 'clock-warp.c',
)
systemd_executor_sources = files(
diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c
index 37d02325b7..9bbfdbae2e 100644
--- a/src/shared/clock-util.c
+++ b/src/shared/clock-util.c
@@ -1,11 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <time.h>
-#include <linux/rtc.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/time.h>
@@ -18,42 +12,7 @@
#include "macro.h"
#include "string-util.h"
-int clock_get_hwclock(struct tm *tm) {
- _cleanup_close_ int fd = -EBADF;
-
- assert(tm);
-
- fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- /* This leaves the timezone fields of struct tm uninitialized! */
- if (ioctl(fd, RTC_RD_TIME, tm) < 0)
- /* Some drivers return -EINVAL in case the time could not be kept, i.e. power loss
- * happened. Let's turn that into a clearly recognizable error */
- return errno == EINVAL ? -ENODATA : -errno;
-
- /* We don't know daylight saving, so we reset this in order not
- * to confuse mktime(). */
- tm->tm_isdst = -1;
-
- return 0;
-}
-
-int clock_set_hwclock(const struct tm *tm) {
- _cleanup_close_ int fd = -EBADF;
-
- assert(tm);
-
- fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- return RET_NERRNO(ioctl(fd, RTC_SET_TIME, tm));
-}
-
-int clock_is_localtime(const char* adjtime_path) {
- _cleanup_fclose_ FILE *f = NULL;
+int clock_is_localtime(const char *adjtime_path) {
int r;
if (!adjtime_path)
@@ -66,45 +25,42 @@ int clock_is_localtime(const char* adjtime_path) {
* 0
* UTC
*/
- f = fopen(adjtime_path, "re");
- if (f) {
- _cleanup_free_ char *line = NULL;
- unsigned i;
+ _cleanup_fclose_ FILE *f = fopen(adjtime_path, "re");
+ if (!f) {
+ if (errno != ENOENT)
+ return -errno;
- for (i = 0; i < 2; i++) { /* skip the first two lines */
- r = read_line(f, LONG_LINE_MAX, NULL);
- if (r < 0)
- return r;
- if (r == 0)
- return false; /* less than three lines → default to UTC */
- }
+ /* adjtime_path not present → default to UTC */
+ return false;
+ }
- r = read_line(f, LONG_LINE_MAX, &line);
+ _cleanup_free_ char *line = NULL;
+ for (unsigned i = 0; i < 2; i++) { /* skip the first two lines */
+ r = read_line(f, LONG_LINE_MAX, NULL);
if (r < 0)
return r;
if (r == 0)
return false; /* less than three lines → default to UTC */
+ }
- return streq(line, "LOCAL");
-
- } else if (errno != ENOENT)
- return -errno;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return false; /* less than three lines → default to UTC */
- /* adjtime not present → default to UTC */
- return false;
+ return streq(line, "LOCAL");
}
int clock_set_timezone(int *ret_minutesdelta) {
struct timespec ts;
struct tm tm;
- int minutesdelta;
- struct timezone tz;
assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
assert_se(localtime_r(&ts.tv_sec, &tm));
- minutesdelta = tm.tm_gmtoff / 60;
+ int minutesdelta = tm.tm_gmtoff / 60;
- tz = (struct timezone) {
+ struct timezone tz = {
.tz_minuteswest = -minutesdelta,
.tz_dsttime = 0, /* DST_NONE */
};
@@ -120,49 +76,3 @@ int clock_set_timezone(int *ret_minutesdelta) {
return 0;
}
-
-int clock_reset_timewarp(void) {
- static const struct timezone tz = {
- .tz_minuteswest = 0,
- .tz_dsttime = 0, /* DST_NONE */
- };
-
- /* 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. */
- return RET_NERRNO(settimeofday(NULL, &tz));
-}
-
-#define EPOCH_FILE "/usr/lib/clock-epoch"
-
-int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) {
- usec_t epoch_usec, now_usec;
- struct stat st;
-
- /* NB: we update *ret_attempted_change in *all* cases, both
- * on success and failure, to indicate what we intended to do! */
-
- assert(ret_attempted_change);
-
- if (stat(EPOCH_FILE, &st) < 0) {
- if (errno != ENOENT)
- log_warning_errno(errno, "Cannot stat " EPOCH_FILE ": %m");
-
- epoch_usec = (usec_t) TIME_EPOCH * USEC_PER_SEC;
- } else
- epoch_usec = timespec_load(&st.st_mtim);
-
- now_usec = now(CLOCK_REALTIME);
- if (now_usec < epoch_usec)
- *ret_attempted_change = CLOCK_CHANGE_FORWARD;
- else if (CLOCK_VALID_RANGE_USEC_MAX > 0 && now_usec > usec_add(epoch_usec, CLOCK_VALID_RANGE_USEC_MAX))
- *ret_attempted_change = CLOCK_CHANGE_BACKWARD;
- else {
- *ret_attempted_change = CLOCK_CHANGE_NOOP;
- return 0;
- }
-
- if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(epoch_usec)) < 0)
- return -errno;
-
- return 1;
-}
diff --git a/src/shared/clock-util.h b/src/shared/clock-util.h
index c8f6d1b1f1..d8992f6074 100644
--- a/src/shared/clock-util.h
+++ b/src/shared/clock-util.h
@@ -1,20 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include <errno.h>
-#include <time.h>
-
-typedef enum ClockChangeDirection {
- CLOCK_CHANGE_NOOP,
- CLOCK_CHANGE_FORWARD,
- CLOCK_CHANGE_BACKWARD,
- _CLOCK_CHANGE_MAX,
- _CLOCK_CHANGE_INVALID = -EINVAL,
-} ClockChangeDirection;
-
-int clock_is_localtime(const char* adjtime_path);
+int clock_is_localtime(const char *adjtime_path);
int clock_set_timezone(int *ret_minutesdelta);
-int clock_reset_timewarp(void);
-int clock_get_hwclock(struct tm *tm);
-int clock_set_hwclock(const struct tm *tm);
-int clock_apply_epoch(ClockChangeDirection *ret_attempted_change);
+
+#define EPOCH_CLOCK_FILE "/usr/lib/clock-epoch"
+#define TIMESYNCD_CLOCK_FILE_DIR "/var/lib/systemd/timesync/"
+#define TIMESYNCD_CLOCK_FILE TIMESYNCD_CLOCK_FILE_DIR "clock"
diff --git a/src/timedate/hwclock-util.c b/src/timedate/hwclock-util.c
new file mode 100644
index 0000000000..e924f94872
--- /dev/null
+++ b/src/timedate/hwclock-util.c
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <linux/rtc.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include "errno-util.h"
+#include "fd-util.h"
+#include "hwclock-util.h"
+
+int hwclock_get(struct tm *ret) {
+ _cleanup_close_ int fd = -EBADF;
+
+ assert(ret);
+
+ fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ /* This leaves the timezone fields of struct ret uninitialized! */
+ if (ioctl(fd, RTC_RD_TIME, ret) < 0)
+ /* Some drivers return -EINVAL in case the time could not be kept, i.e. power loss
+ * happened. Let's turn that into a clearly recognizable error */
+ return errno == EINVAL ? -ENODATA : -errno;
+
+ /* We don't know daylight saving, so we reset this in order not
+ * to confuse mktime(). */
+ ret->tm_isdst = -1;
+
+ return 0;
+}
+
+int hwclock_set(const struct tm *tm) {
+ _cleanup_close_ int fd = -EBADF;
+
+ assert(tm);
+
+ fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return RET_NERRNO(ioctl(fd, RTC_SET_TIME, tm));
+}
diff --git a/src/timedate/hwclock-util.h b/src/timedate/hwclock-util.h
new file mode 100644
index 0000000000..8663c02682
--- /dev/null
+++ b/src/timedate/hwclock-util.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <time.h>
+
+int hwclock_get(struct tm *ret);
+int hwclock_set(const struct tm *tm);
diff --git a/src/timedate/meson.build b/src/timedate/meson.build
index 48054bb604..0790695be9 100644
--- a/src/timedate/meson.build
+++ b/src/timedate/meson.build
@@ -5,7 +5,11 @@ executables += [
'name' : 'systemd-timedated',
'dbus' : true,
'conditions' : ['ENABLE_TIMEDATED'],
- 'sources' : files('timedated.c'),
+ 'sources' : files(
+ 'timedated.c',
+ 'hwclock-util.c',
+ ),
+
},
executable_template + {
'name' : 'timedatectl',
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index b4cc5f9dd5..d8b509d134 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -28,6 +28,7 @@
#include "fileio.h"
#include "fs-util.h"
#include "hashmap.h"
+#include "hwclock-util.h"
#include "list.h"
#include "main-func.h"
#include "memory-util.h"
@@ -591,7 +592,7 @@ static int property_get_rtc_time(
usec_t t = 0;
int r;
- r = clock_get_hwclock(&tm);
+ r = hwclock_get(&tm);
if (r == -EBUSY)
log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
else if (r == -ENOENT)
@@ -719,7 +720,7 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *
assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
assert_se(localtime_r(&ts.tv_sec, &tm));
- r = clock_set_hwclock(&tm);
+ r = hwclock_set(&tm);
if (r < 0)
log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
}
@@ -792,7 +793,7 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error
localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
/* Override the main fields of struct tm, but not the timezone fields */
- r = clock_get_hwclock(&tm);
+ r = hwclock_get(&tm);
if (r < 0)
log_debug_errno(r, "Failed to get hardware clock, ignoring: %m");
else {
@@ -809,7 +810,7 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error
/* Sync RTC from system clock */
localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
- r = clock_set_hwclock(&tm);
+ r = hwclock_set(&tm);
if (r < 0)
log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
}
@@ -902,7 +903,7 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro
/* Sync down to RTC */
localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
- r = clock_set_hwclock(&tm);
+ r = hwclock_set(&tm);
if (r < 0)
log_debug_errno(r, "Failed to update hardware clock, ignoring: %m");
diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c
index 8e0eda0797..faf894fb47 100644
--- a/src/timesync/timesyncd-manager.c
+++ b/src/timesync/timesyncd-manager.c
@@ -15,6 +15,7 @@
#include "alloc-util.h"
#include "bus-polkit.h"
+#include "clock-util.h"
#include "common-signal.h"
#include "dns-domain.h"
#include "event-util.h"
@@ -1199,9 +1200,9 @@ static int manager_save_time_and_rearm(Manager *m, usec_t t) {
* clock, but otherwise uses the specified timestamp. Note that whenever we acquire an NTP sync the
* specified timestamp value might be more accurate than the system clock, since the latter is
* subject to slow adjustments. */
- r = touch_file(CLOCK_FILE, false, t, UID_INVALID, GID_INVALID, MODE_INVALID);
+ r = touch_file(TIMESYNCD_CLOCK_FILE, false, t, UID_INVALID, GID_INVALID, MODE_INVALID);
if (r < 0)
- log_debug_errno(r, "Failed to update " CLOCK_FILE ", ignoring: %m");
+ log_debug_errno(r, "Failed to update "TIMESYNCD_CLOCK_FILE", ignoring: %m");
m->save_on_exit = true;
diff --git a/src/timesync/timesyncd-manager.h b/src/timesync/timesyncd-manager.h
index 027ec52dac..4313d72ce4 100644
--- a/src/timesync/timesyncd-manager.h
+++ b/src/timesync/timesyncd-manager.h
@@ -32,9 +32,6 @@ typedef struct Manager Manager;
#define DEFAULT_SAVE_TIME_INTERVAL_USEC (60 * USEC_PER_SEC)
-#define STATE_DIR "/var/lib/systemd/timesync"
-#define CLOCK_FILE STATE_DIR "/clock"
-
struct Manager {
sd_bus *bus;
sd_event *event;
diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
index 5c308a04bc..a1b63038c4 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -22,9 +22,8 @@
#include "timesyncd-manager.h"
#include "user-util.h"
-static int advance_tstamp(int fd, const struct stat *st) {
- assert_se(fd >= 0);
- assert_se(st);
+static int advance_tstamp(int fd, usec_t epoch) {
+ assert(fd >= 0);
/* So here's the problem: whenever we read the timestamp we'd like to ensure the next time we won't
* restore the exact same time again, but one at least one step further (so that comparing mtimes of
@@ -36,22 +35,20 @@ static int advance_tstamp(int fd, const struct stat *st) {
* increase the timestamp by 10μs, and do the same, then 100μs, then 1ms, and so on, until it works,
* or we reach 10s. If it still didn't work then, the fs is just broken and we give up. */
- usec_t target = MAX3(now(CLOCK_REALTIME),
- TIME_EPOCH * USEC_PER_SEC,
- timespec_load(&st->st_mtim));
+ usec_t target = MAX(epoch, now(CLOCK_REALTIME));
for (usec_t a = 1; a <= 10 * USEC_PER_SEC; a *= 10) { /* 1μs, 10μs, 100μs, 1ms, … 10s */
struct timespec ts[2];
struct stat new_st;
- /* Bump to the maximum of the old timestamp advanced by the specified unit, */
+ /* Bump to the maximum of the old timestamp advanced by the specified unit. */
usec_t c = usec_add(target, a);
timespec_store(&ts[0], c);
ts[1] = ts[0];
if (futimens(fd, ts) < 0) {
- /* If this doesn't work at all, log, don't fail but give up */
+ /* If this doesn't work at all, log and don't fail, but give up. */
log_warning_errno(errno, "Unable to update mtime of timestamp file, ignoring: %m");
return 0;
}
@@ -60,11 +57,11 @@ static int advance_tstamp(int fd, const struct stat *st) {
return log_error_errno(errno, "Failed to stat timestamp file: %m");
if (timespec_load(&new_st.st_mtim) > target) {
- log_debug("Successfully bumped timestamp file.");
+ log_debug("Successfully touched timestamp file.");
return 1;
}
- log_debug("Tried to advance timestamp file by " USEC_FMT ", but this didn't work, file system timestamp granularity too coarse?", a);
+ log_debug("Tried to advance timestamp mtime by "USEC_FMT", but this didn't work, file system timestamp granularity too coarse?", a);
}
log_debug("Gave up trying to advance timestamp file.");
@@ -72,7 +69,7 @@ static int advance_tstamp(int fd, const struct stat *st) {
}
static int load_clock_timestamp(uid_t uid, gid_t gid) {
- usec_t min = TIME_EPOCH * USEC_PER_SEC, ct;
+ usec_t epoch = TIME_EPOCH * USEC_PER_SEC, ct;
_cleanup_close_ int fd = -EBADF;
int r;
@@ -81,59 +78,58 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) {
* is particularly helpful on systems lacking a battery backed RTC. We also will adjust the time to
* at least the build time of systemd. */
- fd = open(CLOCK_FILE, O_RDWR|O_CLOEXEC, 0644);
+ fd = open(TIMESYNCD_CLOCK_FILE, O_RDWR|O_CLOEXEC, 0644);
if (fd < 0) {
if (errno != ENOENT)
- log_debug_errno(errno, "Unable to open timestamp file '" CLOCK_FILE "', ignoring: %m");
+ log_debug_errno(errno, "Unable to open timestamp file "TIMESYNCD_CLOCK_FILE", ignoring: %m");
- r = mkdir_safe_label(STATE_DIR, 0755, uid, gid,
+ r = mkdir_safe_label(TIMESYNCD_CLOCK_FILE_DIR, 0755, uid, gid,
MKDIR_FOLLOW_SYMLINK | MKDIR_WARN_MODE);
if (r < 0)
- log_debug_errno(r, "Failed to create state directory, ignoring: %m");
+ log_debug_errno(r, "Failed to create "TIMESYNCD_CLOCK_FILE_DIR", ignoring: %m");
- /* create stamp file with the compiled-in date */
- r = touch_file(CLOCK_FILE, /* parents= */ false, min, uid, gid, 0644);
+ /* Create stamp file with the compiled-in date */
+ r = touch_file(TIMESYNCD_CLOCK_FILE, /* parents= */ false, epoch, uid, gid, 0644);
if (r < 0)
- log_debug_errno(r, "Failed to create %s, ignoring: %m", CLOCK_FILE);
+ log_debug_errno(r, "Failed to create %s, ignoring: %m", TIMESYNCD_CLOCK_FILE);
} else {
struct stat st;
- usec_t stamp;
- /* check if the recorded time is later than the compiled-in one */
+ /* Check if the recorded time is later than the compiled-in one */
if (fstat(fd, &st) < 0)
- return log_error_errno(errno, "Unable to stat timestamp file '" CLOCK_FILE "': %m");
-
- stamp = timespec_load(&st.st_mtim);
- if (stamp > min)
- min = stamp;
+ return log_error_errno(errno, "Unable to stat timestamp file "TIMESYNCD_CLOCK_FILE": %m");
/* Try to fix the access mode, so that we can still touch the file after dropping
* privileges */
r = fchmod_and_chown(fd, 0644, uid, gid);
if (r < 0)
log_full_errno(ERRNO_IS_PRIVILEGE(r) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to chmod or chown %s, ignoring: %m", CLOCK_FILE);
+ "Failed to chmod or chown %s, ignoring: %m", TIMESYNCD_CLOCK_FILE);
+
+ epoch = MAX(epoch, timespec_load(&st.st_mtim));
- (void) advance_tstamp(fd, &st);
+ (void) advance_tstamp(fd, epoch);
}
ct = now(CLOCK_REALTIME);
- if (ct > min)
+ if (ct > epoch)
return 0;
/* Not that it matters much, but we actually restore the clock to n+1 here rather than n, simply
* because we read n as time previously already and we want to progress here, i.e. not report the
* same time again. */
- if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(min+1)) < 0) {
- log_warning_errno(errno, "Failed to restore system clock, ignoring: %m");
+ if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(epoch + 1)) < 0) {
+ log_warning_errno(errno, "Failed to advance system clock, ignoring: %m");
return 0;
}
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_TIME_BUMP_STR,
- "REALTIME_USEC=" USEC_FMT, min+1,
- LOG_MESSAGE("System clock time unset or jumped backwards, restored from recorded timestamp: %s",
- FORMAT_TIMESTAMP(min+1)));
+ "REALTIME_USEC=" USEC_FMT, epoch + 1,
+ "DIRECTION=forwards",
+ LOG_MESSAGE("System clock time advanced to %s: %s",
+ epoch > TIME_EPOCH * USEC_PER_SEC ? "recorded timestamp" : "built-in epoch",
+ FORMAT_TIMESTAMP(epoch + 1)));
return 0;
}
@@ -220,9 +216,9 @@ static int run(int argc, char *argv[]) {
/* if we got an authoritative time, store it in the file system */
if (m->save_on_exit) {
- r = touch(CLOCK_FILE);
+ r = touch(TIMESYNCD_CLOCK_FILE);
if (r < 0)
- log_debug_errno(r, "Failed to touch " CLOCK_FILE ", ignoring: %m");
+ log_debug_errno(r, "Failed to touch "TIMESYNCD_CLOCK_FILE", ignoring: %m");
}
return 0;