1 /* $OpenBSD: src/usr.sbin/ntpd/ntp.c,v 1.40 2004/10/27 14:19:12 dtucker Exp $ */
4 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5 * Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/param.h>
35 #define PFD_PIPE_MAIN 0
38 volatile sig_atomic_t ntp_quit = 0;
39 struct imsgbuf *ibuf_main;
40 struct ntpd_conf *conf;
43 void ntp_sighdlr(int);
44 int ntp_dispatch_imsg(void);
45 void peer_add(struct ntp_peer *);
46 void peer_remove(struct ntp_peer *);
47 int offset_compare(const void *, const void *);
61 ntp_main(int pipe_prnt[2], struct ntpd_conf *nconf)
63 int a, b, nfds, i, j, idx_peers, timeout, nullfd;
64 u_int pfd_elms = 0, idx2peer_elms = 0;
65 u_int listener_cnt, new_cnt;
67 struct pollfd *pfd = NULL;
70 struct listen_addr *la;
72 struct ntp_peer **idx2peer = NULL;
77 switch (pid = fork()) {
86 if ((se = getservbyname("ntp", "udp")) == NULL)
87 fatal("getservbyname");
89 if ((pw = getpwnam(NTPD_USER)) == NULL)
92 if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
95 if (chroot(pw->pw_dir) == -1)
98 fatal("chdir(\"/\")");
101 dup2(nullfd, STDIN_FILENO);
102 dup2(nullfd, STDOUT_FILENO);
103 dup2(nullfd, STDERR_FILENO);
107 setproctitle("ntp engine");
110 setup_listeners(se, conf, &listener_cnt);
112 if (setgroups(1, &pw->pw_gid) ||
113 setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
114 seteuid(pw->pw_uid) || setuid(pw->pw_uid))
115 fatal("can't drop privileges");
120 signal(SIGTERM, ntp_sighdlr);
121 signal(SIGINT, ntp_sighdlr);
122 signal(SIGPIPE, SIG_IGN);
123 signal(SIGHUP, SIG_IGN);
126 if ((ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
128 imsg_init(ibuf_main, pipe_prnt[1]);
130 TAILQ_FOREACH(p, &conf->ntp_peers, entry)
133 bzero(&conf->status, sizeof(conf->status));
134 conf->status.leap = LI_ALARM;
135 clock_getres(CLOCK_REALTIME, &tp);
136 b = 1000000000 / tp.tv_nsec; /* convert to Hz */
137 for (a = 0; b > 1; a--, b >>= 1);
138 conf->status.precision = a;
141 log_info("ntp engine ready");
144 TAILQ_FOREACH(p, &conf->ntp_peers, entry)
147 while (ntp_quit == 0) {
148 if (peer_cnt > idx2peer_elms) {
149 if ((newp = realloc(idx2peer, sizeof(void *) *
150 peer_cnt)) == NULL) {
152 log_warn("could not resize idx2peer from %u -> "
153 "%u entries", idx2peer_elms, peer_cnt);
157 idx2peer_elms = peer_cnt;
160 new_cnt = PFD_MAX + peer_cnt + listener_cnt;
161 if (new_cnt > pfd_elms) {
162 if ((newp = realloc(pfd, sizeof(struct pollfd) *
165 log_warn("could not resize pfd from %u -> "
166 "%u entries", pfd_elms, new_cnt);
173 bzero(pfd, sizeof(struct pollfd) * pfd_elms);
174 bzero(idx2peer, sizeof(void *) * idx2peer_elms);
175 nextaction = time(NULL) + 3600;
176 pfd[PFD_PIPE_MAIN].fd = ibuf_main->fd;
177 pfd[PFD_PIPE_MAIN].events = POLLIN;
180 TAILQ_FOREACH(la, &conf->listen_addrs, entry) {
182 pfd[i].events = POLLIN;
187 TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
188 if (p->next > 0 && p->next < nextaction)
189 nextaction = p->next;
190 if (p->next > 0 && p->next <= time(NULL))
193 if (p->deadline > 0 && p->deadline < nextaction)
194 nextaction = p->deadline;
195 if (p->deadline > 0 && p->deadline <= time(NULL)) {
196 log_debug("no reply from %s received in time",
198 (struct sockaddr *)&p->addr->ss));
199 if (p->trustlevel >= TRUSTLEVEL_BADPEER &&
200 (p->trustlevel /= 2) < TRUSTLEVEL_BADPEER)
201 log_info("peer %s now invalid",
203 (struct sockaddr *)&p->addr->ss));
208 if (p->state == STATE_QUERY_SENT) {
209 pfd[i].fd = p->query->fd;
210 pfd[i].events = POLLIN;
211 idx2peer[i - idx_peers] = p;
216 if (ibuf_main->w.queued > 0)
217 pfd[PFD_PIPE_MAIN].events |= POLLOUT;
219 timeout = nextaction - time(NULL);
223 if ((nfds = poll(pfd, i, timeout * 1000)) == -1)
224 if (errno != EINTR) {
225 log_warn("poll error");
229 if (nfds > 0 && (pfd[PFD_PIPE_MAIN].revents & POLLOUT))
230 if (msgbuf_write(&ibuf_main->w) < 0) {
231 log_warn("pipe write error (to parent)");
235 if (nfds > 0 && pfd[PFD_PIPE_MAIN].revents & POLLIN) {
237 if (ntp_dispatch_imsg() == -1)
241 for (j = 1; nfds > 0 && j < idx_peers; j++)
242 if (pfd[j].revents & POLLIN) {
244 if (server_dispatch(pfd[j].fd, conf) == -1)
248 for (; nfds > 0 && j < i; j++)
249 if (pfd[j].revents & POLLIN) {
251 if (client_dispatch(idx2peer[j - idx_peers],
252 conf->settime) == -1)
257 msgbuf_write(&ibuf_main->w);
258 msgbuf_clear(&ibuf_main->w);
261 log_info("ntp engine exiting");
266 ntp_dispatch_imsg(void)
270 struct ntp_peer *peer, *npeer;
275 if ((n = imsg_read(ibuf_main)) == -1)
278 if (n == 0) { /* connection closed */
279 log_warnx("ntp_dispatch_imsg in ntp engine: pipe closed");
284 if ((n = imsg_get(ibuf_main, &imsg)) == -1)
290 switch (imsg.hdr.type) {
292 TAILQ_FOREACH(peer, &conf->ntp_peers, entry)
293 if (peer->id == imsg.hdr.peerid)
296 log_warnx("IMSG_HOST_DNS with invalid peerID");
299 if (peer->addr != NULL) {
300 log_warnx("IMSG_HOST_DNS but addr != NULL!");
303 dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
304 p = (u_char *)imsg.data;
305 while (dlen >= sizeof(struct sockaddr_storage)) {
306 if ((h = calloc(1, sizeof(struct ntp_addr))) ==
309 memcpy(&h->ss, p, sizeof(h->ss));
311 dlen -= sizeof(h->ss);
312 if (peer->addr_head.pool) {
316 npeer->addr_head.a = h;
317 client_peer_init(npeer);
320 h->next = peer->addr;
322 peer->addr_head.a = peer->addr;
326 fatal("IMSG_HOST_DNS: dlen != 0");
327 if (peer->addr_head.pool)
330 client_addr_init(peer);
341 peer_add(struct ntp_peer *p)
343 TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
348 peer_remove(struct ntp_peer *p)
350 TAILQ_REMOVE(&conf->ntp_peers, p, entry);
359 int offset_cnt = 0, i = 0;
360 struct ntp_peer **peers;
361 double offset_median;
363 TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
364 if (p->trustlevel < TRUSTLEVEL_BADPEER)
371 if ((peers = calloc(offset_cnt, sizeof(struct ntp_peer *))) == NULL)
372 fatal("calloc ntp_adjtime");
374 TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
375 if (p->trustlevel < TRUSTLEVEL_BADPEER)
380 qsort(peers, offset_cnt, sizeof(struct ntp_peer *), offset_compare);
382 if (offset_cnt > 0) {
383 if (offset_cnt > 1 && offset_cnt % 2 == 0) {
385 (peers[offset_cnt / 2 - 1]->update.offset +
386 peers[offset_cnt / 2]->update.offset) / 2;
387 conf->status.rootdelay =
388 (peers[offset_cnt / 2 - 1]->update.delay +
389 peers[offset_cnt / 2]->update.delay) / 2;
390 conf->status.stratum = MAX(
391 peers[offset_cnt / 2 - 1]->update.status.stratum,
392 peers[offset_cnt / 2]->update.status.stratum);
394 offset_median = peers[offset_cnt / 2]->update.offset;
395 conf->status.rootdelay =
396 peers[offset_cnt / 2]->update.delay;
397 conf->status.stratum =
398 peers[offset_cnt / 2]->update.status.stratum;
401 imsg_compose(ibuf_main, IMSG_ADJTIME, 0, 0,
402 &offset_median, sizeof(offset_median));
404 conf->status.reftime = gettime();
405 conf->status.leap = LI_NOWARNING;
406 conf->status.stratum++; /* one more than selected peer */
408 if (peers[offset_cnt / 2]->addr->ss.ss_family == AF_INET)
409 conf->status.refid = ((struct sockaddr_in *)
410 &peers[offset_cnt / 2]->addr->ss)->sin_addr.s_addr;
415 TAILQ_FOREACH(p, &conf->ntp_peers, entry)
420 offset_compare(const void *aa, const void *bb)
422 const struct ntp_peer * const *a;
423 const struct ntp_peer * const *b;
428 if ((*a)->update.offset < (*b)->update.offset)
430 else if ((*a)->update.offset > (*b)->update.offset)
437 ntp_settime(double offset)
439 imsg_compose(ibuf_main, IMSG_SETTIME, 0, 0, &offset, sizeof(offset));
444 ntp_host_dns(char *name, u_int32_t peerid)
448 dlen = strlen(name) + 1;
449 imsg_compose(ibuf_main, IMSG_HOST_DNS, peerid, 0, name, dlen);