]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/time-wait-sync/time-wait-sync.c
time-wait-sync: drop gpl snail mail boilerplate
[thirdparty/systemd.git] / src / time-wait-sync / time-wait-sync.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /* systemd service to wait until kernel realtime clock is synchronized */
3
4 #include <errno.h>
5 #include <stdbool.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/inotify.h>
10 #include <sys/timerfd.h>
11 #include <sys/timex.h>
12 #include <unistd.h>
13
14 #include "sd-event.h"
15
16 #include "fd-util.h"
17 #include "fs-util.h"
18 #include "main-func.h"
19 #include "signal-util.h"
20 #include "time-util.h"
21
22 typedef struct ClockState {
23 int timerfd_fd; /* non-negative is descriptor from timerfd_create */
24 int adjtime_state; /* return value from last adjtimex(2) call */
25 sd_event_source *timerfd_event_source; /* non-null is the active io event source */
26 int inotify_fd;
27 sd_event_source *inotify_event_source;
28 int run_systemd_wd;
29 int run_systemd_timesync_wd;
30 bool has_watchfile;
31 } ClockState;
32
33 static void clock_state_release_timerfd(ClockState *sp) {
34 sp->timerfd_event_source = sd_event_source_unref(sp->timerfd_event_source);
35 sp->timerfd_fd = safe_close(sp->timerfd_fd);
36 }
37
38 static void clock_state_release(ClockState *sp) {
39 clock_state_release_timerfd(sp);
40 sp->inotify_event_source = sd_event_source_unref(sp->inotify_event_source);
41 sp->inotify_fd = safe_close(sp->inotify_fd);
42 }
43
44 static int clock_state_update(ClockState *sp, sd_event *event);
45
46 static int update_notify_run_systemd_timesync(ClockState *sp) {
47 sp->run_systemd_timesync_wd = inotify_add_watch(sp->inotify_fd, "/run/systemd/timesync", IN_CREATE|IN_DELETE_SELF);
48 return sp->run_systemd_timesync_wd;
49 }
50
51 static int timerfd_handler(sd_event_source *s,
52 int fd,
53 uint32_t revents,
54 void *userdata) {
55 ClockState *sp = userdata;
56
57 return clock_state_update(sp, sd_event_source_get_event(s));
58 }
59
60 static void process_inotify_event(sd_event *event, ClockState *sp, struct inotify_event *e) {
61 if (e->wd == sp->run_systemd_wd) {
62 /* Only thing we care about is seeing if we can start watching /run/systemd/timesync. */
63 if (sp->run_systemd_timesync_wd < 0)
64 update_notify_run_systemd_timesync(sp);
65 } else if (e->wd == sp->run_systemd_timesync_wd) {
66 if (e->mask & IN_DELETE_SELF) {
67 /* Somebody removed /run/systemd/timesync. */
68 (void) inotify_rm_watch(sp->inotify_fd, sp->run_systemd_timesync_wd);
69 sp->run_systemd_timesync_wd = -1;
70 } else
71 /* Somebody might have created /run/systemd/timesync/synchronized. */
72 clock_state_update(sp, event);
73 }
74 }
75
76 static int inotify_handler(sd_event_source *s,
77 int fd,
78 uint32_t revents,
79 void *userdata) {
80 sd_event *event = sd_event_source_get_event(s);
81 ClockState *sp = userdata;
82 union inotify_event_buffer buffer;
83 struct inotify_event *e;
84 ssize_t l;
85
86 l = read(fd, &buffer, sizeof(buffer));
87 if (l < 0) {
88 if (IN_SET(errno, EAGAIN, EINTR))
89 return 0;
90
91 return log_warning_errno(errno, "Lost access to inotify: %m");
92 }
93 FOREACH_INOTIFY_EVENT(e, buffer, l)
94 process_inotify_event(event, sp, e);
95
96 return 0;
97 }
98
99 static int clock_state_update(
100 ClockState *sp,
101 sd_event *event) {
102
103 char buf[MAX((size_t)FORMAT_TIMESTAMP_MAX, STRLEN("unrepresentable"))];
104 struct timex tx = {};
105 const char * ts;
106 usec_t t;
107 int r;
108
109 clock_state_release_timerfd(sp);
110
111 /* The kernel supports cancelling timers whenever its realtime clock is "set" (which can happen in a variety of
112 * ways, generally adjustments of at least 500 ms). The way this module works is we set up a timerfd that will
113 * wake when the clock is set, and when that happens we read the clock synchronization state from the return
114 * value of adjtimex(2), which supports the NTP time adjustment protocol.
115 *
116 * The kernel determines whether the clock is synchronized using driver-specific tests, based on time
117 * information passed by an application, generally through adjtimex(2). If the application asserts the clock is
118 * synchronized, but does not also do something that "sets the clock", the timer will not be cancelled and
119 * synchronization will not be detected.
120 *
121 * Similarly, this service will never complete if the application sets the time without also providing
122 * information that adjtimex(2) can use to determine that the clock is synchronized. This generally doesn't
123 * happen, but can if the system has a hardware clock that is accurate enough that the adjustment is too small
124 * to be a "set".
125 *
126 * Both these failure-to-detect situations are covered by having the presence/creation of
127 * /run/systemd/timesync/synchronized, which is considered sufficient to indicate a synchronized clock even if
128 * the kernel has not been updated.
129 *
130 * For timesyncd the initial setting of the time uses settimeofday(2), which sets the clock but does not mark
131 * it synchronized. When an NTP source is selected it sets the clock again with clock_adjtime(2) which marks it
132 * synchronized and also touches /run/systemd/timesync/synchronized which covers the case when the clock wasn't
133 * "set". */
134
135 r = time_change_fd();
136 if (r < 0) {
137 log_error_errno(r, "Failed to create timerfd: %m");
138 goto finish;
139 }
140 sp->timerfd_fd = r;
141
142 r = adjtimex(&tx);
143 if (r < 0) {
144 log_error_errno(errno, "Failed to read adjtimex state: %m");
145 goto finish;
146 }
147 sp->adjtime_state = r;
148
149 if (tx.status & STA_NANO)
150 tx.time.tv_usec /= 1000;
151 t = timeval_load(&tx.time);
152 ts = format_timestamp_style(buf, sizeof(buf), t, TIMESTAMP_US_UTC);
153 if (!ts)
154 strcpy(buf, "unrepresentable");
155 log_info("adjtime state %d status %x time %s", sp->adjtime_state, tx.status, ts);
156
157 sp->has_watchfile = access("/run/systemd/timesync/synchronized", F_OK) >= 0;
158 if (sp->has_watchfile)
159 /* Presence of watch file overrides adjtime_state */
160 r = 0;
161 else if (sp->adjtime_state == TIME_ERROR) {
162 /* Not synchronized. Do a one-shot wait on the descriptor and inform the caller we need to keep
163 * running. */
164 r = sd_event_add_io(event, &sp->timerfd_event_source, sp->timerfd_fd,
165 EPOLLIN, timerfd_handler, sp);
166 if (r < 0) {
167 log_error_errno(r, "Failed to create time change monitor source: %m");
168 goto finish;
169 }
170 r = 1;
171 } else
172 /* Synchronized; we can exit. */
173 r = 0;
174
175 finish:
176 if (r <= 0)
177 (void) sd_event_exit(event, r);
178 return r;
179 }
180
181 static int run(int argc, char * argv[]) {
182 _cleanup_(sd_event_unrefp) sd_event *event;
183 _cleanup_(clock_state_release) ClockState state = {
184 .timerfd_fd = -1,
185 .inotify_fd = -1,
186 .run_systemd_wd = -1,
187 .run_systemd_timesync_wd = -1,
188 };
189 int r;
190
191 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
192
193 r = sd_event_default(&event);
194 if (r < 0)
195 return log_error_errno(r, "Failed to allocate event loop: %m");
196
197 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
198 if (r < 0)
199 return log_error_errno(r, "Failed to create sigterm event source: %m");
200
201 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
202 if (r < 0)
203 return log_error_errno(r, "Failed to create sigint event source: %m");
204
205 r = sd_event_set_watchdog(event, true);
206 if (r < 0)
207 return log_error_errno(r, "Failed to create watchdog event source: %m");
208
209 r = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
210 if (r < 0)
211 return log_error_errno(errno, "Failed to create inotify descriptor: %m");
212
213 state.inotify_fd = r;
214
215 r = sd_event_add_io(event, &state.inotify_event_source, state.inotify_fd,
216 EPOLLIN, inotify_handler, &state);
217 if (r < 0)
218 return log_error_errno(r, "Failed to create notify event source: %m");
219
220 r = inotify_add_watch_and_warn(state.inotify_fd, "/run/systemd/", IN_CREATE);
221 if (r < 0)
222 return r;
223
224 state.run_systemd_wd = r;
225
226 (void) update_notify_run_systemd_timesync(&state);
227
228 r = clock_state_update(&state, event);
229 if (r > 0) {
230 r = sd_event_loop(event);
231 if (r < 0)
232 log_error_errno(r, "Failed in event loop: %m");
233 }
234
235 if (state.has_watchfile)
236 log_debug("Exit enabled by: /run/systemd/timesync/synchronized");
237
238 if (state.adjtime_state == TIME_ERROR)
239 log_info("Exit without adjtimex synchronized.");
240
241 return r;
242 }
243
244 DEFINE_MAIN_FUNCTION(run);