Merge from vendor branch NTPD:
[dragonfly.git] / contrib / ntpd / ntpd.c
1 /*      $OpenBSD: ntpd.c,v 1.28 2005/01/28 12:32:24 henning 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 __dead 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 __dead 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                         if (daemon(1, 0))
132                                 fatal("daemon");
133         } else
134                 timeout = 15 * 1000;
135
136         if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_chld) == -1)
137                 fatal("socketpair");
138
139         /* fork child process */
140         chld_pid = ntp_main(pipe_chld, &conf);
141
142         setproctitle("[priv]");
143
144         signal(SIGTERM, sighdlr);
145         signal(SIGINT, sighdlr);
146         signal(SIGCHLD, sighdlr);
147         signal(SIGHUP, sighdlr);
148
149         close(pipe_chld[1]);
150
151         if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
152                 fatal(NULL);
153         imsg_init(ibuf, pipe_chld[0]);
154
155         while (quit == 0) {
156                 pfd[PFD_PIPE].fd = ibuf->fd;
157                 pfd[PFD_PIPE].events = POLLIN;
158                 if (ibuf->w.queued)
159                         pfd[PFD_PIPE].events |= POLLOUT;
160
161                 if ((nfds = poll(pfd, 1, timeout)) == -1)
162                         if (errno != EINTR) {
163                                 log_warn("poll error");
164                                 quit = 1;
165                         }
166
167                 if (nfds == 0 && conf.settime) {
168                         log_debug("no reply received, skipping initial time "
169                             "setting");
170                         conf.settime = 0;
171                         timeout = INFTIM;
172                         log_init(conf.debug);
173                         if (!conf.debug)
174                                 if (daemon(1, 0))
175                                         fatal("daemon");
176                 }
177
178                 if (nfds > 0 && (pfd[PFD_PIPE].revents & POLLOUT))
179                         if (msgbuf_write(&ibuf->w) < 0) {
180                                 log_warn("pipe write error (to child)");
181                                 quit = 1;
182                         }
183
184                 if (nfds > 0 && pfd[PFD_PIPE].revents & POLLIN) {
185                         nfds--;
186                         if (dispatch_imsg(&conf) == -1)
187                                 quit = 1;
188                 }
189
190                 if (sigchld) {
191                         if (check_child(chld_pid, "child")) {
192                                 quit = 1;
193                                 chld_pid = 0;
194                         }
195                         sigchld = 0;
196                 }
197
198         }
199
200         signal(SIGCHLD, SIG_DFL);
201
202         if (chld_pid)
203                 kill(chld_pid, SIGTERM);
204
205         do {
206                 if ((pid = wait(NULL)) == -1 &&
207                     errno != EINTR && errno != ECHILD)
208                         fatal("wait");
209         } while (pid != -1 || (pid == -1 && errno == EINTR));
210
211         msgbuf_clear(&ibuf->w);
212         free(ibuf);
213         log_info("Terminating");
214         return (0);
215 }
216
217 int
218 check_child(pid_t pid, const char *pname)
219 {
220         int     status;
221
222         if (waitpid(pid, &status, WNOHANG) > 0) {
223                 if (WIFEXITED(status)) {
224                         log_warnx("Lost child: %s exited", pname);
225                         return (1);
226                 }
227                 if (WIFSIGNALED(status)) {
228                         log_warnx("Lost child: %s terminated; signal %d",
229                             pname, WTERMSIG(status));
230                         return (1);
231                 }
232         }
233
234         return (0);
235 }
236
237 int
238 dispatch_imsg(struct ntpd_conf *conf)
239 {
240         struct imsg              imsg;
241         int                      n, cnt;
242         double                   d;
243         char                    *name;
244         struct ntp_addr         *h, *hn;
245         struct buf              *buf;
246
247         if ((n = imsg_read(ibuf)) == -1)
248                 return (-1);
249
250         if (n == 0) {   /* connection closed */
251                 log_warnx("dispatch_imsg in main: pipe closed");
252                 return (-1);
253         }
254
255         for (;;) {
256                 if ((n = imsg_get(ibuf, &imsg)) == -1)
257                         return (-1);
258
259                 if (n == 0)
260                         break;
261
262                 switch (imsg.hdr.type) {
263                 case IMSG_ADJTIME:
264                         if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
265                                 fatal("invalid IMSG_ADJTIME received");
266                         memcpy(&d, imsg.data, sizeof(d));
267                         ntpd_adjtime(d);
268                         break;
269                 case IMSG_SETTIME:
270                         if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
271                                 fatal("invalid IMSG_SETTIME received");
272                         if (!conf->settime)
273                                 break;
274                         memcpy(&d, imsg.data, sizeof(d));
275                         ntpd_settime(d);
276                         /* daemonize now */
277                         log_init(conf->debug);
278                         if (!conf->debug)
279                                 if (daemon(1, 0))
280                                         fatal("daemon");
281                         conf->settime = 0;
282                         break;
283                 case IMSG_HOST_DNS:
284                         name = imsg.data;
285                         if (imsg.hdr.len != strlen(name) + 1 + IMSG_HEADER_SIZE)
286                                 fatal("invalid IMSG_HOST_DNS received");
287                         if ((cnt = host_dns(name, &hn)) > 0) {
288                                 buf = imsg_create(ibuf, IMSG_HOST_DNS,
289                                     imsg.hdr.peerid, 0,
290                                     cnt * sizeof(struct sockaddr_storage));
291                                 if (buf == NULL)
292                                         break;
293                                 for (h = hn; h != NULL; h = h->next) {
294                                         imsg_add(buf, &h->ss, sizeof(h->ss));
295                                 }
296                                 imsg_close(ibuf, buf);
297                         }
298                         break;
299                 default:
300                         break;
301                 }
302                 imsg_free(&imsg);
303         }
304         return (0);
305 }
306
307 void
308 ntpd_adjtime(double d)
309 {
310         struct timeval  tv;
311
312         if (d >= (double)LOG_NEGLIGEE / 1000 ||
313             d <= -1 * (double)LOG_NEGLIGEE / 1000)
314                 log_info("adjusting local clock by %fs", d);
315         else
316                 log_debug("adjusting local clock by %fs", d);
317         d_to_tv(d, &tv);
318         if (adjtime(&tv, NULL) == -1)
319                 log_warn("adjtime failed");
320 }
321
322 void
323 ntpd_settime(double d)
324 {
325         struct timeval  tv, curtime;
326         char            buf[80];
327         time_t          tval;
328
329         /* if the offset is small, don't call settimeofday */
330         if (d < SETTIME_MIN_OFFSET && d > -SETTIME_MIN_OFFSET)
331                 return;
332
333         d_to_tv(d, &tv);
334         if (gettimeofday(&curtime, NULL) == -1)
335                 log_warn("gettimeofday");
336         curtime.tv_sec += tv.tv_sec;
337         curtime.tv_usec += tv.tv_usec;
338         if (curtime.tv_usec > 1000000) {
339                 curtime.tv_sec++;
340                 curtime.tv_usec -= 1000000;
341         }
342         if (settimeofday(&curtime, NULL) == -1)
343                 log_warn("settimeofday");
344         tval = curtime.tv_sec;
345         strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Z %Y",
346             localtime(&tval));
347         log_info("set local clock to %s (offset %fs)", buf, d);
348 }