diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in index 2831152763..577be299aa 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/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 66db5bbf25..af3d57b657 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -62,10 +62,32 @@ 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/. + + 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. + + 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 +283,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. @@ -1487,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 @@ -1521,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. + + + + @@ -1558,6 +1595,10 @@ bootup7 systemd.directives7 + + For more information about the concepts and + ideas behind systemd, please refer to the + Original Design Document. 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 +#include + +#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 + +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 -#include -#include -#include -#include -#include #include #include #include @@ -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"); + 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 */ }; @@ -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 -#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_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 +#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 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); - (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, + "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; 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"]