• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14872145375

06 May 2025 09:07PM UTC coverage: 72.232% (+0.02%) from 72.214%
14872145375

push

github

DaanDeMeyer
string-table: annotate _to_string and _from_string with _const_ and _pure_, respectively

Follow-up for c94f6ab1b

297286 of 411572 relevant lines covered (72.23%)

695615.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/timesync/wait-sync.c
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 "errno-util.h"
17
#include "fd-util.h"
18
#include "inotify-util.h"
19
#include "main-func.h"
20
#include "signal-util.h"
21
#include "time-util.h"
22

23
typedef struct ClockState {
24
        int timerfd_fd;                  /* non-negative is descriptor from timerfd_create */
25
        int adjtime_state;               /* return value from last adjtimex(2) call */
26
        sd_event_source *timerfd_event_source; /* non-null is the active io event source */
27
        int inotify_fd;
28
        sd_event_source *inotify_event_source;
29
        int run_systemd_wd;
30
        int run_systemd_timesync_wd;
31
        bool has_watchfile;
32
} ClockState;
33

34
static void clock_state_release_timerfd(ClockState *sp) {
×
35
        sp->timerfd_event_source = sd_event_source_unref(sp->timerfd_event_source);
×
36
        sp->timerfd_fd = safe_close(sp->timerfd_fd);
×
37
}
×
38

39
static void clock_state_release(ClockState *sp) {
×
40
        clock_state_release_timerfd(sp);
×
41
        sp->inotify_event_source = sd_event_source_unref(sp->inotify_event_source);
×
42
        sp->inotify_fd = safe_close(sp->inotify_fd);
×
43
}
×
44

45
static int clock_state_update(ClockState *sp, sd_event *event);
46

47
static int update_notify_run_systemd_timesync(ClockState *sp) {
×
48
        sp->run_systemd_timesync_wd = inotify_add_watch(sp->inotify_fd, "/run/systemd/timesync", IN_CREATE|IN_DELETE_SELF);
×
49
        return sp->run_systemd_timesync_wd;
×
50
}
51

52
static int timerfd_handler(sd_event_source *s,
×
53
                           int fd,
54
                           uint32_t revents,
55
                           void *userdata) {
56
        ClockState *sp = userdata;
×
57

58
        return clock_state_update(sp, sd_event_source_get_event(s));
×
59
}
60

61
static void process_inotify_event(sd_event *event, ClockState *sp, struct inotify_event *e) {
×
62
        if (e->wd == sp->run_systemd_wd) {
×
63
                /* Only thing we care about is seeing if we can start watching /run/systemd/timesync. */
64
                if (sp->run_systemd_timesync_wd < 0)
×
65
                        update_notify_run_systemd_timesync(sp);
×
66
        } else if (e->wd == sp->run_systemd_timesync_wd) {
×
67
                if (e->mask & IN_DELETE_SELF) {
×
68
                        /* Somebody removed /run/systemd/timesync. */
69
                        (void) inotify_rm_watch(sp->inotify_fd, sp->run_systemd_timesync_wd);
×
70
                        sp->run_systemd_timesync_wd = -1;
×
71
                } else
72
                        /* Somebody might have created /run/systemd/timesync/synchronized. */
73
                        clock_state_update(sp, event);
×
74
        }
75
}
×
76

77
static int inotify_handler(sd_event_source *s,
×
78
                           int fd,
79
                           uint32_t revents,
80
                           void *userdata) {
81
        sd_event *event = sd_event_source_get_event(s);
×
82
        ClockState *sp = userdata;
×
83
        union inotify_event_buffer buffer;
×
84
        ssize_t l;
×
85

86
        l = read(fd, &buffer, sizeof(buffer));
×
87
        if (l < 0) {
×
88
                if (ERRNO_IS_TRANSIENT(errno))
×
89
                        return 0;
×
90

91
                return log_warning_errno(errno, "Lost access to inotify: %m");
×
92
        }
93
        FOREACH_INOTIFY_EVENT_WARN(e, buffer, l)
×
94
                process_inotify_event(event, sp, e);
×
95

96
        return 0;
×
97
}
98

99
static int clock_state_update(
×
100
                ClockState *sp,
101
                sd_event *event) {
102

103
        struct timex tx = {};
×
104
        usec_t t;
×
105
        int r;
×
106

107
        clock_state_release_timerfd(sp);
×
108

109
        /* The kernel supports cancelling timers whenever its realtime clock is "set" (which can happen in a variety of
110
         * ways, generally adjustments of at least 500 ms). The way this module works is we set up a timerfd that will
111
         * wake when the clock is set, and when that happens we read the clock synchronization state from the return
112
         * value of adjtimex(2), which supports the NTP time adjustment protocol.
113
         *
114
         * The kernel determines whether the clock is synchronized using driver-specific tests, based on time
115
         * information passed by an application, generally through adjtimex(2). If the application asserts the clock is
116
         * synchronized, but does not also do something that "sets the clock", the timer will not be cancelled and
117
         * synchronization will not be detected.
118
         *
119
         * Similarly, this service will never complete if the application sets the time without also providing
120
         * information that adjtimex(2) can use to determine that the clock is synchronized. This generally doesn't
121
         * happen, but can if the system has a hardware clock that is accurate enough that the adjustment is too small
122
         * to be a "set".
123
         *
124
         * Both these failure-to-detect situations are covered by having the presence/creation of
125
         * /run/systemd/timesync/synchronized, which is considered sufficient to indicate a synchronized clock even if
126
         * the kernel has not been updated.
127
         *
128
         * For timesyncd the initial setting of the time uses settimeofday(2), which sets the clock but does not mark
129
         * it synchronized. When an NTP source is selected it sets the clock again with clock_adjtime(2) which marks it
130
         * synchronized and also touches /run/systemd/timesync/synchronized which covers the case when the clock wasn't
131
         * "set". */
132

133
        r = time_change_fd();
×
134
        if (r < 0) {
×
135
                log_error_errno(r, "Failed to create timerfd: %m");
×
136
                goto finish;
×
137
        }
138
        sp->timerfd_fd = r;
×
139

140
        r = adjtimex(&tx);
×
141
        if (r < 0) {
×
142
                log_error_errno(errno, "Failed to read adjtimex state: %m");
×
143
                goto finish;
×
144
        }
145
        sp->adjtime_state = r;
×
146

147
        if (tx.status & STA_NANO)
×
148
                tx.time.tv_usec /= 1000;
×
149
        t = timeval_load(&tx.time);
×
150

151
        log_info("adjtime state %i status %x time %s", sp->adjtime_state, (unsigned) tx.status,
×
152
                 FORMAT_TIMESTAMP_STYLE(t, TIMESTAMP_US_UTC) ?: "unrepresentable");
153

154
        sp->has_watchfile = access("/run/systemd/timesync/synchronized", F_OK) >= 0;
×
155
        if (sp->has_watchfile)
×
156
                /* Presence of watch file overrides adjtime_state */
157
                r = 0;
158
        else if (sp->adjtime_state == TIME_ERROR) {
×
159
                /* Not synchronized.  Do a one-shot wait on the descriptor and inform the caller we need to keep
160
                 * running. */
161
                r = sd_event_add_io(event, &sp->timerfd_event_source, sp->timerfd_fd,
×
162
                                    EPOLLIN, timerfd_handler, sp);
163
                if (r < 0) {
×
164
                        log_error_errno(r, "Failed to create time change monitor source: %m");
×
165
                        goto finish;
×
166
                }
167
                r = 1;
168
        } else
169
                /* Synchronized; we can exit. */
170
                r = 0;
171

172
 finish:
×
173
        if (r <= 0)
×
174
                (void) sd_event_exit(event, r);
×
175
        return r;
×
176
}
177

178
static int run(int argc, char * argv[]) {
×
179
        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
×
180
        _cleanup_(clock_state_release) ClockState state = {
×
181
                .timerfd_fd = -EBADF,
182
                .inotify_fd = -EBADF,
183
                .run_systemd_wd = -1,
184
                .run_systemd_timesync_wd = -1,
185
        };
186
        int r;
×
187

188
        r = sd_event_default(&event);
×
189
        if (r < 0)
×
190
                return log_error_errno(r, "Failed to allocate event loop: %m");
×
191

192
        r = sd_event_set_signal_exit(event, true);
×
193
        if (r < 0)
×
194
                return log_error_errno(r, "Failed to enable SIGTERM/SIGINT handling: %m");
×
195

196
        r = sd_event_set_watchdog(event, true);
×
197
        if (r < 0)
×
198
                return log_error_errno(r, "Failed to create watchdog event source: %m");
×
199

200
        r = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
×
201
        if (r < 0)
×
202
                return log_error_errno(errno, "Failed to create inotify descriptor: %m");
×
203

204
        state.inotify_fd = r;
×
205

206
        r = sd_event_add_io(event, &state.inotify_event_source, state.inotify_fd,
×
207
                            EPOLLIN, inotify_handler, &state);
208
        if (r < 0)
×
209
                return log_error_errno(r, "Failed to create notify event source: %m");
×
210

211
        r = inotify_add_watch_and_warn(state.inotify_fd, "/run/systemd/", IN_CREATE);
×
212
        if (r < 0)
×
213
                return r;
214

215
        state.run_systemd_wd = r;
×
216

217
        (void) update_notify_run_systemd_timesync(&state);
×
218

219
        r = clock_state_update(&state, event);
×
220
        if (r > 0) {
×
221
                r = sd_event_loop(event);
×
222
                if (r < 0)
×
223
                        log_error_errno(r, "Failed in event loop: %m");
×
224
        }
225

226
        if (state.has_watchfile)
×
227
                log_debug("Exit enabled by: /run/systemd/timesync/synchronized");
×
228

229
        if (state.adjtime_state == TIME_ERROR)
×
230
                log_info("Exit without adjtimex synchronized.");
×
231

232
        return r;
233
}
234

235
DEFINE_MAIN_FUNCTION(run);
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc