mirror of
https://github.com/systemd/systemd.git
synced 2025-03-10 16:58:28 +03: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:
commit
d4d90ef900
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
89
src/core/clock-warp.c
Normal 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
7
src/core/clock-warp.h
Normal 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);
|
@ -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
|
||||
|
@ -143,6 +143,7 @@ core_includes = [includes, include_directories('.')]
|
||||
systemd_sources = files(
|
||||
'main.c',
|
||||
'crash-handler.c',
|
||||
'clock-warp.c',
|
||||
)
|
||||
|
||||
systemd_executor_sources = files(
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
44
src/timedate/hwclock-util.c
Normal file
44
src/timedate/hwclock-util.c
Normal 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));
|
||||
}
|
7
src/timedate/hwclock-util.h
Normal file
7
src/timedate/hwclock-util.h
Normal 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);
|
@ -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',
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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"]
|
||||
|
Loading…
x
Reference in New Issue
Block a user