]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/time-wait-sync/time-wait-sync.c
9b558532ce8eff327747e350f25f553d95fc19f5
2 * systemd service to wait until kernel realtime clock is synchronized
4 * Copyright 2018 Peter A. Bigot
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
28 #include <sys/timerfd.h>
29 #include <sys/timex.h>
36 #include "signal-util.h"
37 #include "time-util.h"
39 typedef struct ClockState
{
40 int fd
; /* non-negative is descriptor from timerfd_create */
41 int adjtime_state
; /* return value from last adjtimex(2) call */
42 sd_event_source
*event_source
; /* non-null is the active io event source */
45 static void clock_state_release(ClockState
*sp
) {
46 sp
->event_source
= sd_event_source_unref(sp
->event_source
);
47 sp
->fd
= safe_close(sp
->fd
);
50 static int clock_state_update(ClockState
*sp
, sd_event
*event
);
52 static int io_handler(sd_event_source
* s
,
56 ClockState
*sp
= userdata
;
58 return clock_state_update(sp
, sd_event_source_get_event(s
));
61 static int clock_state_update(ClockState
*sp
,
63 static const struct itimerspec its
= {
64 .it_value
.tv_sec
= TIME_T_MAX
,
68 char buf
[MAX((size_t)FORMAT_TIMESTAMP_MAX
, STRLEN("unrepresentable"))];
72 clock_state_release(sp
);
74 /* The kernel supports cancelling timers whenever its realtime clock is "set" (which can happen in a variety of
75 * ways, generally adjustments of at least 500 ms). The way this module works is we set up a timer that will
76 * wake when the clock is set, and when that happens we read the clock synchronization state from the return
77 * value of adjtimex(2), which supports the NTP time adjustment protocol.
79 * The kernel determines whether the clock is synchronized using driver-specific tests, based on time
80 * information passed by an application, generally through adjtimex(2). If the application asserts the clock
81 * is synchronized, but does not also do something that "sets the clock", the timer will not be cancelled and
82 * synchronization will not be detected. Should this behavior be observed with a time synchronization provider
83 * this code might be reworked to do a periodic check as well.
85 * Similarly, this service will never complete if the application sets the time without also providing
86 * information that adjtimex(2) can use to determine that the clock is synchronized.
88 * Well-behaved implementations including systemd-timesyncd should not produce either situation. For timesyncd
89 * the initial setting of the time uses settimeofday(2), which sets the clock but does not mark it
90 * synchronized. When an NTP source is selected it sets the clock again with clock_adjtime(2) which does mark
92 r
= timerfd_create(CLOCK_REALTIME
, TFD_NONBLOCK
| TFD_CLOEXEC
);
94 log_error_errno(errno
, "Failed to create timerfd: %m");
99 r
= timerfd_settime(sp
->fd
, TFD_TIMER_ABSTIME
| TFD_TIMER_CANCEL_ON_SET
, &its
, NULL
);
101 log_error_errno(errno
, "Failed to set timerfd conditions: %m");
107 log_error_errno(errno
, "Failed to read adjtimex state: %m");
110 sp
->adjtime_state
= r
;
112 if (tx
.status
& STA_NANO
)
113 tx
.time
.tv_usec
/= 1000;
114 t
= timeval_load(&tx
.time
);
115 ts
= format_timestamp_us_utc(buf
, sizeof(buf
), t
);
117 strcpy(buf
, "unrepresentable");
118 log_info("adjtime state %d status %x time %s", sp
->adjtime_state
, tx
.status
, ts
);
120 if (sp
->adjtime_state
== TIME_ERROR
) {
121 /* Not synchronized. Do a one-shot wait on the descriptor and inform the caller we need to keep
123 r
= sd_event_add_io(event
, &sp
->event_source
, sp
->fd
,
124 EPOLLIN
, io_handler
, sp
);
126 log_error_errno(r
, "Failed to create time change monitor source: %m");
131 /* Synchronized; we can exit. */
132 (void) sd_event_exit(event
, 0);
138 (void) sd_event_exit(event
, r
);
142 int main(int argc
, char * argv
[]) {
144 _cleanup_(sd_event_unrefp
) sd_event
*event
;
149 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
151 r
= sd_event_default(&event
);
153 log_error_errno(r
, "Failed to allocate event loop: %m");
157 r
= sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
159 log_error_errno(r
, "Failed to create sigterm event source: %m");
163 r
= sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
165 log_error_errno(r
, "Failed to create sigint event source: %m");
169 r
= sd_event_set_watchdog(event
, true);
171 log_error_errno(r
, "Failed to create watchdog event source: %m");
175 r
= clock_state_update(&state
, event
);
177 r
= sd_event_loop(event
);
179 log_error_errno(r
, "Failed in event loop: %m");
180 else if (state
.adjtime_state
== TIME_ERROR
) {
181 log_error("Event loop terminated without synchronizing");
187 clock_state_release(&state
);
188 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;