main.c revision 1.20 1 1.20 christos /* $NetBSD: main.c,v 1.20 2026/02/08 14:53:15 christos Exp $ */
2 1.1 kardel
3 1.3 kardel #include <config.h>
4 1.1 kardel
5 1.8 christos #include <event2/util.h>
6 1.8 christos #include <event2/event.h>
7 1.8 christos
8 1.8 christos #include "ntp_workimpl.h"
9 1.8 christos #ifdef WORK_THREAD
10 1.8 christos # include <event2/thread.h>
11 1.8 christos #endif
12 1.8 christos
13 1.18 christos #ifdef HAVE_SYSEXITS_H
14 1.18 christos # include <sysexits.h>
15 1.18 christos #endif
16 1.18 christos
17 1.3 kardel #include "main.h"
18 1.8 christos #include "ntp_libopts.h"
19 1.1 kardel #include "kod_management.h"
20 1.1 kardel #include "networking.h"
21 1.1 kardel #include "utilities.h"
22 1.1 kardel #include "log.h"
23 1.8 christos #include "libntp.h"
24 1.1 kardel
25 1.19 christos extern const char *progname;
26 1.8 christos
27 1.8 christos int shutting_down;
28 1.8 christos int time_derived;
29 1.8 christos int time_adjusted;
30 1.8 christos int n_pending_dns = 0;
31 1.8 christos int n_pending_ntp = 0;
32 1.1 kardel int ai_fam_pref = AF_UNSPEC;
33 1.8 christos int ntpver = 4;
34 1.8 christos double steplimit = -1;
35 1.8 christos SOCKET sock4 = -1; /* Socket for IPv4 */
36 1.8 christos SOCKET sock6 = -1; /* Socket for IPv6 */
37 1.8 christos /*
38 1.8 christos ** BCAST *must* listen on port 123 (by default), so we can only
39 1.8 christos ** use the UCST sockets (above) if they too are using port 123
40 1.8 christos */
41 1.8 christos SOCKET bsock4 = -1; /* Broadcast Socket for IPv4 */
42 1.8 christos SOCKET bsock6 = -1; /* Broadcast Socket for IPv6 */
43 1.8 christos struct event_base *base;
44 1.8 christos struct event *ev_sock4;
45 1.8 christos struct event *ev_sock6;
46 1.8 christos struct event *ev_worker_timeout;
47 1.8 christos struct event *ev_xmt_timer;
48 1.8 christos
49 1.8 christos struct dns_ctx {
50 1.8 christos const char * name;
51 1.8 christos int flags;
52 1.8 christos #define CTX_BCST 0x0001
53 1.8 christos #define CTX_UCST 0x0002
54 1.8 christos #define CTX_xCST 0x0003
55 1.8 christos #define CTX_CONC 0x0004
56 1.8 christos #define CTX_unused 0xfffd
57 1.8 christos int key_id;
58 1.8 christos struct timeval timeout;
59 1.8 christos struct key * key;
60 1.8 christos };
61 1.8 christos
62 1.8 christos typedef struct sent_pkt_tag sent_pkt;
63 1.8 christos struct sent_pkt_tag {
64 1.8 christos sent_pkt * link;
65 1.8 christos struct dns_ctx * dctx;
66 1.8 christos sockaddr_u addr;
67 1.8 christos time_t stime;
68 1.8 christos int done;
69 1.8 christos struct pkt x_pkt;
70 1.8 christos };
71 1.8 christos
72 1.8 christos typedef struct xmt_ctx_tag xmt_ctx;
73 1.8 christos struct xmt_ctx_tag {
74 1.8 christos xmt_ctx * link;
75 1.8 christos SOCKET sock;
76 1.8 christos time_t sched;
77 1.8 christos sent_pkt * spkt;
78 1.8 christos };
79 1.8 christos
80 1.8 christos struct timeval gap;
81 1.8 christos xmt_ctx * xmt_q;
82 1.8 christos struct key * keys = NULL;
83 1.8 christos int response_timeout;
84 1.8 christos struct timeval response_tv;
85 1.8 christos struct timeval start_tv;
86 1.8 christos /* check the timeout at least once per second */
87 1.8 christos struct timeval wakeup_tv = { 0, 888888 };
88 1.8 christos
89 1.8 christos sent_pkt * fam_listheads[2];
90 1.8 christos #define v4_pkts_list (fam_listheads[0])
91 1.8 christos #define v6_pkts_list (fam_listheads[1])
92 1.8 christos
93 1.8 christos static union {
94 1.8 christos struct pkt pkt;
95 1.11 christos char buf[LEN_PKT_NOMAC + NTP_MAXEXTEN + MAX_MAC_LEN];
96 1.8 christos } rbuf;
97 1.8 christos
98 1.8 christos #define r_pkt rbuf.pkt
99 1.1 kardel
100 1.8 christos #ifdef HAVE_DROPROOT
101 1.8 christos int droproot; /* intres imports these */
102 1.8 christos int root_dropped;
103 1.8 christos #endif
104 1.8 christos u_long current_time; /* libntp/authkeys.c */
105 1.1 kardel
106 1.8 christos void open_sockets(void);
107 1.8 christos void handle_lookup(const char *name, int flags);
108 1.8 christos void sntp_addremove_fd(int fd, int is_pipe, int remove_it);
109 1.8 christos void worker_timeout(evutil_socket_t, short, void *);
110 1.8 christos void worker_resp_cb(evutil_socket_t, short, void *);
111 1.8 christos void sntp_name_resolved(int, int, void *, const char *, const char *,
112 1.8 christos const struct addrinfo *,
113 1.8 christos const struct addrinfo *);
114 1.8 christos void queue_xmt(SOCKET sock, struct dns_ctx *dctx, sent_pkt *spkt,
115 1.8 christos u_int xmt_delay);
116 1.8 christos void xmt_timer_cb(evutil_socket_t, short, void *ptr);
117 1.8 christos void xmt(xmt_ctx *xctx);
118 1.8 christos int check_kod(const struct addrinfo *ai);
119 1.8 christos void timeout_query(sent_pkt *);
120 1.8 christos void timeout_queries(void);
121 1.8 christos void sock_cb(evutil_socket_t, short, void *);
122 1.8 christos void check_exit_conditions(void);
123 1.8 christos void sntp_libevent_log_cb(int, const char *);
124 1.8 christos void set_li_vn_mode(struct pkt *spkt, char leap, char version, char mode);
125 1.8 christos int set_time(double offset);
126 1.8 christos void dec_pending_ntp(const char *, sockaddr_u *);
127 1.8 christos int libevent_version_ok(void);
128 1.8 christos int gettimeofday_cached(struct event_base *b, struct timeval *tv);
129 1.1 kardel
130 1.1 kardel
131 1.1 kardel /*
132 1.1 kardel * The actual main function.
133 1.1 kardel */
134 1.8 christos int
135 1.1 kardel sntp_main (
136 1.8 christos int argc,
137 1.8 christos char **argv,
138 1.8 christos const char *sntpVersion
139 1.8 christos )
140 1.8 christos {
141 1.8 christos int i;
142 1.8 christos int exitcode;
143 1.8 christos int optct;
144 1.8 christos struct event_config * evcfg;
145 1.8 christos
146 1.8 christos /* Initialize logging system - sets up progname */
147 1.8 christos sntp_init_logging(argv[0]);
148 1.8 christos
149 1.8 christos if (!libevent_version_ok())
150 1.8 christos exit(EX_SOFTWARE);
151 1.8 christos
152 1.8 christos init_lib();
153 1.8 christos init_auth();
154 1.1 kardel
155 1.8 christos optct = ntpOptionProcess(&sntpOptions, argc, argv);
156 1.3 kardel argc -= optct;
157 1.8 christos argv += optct;
158 1.8 christos
159 1.8 christos
160 1.8 christos debug = OPT_VALUE_SET_DEBUG_LEVEL;
161 1.8 christos
162 1.8 christos TRACE(2, ("init_lib() done, %s%s\n",
163 1.8 christos (ipv4_works)
164 1.8 christos ? "ipv4_works "
165 1.8 christos : "",
166 1.8 christos (ipv6_works)
167 1.8 christos ? "ipv6_works "
168 1.8 christos : ""));
169 1.8 christos ntpver = OPT_VALUE_NTPVERSION;
170 1.8 christos steplimit = OPT_VALUE_STEPLIMIT / 1e3;
171 1.8 christos gap.tv_usec = max(0, OPT_VALUE_GAP * 1000);
172 1.8 christos gap.tv_usec = min(gap.tv_usec, 999999);
173 1.3 kardel
174 1.3 kardel if (HAVE_OPT(LOGFILE))
175 1.3 kardel open_logfile(OPT_ARG(LOGFILE));
176 1.3 kardel
177 1.8 christos msyslog(LOG_INFO, "%s", sntpVersion);
178 1.8 christos
179 1.8 christos if (0 == argc && !HAVE_OPT(BROADCAST) && !HAVE_OPT(CONCURRENT)) {
180 1.8 christos printf("%s: Must supply at least one of -b hostname, -c hostname, or hostname.\n",
181 1.8 christos progname);
182 1.8 christos exit(EX_USAGE);
183 1.8 christos }
184 1.8 christos
185 1.8 christos
186 1.8 christos /*
187 1.8 christos ** Eventually, we probably want:
188 1.8 christos ** - separate bcst and ucst timeouts (why?)
189 1.8 christos ** - multiple --timeout values in the commandline
190 1.8 christos */
191 1.8 christos
192 1.8 christos response_timeout = OPT_VALUE_TIMEOUT;
193 1.8 christos response_tv.tv_sec = response_timeout;
194 1.8 christos response_tv.tv_usec = 0;
195 1.3 kardel
196 1.1 kardel /* IPv6 available? */
197 1.1 kardel if (isc_net_probeipv6() != ISC_R_SUCCESS) {
198 1.1 kardel ai_fam_pref = AF_INET;
199 1.8 christos TRACE(1, ("No ipv6 support available, forcing ipv4\n"));
200 1.3 kardel } else {
201 1.1 kardel /* Check for options -4 and -6 */
202 1.1 kardel if (HAVE_OPT(IPV4))
203 1.1 kardel ai_fam_pref = AF_INET;
204 1.1 kardel else if (HAVE_OPT(IPV6))
205 1.1 kardel ai_fam_pref = AF_INET6;
206 1.1 kardel }
207 1.1 kardel
208 1.8 christos /* TODO: Parse config file if declared */
209 1.1 kardel
210 1.8 christos /*
211 1.8 christos ** Init the KOD system.
212 1.8 christos ** For embedded systems with no writable filesystem,
213 1.8 christos ** -K /dev/null can be used to disable KoD storage.
214 1.8 christos */
215 1.8 christos kod_init_kod_db(OPT_ARG(KOD), FALSE);
216 1.1 kardel
217 1.16 christos /* HMS: Check and see what happens if KEYFILE doesn't exist */
218 1.16 christos auth_init(OPT_ARG(KEYFILE), &keys);
219 1.1 kardel
220 1.8 christos /*
221 1.8 christos ** Considering employing a variable that prevents functions of doing
222 1.8 christos ** anything until everything is initialized properly
223 1.8 christos **
224 1.8 christos ** HMS: What exactly does the above mean?
225 1.8 christos */
226 1.8 christos event_set_log_callback(&sntp_libevent_log_cb);
227 1.8 christos if (debug > 0)
228 1.8 christos event_enable_debug_mode();
229 1.8 christos #ifdef WORK_THREAD
230 1.8 christos evthread_use_pthreads();
231 1.8 christos /* we use libevent from main thread only, locks should be academic */
232 1.8 christos if (debug > 0)
233 1.8 christos evthread_enable_lock_debuging();
234 1.8 christos #endif
235 1.8 christos evcfg = event_config_new();
236 1.8 christos if (NULL == evcfg) {
237 1.8 christos printf("%s: event_config_new() failed!\n", progname);
238 1.8 christos return -1;
239 1.8 christos }
240 1.8 christos #ifndef HAVE_SOCKETPAIR
241 1.8 christos event_config_require_features(evcfg, EV_FEATURE_FDS);
242 1.8 christos #endif
243 1.8 christos /* all libevent calls are from main thread */
244 1.8 christos /* event_config_set_flag(evcfg, EVENT_BASE_FLAG_NOLOCK); */
245 1.8 christos base = event_base_new_with_config(evcfg);
246 1.8 christos event_config_free(evcfg);
247 1.8 christos if (NULL == base) {
248 1.8 christos printf("%s: event_base_new() failed!\n", progname);
249 1.8 christos return -1;
250 1.8 christos }
251 1.8 christos
252 1.8 christos /* wire into intres resolver */
253 1.8 christos worker_per_query = TRUE;
254 1.8 christos addremove_io_fd = &sntp_addremove_fd;
255 1.8 christos
256 1.8 christos open_sockets();
257 1.8 christos
258 1.8 christos if (HAVE_OPT(BROADCAST)) {
259 1.8 christos int cn = STACKCT_OPT( BROADCAST );
260 1.8 christos const char ** cp = STACKLST_OPT( BROADCAST );
261 1.8 christos
262 1.8 christos while (cn-- > 0) {
263 1.8 christos handle_lookup(*cp, CTX_BCST);
264 1.8 christos cp++;
265 1.8 christos }
266 1.8 christos }
267 1.8 christos
268 1.8 christos if (HAVE_OPT(CONCURRENT)) {
269 1.8 christos int cn = STACKCT_OPT( CONCURRENT );
270 1.8 christos const char ** cp = STACKLST_OPT( CONCURRENT );
271 1.8 christos
272 1.8 christos while (cn-- > 0) {
273 1.8 christos handle_lookup(*cp, CTX_UCST | CTX_CONC);
274 1.8 christos cp++;
275 1.8 christos }
276 1.8 christos }
277 1.8 christos
278 1.8 christos for (i = 0; i < argc; ++i)
279 1.8 christos handle_lookup(argv[i], CTX_UCST);
280 1.8 christos
281 1.8 christos gettimeofday_cached(base, &start_tv);
282 1.8 christos event_base_dispatch(base);
283 1.8 christos event_base_free(base);
284 1.8 christos
285 1.8 christos if (!time_adjusted &&
286 1.8 christos (ENABLED_OPT(STEP) || ENABLED_OPT(SLEW)))
287 1.8 christos exitcode = 1;
288 1.1 kardel else
289 1.8 christos exitcode = 0;
290 1.8 christos
291 1.8 christos return exitcode;
292 1.8 christos }
293 1.8 christos
294 1.8 christos
295 1.8 christos /*
296 1.8 christos ** open sockets and make them non-blocking
297 1.8 christos */
298 1.8 christos void
299 1.8 christos open_sockets(
300 1.8 christos void
301 1.8 christos )
302 1.8 christos {
303 1.8 christos sockaddr_u name;
304 1.8 christos
305 1.8 christos if (-1 == sock4) {
306 1.8 christos sock4 = socket(PF_INET, SOCK_DGRAM, 0);
307 1.8 christos if (-1 == sock4) {
308 1.8 christos /* error getting a socket */
309 1.8 christos msyslog(LOG_ERR, "open_sockets: socket(PF_INET) failed: %m");
310 1.8 christos exit(1);
311 1.8 christos }
312 1.8 christos /* Make it non-blocking */
313 1.8 christos make_socket_nonblocking(sock4);
314 1.8 christos
315 1.8 christos /* Let's try using a wildcard... */
316 1.8 christos ZERO(name);
317 1.8 christos AF(&name) = AF_INET;
318 1.8 christos SET_ADDR4N(&name, INADDR_ANY);
319 1.8 christos SET_PORT(&name, (HAVE_OPT(USERESERVEDPORT) ? 123 : 0));
320 1.8 christos
321 1.8 christos if (-1 == bind(sock4, &name.sa,
322 1.8 christos SOCKLEN(&name))) {
323 1.8 christos msyslog(LOG_ERR, "open_sockets: bind(sock4) failed: %m");
324 1.8 christos exit(1);
325 1.8 christos }
326 1.8 christos
327 1.8 christos /* Register an NTP callback for recv/timeout */
328 1.8 christos ev_sock4 = event_new(base, sock4,
329 1.8 christos EV_TIMEOUT | EV_READ | EV_PERSIST,
330 1.8 christos &sock_cb, NULL);
331 1.8 christos if (NULL == ev_sock4) {
332 1.8 christos msyslog(LOG_ERR,
333 1.8 christos "open_sockets: event_new(base, sock4) failed!");
334 1.8 christos } else {
335 1.8 christos event_add(ev_sock4, &wakeup_tv);
336 1.8 christos }
337 1.8 christos }
338 1.8 christos
339 1.8 christos /* We may not always have IPv6... */
340 1.8 christos if (-1 == sock6 && ipv6_works) {
341 1.8 christos sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
342 1.8 christos if (-1 == sock6 && ipv6_works) {
343 1.8 christos /* error getting a socket */
344 1.8 christos msyslog(LOG_ERR, "open_sockets: socket(PF_INET6) failed: %m");
345 1.8 christos exit(1);
346 1.8 christos }
347 1.8 christos /* Make it non-blocking */
348 1.8 christos make_socket_nonblocking(sock6);
349 1.8 christos
350 1.8 christos /* Let's try using a wildcard... */
351 1.8 christos ZERO(name);
352 1.8 christos AF(&name) = AF_INET6;
353 1.8 christos SET_ADDR6N(&name, in6addr_any);
354 1.8 christos SET_PORT(&name, (HAVE_OPT(USERESERVEDPORT) ? 123 : 0));
355 1.8 christos
356 1.8 christos if (-1 == bind(sock6, &name.sa,
357 1.8 christos SOCKLEN(&name))) {
358 1.8 christos msyslog(LOG_ERR, "open_sockets: bind(sock6) failed: %m");
359 1.8 christos exit(1);
360 1.8 christos }
361 1.8 christos /* Register an NTP callback for recv/timeout */
362 1.8 christos ev_sock6 = event_new(base, sock6,
363 1.8 christos EV_TIMEOUT | EV_READ | EV_PERSIST,
364 1.8 christos &sock_cb, NULL);
365 1.8 christos if (NULL == ev_sock6) {
366 1.8 christos msyslog(LOG_ERR,
367 1.8 christos "open_sockets: event_new(base, sock6) failed!");
368 1.8 christos } else {
369 1.8 christos event_add(ev_sock6, &wakeup_tv);
370 1.8 christos }
371 1.8 christos }
372 1.8 christos
373 1.8 christos return;
374 1.8 christos }
375 1.8 christos
376 1.8 christos
377 1.8 christos /*
378 1.8 christos ** handle_lookup
379 1.8 christos */
380 1.8 christos void
381 1.8 christos handle_lookup(
382 1.8 christos const char *name,
383 1.8 christos int flags
384 1.8 christos )
385 1.8 christos {
386 1.8 christos struct addrinfo hints; /* Local copy is OK */
387 1.8 christos struct dns_ctx *ctx;
388 1.8 christos char * name_copy;
389 1.8 christos size_t name_sz;
390 1.8 christos size_t octets;
391 1.8 christos
392 1.8 christos TRACE(1, ("handle_lookup(%s,%#x)\n", name, flags));
393 1.8 christos
394 1.8 christos ZERO(hints);
395 1.8 christos hints.ai_family = ai_fam_pref;
396 1.8 christos hints.ai_flags = AI_CANONNAME | Z_AI_NUMERICSERV;
397 1.8 christos /*
398 1.8 christos ** Unless we specify a socktype, we'll get at least two
399 1.8 christos ** entries for each address: one for TCP and one for
400 1.8 christos ** UDP. That's not what we want.
401 1.8 christos */
402 1.8 christos hints.ai_socktype = SOCK_DGRAM;
403 1.8 christos hints.ai_protocol = IPPROTO_UDP;
404 1.8 christos
405 1.8 christos name_sz = 1 + strlen(name);
406 1.8 christos octets = sizeof(*ctx) + name_sz; // Space for a ctx and the name
407 1.8 christos ctx = emalloc_zero(octets); // ctx at ctx[0]
408 1.8 christos name_copy = (char *)(ctx + 1); // Put the name at ctx[1]
409 1.8 christos memcpy(name_copy, name, name_sz); // copy the name to ctx[1]
410 1.8 christos ctx->name = name_copy; // point to it...
411 1.8 christos ctx->flags = flags;
412 1.8 christos ctx->timeout = response_tv;
413 1.16 christos ctx->key = NULL;
414 1.8 christos
415 1.8 christos /* The following should arguably be passed in... */
416 1.16 christos if (ENABLED_OPT(AUTHENTICATION)) {
417 1.16 christos ctx->key_id = OPT_VALUE_AUTHENTICATION;
418 1.8 christos get_key(ctx->key_id, &ctx->key);
419 1.16 christos if (NULL == ctx->key) {
420 1.16 christos fprintf(stderr, "%s: Authentication with keyID %d requested, but no matching keyID found in <%s>!\n",
421 1.16 christos progname, ctx->key_id, OPT_ARG(KEYFILE));
422 1.16 christos exit(1);
423 1.16 christos }
424 1.8 christos } else {
425 1.8 christos ctx->key_id = -1;
426 1.8 christos }
427 1.8 christos
428 1.8 christos ++n_pending_dns;
429 1.8 christos getaddrinfo_sometime(name, "123", &hints, 0,
430 1.8 christos &sntp_name_resolved, ctx);
431 1.8 christos }
432 1.8 christos
433 1.8 christos
434 1.8 christos /*
435 1.8 christos ** DNS Callback:
436 1.8 christos ** - For each IP:
437 1.8 christos ** - - open a socket
438 1.8 christos ** - - increment n_pending_ntp
439 1.8 christos ** - - send a request if this is a Unicast callback
440 1.8 christos ** - - queue wait for response
441 1.8 christos ** - decrement n_pending_dns
442 1.8 christos */
443 1.8 christos void
444 1.8 christos sntp_name_resolved(
445 1.8 christos int rescode,
446 1.8 christos int gai_errno,
447 1.8 christos void * context,
448 1.8 christos const char * name,
449 1.8 christos const char * service,
450 1.8 christos const struct addrinfo * hints,
451 1.8 christos const struct addrinfo * addr
452 1.8 christos )
453 1.8 christos {
454 1.8 christos struct dns_ctx * dctx;
455 1.8 christos sent_pkt * spkt;
456 1.8 christos const struct addrinfo * ai;
457 1.8 christos SOCKET sock;
458 1.8 christos u_int xmt_delay_v4;
459 1.8 christos u_int xmt_delay_v6;
460 1.8 christos u_int xmt_delay;
461 1.8 christos size_t octets;
462 1.8 christos
463 1.8 christos xmt_delay_v4 = 0;
464 1.8 christos xmt_delay_v6 = 0;
465 1.8 christos dctx = context;
466 1.8 christos if (rescode) {
467 1.8 christos #ifdef EAI_SYSTEM
468 1.8 christos if (EAI_SYSTEM == rescode) {
469 1.8 christos errno = gai_errno;
470 1.8 christos mfprintf(stderr, "%s lookup error %m\n",
471 1.8 christos dctx->name);
472 1.8 christos } else
473 1.1 kardel #endif
474 1.8 christos fprintf(stderr, "%s lookup error %s\n",
475 1.8 christos dctx->name, gai_strerror(rescode));
476 1.8 christos } else {
477 1.8 christos TRACE(3, ("%s [%s]\n", dctx->name,
478 1.8 christos (addr->ai_canonname != NULL)
479 1.8 christos ? addr->ai_canonname
480 1.8 christos : ""));
481 1.8 christos
482 1.8 christos for (ai = addr; ai != NULL; ai = ai->ai_next) {
483 1.8 christos
484 1.8 christos if (check_kod(ai))
485 1.8 christos continue;
486 1.8 christos
487 1.8 christos switch (ai->ai_family) {
488 1.8 christos
489 1.8 christos case AF_INET:
490 1.8 christos sock = sock4;
491 1.8 christos xmt_delay = xmt_delay_v4;
492 1.8 christos xmt_delay_v4++;
493 1.8 christos break;
494 1.8 christos
495 1.8 christos case AF_INET6:
496 1.8 christos if (!ipv6_works)
497 1.8 christos continue;
498 1.8 christos
499 1.8 christos sock = sock6;
500 1.8 christos xmt_delay = xmt_delay_v6;
501 1.8 christos xmt_delay_v6++;
502 1.8 christos break;
503 1.8 christos
504 1.8 christos default:
505 1.8 christos msyslog(LOG_ERR, "sntp_name_resolved: unexpected ai_family: %d",
506 1.8 christos ai->ai_family);
507 1.8 christos exit(1);
508 1.8 christos break;
509 1.8 christos }
510 1.1 kardel
511 1.8 christos /*
512 1.8 christos ** We're waiting for a response for either unicast
513 1.8 christos ** or broadcast, so...
514 1.8 christos */
515 1.8 christos ++n_pending_ntp;
516 1.8 christos
517 1.8 christos /* If this is for a unicast IP, queue a request */
518 1.8 christos if (dctx->flags & CTX_UCST) {
519 1.8 christos spkt = emalloc_zero(sizeof(*spkt));
520 1.8 christos spkt->dctx = dctx;
521 1.8 christos octets = min(ai->ai_addrlen, sizeof(spkt->addr));
522 1.8 christos memcpy(&spkt->addr, ai->ai_addr, octets);
523 1.8 christos queue_xmt(sock, dctx, spkt, xmt_delay);
524 1.8 christos }
525 1.8 christos }
526 1.8 christos }
527 1.8 christos /* n_pending_dns really should be >0 here... */
528 1.8 christos --n_pending_dns;
529 1.8 christos check_exit_conditions();
530 1.8 christos }
531 1.8 christos
532 1.8 christos
533 1.8 christos /*
534 1.8 christos ** queue_xmt
535 1.8 christos */
536 1.8 christos void
537 1.8 christos queue_xmt(
538 1.8 christos SOCKET sock,
539 1.8 christos struct dns_ctx * dctx,
540 1.8 christos sent_pkt * spkt,
541 1.8 christos u_int xmt_delay
542 1.8 christos )
543 1.8 christos {
544 1.8 christos sockaddr_u * dest;
545 1.8 christos sent_pkt ** pkt_listp;
546 1.8 christos sent_pkt * match;
547 1.8 christos xmt_ctx * xctx;
548 1.8 christos struct timeval start_cb;
549 1.8 christos struct timeval delay;
550 1.8 christos
551 1.8 christos dest = &spkt->addr;
552 1.8 christos if (IS_IPV6(dest))
553 1.8 christos pkt_listp = &v6_pkts_list;
554 1.8 christos else
555 1.8 christos pkt_listp = &v4_pkts_list;
556 1.8 christos
557 1.8 christos /* reject attempts to add address already listed */
558 1.8 christos for (match = *pkt_listp; match != NULL; match = match->link) {
559 1.8 christos if (ADDR_PORT_EQ(&spkt->addr, &match->addr)) {
560 1.8 christos if (strcasecmp(spkt->dctx->name,
561 1.8 christos match->dctx->name))
562 1.8 christos printf("%s %s duplicate address from %s ignored.\n",
563 1.8 christos sptoa(&match->addr),
564 1.8 christos match->dctx->name,
565 1.8 christos spkt->dctx->name);
566 1.8 christos else
567 1.8 christos printf("%s %s, duplicate address ignored.\n",
568 1.8 christos sptoa(&match->addr),
569 1.8 christos match->dctx->name);
570 1.8 christos dec_pending_ntp(spkt->dctx->name, &spkt->addr);
571 1.8 christos free(spkt);
572 1.8 christos return;
573 1.8 christos }
574 1.8 christos }
575 1.8 christos
576 1.8 christos LINK_SLIST(*pkt_listp, spkt, link);
577 1.8 christos
578 1.8 christos xctx = emalloc_zero(sizeof(*xctx));
579 1.8 christos xctx->sock = sock;
580 1.8 christos xctx->spkt = spkt;
581 1.8 christos gettimeofday_cached(base, &start_cb);
582 1.8 christos xctx->sched = start_cb.tv_sec + (2 * xmt_delay);
583 1.8 christos
584 1.20 christos if (xmt_q) {
585 1.20 christos LINK_SORT_SLIST(xmt_q, xctx, (xctx->sched < L_S_S_CUR()->sched),
586 1.20 christos link, xmt_ctx);
587 1.20 christos }
588 1.8 christos if (xmt_q == xctx) {
589 1.8 christos /*
590 1.8 christos * The new entry is the first scheduled. The timer is
591 1.8 christos * either not active or is set for the second xmt
592 1.8 christos * context in xmt_q.
593 1.8 christos */
594 1.8 christos if (NULL == ev_xmt_timer)
595 1.8 christos ev_xmt_timer = event_new(base, INVALID_SOCKET,
596 1.8 christos EV_TIMEOUT,
597 1.8 christos &xmt_timer_cb, NULL);
598 1.8 christos if (NULL == ev_xmt_timer) {
599 1.8 christos msyslog(LOG_ERR,
600 1.8 christos "queue_xmt: event_new(base, -1, EV_TIMEOUT) failed!");
601 1.8 christos exit(1);
602 1.8 christos }
603 1.8 christos ZERO(delay);
604 1.8 christos if (xctx->sched > start_cb.tv_sec)
605 1.8 christos delay.tv_sec = xctx->sched - start_cb.tv_sec;
606 1.8 christos event_add(ev_xmt_timer, &delay);
607 1.8 christos TRACE(2, ("queue_xmt: xmt timer for %u usec\n",
608 1.8 christos (u_int)delay.tv_usec));
609 1.8 christos }
610 1.8 christos }
611 1.8 christos
612 1.8 christos
613 1.8 christos /*
614 1.8 christos ** xmt_timer_cb
615 1.8 christos */
616 1.8 christos void
617 1.8 christos xmt_timer_cb(
618 1.8 christos evutil_socket_t fd,
619 1.8 christos short what,
620 1.8 christos void * ctx
621 1.8 christos )
622 1.8 christos {
623 1.8 christos struct timeval start_cb;
624 1.8 christos struct timeval delay;
625 1.8 christos xmt_ctx * x;
626 1.8 christos
627 1.8 christos UNUSED_ARG(fd);
628 1.8 christos UNUSED_ARG(ctx);
629 1.8 christos DEBUG_INSIST(EV_TIMEOUT == what);
630 1.8 christos
631 1.8 christos if (NULL == xmt_q || shutting_down)
632 1.8 christos return;
633 1.8 christos gettimeofday_cached(base, &start_cb);
634 1.8 christos if (xmt_q->sched <= start_cb.tv_sec) {
635 1.8 christos UNLINK_HEAD_SLIST(x, xmt_q, link);
636 1.8 christos TRACE(2, ("xmt_timer_cb: at .%6.6u -> %s\n",
637 1.8 christos (u_int)start_cb.tv_usec, stoa(&x->spkt->addr)));
638 1.8 christos xmt(x);
639 1.8 christos free(x);
640 1.8 christos if (NULL == xmt_q)
641 1.8 christos return;
642 1.8 christos }
643 1.8 christos if (xmt_q->sched <= start_cb.tv_sec) {
644 1.8 christos event_add(ev_xmt_timer, &gap);
645 1.8 christos TRACE(2, ("xmt_timer_cb: at .%6.6u gap %6.6u\n",
646 1.8 christos (u_int)start_cb.tv_usec,
647 1.8 christos (u_int)gap.tv_usec));
648 1.8 christos } else {
649 1.8 christos delay.tv_sec = xmt_q->sched - start_cb.tv_sec;
650 1.8 christos delay.tv_usec = 0;
651 1.8 christos event_add(ev_xmt_timer, &delay);
652 1.8 christos TRACE(2, ("xmt_timer_cb: at .%6.6u next %ld seconds\n",
653 1.8 christos (u_int)start_cb.tv_usec,
654 1.8 christos (long)delay.tv_sec));
655 1.8 christos }
656 1.8 christos }
657 1.8 christos
658 1.8 christos
659 1.8 christos /*
660 1.8 christos ** xmt()
661 1.8 christos */
662 1.8 christos void
663 1.8 christos xmt(
664 1.8 christos xmt_ctx * xctx
665 1.8 christos )
666 1.8 christos {
667 1.8 christos SOCKET sock = xctx->sock;
668 1.8 christos struct dns_ctx *dctx = xctx->spkt->dctx;
669 1.8 christos sent_pkt * spkt = xctx->spkt;
670 1.8 christos sockaddr_u * dst = &spkt->addr;
671 1.8 christos struct timeval tv_xmt;
672 1.8 christos struct pkt x_pkt;
673 1.8 christos size_t pkt_len;
674 1.8 christos int sent;
675 1.8 christos
676 1.8 christos if (0 != gettimeofday(&tv_xmt, NULL)) {
677 1.8 christos msyslog(LOG_ERR,
678 1.8 christos "xmt: gettimeofday() failed: %m");
679 1.8 christos exit(1);
680 1.8 christos }
681 1.8 christos tv_xmt.tv_sec += JAN_1970;
682 1.8 christos
683 1.8 christos pkt_len = generate_pkt(&x_pkt, &tv_xmt, dctx->key_id,
684 1.8 christos dctx->key);
685 1.8 christos
686 1.8 christos sent = sendpkt(sock, dst, &x_pkt, pkt_len);
687 1.8 christos if (sent) {
688 1.8 christos /* Save the packet we sent... */
689 1.8 christos memcpy(&spkt->x_pkt, &x_pkt, min(sizeof(spkt->x_pkt),
690 1.8 christos pkt_len));
691 1.8 christos spkt->stime = tv_xmt.tv_sec - JAN_1970;
692 1.8 christos
693 1.8 christos TRACE(2, ("xmt: %lx.%6.6u %s %s\n", (u_long)tv_xmt.tv_sec,
694 1.8 christos (u_int)tv_xmt.tv_usec, dctx->name, stoa(dst)));
695 1.8 christos } else {
696 1.8 christos dec_pending_ntp(dctx->name, dst);
697 1.1 kardel }
698 1.8 christos
699 1.8 christos return;
700 1.8 christos }
701 1.8 christos
702 1.8 christos
703 1.8 christos /*
704 1.8 christos * timeout_queries() -- give up on unrequited NTP queries
705 1.8 christos */
706 1.8 christos void
707 1.8 christos timeout_queries(void)
708 1.8 christos {
709 1.8 christos struct timeval start_cb;
710 1.8 christos u_int idx;
711 1.8 christos sent_pkt * head;
712 1.8 christos sent_pkt * spkt;
713 1.8 christos sent_pkt * spkt_next;
714 1.8 christos long age;
715 1.8 christos int didsomething = 0;
716 1.8 christos
717 1.8 christos TRACE(3, ("timeout_queries: called to check %u items\n",
718 1.8 christos (unsigned)COUNTOF(fam_listheads)));
719 1.8 christos
720 1.8 christos gettimeofday_cached(base, &start_cb);
721 1.8 christos for (idx = 0; idx < COUNTOF(fam_listheads); idx++) {
722 1.8 christos head = fam_listheads[idx];
723 1.8 christos for (spkt = head; spkt != NULL; spkt = spkt_next) {
724 1.8 christos char xcst;
725 1.8 christos
726 1.8 christos didsomething = 1;
727 1.8 christos switch (spkt->dctx->flags & CTX_xCST) {
728 1.8 christos case CTX_BCST:
729 1.8 christos xcst = 'B';
730 1.8 christos break;
731 1.8 christos
732 1.8 christos case CTX_UCST:
733 1.8 christos xcst = 'U';
734 1.8 christos break;
735 1.8 christos
736 1.8 christos default:
737 1.8 christos INSIST(!"spkt->dctx->flags neither UCST nor BCST");
738 1.8 christos break;
739 1.1 kardel }
740 1.8 christos
741 1.8 christos spkt_next = spkt->link;
742 1.8 christos if (0 == spkt->stime || spkt->done)
743 1.8 christos continue;
744 1.8 christos age = start_cb.tv_sec - spkt->stime;
745 1.8 christos TRACE(3, ("%s %s %cCST age %ld\n",
746 1.8 christos stoa(&spkt->addr),
747 1.8 christos spkt->dctx->name, xcst, age));
748 1.8 christos if (age > response_timeout)
749 1.8 christos timeout_query(spkt);
750 1.8 christos }
751 1.8 christos }
752 1.8 christos // Do we care about didsomething?
753 1.8 christos TRACE(3, ("timeout_queries: didsomething is %d, age is %ld\n",
754 1.8 christos didsomething, (long) (start_cb.tv_sec - start_tv.tv_sec)));
755 1.8 christos if (start_cb.tv_sec - start_tv.tv_sec > response_timeout) {
756 1.8 christos TRACE(3, ("timeout_queries: bail!\n"));
757 1.8 christos event_base_loopexit(base, NULL);
758 1.8 christos shutting_down = TRUE;
759 1.8 christos }
760 1.8 christos }
761 1.8 christos
762 1.8 christos
763 1.8 christos void dec_pending_ntp(
764 1.8 christos const char * name,
765 1.8 christos sockaddr_u * server
766 1.8 christos )
767 1.8 christos {
768 1.8 christos if (n_pending_ntp > 0) {
769 1.8 christos --n_pending_ntp;
770 1.8 christos check_exit_conditions();
771 1.8 christos } else {
772 1.8 christos INSIST(0 == n_pending_ntp);
773 1.8 christos TRACE(1, ("n_pending_ntp was zero before decrement for %s\n",
774 1.8 christos hostnameaddr(name, server)));
775 1.1 kardel }
776 1.8 christos }
777 1.8 christos
778 1.8 christos
779 1.8 christos void timeout_query(
780 1.8 christos sent_pkt * spkt
781 1.8 christos )
782 1.8 christos {
783 1.8 christos sockaddr_u * server;
784 1.8 christos char xcst;
785 1.8 christos
786 1.8 christos
787 1.8 christos switch (spkt->dctx->flags & CTX_xCST) {
788 1.8 christos case CTX_BCST:
789 1.8 christos xcst = 'B';
790 1.8 christos break;
791 1.8 christos
792 1.8 christos case CTX_UCST:
793 1.8 christos xcst = 'U';
794 1.8 christos break;
795 1.8 christos
796 1.8 christos default:
797 1.8 christos INSIST(!"spkt->dctx->flags neither UCST nor BCST");
798 1.8 christos break;
799 1.8 christos }
800 1.8 christos spkt->done = TRUE;
801 1.8 christos server = &spkt->addr;
802 1.8 christos msyslog(LOG_INFO, "%s no %cCST response after %d seconds",
803 1.8 christos hostnameaddr(spkt->dctx->name, server), xcst,
804 1.8 christos response_timeout);
805 1.8 christos dec_pending_ntp(spkt->dctx->name, server);
806 1.8 christos return;
807 1.8 christos }
808 1.8 christos
809 1.8 christos
810 1.8 christos /*
811 1.8 christos ** check_kod
812 1.8 christos */
813 1.8 christos int
814 1.8 christos check_kod(
815 1.8 christos const struct addrinfo * ai
816 1.8 christos )
817 1.8 christos {
818 1.8 christos char *hostname;
819 1.8 christos struct kod_entry *reason;
820 1.8 christos
821 1.8 christos /* Is there a KoD on file for this address? */
822 1.8 christos hostname = addrinfo_to_str(ai);
823 1.8 christos TRACE(2, ("check_kod: checking <%s>\n", hostname));
824 1.8 christos if (search_entry(hostname, &reason)) {
825 1.8 christos printf("prior KoD for %s, skipping.\n",
826 1.8 christos hostname);
827 1.8 christos free(reason);
828 1.8 christos free(hostname);
829 1.1 kardel
830 1.3 kardel return 1;
831 1.8 christos }
832 1.8 christos free(hostname);
833 1.8 christos
834 1.1 kardel return 0;
835 1.1 kardel }
836 1.1 kardel
837 1.3 kardel
838 1.8 christos /*
839 1.8 christos ** Socket readable/timeout Callback:
840 1.8 christos ** Read in the packet
841 1.8 christos ** Unicast:
842 1.8 christos ** - close socket
843 1.8 christos ** - decrement n_pending_ntp
844 1.8 christos ** - If packet is good, set the time and "exit"
845 1.8 christos ** Broadcast:
846 1.8 christos ** - If packet is good, set the time and "exit"
847 1.8 christos */
848 1.8 christos void
849 1.8 christos sock_cb(
850 1.8 christos evutil_socket_t fd,
851 1.8 christos short what,
852 1.8 christos void *ptr
853 1.8 christos )
854 1.8 christos {
855 1.8 christos sockaddr_u sender;
856 1.8 christos sockaddr_u * psau;
857 1.8 christos sent_pkt ** p_pktlist;
858 1.8 christos sent_pkt * spkt;
859 1.8 christos int rpktl;
860 1.8 christos int rc;
861 1.8 christos
862 1.8 christos INSIST(sock4 == fd || sock6 == fd);
863 1.8 christos
864 1.8 christos TRACE(3, ("sock_cb: event on sock%s:%s%s%s%s\n",
865 1.8 christos (fd == sock6)
866 1.8 christos ? "6"
867 1.8 christos : "4",
868 1.8 christos (what & EV_TIMEOUT) ? " timeout" : "",
869 1.8 christos (what & EV_READ) ? " read" : "",
870 1.8 christos (what & EV_WRITE) ? " write" : "",
871 1.8 christos (what & EV_SIGNAL) ? " signal" : ""));
872 1.8 christos
873 1.8 christos if (!(EV_READ & what)) {
874 1.8 christos if (EV_TIMEOUT & what)
875 1.8 christos timeout_queries();
876 1.8 christos
877 1.8 christos return;
878 1.8 christos }
879 1.8 christos
880 1.8 christos /* Read in the packet */
881 1.8 christos rpktl = recvdata(fd, &sender, &rbuf, sizeof(rbuf));
882 1.8 christos if (rpktl < 0) {
883 1.8 christos msyslog(LOG_DEBUG, "recvfrom error %m");
884 1.8 christos return;
885 1.8 christos }
886 1.8 christos
887 1.8 christos if (sock6 == fd)
888 1.8 christos p_pktlist = &v6_pkts_list;
889 1.8 christos else
890 1.8 christos p_pktlist = &v4_pkts_list;
891 1.8 christos
892 1.8 christos for (spkt = *p_pktlist; spkt != NULL; spkt = spkt->link) {
893 1.8 christos psau = &spkt->addr;
894 1.8 christos if (SOCK_EQ(&sender, psau))
895 1.8 christos break;
896 1.8 christos }
897 1.8 christos if (NULL == spkt) {
898 1.8 christos msyslog(LOG_WARNING,
899 1.8 christos "Packet from unexpected source %s dropped",
900 1.8 christos sptoa(&sender));
901 1.8 christos return;
902 1.8 christos }
903 1.8 christos
904 1.8 christos TRACE(1, ("sock_cb: %s %s\n", spkt->dctx->name,
905 1.8 christos sptoa(&sender)));
906 1.8 christos
907 1.8 christos rpktl = process_pkt(&r_pkt, &sender, rpktl, MODE_SERVER,
908 1.8 christos &spkt->x_pkt, "sock_cb");
909 1.8 christos
910 1.8 christos TRACE(2, ("sock_cb: process_pkt returned %d\n", rpktl));
911 1.8 christos
912 1.8 christos /* If this is a Unicast packet, one down ... */
913 1.8 christos if (!spkt->done && (CTX_UCST & spkt->dctx->flags)) {
914 1.8 christos dec_pending_ntp(spkt->dctx->name, &spkt->addr);
915 1.8 christos spkt->done = TRUE;
916 1.8 christos }
917 1.8 christos
918 1.8 christos
919 1.8 christos /* If the packet is good, set the time and we're all done */
920 1.8 christos rc = handle_pkt(rpktl, &r_pkt, &spkt->addr, spkt->dctx->name);
921 1.8 christos if (0 != rc)
922 1.8 christos TRACE(1, ("sock_cb: handle_pkt() returned %d\n", rc));
923 1.8 christos check_exit_conditions();
924 1.8 christos }
925 1.8 christos
926 1.8 christos
927 1.8 christos /*
928 1.8 christos * check_exit_conditions()
929 1.8 christos *
930 1.8 christos * If sntp has a reply, ask the event loop to stop after this round of
931 1.8 christos * callbacks, unless --wait was used.
932 1.8 christos */
933 1.8 christos void
934 1.8 christos check_exit_conditions(void)
935 1.8 christos {
936 1.8 christos if ((0 == n_pending_ntp && 0 == n_pending_dns) ||
937 1.8 christos (time_derived && !HAVE_OPT(WAIT))) {
938 1.8 christos event_base_loopexit(base, NULL);
939 1.8 christos shutting_down = TRUE;
940 1.8 christos } else {
941 1.8 christos TRACE(2, ("%d NTP and %d name queries pending\n",
942 1.8 christos n_pending_ntp, n_pending_dns));
943 1.8 christos }
944 1.8 christos }
945 1.8 christos
946 1.8 christos
947 1.8 christos /*
948 1.8 christos * sntp_addremove_fd() is invoked by the intres blocking worker code
949 1.8 christos * to read from a pipe, or to stop same.
950 1.8 christos */
951 1.8 christos void sntp_addremove_fd(
952 1.8 christos int fd,
953 1.8 christos int is_pipe,
954 1.8 christos int remove_it
955 1.8 christos )
956 1.8 christos {
957 1.8 christos u_int idx;
958 1.8 christos blocking_child *c;
959 1.8 christos struct event * ev;
960 1.8 christos
961 1.8 christos #ifdef HAVE_SOCKETPAIR
962 1.8 christos if (is_pipe) {
963 1.8 christos /* sntp only asks for EV_FEATURE_FDS without HAVE_SOCKETPAIR */
964 1.8 christos msyslog(LOG_ERR, "fatal: pipes not supported on systems with socketpair()");
965 1.8 christos exit(1);
966 1.8 christos }
967 1.8 christos #endif
968 1.8 christos
969 1.8 christos c = NULL;
970 1.8 christos for (idx = 0; idx < blocking_children_alloc; idx++) {
971 1.8 christos c = blocking_children[idx];
972 1.8 christos if (NULL == c)
973 1.8 christos continue;
974 1.8 christos if (fd == c->resp_read_pipe)
975 1.8 christos break;
976 1.8 christos }
977 1.8 christos if (idx == blocking_children_alloc)
978 1.8 christos return;
979 1.8 christos
980 1.8 christos if (remove_it) {
981 1.8 christos ev = c->resp_read_ctx;
982 1.8 christos c->resp_read_ctx = NULL;
983 1.8 christos event_del(ev);
984 1.8 christos event_free(ev);
985 1.8 christos
986 1.8 christos return;
987 1.8 christos }
988 1.8 christos
989 1.20 christos make_socket_nonblocking(fd);
990 1.8 christos ev = event_new(base, fd, EV_READ | EV_PERSIST,
991 1.8 christos &worker_resp_cb, c);
992 1.8 christos if (NULL == ev) {
993 1.8 christos msyslog(LOG_ERR,
994 1.8 christos "sntp_addremove_fd: event_new(base, fd) failed!");
995 1.8 christos return;
996 1.8 christos }
997 1.8 christos c->resp_read_ctx = ev;
998 1.8 christos event_add(ev, NULL);
999 1.8 christos }
1000 1.8 christos
1001 1.8 christos
1002 1.8 christos /* called by forked intres child to close open descriptors */
1003 1.8 christos #ifdef WORK_FORK
1004 1.8 christos void
1005 1.8 christos kill_asyncio(
1006 1.8 christos int startfd
1007 1.8 christos )
1008 1.8 christos {
1009 1.8 christos if (INVALID_SOCKET != sock4) {
1010 1.8 christos closesocket(sock4);
1011 1.8 christos sock4 = INVALID_SOCKET;
1012 1.8 christos }
1013 1.8 christos if (INVALID_SOCKET != sock6) {
1014 1.8 christos closesocket(sock6);
1015 1.8 christos sock6 = INVALID_SOCKET;
1016 1.8 christos }
1017 1.8 christos if (INVALID_SOCKET != bsock4) {
1018 1.19 christos closesocket(bsock4);
1019 1.19 christos bsock4 = INVALID_SOCKET;
1020 1.8 christos }
1021 1.8 christos if (INVALID_SOCKET != bsock6) {
1022 1.19 christos closesocket(bsock6);
1023 1.19 christos bsock6 = INVALID_SOCKET;
1024 1.8 christos }
1025 1.8 christos }
1026 1.8 christos #endif
1027 1.8 christos
1028 1.8 christos
1029 1.8 christos /*
1030 1.8 christos * worker_resp_cb() is invoked when resp_read_pipe is readable.
1031 1.8 christos */
1032 1.8 christos void
1033 1.8 christos worker_resp_cb(
1034 1.8 christos evutil_socket_t fd,
1035 1.8 christos short what,
1036 1.8 christos void * ctx /* blocking_child * */
1037 1.8 christos )
1038 1.8 christos {
1039 1.8 christos blocking_child * c;
1040 1.8 christos
1041 1.19 christos REQUIRE(EV_READ & what);
1042 1.8 christos c = ctx;
1043 1.19 christos INSIST(fd == c->resp_read_pipe);
1044 1.8 christos process_blocking_resp(c);
1045 1.8 christos }
1046 1.8 christos
1047 1.8 christos
1048 1.8 christos /*
1049 1.8 christos * intres_timeout_req(s) is invoked in the parent to schedule an idle
1050 1.8 christos * timeout to fire in s seconds, if not reset earlier by a call to
1051 1.8 christos * intres_timeout_req(0), which clears any pending timeout. When the
1052 1.8 christos * timeout expires, worker_idle_timer_fired() is invoked (again, in the
1053 1.8 christos * parent).
1054 1.8 christos *
1055 1.8 christos * sntp and ntpd each provide implementations adapted to their timers.
1056 1.8 christos */
1057 1.8 christos void
1058 1.8 christos intres_timeout_req(
1059 1.8 christos u_int seconds /* 0 cancels */
1060 1.8 christos )
1061 1.8 christos {
1062 1.8 christos struct timeval tv_to;
1063 1.8 christos
1064 1.8 christos if (NULL == ev_worker_timeout) {
1065 1.8 christos ev_worker_timeout = event_new(base, -1,
1066 1.8 christos EV_TIMEOUT | EV_PERSIST,
1067 1.8 christos &worker_timeout, NULL);
1068 1.19 christos INSIST(NULL != ev_worker_timeout);
1069 1.8 christos } else {
1070 1.8 christos event_del(ev_worker_timeout);
1071 1.8 christos }
1072 1.8 christos if (0 == seconds)
1073 1.8 christos return;
1074 1.8 christos tv_to.tv_sec = seconds;
1075 1.8 christos tv_to.tv_usec = 0;
1076 1.8 christos event_add(ev_worker_timeout, &tv_to);
1077 1.8 christos }
1078 1.8 christos
1079 1.8 christos
1080 1.8 christos void
1081 1.8 christos worker_timeout(
1082 1.8 christos evutil_socket_t fd,
1083 1.8 christos short what,
1084 1.8 christos void * ctx
1085 1.8 christos )
1086 1.8 christos {
1087 1.8 christos UNUSED_ARG(fd);
1088 1.8 christos UNUSED_ARG(ctx);
1089 1.8 christos
1090 1.19 christos REQUIRE(EV_TIMEOUT & what);
1091 1.8 christos worker_idle_timer_fired();
1092 1.8 christos }
1093 1.8 christos
1094 1.8 christos
1095 1.8 christos void
1096 1.8 christos sntp_libevent_log_cb(
1097 1.8 christos int severity,
1098 1.8 christos const char * msg
1099 1.8 christos )
1100 1.8 christos {
1101 1.8 christos int level;
1102 1.8 christos
1103 1.8 christos switch (severity) {
1104 1.8 christos
1105 1.8 christos default:
1106 1.8 christos case _EVENT_LOG_DEBUG:
1107 1.8 christos level = LOG_DEBUG;
1108 1.8 christos break;
1109 1.8 christos
1110 1.8 christos case _EVENT_LOG_MSG:
1111 1.8 christos level = LOG_NOTICE;
1112 1.8 christos break;
1113 1.8 christos
1114 1.8 christos case _EVENT_LOG_WARN:
1115 1.8 christos level = LOG_WARNING;
1116 1.8 christos break;
1117 1.8 christos
1118 1.8 christos case _EVENT_LOG_ERR:
1119 1.8 christos level = LOG_ERR;
1120 1.8 christos break;
1121 1.8 christos }
1122 1.8 christos
1123 1.8 christos msyslog(level, "%s", msg);
1124 1.8 christos }
1125 1.8 christos
1126 1.3 kardel
1127 1.3 kardel int
1128 1.3 kardel generate_pkt (
1129 1.3 kardel struct pkt *x_pkt,
1130 1.3 kardel const struct timeval *tv_xmt,
1131 1.3 kardel int key_id,
1132 1.3 kardel struct key *pkt_key
1133 1.3 kardel )
1134 1.3 kardel {
1135 1.8 christos l_fp xmt_fp;
1136 1.8 christos int pkt_len;
1137 1.8 christos int mac_size;
1138 1.8 christos
1139 1.8 christos pkt_len = LEN_PKT_NOMAC;
1140 1.8 christos ZERO(*x_pkt);
1141 1.8 christos TVTOTS(tv_xmt, &xmt_fp);
1142 1.8 christos HTONL_FP(&xmt_fp, &x_pkt->xmt);
1143 1.3 kardel x_pkt->stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
1144 1.3 kardel x_pkt->ppoll = 8;
1145 1.3 kardel /* FIXME! Modus broadcast + adr. check -> bdr. pkt */
1146 1.8 christos set_li_vn_mode(x_pkt, LEAP_NOTINSYNC, ntpver, 3);
1147 1.16 christos if (debug > 0) {
1148 1.16 christos printf("generate_pkt: key_id %d, key pointer %p\n", key_id, pkt_key);
1149 1.16 christos }
1150 1.3 kardel if (pkt_key != NULL) {
1151 1.3 kardel x_pkt->exten[0] = htonl(key_id);
1152 1.19 christos mac_size = make_mac(x_pkt, pkt_len, pkt_key,
1153 1.19 christos (char *)&x_pkt->exten[1], MAX_MDG_LEN);
1154 1.8 christos if (mac_size > 0)
1155 1.16 christos pkt_len += mac_size + KEY_MAC_LEN;
1156 1.16 christos #ifdef DEBUG
1157 1.16 christos if (debug > 0) {
1158 1.16 christos printf("generate_pkt: mac_size is %d\n", mac_size);
1159 1.16 christos }
1160 1.16 christos #endif
1161 1.16 christos
1162 1.3 kardel }
1163 1.3 kardel return pkt_len;
1164 1.3 kardel }
1165 1.3 kardel
1166 1.8 christos
1167 1.1 kardel int
1168 1.8 christos handle_pkt(
1169 1.8 christos int rpktl,
1170 1.8 christos struct pkt * rpkt,
1171 1.8 christos sockaddr_u * host,
1172 1.8 christos const char * hostname
1173 1.3 kardel )
1174 1.1 kardel {
1175 1.8 christos char disptxt[32];
1176 1.8 christos const char * addrtxt;
1177 1.8 christos struct timeval tv_dst;
1178 1.8 christos int cnt;
1179 1.8 christos int sw_case;
1180 1.8 christos int digits;
1181 1.8 christos int stratum;
1182 1.8 christos char * ref;
1183 1.8 christos char * ts_str;
1184 1.12 christos const char * leaptxt;
1185 1.8 christos double offset;
1186 1.8 christos double precision;
1187 1.8 christos double synch_distance;
1188 1.8 christos char * p_SNTP_PRETEND_TIME;
1189 1.8 christos time_t pretend_time;
1190 1.8 christos #if SIZEOF_TIME_T == 8
1191 1.8 christos long long ll;
1192 1.8 christos #else
1193 1.8 christos long l;
1194 1.8 christos #endif
1195 1.8 christos
1196 1.8 christos ts_str = NULL;
1197 1.1 kardel
1198 1.8 christos if (rpktl > 0)
1199 1.3 kardel sw_case = 1;
1200 1.3 kardel else
1201 1.3 kardel sw_case = rpktl;
1202 1.1 kardel
1203 1.8 christos switch (sw_case) {
1204 1.8 christos
1205 1.3 kardel case SERVER_UNUSEABLE:
1206 1.3 kardel return -1;
1207 1.3 kardel break;
1208 1.1 kardel
1209 1.3 kardel case PACKET_UNUSEABLE:
1210 1.3 kardel break;
1211 1.8 christos
1212 1.3 kardel case SERVER_AUTH_FAIL:
1213 1.3 kardel break;
1214 1.3 kardel
1215 1.3 kardel case KOD_DEMOBILIZE:
1216 1.3 kardel /* Received a DENY or RESTR KOD packet */
1217 1.8 christos addrtxt = stoa(host);
1218 1.3 kardel ref = (char *)&rpkt->refid;
1219 1.8 christos add_entry(addrtxt, ref);
1220 1.8 christos msyslog(LOG_WARNING, "KOD code %c%c%c%c from %s %s",
1221 1.8 christos ref[0], ref[1], ref[2], ref[3], addrtxt, hostname);
1222 1.3 kardel break;
1223 1.3 kardel
1224 1.3 kardel case KOD_RATE:
1225 1.8 christos /*
1226 1.8 christos ** Hmm...
1227 1.8 christos ** We should probably call add_entry() with an
1228 1.8 christos ** expiration timestamp of several seconds in the future,
1229 1.8 christos ** and back-off even more if we get more RATE responses.
1230 1.8 christos */
1231 1.3 kardel break;
1232 1.3 kardel
1233 1.3 kardel case 1:
1234 1.8 christos TRACE(3, ("handle_pkt: %d bytes from %s %s\n",
1235 1.8 christos rpktl, stoa(host), hostname));
1236 1.1 kardel
1237 1.8 christos gettimeofday_cached(base, &tv_dst);
1238 1.1 kardel
1239 1.3 kardel p_SNTP_PRETEND_TIME = getenv("SNTP_PRETEND_TIME");
1240 1.3 kardel if (p_SNTP_PRETEND_TIME) {
1241 1.8 christos pretend_time = 0;
1242 1.8 christos #if SIZEOF_TIME_T == 4
1243 1.8 christos if (1 == sscanf(p_SNTP_PRETEND_TIME, "%ld", &l))
1244 1.8 christos pretend_time = (time_t)l;
1245 1.8 christos #elif SIZEOF_TIME_T == 8
1246 1.8 christos if (1 == sscanf(p_SNTP_PRETEND_TIME, "%lld", &ll))
1247 1.8 christos pretend_time = (time_t)ll;
1248 1.8 christos #else
1249 1.8 christos # include "GRONK: unexpected value for SIZEOF_TIME_T"
1250 1.8 christos #endif
1251 1.8 christos if (0 != pretend_time)
1252 1.8 christos tv_dst.tv_sec = pretend_time;
1253 1.3 kardel }
1254 1.1 kardel
1255 1.3 kardel offset_calculation(rpkt, rpktl, &tv_dst, &offset,
1256 1.8 christos &precision, &synch_distance);
1257 1.8 christos time_derived = TRUE;
1258 1.1 kardel
1259 1.3 kardel for (digits = 0; (precision *= 10.) < 1.; ++digits)
1260 1.3 kardel /* empty */ ;
1261 1.3 kardel if (digits > 6)
1262 1.3 kardel digits = 6;
1263 1.3 kardel
1264 1.3 kardel ts_str = tv_to_str(&tv_dst);
1265 1.8 christos stratum = rpkt->stratum;
1266 1.8 christos if (0 == stratum)
1267 1.8 christos stratum = 16;
1268 1.8 christos
1269 1.8 christos if (synch_distance > 0.) {
1270 1.8 christos cnt = snprintf(disptxt, sizeof(disptxt),
1271 1.8 christos " +/- %f", synch_distance);
1272 1.8 christos if ((size_t)cnt >= sizeof(disptxt))
1273 1.8 christos snprintf(disptxt, sizeof(disptxt),
1274 1.8 christos "ERROR %d >= %d", cnt,
1275 1.8 christos (int)sizeof(disptxt));
1276 1.8 christos } else {
1277 1.8 christos disptxt[0] = '\0';
1278 1.8 christos }
1279 1.8 christos
1280 1.12 christos switch (PKT_LEAP(rpkt->li_vn_mode)) {
1281 1.12 christos case LEAP_NOWARNING:
1282 1.12 christos leaptxt = "no-leap";
1283 1.12 christos break;
1284 1.12 christos case LEAP_ADDSECOND:
1285 1.12 christos leaptxt = "add-leap";
1286 1.12 christos break;
1287 1.12 christos case LEAP_DELSECOND:
1288 1.12 christos leaptxt = "del-leap";
1289 1.12 christos break;
1290 1.12 christos case LEAP_NOTINSYNC:
1291 1.12 christos leaptxt = "unsync";
1292 1.12 christos break;
1293 1.12 christos default:
1294 1.12 christos leaptxt = "LEAP-ERROR";
1295 1.12 christos break;
1296 1.12 christos }
1297 1.12 christos
1298 1.12 christos msyslog(LOG_INFO, "%s %+.*f%s %s s%d %s%s", ts_str,
1299 1.8 christos digits, offset, disptxt,
1300 1.8 christos hostnameaddr(hostname, host), stratum,
1301 1.12 christos leaptxt,
1302 1.8 christos (time_adjusted)
1303 1.8 christos ? " [excess]"
1304 1.8 christos : "");
1305 1.3 kardel free(ts_str);
1306 1.1 kardel
1307 1.3 kardel if (p_SNTP_PRETEND_TIME)
1308 1.3 kardel return 0;
1309 1.3 kardel
1310 1.8 christos if (!time_adjusted &&
1311 1.8 christos (ENABLED_OPT(STEP) || ENABLED_OPT(SLEW)))
1312 1.8 christos return set_time(offset);
1313 1.3 kardel
1314 1.8 christos return EX_OK;
1315 1.3 kardel }
1316 1.3 kardel
1317 1.3 kardel return 1;
1318 1.3 kardel }
1319 1.3 kardel
1320 1.8 christos
1321 1.3 kardel void
1322 1.8 christos offset_calculation(
1323 1.3 kardel struct pkt *rpkt,
1324 1.3 kardel int rpktl,
1325 1.3 kardel struct timeval *tv_dst,
1326 1.3 kardel double *offset,
1327 1.3 kardel double *precision,
1328 1.8 christos double *synch_distance
1329 1.3 kardel )
1330 1.3 kardel {
1331 1.3 kardel l_fp p_rec, p_xmt, p_ref, p_org, tmp, dst;
1332 1.3 kardel u_fp p_rdly, p_rdsp;
1333 1.3 kardel double t21, t34, delta;
1334 1.3 kardel
1335 1.3 kardel /* Convert timestamps from network to host byte order */
1336 1.3 kardel p_rdly = NTOHS_FP(rpkt->rootdelay);
1337 1.3 kardel p_rdsp = NTOHS_FP(rpkt->rootdisp);
1338 1.3 kardel NTOHL_FP(&rpkt->reftime, &p_ref);
1339 1.3 kardel NTOHL_FP(&rpkt->org, &p_org);
1340 1.3 kardel NTOHL_FP(&rpkt->rec, &p_rec);
1341 1.3 kardel NTOHL_FP(&rpkt->xmt, &p_xmt);
1342 1.1 kardel
1343 1.3 kardel *precision = LOGTOD(rpkt->precision);
1344 1.1 kardel
1345 1.8 christos TRACE(3, ("offset_calculation: LOGTOD(rpkt->precision): %f\n", *precision));
1346 1.1 kardel
1347 1.3 kardel /* Compute offset etc. */
1348 1.3 kardel tmp = p_rec;
1349 1.3 kardel L_SUB(&tmp, &p_org);
1350 1.3 kardel LFPTOD(&tmp, t21);
1351 1.3 kardel TVTOTS(tv_dst, &dst);
1352 1.3 kardel dst.l_ui += JAN_1970;
1353 1.3 kardel tmp = p_xmt;
1354 1.3 kardel L_SUB(&tmp, &dst);
1355 1.3 kardel LFPTOD(&tmp, t34);
1356 1.3 kardel *offset = (t21 + t34) / 2.;
1357 1.3 kardel delta = t21 - t34;
1358 1.1 kardel
1359 1.8 christos // synch_distance is:
1360 1.8 christos // (peer->delay + peer->rootdelay) / 2 + peer->disp
1361 1.8 christos // + peer->rootdisp + clock_phi * (current_time - peer->update)
1362 1.8 christos // + peer->jitter;
1363 1.8 christos //
1364 1.8 christos // and peer->delay = fabs(peer->offset - p_offset) * 2;
1365 1.8 christos // and peer->offset needs history, so we're left with
1366 1.8 christos // p_offset = (t21 + t34) / 2.;
1367 1.8 christos // peer->disp = 0; (we have no history to augment this)
1368 1.8 christos // clock_phi = 15e-6;
1369 1.8 christos // peer->jitter = LOGTOD(sys_precision); (we have no history to augment this)
1370 1.8 christos // and ntp_proto.c:set_sys_tick_precision() should get us sys_precision.
1371 1.8 christos //
1372 1.8 christos // so our answer seems to be:
1373 1.8 christos //
1374 1.8 christos // (fabs(t21 + t34) + peer->rootdelay) / 3.
1375 1.8 christos // + 0 (peer->disp)
1376 1.8 christos // + peer->rootdisp
1377 1.8 christos // + 15e-6 (clock_phi)
1378 1.8 christos // + LOGTOD(sys_precision)
1379 1.8 christos
1380 1.8 christos INSIST( FPTOD(p_rdly) >= 0. );
1381 1.8 christos #if 1
1382 1.8 christos *synch_distance = (fabs(t21 + t34) + FPTOD(p_rdly)) / 3.
1383 1.8 christos + 0.
1384 1.8 christos + FPTOD(p_rdsp)
1385 1.8 christos + 15e-6
1386 1.8 christos + 0. /* LOGTOD(sys_precision) when we can get it */
1387 1.8 christos ;
1388 1.8 christos INSIST( *synch_distance >= 0. );
1389 1.8 christos #else
1390 1.8 christos *synch_distance = (FPTOD(p_rdly) + FPTOD(p_rdsp))/2.0;
1391 1.8 christos #endif
1392 1.1 kardel
1393 1.3 kardel #ifdef DEBUG
1394 1.8 christos if (debug > 3) {
1395 1.8 christos printf("sntp rootdelay: %f\n", FPTOD(p_rdly));
1396 1.8 christos printf("sntp rootdisp: %f\n", FPTOD(p_rdsp));
1397 1.8 christos printf("sntp syncdist: %f\n", *synch_distance);
1398 1.8 christos
1399 1.8 christos pkt_output(rpkt, rpktl, stdout);
1400 1.8 christos
1401 1.8 christos printf("sntp offset_calculation: rpkt->reftime:\n");
1402 1.8 christos l_fp_output(&p_ref, stdout);
1403 1.8 christos printf("sntp offset_calculation: rpkt->org:\n");
1404 1.8 christos l_fp_output(&p_org, stdout);
1405 1.8 christos printf("sntp offset_calculation: rpkt->rec:\n");
1406 1.8 christos l_fp_output(&p_rec, stdout);
1407 1.8 christos printf("sntp offset_calculation: rpkt->xmt:\n");
1408 1.8 christos l_fp_output(&p_xmt, stdout);
1409 1.8 christos }
1410 1.3 kardel #endif
1411 1.1 kardel
1412 1.8 christos TRACE(3, ("sntp offset_calculation:\trec - org t21: %.6f\n"
1413 1.8 christos "\txmt - dst t34: %.6f\tdelta: %.6f\toffset: %.6f\n",
1414 1.8 christos t21, t34, delta, *offset));
1415 1.3 kardel
1416 1.8 christos return;
1417 1.8 christos }
1418 1.1 kardel
1419 1.1 kardel
1420 1.1 kardel
1421 1.1 kardel /* Compute the 8 bits for li_vn_mode */
1422 1.1 kardel void
1423 1.1 kardel set_li_vn_mode (
1424 1.3 kardel struct pkt *spkt,
1425 1.3 kardel char leap,
1426 1.3 kardel char version,
1427 1.3 kardel char mode
1428 1.8 christos )
1429 1.1 kardel {
1430 1.3 kardel if (leap > 3) {
1431 1.8 christos msyslog(LOG_DEBUG, "set_li_vn_mode: leap > 3, using max. 3");
1432 1.1 kardel leap = 3;
1433 1.1 kardel }
1434 1.1 kardel
1435 1.9 christos if ((unsigned char)version > 7) {
1436 1.8 christos msyslog(LOG_DEBUG, "set_li_vn_mode: version < 0 or > 7, using 4");
1437 1.8 christos version = 4;
1438 1.8 christos }
1439 1.8 christos
1440 1.3 kardel if (mode > 7) {
1441 1.3 kardel msyslog(LOG_DEBUG, "set_li_vn_mode: mode > 7, using client mode 3");
1442 1.1 kardel mode = 3;
1443 1.1 kardel }
1444 1.1 kardel
1445 1.1 kardel spkt->li_vn_mode = leap << 6;
1446 1.1 kardel spkt->li_vn_mode |= version << 3;
1447 1.1 kardel spkt->li_vn_mode |= mode;
1448 1.1 kardel }
1449 1.1 kardel
1450 1.8 christos
1451 1.8 christos /*
1452 1.8 christos ** set_time applies 'offset' to the local clock.
1453 1.8 christos */
1454 1.1 kardel int
1455 1.3 kardel set_time(
1456 1.3 kardel double offset
1457 1.3 kardel )
1458 1.1 kardel {
1459 1.8 christos int rc;
1460 1.8 christos
1461 1.8 christos if (time_adjusted)
1462 1.8 christos return EX_OK;
1463 1.1 kardel
1464 1.8 christos /*
1465 1.8 christos ** If we can step but we cannot slew, then step.
1466 1.8 christos ** If we can step or slew and and |offset| > steplimit, then step.
1467 1.8 christos */
1468 1.8 christos if (ENABLED_OPT(STEP) &&
1469 1.8 christos ( !ENABLED_OPT(SLEW)
1470 1.8 christos || (ENABLED_OPT(SLEW) && (fabs(offset) > steplimit))
1471 1.8 christos )) {
1472 1.8 christos rc = step_systime(offset);
1473 1.8 christos
1474 1.8 christos /* If there was a problem, can we rely on errno? */
1475 1.8 christos if (1 == rc)
1476 1.8 christos time_adjusted = TRUE;
1477 1.8 christos return (time_adjusted)
1478 1.8 christos ? EX_OK
1479 1.8 christos : 1;
1480 1.8 christos /*
1481 1.8 christos ** In case of error, what should we use?
1482 1.8 christos ** EX_UNAVAILABLE?
1483 1.8 christos ** EX_OSERR?
1484 1.8 christos ** EX_NOPERM?
1485 1.8 christos */
1486 1.8 christos }
1487 1.8 christos
1488 1.8 christos if (ENABLED_OPT(SLEW)) {
1489 1.8 christos rc = adj_systime(offset);
1490 1.1 kardel
1491 1.8 christos /* If there was a problem, can we rely on errno? */
1492 1.8 christos if (1 == rc)
1493 1.8 christos time_adjusted = TRUE;
1494 1.8 christos return (time_adjusted)
1495 1.8 christos ? EX_OK
1496 1.8 christos : 1;
1497 1.8 christos /*
1498 1.8 christos ** In case of error, what should we use?
1499 1.8 christos ** EX_UNAVAILABLE?
1500 1.8 christos ** EX_OSERR?
1501 1.8 christos ** EX_NOPERM?
1502 1.8 christos */
1503 1.8 christos }
1504 1.8 christos
1505 1.8 christos return EX_SOFTWARE;
1506 1.8 christos }
1507 1.8 christos
1508 1.8 christos
1509 1.8 christos int
1510 1.8 christos libevent_version_ok(void)
1511 1.8 christos {
1512 1.8 christos ev_uint32_t v_compile_maj;
1513 1.8 christos ev_uint32_t v_run_maj;
1514 1.8 christos
1515 1.8 christos v_compile_maj = LIBEVENT_VERSION_NUMBER & 0xffff0000;
1516 1.8 christos v_run_maj = event_get_version_number() & 0xffff0000;
1517 1.8 christos if (v_compile_maj != v_run_maj) {
1518 1.8 christos fprintf(stderr,
1519 1.8 christos "Incompatible libevent versions: have %s, built with %s\n",
1520 1.8 christos event_get_version(),
1521 1.8 christos LIBEVENT_VERSION);
1522 1.3 kardel return 0;
1523 1.1 kardel }
1524 1.8 christos return 1;
1525 1.8 christos }
1526 1.3 kardel
1527 1.8 christos /*
1528 1.8 christos * gettimeofday_cached()
1529 1.8 christos *
1530 1.8 christos * Clones the event_base_gettimeofday_cached() interface but ensures the
1531 1.8 christos * times are always on the gettimeofday() 1970 scale. Older libevent 2
1532 1.8 christos * sometimes used gettimeofday(), sometimes the since-system-start
1533 1.8 christos * clock_gettime(CLOCK_MONOTONIC), depending on the platform.
1534 1.8 christos *
1535 1.8 christos * It is not cleanly possible to tell which timescale older libevent is
1536 1.8 christos * using.
1537 1.8 christos *
1538 1.8 christos * The strategy involves 1 hour thresholds chosen to be far longer than
1539 1.8 christos * the duration of a round of libevent callbacks, which share a cached
1540 1.8 christos * start-of-round time. First compare the last cached time with the
1541 1.8 christos * current gettimeofday() time. If they are within one hour, libevent
1542 1.8 christos * is using the proper timescale so leave the offset 0. Otherwise,
1543 1.8 christos * compare libevent's cached time and the current time on the monotonic
1544 1.8 christos * scale. If they are within an hour, libevent is using the monotonic
1545 1.8 christos * scale so calculate the offset to add to such times to bring them to
1546 1.8 christos * gettimeofday()'s scale.
1547 1.8 christos */
1548 1.8 christos int
1549 1.8 christos gettimeofday_cached(
1550 1.8 christos struct event_base * b,
1551 1.8 christos struct timeval * caller_tv
1552 1.8 christos )
1553 1.8 christos {
1554 1.8 christos #if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
1555 1.8 christos static struct event_base * cached_b;
1556 1.8 christos static struct timeval cached;
1557 1.8 christos static struct timeval adj_cached;
1558 1.8 christos static struct timeval offset;
1559 1.8 christos static int offset_ready;
1560 1.8 christos struct timeval latest;
1561 1.8 christos struct timeval systemt;
1562 1.8 christos struct timespec ts;
1563 1.8 christos struct timeval mono;
1564 1.8 christos struct timeval diff;
1565 1.8 christos int cgt_rc;
1566 1.8 christos int gtod_rc;
1567 1.8 christos
1568 1.8 christos event_base_gettimeofday_cached(b, &latest);
1569 1.8 christos if (b == cached_b &&
1570 1.8 christos !memcmp(&latest, &cached, sizeof(latest))) {
1571 1.8 christos *caller_tv = adj_cached;
1572 1.8 christos return 0;
1573 1.8 christos }
1574 1.8 christos cached = latest;
1575 1.8 christos cached_b = b;
1576 1.8 christos if (!offset_ready) {
1577 1.8 christos cgt_rc = clock_gettime(CLOCK_MONOTONIC, &ts);
1578 1.8 christos gtod_rc = gettimeofday(&systemt, NULL);
1579 1.8 christos if (0 != gtod_rc) {
1580 1.8 christos msyslog(LOG_ERR,
1581 1.8 christos "%s: gettimeofday() error %m",
1582 1.8 christos progname);
1583 1.8 christos exit(1);
1584 1.8 christos }
1585 1.8 christos diff = sub_tval(systemt, latest);
1586 1.8 christos if (debug > 1)
1587 1.8 christos printf("system minus cached %+ld.%06ld\n",
1588 1.8 christos (long)diff.tv_sec, (long)diff.tv_usec);
1589 1.8 christos if (0 != cgt_rc || labs((long)diff.tv_sec) < 3600) {
1590 1.8 christos /*
1591 1.8 christos * Either use_monotonic == 0, or this libevent
1592 1.8 christos * has been repaired. Leave offset at zero.
1593 1.8 christos */
1594 1.8 christos } else {
1595 1.8 christos mono.tv_sec = ts.tv_sec;
1596 1.8 christos mono.tv_usec = ts.tv_nsec / 1000;
1597 1.8 christos diff = sub_tval(latest, mono);
1598 1.8 christos if (debug > 1)
1599 1.8 christos printf("cached minus monotonic %+ld.%06ld\n",
1600 1.8 christos (long)diff.tv_sec, (long)diff.tv_usec);
1601 1.8 christos if (labs((long)diff.tv_sec) < 3600) {
1602 1.8 christos /* older libevent2 using monotonic */
1603 1.8 christos offset = sub_tval(systemt, mono);
1604 1.8 christos TRACE(1, ("%s: Offsetting libevent CLOCK_MONOTONIC times by %+ld.%06ld\n",
1605 1.8 christos "gettimeofday_cached",
1606 1.8 christos (long)offset.tv_sec,
1607 1.8 christos (long)offset.tv_usec));
1608 1.8 christos }
1609 1.8 christos }
1610 1.8 christos offset_ready = TRUE;
1611 1.8 christos }
1612 1.8 christos adj_cached = add_tval(cached, offset);
1613 1.8 christos *caller_tv = adj_cached;
1614 1.3 kardel
1615 1.3 kardel return 0;
1616 1.8 christos #else
1617 1.8 christos return event_base_gettimeofday_cached(b, caller_tv);
1618 1.8 christos #endif
1619 1.1 kardel }
1620 1.8 christos
1621 1.17 christos /* Dummy function to satisfy libntp/work_fork.c */
1622 1.18 christos extern int set_user_group_ids(void);
1623 1.18 christos int set_user_group_ids(void)
1624 1.17 christos {
1625 1.17 christos return 1;
1626 1.17 christos }
1627