]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/time-wait-sync/time-wait-sync.c
Merge pull request #13727 from keszybz/pstore-greedy-realloc
[thirdparty/systemd.git] / src / time-wait-sync / time-wait-sync.c
CommitLineData
5c3376ef
PB
1/*
2 * systemd service to wait until kernel realtime clock is synchronized
3 *
5c3376ef
PB
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>
2dd79846 16#include <sys/inotify.h>
5c3376ef
PB
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"
2dd79846 24#include "fs-util.h"
1d03c5c1 25#include "main-func.h"
5c3376ef
PB
26#include "missing.h"
27#include "signal-util.h"
28#include "time-util.h"
29
30typedef struct ClockState {
2dd79846
PB
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;
5c3376ef
PB
39} ClockState;
40
2dd79846
PB
41static void clock_state_release_timerfd(ClockState *sp) {
42 sp->timerfd_event_source = sd_event_source_unref(sp->timerfd_event_source);
43 sp->timerfd_fd = safe_close(sp->timerfd_fd);
44}
45
5c3376ef 46static void clock_state_release(ClockState *sp) {
2dd79846
PB
47 clock_state_release_timerfd(sp);
48 sp->inotify_event_source = sd_event_source_unref(sp->inotify_event_source);
49 sp->inotify_fd = safe_close(sp->inotify_fd);
5c3376ef
PB
50}
51
0fad9daf 52static int clock_state_update(ClockState *sp, sd_event *event);
5c3376ef 53
2dd79846
PB
54static int update_notify_run_systemd_timesync(ClockState *sp) {
55 sp->run_systemd_timesync_wd = inotify_add_watch(sp->inotify_fd, "/run/systemd/timesync", IN_CREATE|IN_DELETE_SELF);
56 return sp->run_systemd_timesync_wd;
57}
58
59static int timerfd_handler(sd_event_source *s,
60 int fd,
61 uint32_t revents,
62 void *userdata) {
5c3376ef
PB
63 ClockState *sp = userdata;
64
65 return clock_state_update(sp, sd_event_source_get_event(s));
66}
67
2dd79846
PB
68static void process_inotify_event(sd_event *event, ClockState *sp, struct inotify_event *e) {
69 if (e->wd == sp->run_systemd_wd) {
70 /* Only thing we care about is seeing if we can start watching /run/systemd/timesync. */
71 if (sp->run_systemd_timesync_wd < 0)
72 update_notify_run_systemd_timesync(sp);
73 } else if (e->wd == sp->run_systemd_timesync_wd) {
74 if (e->mask & IN_DELETE_SELF) {
75 /* Somebody removed /run/systemd/timesync. */
76 (void) inotify_rm_watch(sp->inotify_fd, sp->run_systemd_timesync_wd);
77 sp->run_systemd_timesync_wd = -1;
78 } else
79 /* Somebody might have created /run/systemd/timesync/synchronized. */
80 clock_state_update(sp, event);
81 }
82}
83
84static int inotify_handler(sd_event_source *s,
85 int fd,
86 uint32_t revents,
87 void *userdata) {
88 sd_event *event = sd_event_source_get_event(s);
89 ClockState *sp = userdata;
90 union inotify_event_buffer buffer;
91 struct inotify_event *e;
92 ssize_t l;
93
94 l = read(fd, &buffer, sizeof(buffer));
95 if (l < 0) {
96 if (IN_SET(errno, EAGAIN, EINTR))
97 return 0;
98
99 return log_warning_errno(errno, "Lost access to inotify: %m");
100 }
101 FOREACH_INOTIFY_EVENT(e, buffer, l)
102 process_inotify_event(event, sp, e);
103
104 return 0;
105}
106
4f811d27
LP
107static int clock_state_update(
108 ClockState *sp,
109 sd_event *event) {
110
5c3376ef 111 char buf[MAX((size_t)FORMAT_TIMESTAMP_MAX, STRLEN("unrepresentable"))];
4f811d27 112 struct timex tx = {};
5c3376ef 113 const char * ts;
4f811d27
LP
114 usec_t t;
115 int r;
5c3376ef 116
2dd79846 117 clock_state_release_timerfd(sp);
5c3376ef
PB
118
119 /* The kernel supports cancelling timers whenever its realtime clock is "set" (which can happen in a variety of
2dd79846 120 * ways, generally adjustments of at least 500 ms). The way this module works is we set up a timerfd that will
0fad9daf 121 * wake when the clock is set, and when that happens we read the clock synchronization state from the return
5c3376ef
PB
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
2dd79846
PB
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.
5c3376ef
PB
128 *
129 * Similarly, this service will never complete if the application sets the time without also providing
2dd79846
PB
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.
5c3376ef 137 *
2dd79846
PB
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". */
5c3376ef 142
4f811d27 143 r = time_change_fd();
5c3376ef 144 if (r < 0) {
4f811d27 145 log_error_errno(r, "Failed to create timerfd: %m");
5c3376ef
PB
146 goto finish;
147 }
4f811d27 148 sp->timerfd_fd = r;
5c3376ef
PB
149
150 r = adjtimex(&tx);
151 if (r < 0) {
152 log_error_errno(errno, "Failed to read adjtimex state: %m");
153 goto finish;
154 }
155 sp->adjtime_state = r;
156
157 if (tx.status & STA_NANO)
158 tx.time.tv_usec /= 1000;
159 t = timeval_load(&tx.time);
160 ts = format_timestamp_us_utc(buf, sizeof(buf), t);
161 if (!ts)
162 strcpy(buf, "unrepresentable");
163 log_info("adjtime state %d status %x time %s", sp->adjtime_state, tx.status, ts);
164
2dd79846
PB
165 sp->has_watchfile = access("/run/systemd/timesync/synchronized", F_OK) >= 0;
166 if (sp->has_watchfile)
167 /* Presence of watch file overrides adjtime_state */
168 r = 0;
169 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
5c3376ef 171 * running. */
2dd79846
PB
172 r = sd_event_add_io(event, &sp->timerfd_event_source, sp->timerfd_fd,
173 EPOLLIN, timerfd_handler, sp);
5c3376ef
PB
174 if (r < 0) {
175 log_error_errno(r, "Failed to create time change monitor source: %m");
176 goto finish;
177 }
178 r = 1;
2dd79846 179 } else
5c3376ef 180 /* Synchronized; we can exit. */
5c3376ef 181 r = 0;
5c3376ef
PB
182
183 finish:
2dd79846 184 if (r <= 0)
5c3376ef
PB
185 (void) sd_event_exit(event, r);
186 return r;
187}
188
1d03c5c1 189static int run(int argc, char * argv[]) {
5c3376ef 190 _cleanup_(sd_event_unrefp) sd_event *event;
1d03c5c1 191 _cleanup_(clock_state_release) ClockState state = {
2dd79846
PB
192 .timerfd_fd = -1,
193 .inotify_fd = -1,
194 .run_systemd_wd = -1,
195 .run_systemd_timesync_wd = -1,
5c3376ef 196 };
1d03c5c1 197 int r;
5c3376ef
PB
198
199 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
200
201 r = sd_event_default(&event);
1d03c5c1
YW
202 if (r < 0)
203 return log_error_errno(r, "Failed to allocate event loop: %m");
5c3376ef
PB
204
205 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1d03c5c1
YW
206 if (r < 0)
207 return log_error_errno(r, "Failed to create sigterm event source: %m");
5c3376ef
PB
208
209 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1d03c5c1
YW
210 if (r < 0)
211 return log_error_errno(r, "Failed to create sigint event source: %m");
5c3376ef
PB
212
213 r = sd_event_set_watchdog(event, true);
1d03c5c1
YW
214 if (r < 0)
215 return log_error_errno(r, "Failed to create watchdog event source: %m");
5c3376ef 216
2dd79846 217 r = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1d03c5c1
YW
218 if (r < 0)
219 return log_error_errno(errno, "Failed to create inotify descriptor: %m");
220
2dd79846
PB
221 state.inotify_fd = r;
222
223 r = sd_event_add_io(event, &state.inotify_event_source, state.inotify_fd,
224 EPOLLIN, inotify_handler, &state);
1d03c5c1
YW
225 if (r < 0)
226 return log_error_errno(r, "Failed to create notify event source: %m");
2dd79846 227
27c3112d 228 r = inotify_add_watch_and_warn(state.inotify_fd, "/run/systemd/", IN_CREATE);
1d03c5c1 229 if (r < 0)
27c3112d 230 return r;
1d03c5c1 231
2dd79846
PB
232 state.run_systemd_wd = r;
233
234 (void) update_notify_run_systemd_timesync(&state);
235
5c3376ef
PB
236 r = clock_state_update(&state, event);
237 if (r > 0) {
238 r = sd_event_loop(event);
0fad9daf 239 if (r < 0)
5c3376ef 240 log_error_errno(r, "Failed in event loop: %m");
5c3376ef
PB
241 }
242
2dd79846 243 if (state.has_watchfile)
5238e957 244 log_debug("Exit enabled by: /run/systemd/timesync/synchronized");
2dd79846
PB
245
246 if (state.adjtime_state == TIME_ERROR)
247 log_info("Exit without adjtimex synchronized.");
248
1d03c5c1 249 return r;
5c3376ef 250}
1d03c5c1
YW
251
252DEFINE_MAIN_FUNCTION(run);