1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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 "inotify-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 
clock_state_release_timerfd(ClockState * sp)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 
clock_state_release(ClockState * sp)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 
update_notify_run_systemd_timesync(ClockState * sp)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 
timerfd_handler(sd_event_source * s,int fd,uint32_t revents,void * userdata)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 
process_inotify_event(sd_event * event,ClockState * sp,struct inotify_event * e)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 
inotify_handler(sd_event_source * s,int fd,uint32_t revents,void * userdata)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         ssize_t l;
84 
85         l = read(fd, &buffer, sizeof(buffer));
86         if (l < 0) {
87                 if (ERRNO_IS_TRANSIENT(errno))
88                         return 0;
89 
90                 return log_warning_errno(errno, "Lost access to inotify: %m");
91         }
92         FOREACH_INOTIFY_EVENT_WARN(e, buffer, l)
93                 process_inotify_event(event, sp, e);
94 
95         return 0;
96 }
97 
clock_state_update(ClockState * sp,sd_event * event)98 static int clock_state_update(
99                 ClockState *sp,
100                 sd_event *event) {
101 
102         struct timex tx = {};
103         usec_t t;
104         int r;
105 
106         clock_state_release_timerfd(sp);
107 
108         /* The kernel supports cancelling timers whenever its realtime clock is "set" (which can happen in a variety of
109          * ways, generally adjustments of at least 500 ms). The way this module works is we set up a timerfd that will
110          * wake when the clock is set, and when that happens we read the clock synchronization state from the return
111          * value of adjtimex(2), which supports the NTP time adjustment protocol.
112          *
113          * The kernel determines whether the clock is synchronized using driver-specific tests, based on time
114          * information passed by an application, generally through adjtimex(2). If the application asserts the clock is
115          * synchronized, but does not also do something that "sets the clock", the timer will not be cancelled and
116          * synchronization will not be detected.
117          *
118          * Similarly, this service will never complete if the application sets the time without also providing
119          * information that adjtimex(2) can use to determine that the clock is synchronized. This generally doesn't
120          * happen, but can if the system has a hardware clock that is accurate enough that the adjustment is too small
121          * to be a "set".
122          *
123          * Both these failure-to-detect situations are covered by having the presence/creation of
124          * /run/systemd/timesync/synchronized, which is considered sufficient to indicate a synchronized clock even if
125          * the kernel has not been updated.
126          *
127          * For timesyncd the initial setting of the time uses settimeofday(2), which sets the clock but does not mark
128          * it synchronized. When an NTP source is selected it sets the clock again with clock_adjtime(2) which marks it
129          * synchronized and also touches /run/systemd/timesync/synchronized which covers the case when the clock wasn't
130          * "set". */
131 
132         r = time_change_fd();
133         if (r < 0) {
134                 log_error_errno(r, "Failed to create timerfd: %m");
135                 goto finish;
136         }
137         sp->timerfd_fd = r;
138 
139         r = adjtimex(&tx);
140         if (r < 0) {
141                 log_error_errno(errno, "Failed to read adjtimex state: %m");
142                 goto finish;
143         }
144         sp->adjtime_state = r;
145 
146         if (tx.status & STA_NANO)
147                 tx.time.tv_usec /= 1000;
148         t = timeval_load(&tx.time);
149 
150         log_info("adjtime state %d status %x time %s", sp->adjtime_state, tx.status,
151                  FORMAT_TIMESTAMP_STYLE(t, TIMESTAMP_US_UTC) ?: "unrepresentable");
152 
153         sp->has_watchfile = access("/run/systemd/timesync/synchronized", F_OK) >= 0;
154         if (sp->has_watchfile)
155                 /* Presence of watch file overrides adjtime_state */
156                 r = 0;
157         else if (sp->adjtime_state == TIME_ERROR) {
158                 /* Not synchronized.  Do a one-shot wait on the descriptor and inform the caller we need to keep
159                  * running. */
160                 r = sd_event_add_io(event, &sp->timerfd_event_source, sp->timerfd_fd,
161                                     EPOLLIN, timerfd_handler, sp);
162                 if (r < 0) {
163                         log_error_errno(r, "Failed to create time change monitor source: %m");
164                         goto finish;
165                 }
166                 r = 1;
167         } else
168                 /* Synchronized; we can exit. */
169                 r = 0;
170 
171  finish:
172         if (r <= 0)
173                 (void) sd_event_exit(event, r);
174         return r;
175 }
176 
run(int argc,char * argv[])177 static int run(int argc, char * argv[]) {
178         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
179         _cleanup_(clock_state_release) ClockState state = {
180                 .timerfd_fd = -1,
181                 .inotify_fd = -1,
182                 .run_systemd_wd = -1,
183                 .run_systemd_timesync_wd = -1,
184         };
185         int r;
186 
187         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
188 
189         r = sd_event_default(&event);
190         if (r < 0)
191                 return log_error_errno(r, "Failed to allocate event loop: %m");
192 
193         r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
194         if (r < 0)
195                 return log_error_errno(r, "Failed to create sigterm event source: %m");
196 
197         r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
198         if (r < 0)
199                 return log_error_errno(r, "Failed to create sigint event source: %m");
200 
201         r = sd_event_set_watchdog(event, true);
202         if (r < 0)
203                 return log_error_errno(r, "Failed to create watchdog event source: %m");
204 
205         r = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
206         if (r < 0)
207                 return log_error_errno(errno, "Failed to create inotify descriptor: %m");
208 
209         state.inotify_fd = r;
210 
211         r = sd_event_add_io(event, &state.inotify_event_source, state.inotify_fd,
212                             EPOLLIN, inotify_handler, &state);
213         if (r < 0)
214                 return log_error_errno(r, "Failed to create notify event source: %m");
215 
216         r = inotify_add_watch_and_warn(state.inotify_fd, "/run/systemd/", IN_CREATE);
217         if (r < 0)
218                 return r;
219 
220         state.run_systemd_wd = r;
221 
222         (void) update_notify_run_systemd_timesync(&state);
223 
224         r = clock_state_update(&state, event);
225         if (r > 0) {
226                 r = sd_event_loop(event);
227                 if (r < 0)
228                         log_error_errno(r, "Failed in event loop: %m");
229         }
230 
231         if (state.has_watchfile)
232                 log_debug("Exit enabled by: /run/systemd/timesync/synchronized");
233 
234         if (state.adjtime_state == TIME_ERROR)
235                 log_info("Exit without adjtimex synchronized.");
236 
237         return r;
238 }
239 
240 DEFINE_MAIN_FUNCTION(run);
241