summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Yuan <me@yhndnzj.com>2024-06-16 17:42:47 +0200
committerGitHub <noreply@github.com>2024-06-16 17:42:47 +0200
commitd4d90ef900cc0eab474d942a3d4ab778c28ef3d9 (patch)
tree974a71ce3515da193d1bf22fd29686851e0fc552
parentMerge pull request #33352 from YHNdnzj/freeconp-void (diff)
parentman: describe setting of the clock by systemd and systemd-timesyncd (diff)
downloadsystemd-d4d90ef900cc0eab474d942a3d4ab778c28ef3d9.tar.xz
systemd-d4d90ef900cc0eab474d942a3d4ab778c28ef3d9.zip
Merge pull request #33214 from keszybz/system-clock-epoch
Rework the setting and description of system clock to the epoch
-rw-r--r--catalog/systemd.catalog.in9
-rw-r--r--catalog/systemd.pl.catalog.in10
-rw-r--r--man/systemd-timesyncd.service.xml38
-rw-r--r--man/systemd.xml93
-rw-r--r--src/core/clock-warp.c89
-rw-r--r--src/core/clock-warp.h7
-rw-r--r--src/core/main.c26
-rw-r--r--src/core/meson.build1
-rw-r--r--src/shared/clock-util.c130
-rw-r--r--src/shared/clock-util.h21
-rw-r--r--src/timedate/hwclock-util.c44
-rw-r--r--src/timedate/hwclock-util.h7
-rw-r--r--src/timedate/meson.build6
-rw-r--r--src/timedate/timedated.c11
-rw-r--r--src/timesync/timesyncd-manager.c5
-rw-r--r--src/timesync/timesyncd-manager.h3
-rw-r--r--src/timesync/timesyncd.c66
-rw-r--r--tools/command_ignorelist6
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"]