Merge from vendor branch CVS:
[dragonfly.git] / contrib / ntpd / ntpd.c
1 /*      $OpenBSD: src/usr.sbin/ntpd/ntpd.c,v 1.25 2004/12/06 20:57:17 mickey Exp $ */
2
3 /*
4  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/wait.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <poll.h>
27 #include <pwd.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include "ntpd.h"
35
36 void    sighdlr(int);
37 void    usage(void);
38 int     main(int, char *[]);
39 int     check_child(pid_t, const char *);
40 int     dispatch_imsg(struct ntpd_conf *);
41 void    ntpd_adjtime(double);
42 void    ntpd_settime(double);
43
44 volatile sig_atomic_t    quit = 0;
45 volatile sig_atomic_t    reconfig = 0;
46 volatile sig_atomic_t    sigchld = 0;
47 struct imsgbuf          *ibuf;
48
49 void
50 sighdlr(int sig)
51 {
52         switch (sig) {
53         case SIGTERM:
54         case SIGINT:
55                 quit = 1;
56                 break;
57         case SIGCHLD:
58                 sigchld = 1;
59                 break;
60         case SIGHUP:
61                 reconfig = 1;
62                 break;
63         }
64 }
65
66 void
67 usage(void)
68 {
69         extern char *__progname;
70
71         fprintf(stderr, "usage: %s [-dSs] [-f file]\n", __progname);
72         exit(1);
73 }
74
75 #define POLL_MAX                8
76 #define PFD_PIPE                0
77
78 int
79 main(int argc, char *argv[])
80 {
81         struct ntpd_conf         conf;
82         struct pollfd            pfd[POLL_MAX];
83         pid_t                    chld_pid = 0, pid;
84         const char              *conffile;
85         int                      ch, nfds, timeout = INFTIM;
86         int                      pipe_chld[2];
87
88         conffile = CONFFILE;
89
90         bzero(&conf, sizeof(conf));
91
92         log_init(1);            /* log to stderr until daemonized */
93
94         while ((ch = getopt(argc, argv, "df:sS")) != -1) {
95                 switch (ch) {
96                 case 'd':
97                         conf.debug = 1;
98                         break;
99                 case 'f':
100                         conffile = optarg;
101                         break;
102                 case 's':
103                         conf.settime = 1;
104                         break;
105                 case 'S':
106                         conf.settime = 0;
107                         break;
108                 default:
109                         usage();
110                         /* NOTREACHED */
111                 }
112         }
113
114         if (parse_config(conffile, &conf))
115                 exit(1);
116
117         if (geteuid()) {
118                 fprintf(stderr, "ntpd: need root privileges\n");
119                 exit(1);
120         }
121
122         if (getpwnam(NTPD_USER) == NULL) {
123                 fprintf(stderr, "ntpd: unknown user %s\n", NTPD_USER);
124                 exit(1);
125         }
126         endpwent();
127
128         if (!conf.settime) {
129                 log_init(conf.debug);
130                 if (!conf.debug)
131                         daemon(1, 0);
132         } else
133                 timeout = 15 * 1000;
134
135         if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_chld) == -1)
136                 fatal("socketpair");
137
138         /* fork children */
139         chld_pid = ntp_main(pipe_chld, &conf);
140
141         setproctitle("[priv]");
142
143         signal(SIGTERM, sighdlr);
144         signal(SIGINT, sighdlr);
145         signal(SIGCHLD, sighdlr);
146         signal(SIGHUP, sighdlr);
147
148         close(pipe_chld[1]);
149
150         if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
151                 fatal(NULL);
152         imsg_init(ibuf, pipe_chld[0]);
153
154         while (quit == 0) {
155                 pfd[PFD_PIPE].fd = ibuf->fd;
156                 pfd[PFD_PIPE].events = POLLIN;
157                 if (ibuf->w.queued)
158                         pfd[PFD_PIPE].events |= POLLOUT;
159
160                 if ((nfds = poll(pfd, 1, timeout)) == -1)
161                         if (errno != EINTR) {
162                                 log_warn("poll error");
163                                 quit = 1;
164                         }
165
166                 if (nfds == 0 && conf.settime) {
167                         log_debug("no reply received, skipping initial time"
168                             "setting");
169                         conf.settime = 0;
170                         timeout = INFTIM;
171                         log_init(conf.debug);
172                         if (!conf.debug)
173                                 daemon(1, 0);
174                 }
175
176                 if (nfds > 0 && (pfd[PFD_PIPE].revents & POLLOUT))
177                         if (msgbuf_write(&ibuf->w) < 0) {
178                                 log_warn("pipe write error (to child");
179                                 quit = 1;
180                         }
181
182                 if (nfds > 0 && pfd[PFD_PIPE].revents & POLLIN) {
183                         nfds--;
184                         if (dispatch_imsg(&conf) == -1)
185                                 quit = 1;
186                 }
187
188                 if (sigchld) {
189                         if (check_child(chld_pid, "child")) {
190                                 quit = 1;
191                                 chld_pid = 0;
192                         }
193                         sigchld = 0;
194                 }
195
196         }
197
198         signal(SIGCHLD, SIG_DFL);
199
200         if (chld_pid)
201                 kill(chld_pid, SIGTERM);
202
203         do {
204                 if ((pid = wait(NULL)) == -1 &&
205                     errno != EINTR && errno != ECHILD)
206                         fatal("wait");
207         } while (pid != -1 || (pid == -1 && errno == EINTR));
208
209         msgbuf_clear(&ibuf->w);
210         free(ibuf);
211         log_info("Terminating");
212         return (0);
213 }
214
215 int
216 check_child(pid_t pid, const char *pname)
217 {
218         int     status;
219
220         if (waitpid(pid, &status, WNOHANG) > 0) {
221                 if (WIFEXITED(status)) {
222                         log_warnx("Lost child: %s exited", pname);
223                         return (1);
224                 }
225                 if (WIFSIGNALED(status)) {
226                         log_warnx("Lost child: %s terminated; signal %d",
227                             pname, WTERMSIG(status));
228                         return (1);
229                 }
230         }
231
232         return (0);
233 }
234
235 int
236 dispatch_imsg(struct ntpd_conf *conf)
237 {
238         struct imsg              imsg;
239         int                      n, cnt;
240         double                   d;
241         char                    *name;
242         struct ntp_addr         *h, *hn;
243         struct buf              *buf;
244
245         if ((n = imsg_read(ibuf)) == -1)
246                 return (-1);
247
248         if (n == 0) {   /* connection closed */
249                 log_warnx("dispatch_imsg in main: pipe closed");
250                 return (-1);
251         }
252
253         for (;;) {
254                 if ((n = imsg_get(ibuf, &imsg)) == -1)
255                         return (-1);
256
257                 if (n == 0)
258                         break;
259
260                 switch (imsg.hdr.type) {
261                 case IMSG_ADJTIME:
262                         if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
263                                 fatal("invalid IMSG_ADJTIME received");
264                         memcpy(&d, imsg.data, sizeof(d));
265                         ntpd_adjtime(d);
266                         break;
267                 case IMSG_SETTIME:
268                         if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
269                                 fatal("invalid IMSG_SETTIME received");
270                         if (!conf->settime)
271                                 break;
272                         memcpy(&d, imsg.data, sizeof(d));
273                         ntpd_settime(d);
274                         /* daemonize now */
275                         log_init(conf->debug);
276                         if (!conf->debug)
277                                 daemon(1, 0);
278                         conf->settime = 0;
279                         break;
280                 case IMSG_HOST_DNS:
281                         name = imsg.data;
282                         if (imsg.hdr.len != strlen(name) + 1 + IMSG_HEADER_SIZE)
283                                 fatal("invalid IMSG_HOST_DNS received");
284                         if ((cnt = host_dns(name, &hn)) > 0) {
285                                 buf = imsg_create(ibuf, IMSG_HOST_DNS,
286                                     imsg.hdr.peerid, 0,
287                                     cnt * sizeof(struct sockaddr_storage));
288                                 if (buf == NULL)
289                                         break;
290                                 for (h = hn; h != NULL; h = h->next) {
291                                         imsg_add(buf, &h->ss, sizeof(h->ss));
292                                 }
293                                 imsg_close(ibuf, buf);
294                         }
295                         break;
296                 default:
297                         break;
298                 }
299                 imsg_free(&imsg);
300         }
301         return (0);
302 }
303
304 void
305 ntpd_adjtime(double d)
306 {
307         struct timeval  tv;
308
309         if (d >= (double)LOG_NEGLIGEE / 1000)
310                 log_info("adjusting local clock by %fs", d);
311         else
312                 log_debug("adjusting local clock by %fs", d);
313         d_to_tv(d, &tv);
314         if (adjtime(&tv, NULL) == -1)
315                 log_warn("adjtime failed");
316 }
317
318 void
319 ntpd_settime(double d)
320 {
321         struct timeval  tv, curtime;
322         char            buf[80];
323         time_t          tval;
324
325         /* if the offset is small, don't call settimeofday */
326         if (d < SETTIME_MIN_OFFSET && d > -SETTIME_MIN_OFFSET)
327                 return;
328
329         d_to_tv(d, &tv);
330         if (gettimeofday(&curtime, NULL) == -1)
331                 log_warn("gettimeofday");
332         curtime.tv_sec += tv.tv_sec;
333         curtime.tv_usec += tv.tv_usec;
334         if (curtime.tv_usec > 1000000) {
335                 curtime.tv_sec++;
336                 curtime.tv_usec -= 1000000;
337         }
338         if (settimeofday(&curtime, NULL) == -1)
339                 log_warn("settimeofday");
340         tval = curtime.tv_sec;
341         strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Z %Y",
342             localtime(&tval));
343         log_info("set local clock to %s (offset %fs)", buf, d);
344 }