<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>
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>
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)
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);
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;
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");
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);
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;
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;
+}
#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;
/* last change */
bool jumped;
- bool sync;
int64_t drift_freq;
/* watch for time changes */
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);
int manager_connect(Manager *m);
void manager_disconnect(Manager *m);
+
+int manager_setup_save_time_event(Manager *m);
#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;
"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)
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;
#RootDistanceMaxSec=5
#PollIntervalMinSec=32
#PollIntervalMaxSec=2048
+#SaveIntervalSec=60