Merge from vendor branch GDB:
[dragonfly.git] / contrib / ntpd / ntpd.c
1 /*      $OpenBSD: src/usr.sbin/ntpd/ntpd.c,v 1.35 2005/04/18 20:46:02 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 <errno.h>
24 #include <poll.h>
25 #include <pwd.h>
26 #include <resolv.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "ntpd.h"
34
35 void            sighdlr(int);
36 __dead void     usage(void);
37 int             main(int, char *[]);
38 int             check_child(pid_t, const char *);
39 int             dispatch_imsg(struct ntpd_conf *);
40 void            ntpd_adjtime(double);
41 void            ntpd_settime(double);
42
43 volatile sig_atomic_t    quit = 0;
44 volatile sig_atomic_t    reconfig = 0;
45 volatile sig_atomic_t    sigchld = 0;
46 struct imsgbuf          *ibuf;
47
48 void
49 sighdlr(int sig)
50 {
51         switch (sig) {
52         case SIGTERM:
53         case SIGINT:
54                 quit = 1;
55                 break;
56         case SIGCHLD:
57                 sigchld = 1;
58                 break;
59         case SIGHUP:
60                 reconfig = 1;
61                 break;
62         }
63 }
64
65 __dead void
66 usage(void)
67 {
68         extern char *__progname;
69
70         fprintf(stderr, "usage: %s [-dSs] [-f file]\n", __progname);
71         exit(1);
72 }
73
74 #define POLL_MAX                8
75 #define PFD_PIPE                0
76
77 int
78 main(int argc, char *argv[])
79 {
80         struct ntpd_conf         conf;
81         struct pollfd            pfd[POLL_MAX];
82         pid_t                    chld_pid = 0, pid;
83         const char              *conffile;
84         int                      ch, nfds, timeout = INFTIM;
85         int                      pipe_chld[2];
86
87         conffile = CONFFILE;
88
89         bzero(&conf, sizeof(conf));
90
91         log_init(1);            /* log to stderr until daemonized */
92         res_init();             /* XXX */
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                         conf.settime = 0;
169                         timeout = INFTIM;
170                         log_init(conf.debug);
171                         log_debug("no reply received, skipping initial time "
172                             "setting");
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                                 fatalx("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                                 fatalx("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 < 1 + IMSG_HEADER_SIZE)
286                                 fatalx("invalid IMSG_HOST_DNS received");
287                         imsg.hdr.len -= 1 + IMSG_HEADER_SIZE;
288                         if (name[imsg.hdr.len] != '\0' ||
289                             strlen(name) != imsg.hdr.len)
290                                 fatalx("invalid IMSG_HOST_DNS received");
291                         if ((cnt = host_dns(name, &hn)) > 0) {
292                                 buf = imsg_create(ibuf, IMSG_HOST_DNS,
293                                     imsg.hdr.peerid, 0,
294                                     cnt * sizeof(struct sockaddr_storage));
295                                 if (buf == NULL)
296                                         break;
297                                 for (h = hn; h != NULL; h = h->next) {
298                                         imsg_add(buf, &h->ss, sizeof(h->ss));
299                                 }
300                                 imsg_close(ibuf, buf);
301                         }
302                         break;
303                 default:
304                         break;
305                 }
306                 imsg_free(&imsg);
307         }
308         return (0);
309 }
310
311 void
312 ntpd_adjtime(double d)
313 {
314         struct timeval  tv;
315
316         if (d >= (double)LOG_NEGLIGEE / 1000 ||
317             d <= -1 * (double)LOG_NEGLIGEE / 1000)
318                 log_info("adjusting local clock by %fs", d);
319         else
320                 log_debug("adjusting local clock by %fs", d);
321         d_to_tv(d, &tv);
322         if (adjtime(&tv, NULL) == -1)
323                 log_warn("adjtime failed");
324 }
325
326 void
327 ntpd_settime(double d)
328 {
329         struct timeval  tv, curtime;
330         char            buf[80];
331         time_t          tval;
332
333         /* if the offset is small, don't call settimeofday */
334         if (d < SETTIME_MIN_OFFSET && d > -SETTIME_MIN_OFFSET)
335                 return;
336
337         if (gettimeofday(&curtime, NULL) == -1) {
338                 log_warn("gettimeofday");
339                 return;
340         }
341         d_to_tv(d, &tv);
342         curtime.tv_usec += tv.tv_usec + 1000000;
343         curtime.tv_sec += tv.tv_sec - 1 + (curtime.tv_usec / 1000000);
344         curtime.tv_usec %= 1000000;
345
346         if (settimeofday(&curtime, NULL) == -1) {
347                 log_warn("settimeofday");
348                 return;
349         }
350         tval = curtime.tv_sec;
351         strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Z %Y",
352             localtime(&tval));
353         log_info("set local clock to %s (offset %fs)", buf, d);
354 }