]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
timesync: add option to periodically save time
authorDimitry Ishenko <dimitry.ishenko@gmail.com>
Wed, 7 Jul 2021 17:54:47 +0000 (13:54 -0400)
committerLennart Poettering <lennart@poettering.net>
Mon, 9 Aug 2021 19:06:28 +0000 (21:06 +0200)
man/systemd-timesyncd.service.xml
man/timesyncd.conf.xml
src/timesync/timesyncd-gperf.gperf
src/timesync/timesyncd-manager.c
src/timesync/timesyncd-manager.h
src/timesync/timesyncd.c
src/timesync/timesyncd.conf.in

index 9ab4af9763bef47b5d48aa443ec96b2a3d7278f9..34e0a674720078d1f5cf132573adee8e34008bda 100644 (file)
         <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>
 
index 3fd11cea04c7d2337bcfa96d252cd90cf847bf52..07472cdb39b0d921104c35316b14c8614029136a 100644 (file)
         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>
 
index 556a2e9ba866040f5c67b84e164099c83e2abe9d..731dea12e3a54512338365f8013afab993b625d1 100644 (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)
index cb5d42b1d3ff1c7e1fdc34fcbe021e403c31bf3c..1c284f31e3ee6baf85043eaecc4397a1f1aef395 100644 (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;
+}
index 4aa7575a8029f877d39d424be12148609dfc3107..aceb0098cef37cc26cb5e7218d0af538de44075f 100644 (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);
index e6a2b06687346460d526c77be00a324f8613a246..179562696d2dd4206566799a7de91c1fe13488c9 100644 (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;
index d5f29e1598cb4ecba393b20d5645c7907d95ff57..df02cb5bd60bcddd4774685b68a39aafa10ff7ca 100644 (file)
@@ -18,3 +18,4 @@
 #RootDistanceMaxSec=5
 #PollIntervalMinSec=32
 #PollIntervalMaxSec=2048
+#SaveIntervalSec=60