After another look at the current changes, switch directly to the in-tree
[dragonfly.git] / contrib / ntpd / ntp.c
1 /*      $OpenBSD: src/usr.sbin/ntpd/ntp.c,v 1.40 2004/10/27 14:19:12 dtucker Exp $ */
2
3 /*
4  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5  * Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
6  *
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.
10  *
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.
18  */
19
20 #include <sys/param.h>
21 #include <sys/time.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <paths.h>
25 #include <poll.h>
26 #include <pwd.h>
27 #include <signal.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include "ntpd.h"
33 #include "ntp.h"
34
35 #define PFD_PIPE_MAIN   0
36 #define PFD_MAX         1
37
38 volatile sig_atomic_t    ntp_quit = 0;
39 struct imsgbuf          *ibuf_main;
40 struct ntpd_conf        *conf;
41 u_int                    peer_cnt;
42
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 *);
48
49 void
50 ntp_sighdlr(int sig)
51 {
52         switch (sig) {
53         case SIGINT:
54         case SIGTERM:
55                 ntp_quit = 1;
56                 break;
57         }
58 }
59
60 pid_t
61 ntp_main(int pipe_prnt[2], struct ntpd_conf *nconf)
62 {
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;
66         pid_t                    pid;
67         struct pollfd           *pfd = NULL;
68         struct passwd           *pw;
69         struct servent          *se;
70         struct listen_addr      *la;
71         struct ntp_peer         *p;
72         struct ntp_peer         **idx2peer = NULL;
73         struct timespec          tp;
74         time_t                   nextaction;
75         void                    *newp;
76
77         switch (pid = fork()) {
78         case -1:
79                 fatal("cannot fork");
80         case 0:
81                 break;
82         default:
83                 return (pid);
84         }
85
86         if ((se = getservbyname("ntp", "udp")) == NULL)
87                 fatal("getservbyname");
88
89         if ((pw = getpwnam(NTPD_USER)) == NULL)
90                 fatal(NULL);
91
92         if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
93                 fatal(NULL);
94
95         if (chroot(pw->pw_dir) == -1)
96                 fatal("chroot");
97         if (chdir("/") == -1)
98                 fatal("chdir(\"/\")");
99
100         if (!nconf->debug) {
101                 dup2(nullfd, STDIN_FILENO);
102                 dup2(nullfd, STDOUT_FILENO);
103                 dup2(nullfd, STDERR_FILENO);
104         }
105         close(nullfd);
106
107         setproctitle("ntp engine");
108
109         conf = nconf;
110         setup_listeners(se, conf, &listener_cnt);
111
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");
116
117         endpwent();
118         endservent();
119
120         signal(SIGTERM, ntp_sighdlr);
121         signal(SIGINT, ntp_sighdlr);
122         signal(SIGPIPE, SIG_IGN);
123         signal(SIGHUP, SIG_IGN);
124
125         close(pipe_prnt[0]);
126         if ((ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
127                 fatal(NULL);
128         imsg_init(ibuf_main, pipe_prnt[1]);
129
130         TAILQ_FOREACH(p, &conf->ntp_peers, entry)
131                 client_peer_init(p);
132
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;
139         
140
141         log_info("ntp engine ready");
142
143         peer_cnt = 0;
144         TAILQ_FOREACH(p, &conf->ntp_peers, entry)
145                 peer_cnt++;
146
147         while (ntp_quit == 0) {
148                 if (peer_cnt > idx2peer_elms) {
149                         if ((newp = realloc(idx2peer, sizeof(void *) *
150                             peer_cnt)) == NULL) {
151                                 /* panic for now */
152                                 log_warn("could not resize idx2peer from %u -> "
153                                     "%u entries", idx2peer_elms, peer_cnt);
154                                 fatalx("exiting");
155                         }
156                         idx2peer = newp;
157                         idx2peer_elms = peer_cnt;
158                 }
159
160                 new_cnt = PFD_MAX + peer_cnt + listener_cnt;
161                 if (new_cnt > pfd_elms) {
162                         if ((newp = realloc(pfd, sizeof(struct pollfd) *
163                             new_cnt)) == NULL) {
164                                 /* panic for now */
165                                 log_warn("could not resize pfd from %u -> "
166                                     "%u entries", pfd_elms, new_cnt);
167                                 fatalx("exiting");
168                         }
169                         pfd = newp;
170                         pfd_elms = new_cnt;
171                 }
172
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;
178
179                 i = 1;
180                 TAILQ_FOREACH(la, &conf->listen_addrs, entry) {
181                         pfd[i].fd = la->fd;
182                         pfd[i].events = POLLIN;
183                         i++;
184                 }
185
186                 idx_peers = i;
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))
191                                 client_query(p);
192
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",
197                                     log_sockaddr(
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",
202                                             log_sockaddr(
203                                             (struct sockaddr *)&p->addr->ss));
204                                 client_nextaddr(p);
205                                 client_query(p);
206                         }
207
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;
212                                 i++;
213                         }
214                 }
215
216                 if (ibuf_main->w.queued > 0)
217                         pfd[PFD_PIPE_MAIN].events |= POLLOUT;
218
219                 timeout = nextaction - time(NULL);
220                 if (timeout < 0)
221                         timeout = 0;
222
223                 if ((nfds = poll(pfd, i, timeout * 1000)) == -1)
224                         if (errno != EINTR) {
225                                 log_warn("poll error");
226                                 ntp_quit = 1;
227                         }
228
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)");
232                                 ntp_quit = 1;
233                         }
234
235                 if (nfds > 0 && pfd[PFD_PIPE_MAIN].revents & POLLIN) {
236                         nfds--;
237                         if (ntp_dispatch_imsg() == -1)
238                                 ntp_quit = 1;
239                 }
240
241                 for (j = 1; nfds > 0 && j < idx_peers; j++)
242                         if (pfd[j].revents & POLLIN) {
243                                 nfds--;
244                                 if (server_dispatch(pfd[j].fd, conf) == -1)
245                                         ntp_quit = 1;
246                         }
247
248                 for (; nfds > 0 && j < i; j++)
249                         if (pfd[j].revents & POLLIN) {
250                                 nfds--;
251                                 if (client_dispatch(idx2peer[j - idx_peers],
252                                     conf->settime) == -1)
253                                         ntp_quit = 1;
254                         }
255         }
256
257         msgbuf_write(&ibuf_main->w);
258         msgbuf_clear(&ibuf_main->w);
259         free(ibuf_main);
260
261         log_info("ntp engine exiting");
262         _exit(0);
263 }
264
265 int
266 ntp_dispatch_imsg(void)
267 {
268         struct imsg              imsg;
269         int                      n;
270         struct ntp_peer         *peer, *npeer;
271         u_int16_t                dlen;
272         u_char                  *p;
273         struct ntp_addr         *h;
274
275         if ((n = imsg_read(ibuf_main)) == -1)
276                 return (-1);
277
278         if (n == 0) {   /* connection closed */
279                 log_warnx("ntp_dispatch_imsg in ntp engine: pipe closed");
280                 return (-1);
281         }
282
283         for (;;) {
284                 if ((n = imsg_get(ibuf_main, &imsg)) == -1)
285                         return (-1);
286
287                 if (n == 0)
288                         break;
289
290                 switch (imsg.hdr.type) {
291                 case IMSG_HOST_DNS:
292                         TAILQ_FOREACH(peer, &conf->ntp_peers, entry)
293                                 if (peer->id == imsg.hdr.peerid)
294                                         break;
295                         if (peer == NULL) {
296                                 log_warnx("IMSG_HOST_DNS with invalid peerID");
297                                 break;
298                         }
299                         if (peer->addr != NULL) {
300                                 log_warnx("IMSG_HOST_DNS but addr != NULL!");
301                                 break;
302                         }
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))) ==
307                                     NULL)
308                                         fatal(NULL);
309                                 memcpy(&h->ss, p, sizeof(h->ss));
310                                 p += sizeof(h->ss);
311                                 dlen -= sizeof(h->ss);
312                                 if (peer->addr_head.pool) {
313                                         npeer = new_peer();
314                                         h->next = NULL;
315                                         npeer->addr = h;
316                                         npeer->addr_head.a = h;
317                                         client_peer_init(npeer);
318                                         peer_add(npeer);
319                                 } else {
320                                         h->next = peer->addr;
321                                         peer->addr = h;
322                                         peer->addr_head.a = peer->addr;
323                                 }
324                         }
325                         if (dlen != 0)
326                                 fatal("IMSG_HOST_DNS: dlen != 0");
327                         if (peer->addr_head.pool)
328                                 peer_remove(peer);
329                         else
330                                 client_addr_init(peer);
331                         break;
332                 default:
333                         break;
334                 }
335                 imsg_free(&imsg);
336         }
337         return (0);
338 }
339
340 void
341 peer_add(struct ntp_peer *p)
342 {
343         TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
344         peer_cnt++;
345 }
346
347 void
348 peer_remove(struct ntp_peer *p)
349 {
350         TAILQ_REMOVE(&conf->ntp_peers, p, entry);
351         free(p);
352         peer_cnt--;
353 }
354
355 void
356 ntp_adjtime(void)
357 {
358         struct ntp_peer  *p;
359         int               offset_cnt = 0, i = 0;
360         struct ntp_peer **peers;
361         double            offset_median;
362
363         TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
364                 if (p->trustlevel < TRUSTLEVEL_BADPEER)
365                         continue;
366                 if (!p->update.good)
367                         return;
368                 offset_cnt++;
369         }
370
371         if ((peers = calloc(offset_cnt, sizeof(struct ntp_peer *))) == NULL)
372                 fatal("calloc ntp_adjtime");
373
374         TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
375                 if (p->trustlevel < TRUSTLEVEL_BADPEER)
376                         continue;
377                 peers[i++] = p;
378         }
379
380         qsort(peers, offset_cnt, sizeof(struct ntp_peer *), offset_compare);
381
382         if (offset_cnt > 0) {
383                 if (offset_cnt > 1 && offset_cnt % 2 == 0) {
384                         offset_median =
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);
393                 } else {
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;
399                 }
400
401                 imsg_compose(ibuf_main, IMSG_ADJTIME, 0, 0,
402                     &offset_median, sizeof(offset_median));
403
404                 conf->status.reftime = gettime();
405                 conf->status.leap = LI_NOWARNING;
406                 conf->status.stratum++; /* one more than selected peer */
407
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;
411         }
412
413         free(peers);
414
415         TAILQ_FOREACH(p, &conf->ntp_peers, entry)
416                 p->update.good = 0;
417 }
418
419 int
420 offset_compare(const void *aa, const void *bb)
421 {
422         const struct ntp_peer * const *a;
423         const struct ntp_peer * const *b;
424
425         a = aa;
426         b = bb;
427
428         if ((*a)->update.offset < (*b)->update.offset)
429                 return (-1);
430         else if ((*a)->update.offset > (*b)->update.offset)
431                 return (1);
432         else
433                 return (0);
434 }
435
436 void
437 ntp_settime(double offset)
438 {
439         imsg_compose(ibuf_main, IMSG_SETTIME, 0, 0, &offset, sizeof(offset));
440         conf->settime = 0;
441 }
442
443 void
444 ntp_host_dns(char *name, u_int32_t peerid)
445 {
446         u_int16_t       dlen;
447
448         dlen = strlen(name) + 1;
449         imsg_compose(ibuf_main, IMSG_HOST_DNS, peerid, 0, name, dlen);
450 }