]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/timesync/timesyncd.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "sd-messages.h"
10 #include "capability-util.h"
11 #include "clock-util.h"
12 #include "daemon-util.h"
15 #include "main-func.h"
16 #include "mkdir-label.h"
17 #include "network-util.h"
18 #include "process-util.h"
19 #include "signal-util.h"
20 #include "timesyncd-bus.h"
21 #include "timesyncd-conf.h"
22 #include "timesyncd-manager.h"
23 #include "user-util.h"
25 static int advance_tstamp(int fd
, const struct stat
*st
) {
29 /* So here's the problem: whenever we read the timestamp we'd like to ensure the next time we won't
30 * restore the exact same time again, but one at least one step further (so that comparing mtimes of
31 * the timestamp file is a reliable check that timesync did its thing). But file systems have
32 * different timestamp accuracy: traditional fat has 2s granularity, and even ext2 and friends expose
33 * different granularity depending on selected inode size during formatting! Hence, to ensure the
34 * timestamp definitely is increased, here's what we'll do: we'll first try to increase the timestamp
35 * by 1μs, write that and read it back. If it was updated, great. But if it was not, we'll instead
36 * increase the timestamp by 10μs, and do the same, then 100μs, then 1ms, and so on, until it works,
37 * or we reach 10s. If it still didn't work then, the fs is just broken and we give up. */
39 usec_t target
= MAX3(now(CLOCK_REALTIME
),
40 TIME_EPOCH
* USEC_PER_SEC
,
41 timespec_load(&st
->st_mtim
));
43 for (usec_t a
= 1; a
<= 10 * USEC_PER_SEC
; a
*= 10) { /* 1μs, 10μs, 100μs, 1ms, … 10s */
44 struct timespec ts
[2];
47 /* Bump to the maximum of the old timestamp advanced by the specified unit, */
48 usec_t c
= usec_add(target
, a
);
50 timespec_store(&ts
[0], c
);
53 if (futimens(fd
, ts
) < 0) {
54 /* If this doesn't work at all, log, don't fail but give up */
55 log_warning_errno(errno
, "Unable to update mtime of timestamp file, ignoring: %m");
59 if (fstat(fd
, &new_st
) < 0)
60 return log_error_errno(errno
, "Failed to stat timestamp file: %m");
62 if (timespec_load(&new_st
.st_mtim
) > target
) {
63 log_debug("Successfully bumped timestamp file.");
67 log_debug("Tried to advance timestamp file by " USEC_FMT
", but this didn't work, file system timestamp granularity too coarse?", a
);
70 log_debug("Gave up trying to advance timestamp file.");
74 static int load_clock_timestamp(uid_t uid
, gid_t gid
) {
75 usec_t min
= TIME_EPOCH
* USEC_PER_SEC
, ct
;
76 _cleanup_close_
int fd
= -EBADF
;
79 /* Let's try to make sure that the clock is always monotonically increasing, by saving the clock
80 * whenever we have a new NTP time, or when we shut down, and restoring it when we start again. This
81 * is particularly helpful on systems lacking a battery backed RTC. We also will adjust the time to
82 * at least the build time of systemd. */
84 fd
= open(CLOCK_FILE
, O_RDWR
|O_CLOEXEC
, 0644);
87 log_debug_errno(errno
, "Unable to open timestamp file '" CLOCK_FILE
"', ignoring: %m");
89 r
= mkdir_safe_label(STATE_DIR
, 0755, uid
, gid
,
90 MKDIR_FOLLOW_SYMLINK
| MKDIR_WARN_MODE
);
92 log_debug_errno(r
, "Failed to create state directory, ignoring: %m");
94 /* create stamp file with the compiled-in date */
95 r
= touch_file(CLOCK_FILE
, /* parents= */ false, min
, uid
, gid
, 0644);
97 log_debug_errno(r
, "Failed to create %s, ignoring: %m", CLOCK_FILE
);
102 /* check if the recorded time is later than the compiled-in one */
103 if (fstat(fd
, &st
) < 0)
104 return log_error_errno(errno
, "Unable to stat timestamp file '" CLOCK_FILE
"': %m");
106 stamp
= timespec_load(&st
.st_mtim
);
110 /* Try to fix the access mode, so that we can still touch the file after dropping
112 r
= fchmod_and_chown(fd
, 0644, uid
, gid
);
114 log_full_errno(ERRNO_IS_PRIVILEGE(r
) ? LOG_DEBUG
: LOG_WARNING
, r
,
115 "Failed to chmod or chown %s, ignoring: %m", CLOCK_FILE
);
117 (void) advance_tstamp(fd
, &st
);
120 ct
= now(CLOCK_REALTIME
);
124 /* Not that it matters much, but we actually restore the clock to n+1 here rather than n, simply
125 * because we read n as time previously already and we want to progress here, i.e. not report the
126 * same time again. */
127 if (clock_settime(CLOCK_REALTIME
, TIMESPEC_STORE(min
+1)) < 0) {
128 log_warning_errno(errno
, "Failed to restore system clock, ignoring: %m");
133 "MESSAGE_ID=" SD_MESSAGE_TIME_BUMP_STR
,
134 "REALTIME_USEC=" USEC_FMT
, min
+1,
135 LOG_MESSAGE("System clock time unset or jumped backwards, restored from recorded timestamp: %s",
136 FORMAT_TIMESTAMP(min
+1)));
140 static int run(int argc
, char *argv
[]) {
141 _cleanup_(manager_freep
) Manager
*m
= NULL
;
142 _unused_
_cleanup_(notify_on_cleanup
) const char *notify_message
= NULL
;
143 const char *user
= "systemd-timesync";
144 uid_t uid
, uid_current
;
148 log_set_facility(LOG_CRON
);
154 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "This program does not take arguments.");
156 uid
= uid_current
= geteuid();
159 if (uid_current
== 0) {
160 r
= get_user_creds(&user
, &uid
, &gid
, NULL
, NULL
, 0);
162 return log_error_errno(r
, "Cannot resolve user name %s: %m", user
);
165 r
= load_clock_timestamp(uid
, gid
);
169 /* Drop privileges, but only if we have been started as root. If we are not running as root we assume all
170 * privileges are already dropped. */
171 if (uid_current
== 0) {
172 r
= drop_privileges(uid
, gid
, (1ULL << CAP_SYS_TIME
));
174 return log_error_errno(r
, "Failed to drop privileges: %m");
177 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, SIGRTMIN
+18) >= 0);
181 return log_error_errno(r
, "Failed to allocate manager: %m");
183 r
= manager_connect_bus(m
);
185 return log_error_errno(r
, "Could not connect to bus: %m");
187 if (clock_is_localtime(NULL
) > 0) {
188 log_info("The system is configured to read the RTC time in the local time zone. "
189 "This mode cannot be fully supported. All system time to RTC updates are disabled.");
190 m
->rtc_local_time
= true;
193 r
= manager_parse_config_file(m
);
195 log_warning_errno(r
, "Failed to parse configuration file: %m");
197 r
= manager_parse_fallback_string(m
, NTP_SERVERS
);
199 return log_error_errno(r
, "Failed to parse fallback server strings: %m");
201 log_debug("systemd-timesyncd running as pid " PID_FMT
, getpid_cached());
203 notify_message
= notify_start("READY=1\n"
204 "STATUS=Daemon is running",
207 r
= manager_setup_save_time_event(m
);
211 if (network_is_online()) {
212 r
= manager_connect(m
);
217 r
= sd_event_loop(m
->event
);
219 return log_error_errno(r
, "Failed to run event loop: %m");
221 /* if we got an authoritative time, store it in the file system */
222 if (m
->save_on_exit
) {
223 r
= touch(CLOCK_FILE
);
225 log_debug_errno(r
, "Failed to touch " CLOCK_FILE
", ignoring: %m");
231 DEFINE_MAIN_FUNCTION(run
);