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