timesync: add option to periodically save time

This commit is contained in:
Dimitry Ishenko 2021-07-07 13:54:47 -04:00 committed by Lennart Poettering
parent 8a513eee30
commit 33e82f3ef3
7 changed files with 104 additions and 13 deletions

View file

@ -75,10 +75,11 @@
<term><filename>/var/lib/systemd/timesync/clock</filename></term>
<listitem>
<para>The modification time ("mtime") of this file indicates the timestamp of the last successful
synchronization (or at least the systemd build date, in case synchronization was not possible). It
is used to ensure that the system clock remains roughly monotonic across reboots, in case no local
RTC is available.</para>
<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>.
At the minimum, it will be set to the systemd build date. It is used to ensure that the system clock
remains roughly monotonic across reboots, in case no local RTC is available.</para>
</listitem>
</varlistentry>

View file

@ -103,6 +103,18 @@
Defaults to 30 seconds and must not be smaller than 1 second.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>SaveIntervalSec=</varname></term>
<listitem><para>The interval at which the current time is periodically saved to disk, in the absence
of any recent synchronisation from an NTP server. This is especially useful for offline systems
with no local RTC, as it will guarantee that the system clock remains roughly monotonic across
reboots.</para>
<para>Takes a time interval value. The default unit is seconds, but other units may be specified, see
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
Defaults to 60 seconds.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View file

@ -25,3 +25,4 @@ Time.RootDistanceMaxSec, config_parse_sec, 0, offs
Time.PollIntervalMinSec, config_parse_sec, 0, offsetof(Manager, poll_interval_min_usec)
Time.PollIntervalMaxSec, config_parse_sec, 0, offsetof(Manager, poll_interval_max_usec)
Time.ConnectionRetrySec, config_parse_sec, 0, offsetof(Manager, connection_retry_usec)
Time.SaveIntervalSec, config_parse_sec, 0, offsetof(Manager, save_time_interval_usec)

View file

@ -59,6 +59,7 @@ static int manager_arm_timer(Manager *m, usec_t next);
static int manager_clock_watch_setup(Manager *m);
static int manager_listen_setup(Manager *m);
static void manager_listen_stop(Manager *m);
static int manager_save_time_and_rearm(Manager *m);
static double ntp_ts_short_to_d(const struct ntp_ts_short *ts) {
return be16toh(ts->sec) + (be16toh(ts->frac) / 65536.0);
@ -303,8 +304,11 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
if (r < 0)
return -errno;
r = manager_save_time_and_rearm(m);
if (r < 0)
return r;
/* If touch fails, there isn't much we can do. Maybe it'll work next time. */
(void) touch("/var/lib/systemd/timesync/clock");
(void) touch("/run/systemd/timesync/synchronized");
m->drift_freq = tmx.freq;
@ -591,7 +595,6 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
m->poll_interval_usec / USEC_PER_SEC);
if (!spike) {
m->sync = true;
r = manager_adjust_clock(m, offset, leap_sec);
if (r < 0)
log_error_errno(r, "Failed to call clock_adjtime(): %m");
@ -942,6 +945,8 @@ Manager* manager_free(Manager *m) {
sd_event_source_unref(m->network_event_source);
sd_network_monitor_unref(m->network_monitor);
sd_event_source_unref(m->event_save_time);
sd_resolve_unref(m->resolve);
sd_event_unref(m->event);
@ -1104,6 +1109,8 @@ int manager_new(Manager **ret) {
m->ratelimit = (RateLimit) { RATELIMIT_INTERVAL_USEC, RATELIMIT_BURST };
m->save_time_interval_usec = DEFAULT_SAVE_TIME_INTERVAL_USEC;
r = sd_event_default(&m->event);
if (r < 0)
return r;
@ -1131,3 +1138,60 @@ int manager_new(Manager **ret) {
return 0;
}
static int manager_save_time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
Manager *m = userdata;
assert(m);
(void) manager_save_time_and_rearm(m);
return 0;
}
int manager_setup_save_time_event(Manager *m) {
int r;
assert(m);
assert(!m->event_save_time);
if (m->save_time_interval_usec == USEC_INFINITY)
return 0;
/* NB: we'll accumulate scheduling latencies here, but this doesn't matter */
r = sd_event_add_time_relative(
m->event, &m->event_save_time,
clock_boottime_or_monotonic(),
m->save_time_interval_usec,
10 * USEC_PER_SEC,
manager_save_time_handler, m);
if (r < 0)
return log_error_errno(r, "Failed to add save time event: %m");
(void) sd_event_source_set_description(m->event_save_time, "save-time");
return 0;
}
static int manager_save_time_and_rearm(Manager *m) {
int r;
assert(m);
r = touch(CLOCK_FILE);
if (r < 0)
log_debug_errno(r, "Failed to update " CLOCK_FILE ", ignoring: %m");
m->save_on_exit = true;
if (m->save_time_interval_usec != USEC_INFINITY) {
r = sd_event_source_set_time_relative(m->event_save_time, m->save_time_interval_usec);
if (r < 0)
return log_error_errno(r, "Failed to rearm save time event: %m");
r = sd_event_source_set_enabled(m->event_save_time, SD_EVENT_ONESHOT);
if (r < 0)
return log_error_errno(r, "Failed to enable save time event: %m");
}
return 0;
}

View file

@ -27,7 +27,12 @@ typedef struct Manager Manager;
#define NTP_RETRY_INTERVAL_MIN_USEC (15 * USEC_PER_SEC)
#define NTP_RETRY_INTERVAL_MAX_USEC (6 * 60 * USEC_PER_SEC) /* 6 minutes */
#define DEFAULT_CONNECTION_RETRY_USEC (30*USEC_PER_SEC)
#define DEFAULT_CONNECTION_RETRY_USEC (30 * USEC_PER_SEC)
#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;
@ -83,7 +88,6 @@ struct Manager {
/* last change */
bool jumped;
bool sync;
int64_t drift_freq;
/* watch for time changes */
@ -100,6 +104,11 @@ struct Manager {
struct ntp_msg ntpmsg;
struct timespec origin_time, dest_time;
bool spike;
/* save time event */
sd_event_source *event_save_time;
usec_t save_time_interval_usec;
bool save_on_exit;
};
int manager_new(Manager **ret);
@ -113,3 +122,5 @@ void manager_flush_server_names(Manager *m, ServerType t);
int manager_connect(Manager *m);
void manager_disconnect(Manager *m);
int manager_setup_save_time_event(Manager *m);

View file

@ -21,9 +21,6 @@
#include "timesyncd-manager.h"
#include "user-util.h"
#define STATE_DIR "/var/lib/systemd/timesync"
#define CLOCK_FILE STATE_DIR "/clock"
static int load_clock_timestamp(uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
usec_t min = TIME_EPOCH * USEC_PER_SEC;
@ -155,6 +152,10 @@ static int run(int argc, char *argv[]) {
"STATUS=Daemon is running",
NOTIFY_STOPPING);
r = manager_setup_save_time_event(m);
if (r < 0)
return r;
if (network_is_online()) {
r = manager_connect(m);
if (r < 0)
@ -166,10 +167,10 @@ static int run(int argc, char *argv[]) {
return log_error_errno(r, "Failed to run event loop: %m");
/* if we got an authoritative time, store it in the file system */
if (m->sync) {
if (m->save_on_exit) {
r = touch(CLOCK_FILE);
if (r < 0)
log_debug_errno(r, "Failed to touch %s, ignoring: %m", CLOCK_FILE);
log_debug_errno(r, "Failed to touch " CLOCK_FILE ", ignoring: %m");
}
return 0;

View file

@ -18,3 +18,4 @@
#RootDistanceMaxSec=5
#PollIntervalMinSec=32
#PollIntervalMaxSec=2048
#SaveIntervalSec=60