diff options
author | Mike Yuan <me@yhndnzj.com> | 2024-06-16 17:42:47 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-16 17:42:47 +0200 |
commit | d4d90ef900cc0eab474d942a3d4ab778c28ef3d9 (patch) | |
tree | 974a71ce3515da193d1bf22fd29686851e0fc552 | |
parent | Merge pull request #33352 from YHNdnzj/freeconp-void (diff) | |
parent | man: describe setting of the clock by systemd and systemd-timesyncd (diff) | |
download | systemd-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
-rw-r--r-- | catalog/systemd.catalog.in | 9 | ||||
-rw-r--r-- | catalog/systemd.pl.catalog.in | 10 | ||||
-rw-r--r-- | man/systemd-timesyncd.service.xml | 38 | ||||
-rw-r--r-- | man/systemd.xml | 93 | ||||
-rw-r--r-- | src/core/clock-warp.c | 89 | ||||
-rw-r--r-- | src/core/clock-warp.h | 7 | ||||
-rw-r--r-- | src/core/main.c | 26 | ||||
-rw-r--r-- | src/core/meson.build | 1 | ||||
-rw-r--r-- | src/shared/clock-util.c | 130 | ||||
-rw-r--r-- | src/shared/clock-util.h | 21 | ||||
-rw-r--r-- | src/timedate/hwclock-util.c | 44 | ||||
-rw-r--r-- | src/timedate/hwclock-util.h | 7 | ||||
-rw-r--r-- | src/timedate/meson.build | 6 | ||||
-rw-r--r-- | src/timedate/timedated.c | 11 | ||||
-rw-r--r-- | src/timesync/timesyncd-manager.c | 5 | ||||
-rw-r--r-- | src/timesync/timesyncd-manager.h | 3 | ||||
-rw-r--r-- | src/timesync/timesyncd.c | 66 | ||||
-rw-r--r-- | tools/command_ignorelist | 6 |
18 files changed, 314 insertions, 258 deletions
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 <filename>time-sync.target</filename> until synchronization to an accurate reference clock is reached.</para> + + <para><filename>systemd</filename> and <filename>systemd-timesyncd</filename> 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 + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> for details. + <filename>systemd</filename> will set the clock when initializing, but + <filename>/var/lib/systemd/timesync/clock</filename> might not yet be available at that point. + <filename>systemd-timesyncd</filename> will advance the clock when it is started and notices that the + system clock is before the modification time of <filename>/var/lib/systemd/timesync/clock</filename>. + </para> </refsect1> <refsect1> @@ -78,36 +88,24 @@ <listitem> <para>The modification time ("mtime") of this file is updated on each successful NTP synchronization or after each <varname>SaveIntervalSec=</varname> time interval, as specified in - <citerefentry><refentrytitle>timesyncd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> + <citerefentry><refentrytitle>timesyncd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>. + </para> - <para>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 <filename>/usr/lib/clock-epoch</filename> – - 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.</para> + <para>If present, the modification time of this file is used for the epoch by + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> and + <filename>systemd-timesyncd.service</filename>.</para> <xi:include href="version-info.xml" xpointer="v219"/> </listitem> </varlistentry> <varlistentry> - <term><filename>/usr/lib/clock-epoch</filename></term> - - <listitem><para>The modification time ("mtime") of this file is used for advancing the system clock - in case <filename>/var/lib/systemd/timesync/clock</filename> does not exist yet, see - above.</para> - - <xi:include href="version-info.xml" xpointer="v254"/></listitem> - </varlistentry> - - <varlistentry> <term><filename>/run/systemd/timesync/synchronized</filename></term> <listitem> - <para>A file that is touched on each successful synchronization, to assist - <filename>systemd-time-wait-sync</filename> and other applications to detecting synchronization - with accurate reference clocks.</para> + <para>A file that is touched on each successful synchronization to assist + <filename>systemd-time-wait-sync</filename> and other applications in detecting synchronization to + an accurate reference clock.</para> <xi:include href="version-info.xml" xpointer="v239"/> </listitem> 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 @@ <filename>user.conf.d</filename> directories. See <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</para> + + <para><command>systemd</command> 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 <filename>/sys/</filename>, + <filename>/proc/</filename>, and <filename>/dev/</filename>.</para> + + <para><command>systemd</command> will also reset the system clock during early boot if it appears to be + set incorrectly. See "System clock epoch" section below.</para> + + <para>Note that some but not all interfaces provided by systemd are covered by the + <ulink url="https://systemd.io/PORTABILITY_AND_STABILITY/">Interface Portability and Stability Promise</ulink>.</para> + + <para>The D-Bus API of <command>systemd</command> is described in + <citerefentry><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry> + and + <citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>. + </para> + + <para>Systems which invoke systemd in a container or initrd environment should implement the <ulink + url="https://systemd.io/CONTAINER_INTERFACE">Container Interface</ulink> or + <ulink url="https://systemd.io/INITRD_INTERFACE/">initrd Interface</ulink> + specifications, respectively.</para> </refsect1> <refsect1> - <title>Concepts</title> + <title>Units</title> <para>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.</para> - <para>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 - <filename>/sys/</filename> or <filename>/proc/</filename>.</para> - - <para>For more information about the concepts and - ideas behind systemd, please refer to the - <ulink url="https://0pointer.de/blog/projects/systemd.html">Original Design Document</ulink>.</para> - - <para>Note that some but not all interfaces provided by systemd are covered by the - <ulink url="https://systemd.io/PORTABILITY_AND_STABILITY/">Interface Portability and Stability Promise</ulink>.</para> - <para>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 <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para> - - <para>The D-Bus API of <command>systemd</command> is described in - <citerefentry><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry> - and - <citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>. - </para> - - <para>Systems which invoke systemd in a container or initrd environment should implement the <ulink - url="https://systemd.io/CONTAINER_INTERFACE">Container Interface</ulink> or - <ulink url="https://systemd.io/INITRD_INTERFACE/">initrd Interface</ulink> - specifications, respectively.</para> </refsect1> <refsect1> @@ -1487,7 +1485,26 @@ </refsect1> <refsect1> - <title>Sockets and FIFOs</title> + <title>System clock epoch</title> + + <para>When <command>systemd</command> 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.</para> + + <para>The epoch is the lowest date above which the system clock time is assumed to be set correctly. When + initializing, the local clock is <emphasis>advanced</emphasis> 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 <emphasis>rewound</emphasis> to the epoch.</para> + + <para>The epoch is set to the highest of: the build time of <filename>systemd</filename>, the + modification time ("mtime") of <filename>/usr/lib/clock-epoch</filename>, and the modification time of + <filename>/var/lib/systemd/timesync/clock</filename>.</para> + </refsect1> + + <refsect1> + <title>Files</title> <variablelist> <varlistentry> @@ -1521,6 +1538,26 @@ named pipe in the file system. This interface is obsolete and should not be used in new applications.</para></listitem> </varlistentry> + + <varlistentry> + <term><filename>/usr/lib/clock-epoch</filename></term> + + <listitem><para>The modification time ("mtime") of this file is used for the time epoch, see previous + section.</para> + + <xi:include href="version-info.xml" xpointer="v247"/></listitem> + </varlistentry> + + <varlistentry> + <term><filename>/var/lib/systemd/timesync/clock</filename></term> + + <listitem><para>The modification time ("mtime") of this file is updated by + <citerefentry><refentrytitle>systemd-timesyncd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. + If present, the modification time of file is used for the epoch, see previous section. + </para> + + <xi:include href="version-info.xml" xpointer="v257"/></listitem> + </varlistentry> </variablelist> </refsect1> @@ -1558,6 +1595,10 @@ <member><citerefentry project='man-pages'><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry></member> </simplelist></para> + + <para>For more information about the concepts and + ideas behind systemd, please refer to the + <ulink url="https://0pointer.de/blog/projects/systemd.html">Original Design Document</ulink>.</para> </refsect1> </refentry> 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; 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"] |