diff --git a/meson.build b/meson.build index 27dcf956f3..e08a7662a7 100644 --- a/meson.build +++ b/meson.build @@ -722,6 +722,8 @@ if time_epoch == -1 endif conf.set('TIME_EPOCH', time_epoch) +conf.set('CLOCK_VALID_RANGE_USEC_MAX', get_option('clock-valid-range-usec-max')) + foreach tuple : [['system-alloc-uid-min', 'SYS_UID_MIN', 1], # Also see login.defs(5). ['system-uid-max', 'SYS_UID_MAX', 999], ['system-alloc-gid-min', 'SYS_GID_MIN', 1], diff --git a/meson_options.txt b/meson_options.txt index 0b01fb2cbf..110a471b6b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -208,6 +208,8 @@ option('status-unit-format-default', type : 'combo', description : 'use unit name or description in messages by default') option('time-epoch', type : 'integer', value : '-1', description : 'time epoch for time clients') +option('clock-valid-range-usec-max', type : 'integer', value : '473364000000000', # 15 years + description : 'maximum value in microseconds for the difference between RTC and epoch, exceeding which is considered an RTC error') option('system-alloc-uid-min', type : 'integer', value : '-1', description : 'minimum system UID used when allocating') diff --git a/src/core/main.c b/src/core/main.c index 8920d70d5d..473dc0920e 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -83,6 +83,7 @@ #include "switch-root.h" #include "sysctl-util.h" #include "terminal-util.h" +#include "time-util.h" #include "umask-util.h" #include "user-util.h" #include "util.h" @@ -1598,11 +1599,18 @@ static void initialize_clock(void) { */ (void) clock_reset_timewarp(); - r = clock_apply_epoch(); - if (r < 0) - log_error_errno(r, "Current system time is before build time, but cannot correct: %m"); - else if (r > 0) + 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) { diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c index b446daf581..7c1c48d069 100644 --- a/src/shared/clock-util.c +++ b/src/shared/clock-util.c @@ -139,10 +139,15 @@ int clock_reset_timewarp(void) { #define EPOCH_FILE "/usr/lib/clock-epoch" -int clock_apply_epoch(void) { +int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) { struct stat st; struct timespec ts; - usec_t epoch_usec; + usec_t epoch_usec, now_usec; + + /* 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) @@ -152,8 +157,15 @@ int clock_apply_epoch(void) { } else epoch_usec = timespec_load(&st.st_mtim); - if (now(CLOCK_REALTIME) >= epoch_usec) + now_usec = now(CLOCK_REALTIME); + if (now_usec < epoch_usec) + *ret_attempted_change = CLOCK_CHANGE_FORWARD; + else if (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(&ts, epoch_usec)) < 0) return -errno; diff --git a/src/shared/clock-util.h b/src/shared/clock-util.h index 9e96d50d96..c8f6d1b1f1 100644 --- a/src/shared/clock-util.h +++ b/src/shared/clock-util.h @@ -1,11 +1,20 @@ /* 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(void); +int clock_apply_epoch(ClockChangeDirection *ret_attempted_change);