chiark / gitweb /
timesyncd: save clock to disk everytime we get an NTP fix, and bump clock at boot...
authorLennart Poettering <lennart@poettering.net>
Tue, 20 May 2014 15:04:11 +0000 (00:04 +0900)
committerLennart Poettering <lennart@poettering.net>
Tue, 20 May 2014 15:23:39 +0000 (00:23 +0900)
This is useful to make sure the system clock stays monotonic even on
systems that lack an RTC.

Also, why we are at it, also use the systemd release time for bumping
the clock, since it's a slightly less bad than starting with jan 1st,
1970.

This also moves timesyncd into the early bootphase, in order to make
sure this initial bump is guaranteed to have finished by the time we
start real daemons which might write to the file systemd and thus
shouldn't leave 1970's timestamps all over the place...

src/timesync/timesyncd.c
units/systemd-timesyncd.service.in

index 6dcd942..6935635 100644 (file)
@@ -53,6 +53,7 @@
 #include "event-util.h"
 #include "network-util.h"
 #include "capability.h"
+#include "mkdir.h"
 #include "timesyncd.h"
 
 #define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
@@ -134,6 +135,78 @@ static int manager_clock_watch_setup(Manager *m);
 static int manager_connect(Manager *m);
 static void manager_disconnect(Manager *m);
 
+static int load_clock(uid_t uid, gid_t gid) {
+        usec_t nt = TIME_EPOCH * USEC_PER_SEC, ct;
+        _cleanup_close_ int fd = -1;
+
+        /* Let's try to make sure that the clock is always
+         * monotonically increasing, by saving the clock whenever we
+         * have a new NTP time, or when we shut down, and restoring it
+         * when we start again. This is particularly helpful on
+         * systems lacking a battery backed RTC. We also will adjust
+         * the time to at least the build time of systemd. */
+
+        mkdir_p("/var/lib/systemd", 0755);
+
+        /* First, we try to create the clock file if it doesn't exist yet */
+        fd = open("/var/lib/systemd/clock", O_RDWR|O_CLOEXEC|O_EXCL, 0644);
+        if (fd < 0) {
+
+                fd = open("/var/lib/systemd/clock", O_RDWR|O_CLOEXEC|O_CREAT, 0644);
+                if (fd < 0) {
+                        log_error("Failed to create /var/lib/systemd/clock: %m");
+                        return -errno;
+                }
+
+        } else {
+                struct stat st;
+                usec_t ft;
+
+                if (fstat(fd, &st) < 0) {
+                        log_error("fstat() failed: %m");
+                        return -errno;
+                }
+
+                ft = timespec_load(&st.st_mtim);
+                if (ft > nt)
+                        nt = ft;
+        }
+
+        ct = now(CLOCK_REALTIME);
+        if (nt > ct) {
+                struct timespec ts;
+                log_info("System clock time unset or jumed backwards, restoring.");
+
+                if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, nt)) < 0)
+                        log_error("Failed to restore system clock: %m");
+        }
+
+        /* Try to fix the access mode, so that we can still
+           touch the file after dropping priviliges */
+        fchmod(fd, 0644);
+        fchown(fd, uid, gid);
+
+        return 0;
+}
+
+static int save_clock(void) {
+
+        static const struct timespec ts[2] = {
+                { .tv_sec = 0, .tv_nsec = UTIME_NOW },
+                { .tv_sec = 0, .tv_nsec = UTIME_NOW },
+        };
+
+        int r;
+
+        r = utimensat(-1, "/var/lib/systemd/clock", ts, AT_SYMLINK_NOFOLLOW);
+        if (r < 0) {
+                log_warning("Failed to touch /var/lib/systemd/clock: %m");
+                return -errno;
+        }
+
+        return 0;
+}
+
 static double ntp_ts_to_d(const struct ntp_ts *ts) {
         return be32toh(ts->sec) + ((double)be32toh(ts->frac) / UINT_MAX);
 }
@@ -373,6 +446,9 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
         }
 
         r = clock_adjtime(CLOCK_REALTIME, &tmx);
+
+        save_clock();
+
         if (r < 0)
                 return r;
 
@@ -1141,15 +1217,13 @@ static int manager_network_monitor_listen(Manager *m) {
         return 0;
 }
 
-static int drop_privileges(void) {
+static int drop_privileges(uid_t uid, gid_t gid) {
+
         static const cap_value_t bits[] = {
                 CAP_SYS_TIME,
         };
 
         _cleanup_cap_free_ cap_t d = NULL;
-        const char *name = "systemd-timesync";
-        uid_t uid;
-        gid_t gid;
         int r;
 
         /* Unfortunately we cannot leave privilege dropping to PID 1
@@ -1159,12 +1233,6 @@ static int drop_privileges(void) {
          * unless our binary has the capability configured in the file
          * system, which we want to avoid. */
 
-        r = get_user_creds(&name, &uid, &gid, NULL, NULL);
-        if (r < 0) {
-                log_error("Cannot resolve user name %s: %s", name, strerror(-r));
-                return r;
-        }
-
         if (setresgid(gid, gid, gid) < 0) {
                 log_error("Failed change group ID: %m");
                 return -errno;
@@ -1216,7 +1284,10 @@ static int drop_privileges(void) {
 }
 
 int main(int argc, char *argv[]) {
+        const char *user = "systemd-timesync";
         _cleanup_manager_free_ Manager *m = NULL;
+        uid_t uid;
+        gid_t gid;
         int r;
 
         if (argc > 1) {
@@ -1231,7 +1302,17 @@ int main(int argc, char *argv[]) {
 
         umask(0022);
 
-        r = drop_privileges();
+        r = get_user_creds(&user, &uid, &gid, NULL, NULL);
+        if (r < 0) {
+                log_error("Cannot resolve user name %s: %s", user, strerror(-r));
+                return r;
+        }
+
+        r = load_clock(uid, gid);
+        if (r < 0)
+                goto out;
+
+        r = drop_privileges(uid, gid);
         if (r < 0)
                 goto out;
 
@@ -1268,6 +1349,7 @@ int main(int argc, char *argv[]) {
         }
 
         sd_event_get_exit_code(m->event, &r);
+        save_clock();
 
 out:
         sd_notify(false, "STATUS=Shutting down...");
index 1d1f486..ec28714 100644 (file)
@@ -9,17 +9,21 @@
 Description=Network Time Synchronization
 Documentation=man:systemd-timesyncd.service(8)
 ConditionCapability=CAP_SYS_TIME
-After=systemd-networkd.service
+DefaultDependencies=off
+RequiresMountsFor=/var/lib/systemd/clock
+After=systemd-remount-fs.service
+Before=sysinit.target shutdown.target
+Conflicts=shutdown.target
 
 [Service]
 Type=notify
 Restart=always
 RestartSec=0
 ExecStart=@rootlibexecdir@/systemd-timesyncd
-CapabilityBoundingSet=CAP_SYS_TIME CAP_SETUID CAP_SETGID CAP_SETPCAP
+CapabilityBoundingSet=CAP_SYS_TIME CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE
 PrivateTmp=yes
 PrivateDevices=yes
 WatchdogSec=1min
 
 [Install]
-WantedBy=multi-user.target
+WantedBy=sysinit.target