From f11aaf7dfb295de429b1567282b19caaba036bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 5 Jun 2024 11:28:21 +0200 Subject: [PATCH 01/10] man/systemd: reorder content a bit Section "Description" didn't actually say what systemd does. And we had a giant "Concepts" section that actually described units types and other details about them. So let's move the basic description of functionality to "Description" and rename the following section to "Units". The link to the Original Design Document is moved to "See Also", it is of historical interest mostly at this point. The only actual change is that when talking about API filesystems, /dev is also mentioned. (I think /sys+/proc+/dev are the canonical set and should be always listed on one breath.) --- man/systemd.xml | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/man/systemd.xml b/man/systemd.xml index 66db5bbf25..f4aa7e06ca 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -62,10 +62,29 @@ user.conf.d directories. See systemd-system.conf5 for more information. + + systemd contains native implementations of various tasks that need to be + executed as part of the boot process. For example, it sets the hostname or configures the loopback + network device. It also sets up and mounts various API file systems, such as /sys/, + /proc/, and /dev/. + + Note that some but not all interfaces provided by systemd are covered by the + Interface Portability and Stability Promise. + + The D-Bus API of systemd is described in + org.freedesktop.systemd15 + and + org.freedesktop.LogControl15. + + + Systems which invoke systemd in a container or initrd environment should implement the Container Interface or + initrd Interface + specifications, respectively. - Concepts + Units systemd provides a dependency system between various entities called "units" of 11 different types. Units encapsulate @@ -261,34 +280,10 @@ example, start jobs for any of those inactive units getting queued as well. - systemd contains native implementations of various tasks - that need to be executed as part of the boot process. For example, - it sets the hostname or configures the loopback network device. It - also sets up and mounts various API file systems, such as - /sys/ or /proc/. - - For more information about the concepts and - ideas behind systemd, please refer to the - Original Design Document. - - Note that some but not all interfaces provided by systemd are covered by the - Interface Portability and Stability Promise. - Units may be generated dynamically at boot and system manager reload time, for example based on other configuration files or parameters passed on the kernel command line. For details, see systemd.generator7. - - The D-Bus API of systemd is described in - org.freedesktop.systemd15 - and - org.freedesktop.LogControl15. - - - Systems which invoke systemd in a container or initrd environment should implement the Container Interface or - initrd Interface - specifications, respectively. @@ -1558,6 +1553,10 @@ bootup7 systemd.directives7 + + For more information about the concepts and + ideas behind systemd, please refer to the + Original Design Document. From 455a2eb953435941ca558e309eacd1070fdcd012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 5 Jun 2024 12:21:34 +0200 Subject: [PATCH 02/10] timesync: make public defines for clock epoch files No functional change yet, just moving stuff around and message format adjustments. EPOCH_CLOCK_FILE is also made public for consistency, even though I don't plan to use it outside of the one location. --- src/shared/clock-util.c | 6 ++---- src/shared/clock-util.h | 4 ++++ src/timesync/timesyncd-manager.c | 5 +++-- src/timesync/timesyncd-manager.h | 3 --- src/timesync/timesyncd.c | 20 ++++++++++---------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c index 37d02325b7..57041d0e76 100644 --- a/src/shared/clock-util.c +++ b/src/shared/clock-util.c @@ -132,8 +132,6 @@ int clock_reset_timewarp(void) { 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; @@ -143,9 +141,9 @@ int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) { assert(ret_attempted_change); - if (stat(EPOCH_FILE, &st) < 0) { + if (stat(EPOCH_CLOCK_FILE, &st) < 0) { if (errno != ENOENT) - log_warning_errno(errno, "Cannot stat " EPOCH_FILE ": %m"); + log_warning_errno(errno, "Cannot stat " EPOCH_CLOCK_FILE ": %m"); epoch_usec = (usec_t) TIME_EPOCH * USEC_PER_SEC; } else diff --git a/src/shared/clock-util.h b/src/shared/clock-util.h index c8f6d1b1f1..730d1cebe8 100644 --- a/src/shared/clock-util.h +++ b/src/shared/clock-util.h @@ -18,3 +18,7 @@ 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/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..9d36974c71 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -81,27 +81,27 @@ 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); + r = touch_file(TIMESYNCD_CLOCK_FILE, /* parents= */ false, min, 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 */ if (fstat(fd, &st) < 0) - return log_error_errno(errno, "Unable to stat timestamp file '" CLOCK_FILE "': %m"); + return log_error_errno(errno, "Unable to stat timestamp file "TIMESYNCD_CLOCK_FILE": %m"); stamp = timespec_load(&st.st_mtim); if (stamp > min) @@ -112,7 +112,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) { 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); (void) advance_tstamp(fd, &st); } @@ -220,9 +220,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; From 4d47aa8c0435ad7bcd45aea3a091c22027fc0aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 12 Jun 2024 10:08:23 +0200 Subject: [PATCH 03/10] Move two functions only used in pid1 from libshared to the binary Anything that is part of src/shared becomes part of the shared library, which is wasteful, because the library is linked into almost all binaries. The library is also forms a separate link unit, so this prevents the function from being inlined or such. Also, move logging into the function. Now that it's not a library function, there is no reason not to do that. --- src/core/clock-warp.c | 56 +++++++++++++++++++++++++++++++++++++++++ src/core/clock-warp.h | 5 ++++ src/core/main.c | 14 ++--------- src/core/meson.build | 1 + src/shared/clock-util.c | 48 ----------------------------------- src/shared/clock-util.h | 11 -------- 6 files changed, 64 insertions(+), 71 deletions(-) create mode 100644 src/core/clock-warp.c create mode 100644 src/core/clock-warp.h diff --git a/src/core/clock-warp.c b/src/core/clock-warp.c new file mode 100644 index 0000000000..d4dcc0440f --- /dev/null +++ b/src/core/clock-warp.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#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(void) { + usec_t epoch_usec; + struct stat st; + int r; + + r = RET_NERRNO(stat(EPOCH_CLOCK_FILE, &st)); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Cannot stat " EPOCH_CLOCK_FILE ": %m"); + + epoch_usec = (usec_t) TIME_EPOCH * USEC_PER_SEC; + } else + epoch_usec = timespec_load(&st.st_mtim); + + 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)) + advance = false; + else + return; /* Nothing to do. */ + + r = RET_NERRNO(clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(epoch_usec))); + if (r < 0 && advance) + return (void) log_error_errno(r, "Current system time is before build time, but cannot correct: %m"); + else if (r < 0) + return (void) log_error_errno(r, "Current system time is further ahead than %s after build time, but cannot correct: %m", + FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY)); + else if (advance) + log_info("System time was before build time, advanced clock."); + else + log_info("System time was further ahead than %s after build time, reset clock to build time.", + FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY)); +} diff --git a/src/core/clock-warp.h b/src/core/clock-warp.h new file mode 100644 index 0000000000..78a8093b74 --- /dev/null +++ b/src/core/clock-warp.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int clock_reset_timewarp(void); +void clock_apply_epoch(void); diff --git a/src/core/main.c b/src/core/main.c index 4b8a315d86..23b6a1119a 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" @@ -1705,18 +1706,7 @@ static void initialize_clock(void) { */ (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)); + clock_apply_epoch(); } static void apply_clock_update(void) { 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 57041d0e76..963a4fd82d 100644 --- a/src/shared/clock-util.c +++ b/src/shared/clock-util.c @@ -1,10 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include -#include -#include -#include #include #include #include @@ -120,47 +116,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)); -} - -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_CLOCK_FILE, &st) < 0) { - if (errno != ENOENT) - log_warning_errno(errno, "Cannot stat " EPOCH_CLOCK_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 730d1cebe8..e224a4741c 100644 --- a/src/shared/clock-util.h +++ b/src/shared/clock-util.h @@ -1,23 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include #include -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_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/" From 8e215e938a24a9da003bf77c38bf291000239971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 12 Jun 2024 10:22:58 +0200 Subject: [PATCH 04/10] Move two functions only used in timesyncd from libshared to the binary --- src/shared/clock-util.c | 35 ----------------------------- src/shared/clock-util.h | 4 ---- src/timedate/hwclock-util.c | 44 +++++++++++++++++++++++++++++++++++++ src/timedate/hwclock-util.h | 7 ++++++ src/timedate/meson.build | 6 ++++- src/timedate/timedated.c | 11 +++++----- 6 files changed, 62 insertions(+), 45 deletions(-) create mode 100644 src/timedate/hwclock-util.c create mode 100644 src/timedate/hwclock-util.h diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c index 963a4fd82d..83662e75c1 100644 --- a/src/shared/clock-util.c +++ b/src/shared/clock-util.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include #include @@ -14,40 +13,6 @@ #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 r; diff --git a/src/shared/clock-util.h b/src/shared/clock-util.h index e224a4741c..93fbaeb56e 100644 --- a/src/shared/clock-util.h +++ b/src/shared/clock-util.h @@ -1,12 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include - int clock_is_localtime(const char* adjtime_path); int clock_set_timezone(int *ret_minutesdelta); -int clock_get_hwclock(struct tm *tm); -int clock_set_hwclock(const struct tm *tm); #define EPOCH_CLOCK_FILE "/usr/lib/clock-epoch" #define TIMESYNCD_CLOCK_FILE_DIR "/var/lib/systemd/timesync/" 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 +#include +#include +#include + +#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 + +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 e3b4367ec0..7f190388cc 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"); From 18c5979465f4d4f60fbc8ed34d98f14a257bb1ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 12 Jun 2024 10:24:52 +0200 Subject: [PATCH 05/10] shared/clock-util: small modernization --- src/shared/clock-util.c | 45 ++++++++++++++++++----------------------- src/shared/clock-util.h | 2 +- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c index 83662e75c1..9bbfdbae2e 100644 --- a/src/shared/clock-util.c +++ b/src/shared/clock-util.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include #include @@ -13,8 +12,7 @@ #include "macro.h" #include "string-util.h" -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) @@ -27,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"); + 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 */ - } else if (errno != ENOENT) - return -errno; - - /* 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 */ }; diff --git a/src/shared/clock-util.h b/src/shared/clock-util.h index 93fbaeb56e..d8992f6074 100644 --- a/src/shared/clock-util.h +++ b/src/shared/clock-util.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int clock_is_localtime(const char* adjtime_path); +int clock_is_localtime(const char *adjtime_path); int clock_set_timezone(int *ret_minutesdelta); #define EPOCH_CLOCK_FILE "/usr/lib/clock-epoch" From ed23f7cbcb5457be32a5fc133280fb56bfe043d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 5 Jun 2024 12:50:52 +0200 Subject: [PATCH 06/10] manager: use max of: compile epoch, epoch file, timesyncd file Previously systemd would not use /var/lib/systemd/timesync/clock. This means that even if /var/ is mounted when systemd is started and the file is available, we would potentially make one time jump and than another time jump. From a user's POV, this doesn't seem useful at all. Also, we would always let /usr/lib/clock-epoch take priority over the built-in epoch. But there is no guarantee that this file is actually fresh. In particular, a user may touch /usr/lib/clock-epoch to work around a broken clock during installation (as recommended in [1]), and then this file will grow stale over time. So just load the three timestamps and use the highest one as the epoch. [1] https://discussion.fedoraproject.org/t/f38-to-f39-40-dnf-system-upgrade-can-fail-on-raspberry-pi/92403 --- src/core/clock-warp.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/core/clock-warp.c b/src/core/clock-warp.c index d4dcc0440f..eff06ea1b8 100644 --- a/src/core/clock-warp.c +++ b/src/core/clock-warp.c @@ -19,18 +19,28 @@ int clock_reset_timewarp(void) { } void clock_apply_epoch(void) { - usec_t epoch_usec; + usec_t epoch_usec = 0, timesyncd_usec = 0; struct stat st; int r; - r = RET_NERRNO(stat(EPOCH_CLOCK_FILE, &st)); - if (r < 0) { - if (r != -ENOENT) - log_warning_errno(r, "Cannot stat " EPOCH_CLOCK_FILE ": %m"); - - epoch_usec = (usec_t) TIME_EPOCH * USEC_PER_SEC; - } else + 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; From 20fa2bb8939cd258e4f09fb3466d7a8a85af049d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 5 Jun 2024 13:21:45 +0200 Subject: [PATCH 07/10] manager: apply clock epoch on updates too If we're updating on a system with an invalid clock, and we're installing a newer system version with a higher update, adjust the clock. This way the invariant that the clock is always later than max(compile time, timestamp file, other timestamp file) is maintained. Also, adjust the wording of messages. When /usr/lib/clock-epoch was introduced, "build time" stopped being acurate. Just say "epoch" instead. --- src/core/clock-warp.c | 6 ++++-- src/core/clock-warp.h | 4 +++- src/core/main.c | 14 +++++++------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/core/clock-warp.c b/src/core/clock-warp.c index eff06ea1b8..8957d61b8b 100644 --- a/src/core/clock-warp.c +++ b/src/core/clock-warp.c @@ -18,7 +18,7 @@ int clock_reset_timewarp(void) { return RET_NERRNO(settimeofday(NULL, &tz)); } -void clock_apply_epoch(void) { +void clock_apply_epoch(bool allow_backwards) { usec_t epoch_usec = 0, timesyncd_usec = 0; struct stat st; int r; @@ -47,7 +47,9 @@ void clock_apply_epoch(void) { 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)) + 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. */ diff --git a/src/core/clock-warp.h b/src/core/clock-warp.h index 78a8093b74..402c7eee04 100644 --- a/src/core/clock-warp.h +++ b/src/core/clock-warp.h @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include + int clock_reset_timewarp(void); -void clock_apply_epoch(void); +void clock_apply_epoch(bool allow_backwards); diff --git a/src/core/main.c b/src/core/main.c index 23b6a1119a..3422bac155 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1671,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 @@ -1683,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); @@ -1705,13 +1705,11 @@ static void initialize_clock(void) { * time concepts will be treated as UTC that way. */ (void) clock_reset_timewarp(); - - clock_apply_epoch(); } 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; @@ -3038,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 From 8905490d94fe564f0f49fc13550c309374d55a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 5 Jun 2024 13:31:07 +0200 Subject: [PATCH 08/10] timesyncd: simplify handling of timestamps We would attempt to take the built-in epoch twice. Since advance_tstamp() is only called from one place, we don't need to do that. Also, just pass usec_t instead of a pointer to stat buf. Don't say we set the clock to "recorded timestamp" if we just set it to the built-in epoch. Also, consistently say "advance" to make it clear that we'll not attempt to rewind the clock here. --- src/timesync/timesyncd.c | 47 ++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index 9d36974c71..cbd0e6663e 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; @@ -91,22 +88,17 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) { if (r < 0) log_debug_errno(r, "Failed to create "TIMESYNCD_CLOCK_FILE_DIR", ignoring: %m"); - /* create stamp file with the compiled-in date */ - r = touch_file(TIMESYNCD_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", 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 "TIMESYNCD_CLOCK_FILE": %m"); - stamp = timespec_load(&st.st_mtim); - if (stamp > min) - min = stamp; - /* 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); @@ -114,26 +106,29 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) { log_full_errno(ERRNO_IS_PRIVILEGE(r) ? LOG_DEBUG : LOG_WARNING, r, "Failed to chmod or chown %s, ignoring: %m", TIMESYNCD_CLOCK_FILE); - (void) advance_tstamp(fd, &st); + epoch = MAX(epoch, timespec_load(&st.st_mtim)); + + (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, + 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; } From e0c7c9a00d529254c07659d094dd5e91730674bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 12 Jun 2024 11:55:14 +0200 Subject: [PATCH 09/10] manager: add structured log message about clock bump Requested in https://github.com/systemd/systemd/pull/33214#discussion_r1630251308. Also, reword error messages a bit. When /usr/lib/clock-epoch was introduced, "build time" stopped being acurate. Just say "epoch" instead. The same message ID is used in the manager and timesyncd. The event is essentially equivalent for the user, and it seems reasonable that to search for both at the same time. The catalog entry is dropped. It provided almost no additional information above the message. When the same message ID is now applied to messages from PID1 and timesyncd, and the clock can be both advanced and rewound, it becomes very hard to make the catalog entry provide something useful, because catalog entries don't allow conditionalization. --- catalog/systemd.catalog.in | 9 -------- catalog/systemd.pl.catalog.in | 10 --------- src/core/clock-warp.c | 39 +++++++++++++++++++++++++++-------- src/timesync/timesyncd.c | 1 + 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in index 3c9a6860da..e731d577e8 100644 --- a/catalog/systemd.catalog.in +++ b/catalog/systemd.catalog.in @@ -704,15 +704,6 @@ Support: %SUPPORT_URL% For the first time during the current boot an NTP synchronization has been acquired and the local system clock adjustment has been initiated. --- 7db73c8af0d94eeb822ae04323fe6ab6 -Subject: Initial clock bump -Defined-By: systemd -Support: %SUPPORT_URL% - -The system clock has been advanced based on a timestamp file on disk, in order -to ensure it remains roughly monotonic – even across reboots – if an RTC is not -available or is unreliable. - -- 3f7d5ef3e54f4302b4f0b143bb270cab Subject: TPM PCR Extended Defined-By: systemd diff --git a/catalog/systemd.pl.catalog.in b/catalog/systemd.pl.catalog.in index 75039e9fcd..a88997bddc 100644 --- a/catalog/systemd.pl.catalog.in +++ b/catalog/systemd.pl.catalog.in @@ -723,16 +723,6 @@ Support: %SUPPORT_URL% Po raz pierwszy podczas obecnego uruchomienia uzyskano synchronizację NTP i zainicjowano regulację lokalnego zegara systemowego. --- 7db73c8af0d94eeb822ae04323fe6ab6 -Subject: Początkowe przestawienie zegara -Defined-By: systemd -Support: %SUPPORT_URL% - -Przestawiono zegar systemowy na podstawie pliku ze znacznikiem czasu na dysku -w celu zapewnienia, że nadal jest w przybliżeniu monotoniczny — nawet między -ponownymi uruchomieniami — jeśli zegar czasu rzeczywistego jest niedostępny -lub zawodny. - -- 3f7d5ef3e54f4302b4f0b143bb270cab Subject: Rozszerzono PCR układu TPM Defined-By: systemd diff --git a/src/core/clock-warp.c b/src/core/clock-warp.c index 8957d61b8b..49d57afa9a 100644 --- a/src/core/clock-warp.c +++ b/src/core/clock-warp.c @@ -3,6 +3,8 @@ #include #include +#include "sd-messages.h" + #include "clock-util.h" #include "clock-warp.h" #include "errno-util.h" @@ -55,14 +57,33 @@ void clock_apply_epoch(bool allow_backwards) { return; /* Nothing to do. */ r = RET_NERRNO(clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(epoch_usec))); - if (r < 0 && advance) - return (void) log_error_errno(r, "Current system time is before build time, but cannot correct: %m"); - else if (r < 0) - return (void) log_error_errno(r, "Current system time is further ahead than %s after build time, but cannot correct: %m", - FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY)); - else if (advance) - log_info("System time was before build time, advanced clock."); + 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_info("System time was further ahead than %s after build time, reset clock to build time.", - FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY)); + 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/timesync/timesyncd.c b/src/timesync/timesyncd.c index cbd0e6663e..a1b63038c4 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -126,6 +126,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) { log_struct(LOG_INFO, "MESSAGE_ID=" SD_MESSAGE_TIME_BUMP_STR, "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))); From 863098fdc9cd91e4f760085356ac02c4b7ba6df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 5 Jun 2024 14:15:21 +0200 Subject: [PATCH 10/10] man: describe setting of the clock by systemd and systemd-timesyncd The setting of systemd clock is important and deserves an accurate description, see for example: https://discussion.fedoraproject.org/t/f38-to-f39-40-dnf-system-upgrade-can-fail-on-raspberry-pi/92403 https://bugzilla.redhat.com/show_bug.cgi?id=2242759 The meat of the description was in systemd-timesyncd.service(8), but actually it's systemd that sets the clock. In particular, systemd-timesyncd doesn't know anything about /usr/lib/clock-epoch, and since systemd sets the clock to the epoch when initializing, systemd-timesyncd would only get to advance the clock to the epoch under special circumstances. Also, systemd-timesyncd is an optional component, so we can't even rely on its man page being installed in all circumstances. The description needs to be moved to systemd(1). The description is updated to describe the changes that were made in previous commits. --- man/systemd-timesyncd.service.xml | 38 +++++++++++++------------- man/systemd.xml | 44 ++++++++++++++++++++++++++++++- tools/command_ignorelist | 6 ++--- 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/man/systemd-timesyncd.service.xml b/man/systemd-timesyncd.service.xml index 25b236df5f..1f504deac1 100644 --- a/man/systemd-timesyncd.service.xml +++ b/man/systemd-timesyncd.service.xml @@ -66,6 +66,16 @@ to achieve that, which will delay start of units that are ordered after time-sync.target until synchronization to an accurate reference clock is reached. + + systemd and systemd-timesyncd advance the system clock to + the "epoch" (the lowest date above which the system clock time is assumed to be set correctly). See + "System clock epoch" section in + systemd1 for details. + systemd will set the clock when initializing, but + /var/lib/systemd/timesync/clock might not yet be available at that point. + systemd-timesyncd will advance the clock when it is started and notices that the + system clock is before the modification time of /var/lib/systemd/timesync/clock. + @@ -78,36 +88,24 @@ The modification time ("mtime") of this file is updated on each successful NTP synchronization or after each SaveIntervalSec= time interval, as specified in - timesyncd.conf5. + timesyncd.conf5. + - When initializing, the local clock is advanced to the modification time of this file (if the - file timestamp is in the past this adjustment is not made). If the file does not exist yet, the - clock is instead advanced to the modification time of /usr/lib/clock-epoch – - if it exists – or to a time derived from the source tree at build time. This mechanism is used to - ensure that the system clock remains somewhat reasonably initialized and roughly monotonic across - reboots, in case no battery-buffered local RTC is available. + If present, the modification time of this file is used for the epoch by + systemd1 and + systemd-timesyncd.service. - - /usr/lib/clock-epoch - - The modification time ("mtime") of this file is used for advancing the system clock - in case /var/lib/systemd/timesync/clock does not exist yet, see - above. - - - - /run/systemd/timesync/synchronized - A file that is touched on each successful synchronization, to assist - systemd-time-wait-sync and other applications to detecting synchronization - with accurate reference clocks. + A file that is touched on each successful synchronization to assist + systemd-time-wait-sync and other applications in detecting synchronization to + an accurate reference clock. diff --git a/man/systemd.xml b/man/systemd.xml index f4aa7e06ca..af3d57b657 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -68,6 +68,9 @@ network device. It also sets up and mounts various API file systems, such as /sys/, /proc/, and /dev/. + systemd will also reset the system clock during early boot if it appears to be + set incorrectly. See "System clock epoch" section below. + Note that some but not all interfaces provided by systemd are covered by the Interface Portability and Stability Promise. @@ -1482,7 +1485,26 @@ - Sockets and FIFOs + System clock epoch + + When systemd is started or restarted, it may set the system clock to the + "epoch". This mechanism is used to ensure that the system clock remains somewhat reasonably initialized + and roughly monotonic across reboots, in case no battery-backed local RTC is available or it does not + work correctly. + + The epoch is the lowest date above which the system clock time is assumed to be set correctly. When + initializing, the local clock is advanced to the epoch if it was set to a lower + value. As a special case, if the local clock is sufficiently far in the future (by default 15 years, but + this can be configured at build time), the hardware clock is assumed to be broken, and the system clock + is rewound to the epoch. + + The epoch is set to the highest of: the build time of systemd, the + modification time ("mtime") of /usr/lib/clock-epoch, and the modification time of + /var/lib/systemd/timesync/clock. + + + + Files @@ -1516,6 +1538,26 @@ named pipe in the file system. This interface is obsolete and should not be used in new applications. + + + /usr/lib/clock-epoch + + The modification time ("mtime") of this file is used for the time epoch, see previous + section. + + + + + + /var/lib/systemd/timesync/clock + + The modification time ("mtime") of this file is updated by + systemd-timesyncd.service8. + If present, the modification time of file is used for the epoch, see previous section. + + + + diff --git a/tools/command_ignorelist b/tools/command_ignorelist index af694c16db..d88ebbb3ff 100644 --- a/tools/command_ignorelist +++ b/tools/command_ignorelist @@ -394,9 +394,9 @@ systemd.xml /refsect1[title="Kernel Command Line"]/variablelist/varlistentry[ter systemd.xml /refsect1[title="Kernel Command Line"]/variablelist/varlistentry[term="systemd.log_color"] systemd.xml /refsect1[title="Kernel Command Line"]/variablelist/varlistentry[term="systemd.default_standard_output="] systemd.xml /refsect1[title="Kernel Command Line"]/variablelist/varlistentry[term="systemd.setenv="] -systemd.xml /refsect1[title="Sockets and FIFOs"]/variablelist/varlistentry[term="/run/systemd/notify"] -systemd.xml /refsect1[title="Sockets and FIFOs"]/variablelist/varlistentry[term="/run/systemd/private"] -systemd.xml /refsect1[title="Sockets and FIFOs"]/variablelist/varlistentry[term="/dev/initctl"] +systemd.xml /refsect1[title="Files"]/variablelist/varlistentry[term="/run/systemd/notify"] +systemd.xml /refsect1[title="Files"]/variablelist/varlistentry[term="/run/systemd/private"] +systemd.xml /refsect1[title="Files"]/variablelist/varlistentry[term="/dev/initctl"] telinit.xml /refsect1[title="Options"]/variablelist[1]/varlistentry[term="--help"] telinit.xml /refsect1[title="Options"]/variablelist[1]/varlistentry[term="--no-wall"] telinit.xml /refsect1[title="Options"]/variablelist[2]/varlistentry[term="0"]