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