2 * systemd service to wait until kernel realtime clock is synchronized
4 * Copyright 2018 Peter A. Bigot
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
28 #include <sys/inotify.h>
29 #include <sys/timerfd.h>
30 #include <sys/timex.h>
38 #include "signal-util.h"
39 #include "time-util.h"
41 typedef struct ClockState
{
42 int timerfd_fd
; /* non-negative is descriptor from timerfd_create */
43 int adjtime_state
; /* return value from last adjtimex(2) call */
44 sd_event_source
*timerfd_event_source
; /* non-null is the active io event source */
46 sd_event_source
*inotify_event_source
;
48 int run_systemd_timesync_wd
;
52 static void clock_state_release_timerfd(ClockState
*sp
) {
53 sp
->timerfd_event_source
= sd_event_source_unref(sp
->timerfd_event_source
);
54 sp
->timerfd_fd
= safe_close(sp
->timerfd_fd
);
57 static void clock_state_release(ClockState
*sp
) {
58 clock_state_release_timerfd(sp
);
59 sp
->inotify_event_source
= sd_event_source_unref(sp
->inotify_event_source
);
60 sp
->inotify_fd
= safe_close(sp
->inotify_fd
);
63 static int clock_state_update(ClockState
*sp
, sd_event
*event
);
65 static int update_notify_run_systemd_timesync(ClockState
*sp
) {
66 sp
->run_systemd_timesync_wd
= inotify_add_watch(sp
->inotify_fd
, "/run/systemd/timesync", IN_CREATE
|IN_DELETE_SELF
);
67 return sp
->run_systemd_timesync_wd
;
70 static int timerfd_handler(sd_event_source
*s
,
74 ClockState
*sp
= userdata
;
76 return clock_state_update(sp
, sd_event_source_get_event(s
));
79 static void process_inotify_event(sd_event
*event
, ClockState
*sp
, struct inotify_event
*e
) {
80 if (e
->wd
== sp
->run_systemd_wd
) {
81 /* Only thing we care about is seeing if we can start watching /run/systemd/timesync. */
82 if (sp
->run_systemd_timesync_wd
< 0)
83 update_notify_run_systemd_timesync(sp
);
84 } else if (e
->wd
== sp
->run_systemd_timesync_wd
) {
85 if (e
->mask
& IN_DELETE_SELF
) {
86 /* Somebody removed /run/systemd/timesync. */
87 (void) inotify_rm_watch(sp
->inotify_fd
, sp
->run_systemd_timesync_wd
);
88 sp
->run_systemd_timesync_wd
= -1;
90 /* Somebody might have created /run/systemd/timesync/synchronized. */
91 clock_state_update(sp
, event
);
95 static int inotify_handler(sd_event_source
*s
,
99 sd_event
*event
= sd_event_source_get_event(s
);
100 ClockState
*sp
= userdata
;
101 union inotify_event_buffer buffer
;
102 struct inotify_event
*e
;
105 l
= read(fd
, &buffer
, sizeof(buffer
));
107 if (IN_SET(errno
, EAGAIN
, EINTR
))
110 return log_warning_errno(errno
, "Lost access to inotify: %m");
112 FOREACH_INOTIFY_EVENT(e
, buffer
, l
)
113 process_inotify_event(event
, sp
, e
);
118 static int clock_state_update(ClockState
*sp
,
120 static const struct itimerspec its
= {
121 .it_value
.tv_sec
= TIME_T_MAX
,
124 struct timex tx
= {};
125 char buf
[MAX((size_t)FORMAT_TIMESTAMP_MAX
, STRLEN("unrepresentable"))];
129 clock_state_release_timerfd(sp
);
131 /* The kernel supports cancelling timers whenever its realtime clock is "set" (which can happen in a variety of
132 * ways, generally adjustments of at least 500 ms). The way this module works is we set up a timerfd that will
133 * wake when the clock is set, and when that happens we read the clock synchronization state from the return
134 * value of adjtimex(2), which supports the NTP time adjustment protocol.
136 * The kernel determines whether the clock is synchronized using driver-specific tests, based on time
137 * information passed by an application, generally through adjtimex(2). If the application asserts the clock is
138 * synchronized, but does not also do something that "sets the clock", the timer will not be cancelled and
139 * synchronization will not be detected.
141 * Similarly, this service will never complete if the application sets the time without also providing
142 * information that adjtimex(2) can use to determine that the clock is synchronized. This generally doesn't
143 * happen, but can if the system has a hardware clock that is accurate enough that the adjustment is too small
146 * Both these failure-to-detect situations are covered by having the presence/creation of
147 * /run/systemd/timesync/synchronized, which is considered sufficient to indicate a synchronized clock even if
148 * the kernel has not been updated.
150 * For timesyncd the initial setting of the time uses settimeofday(2), which sets the clock but does not mark
151 * it synchronized. When an NTP source is selected it sets the clock again with clock_adjtime(2) which marks it
152 * synchronized and also touches /run/systemd/timesync/synchronized which covers the case when the clock wasn't
154 r
= timerfd_create(CLOCK_REALTIME
, TFD_NONBLOCK
| TFD_CLOEXEC
);
156 log_error_errno(errno
, "Failed to create timerfd: %m");
161 r
= timerfd_settime(sp
->timerfd_fd
, TFD_TIMER_ABSTIME
| TFD_TIMER_CANCEL_ON_SET
, &its
, NULL
);
163 log_error_errno(errno
, "Failed to set timerfd conditions: %m");
169 log_error_errno(errno
, "Failed to read adjtimex state: %m");
172 sp
->adjtime_state
= r
;
174 if (tx
.status
& STA_NANO
)
175 tx
.time
.tv_usec
/= 1000;
176 t
= timeval_load(&tx
.time
);
177 ts
= format_timestamp_us_utc(buf
, sizeof(buf
), t
);
179 strcpy(buf
, "unrepresentable");
180 log_info("adjtime state %d status %x time %s", sp
->adjtime_state
, tx
.status
, ts
);
182 sp
->has_watchfile
= access("/run/systemd/timesync/synchronized", F_OK
) >= 0;
183 if (sp
->has_watchfile
)
184 /* Presence of watch file overrides adjtime_state */
186 else if (sp
->adjtime_state
== TIME_ERROR
) {
187 /* Not synchronized. Do a one-shot wait on the descriptor and inform the caller we need to keep
189 r
= sd_event_add_io(event
, &sp
->timerfd_event_source
, sp
->timerfd_fd
,
190 EPOLLIN
, timerfd_handler
, sp
);
192 log_error_errno(r
, "Failed to create time change monitor source: %m");
197 /* Synchronized; we can exit. */
202 (void) sd_event_exit(event
, r
);
206 int main(int argc
, char * argv
[]) {
208 _cleanup_(sd_event_unrefp
) sd_event
*event
;
212 .run_systemd_wd
= -1,
213 .run_systemd_timesync_wd
= -1,
216 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
218 r
= sd_event_default(&event
);
220 log_error_errno(r
, "Failed to allocate event loop: %m");
224 r
= sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
226 log_error_errno(r
, "Failed to create sigterm event source: %m");
230 r
= sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
232 log_error_errno(r
, "Failed to create sigint event source: %m");
236 r
= sd_event_set_watchdog(event
, true);
238 log_error_errno(r
, "Failed to create watchdog event source: %m");
242 r
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
244 log_error_errno(errno
, "Failed to create inotify descriptor: %m");
247 state
.inotify_fd
= r
;
249 r
= sd_event_add_io(event
, &state
.inotify_event_source
, state
.inotify_fd
,
250 EPOLLIN
, inotify_handler
, &state
);
252 log_error_errno(r
, "Failed to create notify event source: %m");
256 r
= inotify_add_watch(state
.inotify_fd
, "/run/systemd/", IN_CREATE
);
258 log_error_errno(errno
, "Failed to watch /run/systemd/: %m");
261 state
.run_systemd_wd
= r
;
263 (void) update_notify_run_systemd_timesync(&state
);
265 r
= clock_state_update(&state
, event
);
267 r
= sd_event_loop(event
);
269 log_error_errno(r
, "Failed in event loop: %m");
272 if (state
.has_watchfile
)
273 log_debug("Exit enabled by: /run/systemd/timesync/synchonized");
275 if (state
.adjtime_state
== TIME_ERROR
)
276 log_info("Exit without adjtimex synchronized.");
279 clock_state_release(&state
);
280 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;