1
0
mirror of https://github.com/systemd/systemd synced 2024-06-29 06:34:30 +00:00

Merge pull request #33214 from keszybz/system-clock-epoch

Rework the setting and description of system clock to the epoch
This commit is contained in:
Mike Yuan 2024-06-16 17:42:47 +02:00 committed by GitHub
commit d4d90ef900
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 314 additions and 258 deletions

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

89
src/core/clock-warp.c Normal file
View File

@ -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)));
}

7
src/core/clock-warp.h Normal file
View File

@ -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);

View File

@ -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

View File

@ -143,6 +143,7 @@ core_includes = [includes, include_directories('.')]
systemd_sources = files(
'main.c',
'crash-handler.c',
'clock-warp.c',
)
systemd_executor_sources = files(

View File

@ -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");
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;
}

View File

@ -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"

View File

@ -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));
}

View File

@ -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);

View File

@ -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',

View File

@ -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");

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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"]