Merge from vendor branch BINUTILS:
[dragonfly.git] / contrib / ntpd / ntp.c
1 /*      $OpenBSD: src/usr.sbin/ntpd/ntp.c,v 1.44 2004/12/13 12:39:15 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 <sys/stat.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <paths.h>
26 #include <poll.h>
27 #include <pwd.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include <unistd.h>
33
34 #include "ntpd.h"
35 #include "ntp.h"
36
37 #define PFD_PIPE_MAIN   0
38 #define PFD_MAX         1
39
40 volatile sig_atomic_t    ntp_quit = 0;
41 struct imsgbuf          *ibuf_main;
42 struct ntpd_conf        *conf;
43 u_int                    peer_cnt;
44
45 void    ntp_sighdlr(int);
46 int     ntp_dispatch_imsg(void);
47 void    peer_add(struct ntp_peer *);
48 void    peer_remove(struct ntp_peer *);
49 int     offset_compare(const void *, const void *);
50
51 void
52 ntp_sighdlr(int sig)
53 {
54         switch (sig) {
55         case SIGINT:
56         case SIGTERM:
57                 ntp_quit = 1;
58                 break;
59         }
60 }
61
62 pid_t
63 ntp_main(int pipe_prnt[2], struct ntpd_conf *nconf)
64 {
65         int                      a, b, nfds, i, j, idx_peers, timeout, nullfd;
66         u_int                    pfd_elms = 0, idx2peer_elms = 0;
67         u_int                    listener_cnt, new_cnt;
68         pid_t                    pid;
69         struct pollfd           *pfd = NULL;
70         struct passwd           *pw;
71         struct servent          *se;
72         struct listen_addr      *la;
73         struct ntp_peer         *p;
74         struct ntp_peer         **idx2peer = NULL;
75         struct timespec          tp;
76         struct stat              stb;
77         time_t                   nextaction;
78         void                    *newp;
79
80         switch (pid = fork()) {
81         case -1:
82                 fatal("cannot fork");
83         case 0:
84                 break;
85         default:
86                 return (pid);
87         }
88
89         if ((se = getservbyname("ntp", "udp")) == NULL)
90                 fatal("getservbyname");
91
92         if ((pw = getpwnam(NTPD_USER)) == NULL)
93                 fatal(NULL);
94
95         if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
96                 fatal(NULL);
97
98         if (stat(pw->pw_dir, &stb) == -1)
99                 fatal("stat");
100         if (stb.st_uid != 0 || (stb.st_mode & (S_IWGRP|S_IWOTH)) != 0)
101                 fatal("bad privsep dir permissions");
102         if (chroot(pw->pw_dir) == -1)
103                 fatal("chroot");
104         if (chdir("/") == -1)
105                 fatal("chdir(\"/\")");
106
107         if (!nconf->debug) {
108                 dup2(nullfd, STDIN_FILENO);
109                 dup2(nullfd, STDOUT_FILENO);
110                 dup2(nullfd, STDERR_FILENO);
111         }
112         close(nullfd);
113
114         setproctitle("ntp engine");
115
116         conf = nconf;
117         setup_listeners(se, conf, &listener_cnt);
118
119         if (setgroups(1, &pw->pw_gid) ||
120             setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
121             seteuid(pw->pw_uid) || setuid(pw->pw_uid))
122                 fatal("can't drop privileges");
123
124         endpwent();
125         endservent();
126
127         signal(SIGTERM, ntp_sighdlr);
128         signal(SIGINT, ntp_sighdlr);
129         signal(SIGPIPE, SIG_IGN);
130         signal(SIGHUP, SIG_IGN);
131
132         close(pipe_prnt[0]);
133         if ((ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
134                 fatal(NULL);
135         imsg_init(ibuf_main, pipe_prnt[1]);
136
137         TAILQ_FOREACH(p, &conf->ntp_peers, entry)
138                 client_peer_init(p);
139
140         bzero(&conf->status, sizeof(conf->status));
141         conf->status.leap = LI_ALARM;
142         clock_getres(CLOCK_REALTIME, &tp);
143         b = 1000000000 / tp.tv_nsec;    /* convert to Hz */
144         for (a = 0; b > 1; a--, b >>= 1);
145         conf->status.precision = a;
146         
147
148         log_info("ntp engine ready");
149
150         peer_cnt = 0;
151         TAILQ_FOREACH(p, &conf->ntp_peers, entry)
152                 peer_cnt++;
153
154         while (ntp_quit == 0) {
155                 if (peer_cnt > idx2peer_elms) {
156                         if ((newp = realloc(idx2peer, sizeof(void *) *
157                             peer_cnt)) == NULL) {
158                                 /* panic for now */
159                                 log_warn("could not resize idx2peer from %u -> "
160                                     "%u entries", idx2peer_elms, peer_cnt);
161                                 fatalx("exiting");
162                         }
163                         idx2peer = newp;
164                         idx2peer_elms = peer_cnt;
165                 }
166
167                 new_cnt = PFD_MAX + peer_cnt + listener_cnt;
168                 if (new_cnt > pfd_elms) {
169                         if ((newp = realloc(pfd, sizeof(struct pollfd) *
170                             new_cnt)) == NULL) {
171                                 /* panic for now */
172                                 log_warn("could not resize pfd from %u -> "
173                                     "%u entries", pfd_elms, new_cnt);
174                                 fatalx("exiting");
175                         }
176                         pfd = newp;
177                         pfd_elms = new_cnt;
178                 }
179
180                 bzero(pfd, sizeof(struct pollfd) * pfd_elms);
181                 bzero(idx2peer, sizeof(void *) * idx2peer_elms);
182                 nextaction = time(NULL) + 3600;
183                 pfd[PFD_PIPE_MAIN].fd = ibuf_main->fd;
184                 pfd[PFD_PIPE_MAIN].events = POLLIN;
185
186                 i = 1;
187                 TAILQ_FOREACH(la, &conf->listen_addrs, entry) {
188                         pfd[i].fd = la->fd;
189                         pfd[i].events = POLLIN;
190                         i++;
191                 }
192
193                 idx_peers = i;
194                 TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
195                         if (p->next > 0 && p->next < nextaction)
196                                 nextaction = p->next;
197                         if (p->next > 0 && p->next <= time(NULL))
198                                 client_query(p);
199
200                         if (p->deadline > 0 && p->deadline < nextaction)
201                                 nextaction = p->deadline;
202                         if (p->deadline > 0 && p->deadline <= time(NULL)) {
203                                 log_debug("no reply from %s received in time",
204                                     log_sockaddr(
205                                     (struct sockaddr *)&p->addr->ss));
206                                 if (p->trustlevel >= TRUSTLEVEL_BADPEER &&
207                                     (p->trustlevel /= 2) < TRUSTLEVEL_BADPEER)
208                                         log_info("peer %s now invalid",
209                                             log_sockaddr(
210                                             (struct sockaddr *)&p->addr->ss));
211                                 client_nextaddr(p);
212                                 client_query(p);
213                         }
214
215                         if (p->state == STATE_QUERY_SENT) {
216                                 pfd[i].fd = p->query->fd;
217                                 pfd[i].events = POLLIN;
218                                 idx2peer[i - idx_peers] = p;
219                                 i++;
220                         }
221                 }
222
223                 if (ibuf_main->w.queued > 0)
224                         pfd[PFD_PIPE_MAIN].events |= POLLOUT;
225
226                 timeout = nextaction - time(NULL);
227                 if (timeout < 0)
228                         timeout = 0;
229
230                 if ((nfds = poll(pfd, i, timeout * 1000)) == -1)
231                         if (errno != EINTR) {
232                                 log_warn("poll error");
233                                 ntp_quit = 1;
234                         }
235
236                 if (nfds > 0 && (pfd[PFD_PIPE_MAIN].revents & POLLOUT))
237                         if (msgbuf_write(&ibuf_main->w) < 0) {
238                                 log_warn("pipe write error (to parent)");
239                                 ntp_quit = 1;
240                         }
241
242                 if (nfds > 0 && pfd[PFD_PIPE_MAIN].revents & (POLLIN|POLLERR)) {
243                         nfds--;
244                         if (ntp_dispatch_imsg() == -1)
245                                 ntp_quit = 1;
246                 }
247
248                 for (j = 1; nfds > 0 && j < idx_peers; j++)
249                         if (pfd[j].revents & (POLLIN|POLLERR)) {
250                                 nfds--;
251                                 if (server_dispatch(pfd[j].fd, conf) == -1)
252                                         ntp_quit = 1;
253                         }
254
255                 for (; nfds > 0 && j < i; j++)
256                         if (pfd[j].revents & (POLLIN|POLLERR)) {
257                                 nfds--;
258                                 if (client_dispatch(idx2peer[j - idx_peers],
259                                     conf->settime) == -1)
260                                         ntp_quit = 1;
261                         }
262         }
263
264         msgbuf_write(&ibuf_main->w);
265         msgbuf_clear(&ibuf_main->w);
266         free(ibuf_main);
267
268         log_info("ntp engine exiting");
269         _exit(0);
270 }
271
272 int
273 ntp_dispatch_imsg(void)
274 {
275         struct imsg              imsg;
276         int                      n;
277         struct ntp_peer         *peer, *npeer;
278         u_int16_t                dlen;
279         u_char                  *p;
280         struct ntp_addr         *h;
281
282         if ((n = imsg_read(ibuf_main)) == -1)
283                 return (-1);
284
285         if (n == 0) {   /* connection closed */
286                 log_warnx("ntp_dispatch_imsg in ntp engine: pipe closed");
287                 return (-1);
288         }
289
290         for (;;) {
291                 if ((n = imsg_get(ibuf_main, &imsg)) == -1)
292                         return (-1);
293
294                 if (n == 0)
295                         break;
296
297                 switch (imsg.hdr.type) {
298                 case IMSG_HOST_DNS:
299                         TAILQ_FOREACH(peer, &conf->ntp_peers, entry)
300                                 if (peer->id == imsg.hdr.peerid)
301                                         break;
302                         if (peer == NULL) {
303                                 log_warnx("IMSG_HOST_DNS with invalid peerID");
304                                 break;
305                         }
306                         if (peer->addr != NULL) {
307                                 log_warnx("IMSG_HOST_DNS but addr != NULL!");
308                                 break;
309                         }
310                         dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
311                         p = (u_char *)imsg.data;
312                         while (dlen >= sizeof(struct sockaddr_storage)) {
313                                 if ((h = calloc(1, sizeof(struct ntp_addr))) ==
314                                     NULL)
315                                         fatal(NULL);
316                                 memcpy(&h->ss, p, sizeof(h->ss));
317                                 p += sizeof(h->ss);
318                                 dlen -= sizeof(h->ss);
319                                 if (peer->addr_head.pool) {
320                                         npeer = new_peer();
321                                         h->next = NULL;
322                                         npeer->addr = h;
323                                         npeer->addr_head.a = h;
324                                         client_peer_init(npeer);
325                                         peer_add(npeer);
326                                 } else {
327                                         h->next = peer->addr;
328                                         peer->addr = h;
329                                         peer->addr_head.a = peer->addr;
330                                 }
331                         }
332                         if (dlen != 0)
333                                 fatal("IMSG_HOST_DNS: dlen != 0");
334                         if (peer->addr_head.pool)
335                                 peer_remove(peer);
336                         else
337                                 client_addr_init(peer);
338                         break;
339                 default:
340                         break;
341                 }
342                 imsg_free(&imsg);
343         }
344         return (0);
345 }
346
347 void
348 peer_add(struct ntp_peer *p)
349 {
350         TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
351         peer_cnt++;
352 }
353
354 void
355 peer_remove(struct ntp_peer *p)
356 {
357         TAILQ_REMOVE(&conf->ntp_peers, p, entry);
358         free(p);
359         peer_cnt--;
360 }
361
362 void
363 priv_adjtime(void)
364 {
365         struct ntp_peer  *p;
366         int               offset_cnt = 0, i = 0;
367         struct ntp_peer **peers;
368         double            offset_median;
369
370         TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
371                 if (p->trustlevel < TRUSTLEVEL_BADPEER)
372                         continue;
373                 if (!p->update.good)
374                         return;
375                 offset_cnt++;
376         }
377
378         if ((peers = calloc(offset_cnt, sizeof(struct ntp_peer *))) == NULL)
379                 fatal("calloc ntp_adjtime");
380
381         TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
382                 if (p->trustlevel < TRUSTLEVEL_BADPEER)
383                         continue;
384                 peers[i++] = p;
385         }
386
387         qsort(peers, offset_cnt, sizeof(struct ntp_peer *), offset_compare);
388
389         if (offset_cnt > 0) {
390                 if (offset_cnt > 1 && offset_cnt % 2 == 0) {
391                         offset_median =
392                             (peers[offset_cnt / 2 - 1]->update.offset +
393                             peers[offset_cnt / 2]->update.offset) / 2;
394                         conf->status.rootdelay =
395                             (peers[offset_cnt / 2 - 1]->update.delay +
396                             peers[offset_cnt / 2]->update.delay) / 2;
397                         conf->status.stratum = MAX(
398                             peers[offset_cnt / 2 - 1]->update.status.stratum,
399                             peers[offset_cnt / 2]->update.status.stratum);
400                 } else {
401                         offset_median = peers[offset_cnt / 2]->update.offset;
402                         conf->status.rootdelay =
403                             peers[offset_cnt / 2]->update.delay;
404                         conf->status.stratum =
405                             peers[offset_cnt / 2]->update.status.stratum;
406                 }
407
408                 imsg_compose(ibuf_main, IMSG_ADJTIME, 0, 0,
409                     &offset_median, sizeof(offset_median));
410
411                 conf->status.reftime = gettime();
412                 conf->status.leap = LI_NOWARNING;
413                 conf->status.stratum++; /* one more than selected peer */
414
415                 if (peers[offset_cnt / 2]->addr->ss.ss_family == AF_INET)
416                         conf->status.refid = ((struct sockaddr_in *)
417                             &peers[offset_cnt / 2]->addr->ss)->sin_addr.s_addr;
418         }
419
420         free(peers);
421
422         TAILQ_FOREACH(p, &conf->ntp_peers, entry)
423                 p->update.good = 0;
424 }
425
426 int
427 offset_compare(const void *aa, const void *bb)
428 {
429         const struct ntp_peer * const *a;
430         const struct ntp_peer * const *b;
431
432         a = aa;
433         b = bb;
434
435         if ((*a)->update.offset < (*b)->update.offset)
436                 return (-1);
437         else if ((*a)->update.offset > (*b)->update.offset)
438                 return (1);
439         else
440                 return (0);
441 }
442
443 void
444 priv_settime(double offset)
445 {
446         imsg_compose(ibuf_main, IMSG_SETTIME, 0, 0, &offset, sizeof(offset));
447         conf->settime = 0;
448 }
449
450 void
451 priv_host_dns(char *name, u_int32_t peerid)
452 {
453         u_int16_t       dlen;
454
455         dlen = strlen(name) + 1;
456         imsg_compose(ibuf_main, IMSG_HOST_DNS, peerid, 0, name, dlen);
457 }