Merge from vendor branch NTPD:
[dragonfly.git] / contrib / ntpd / ntpd.c
1 /*      $OpenBSD: ntpd.c,v 1.14 2004/08/12 16:33:59 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 void    usage(void);
38 int     main(int, char *[]);
39 int     check_child(pid_t, const char *);
40 int     dispatch_imsg(void);
41 void    ntpd_adjtime(double);
42
43 int                     rfd = -1;
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 [-d] [-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         char                    *conffile;
85         int                      debug = 0;
86         int                      ch, nfds;
87         int                      pipe_chld[2];
88
89         conffile = CONFFILE;
90
91         bzero(&conf, sizeof(conf));
92
93         log_init(1);            /* log to stderr until daemonized */
94
95         while ((ch = getopt(argc, argv, "df:")) != -1) {
96                 switch (ch) {
97                 case 'd':
98                         debug = 1;
99                         break;
100                 case 'f':
101                         conffile = optarg;
102                         break;
103                 default:
104                         usage();
105                         /* NOTREACHED */
106                 }
107         }
108
109         if (parse_config(conffile, &conf))
110                 exit(1);
111
112         if (geteuid()) {
113                 fprintf(stderr, "ntpd: need root privileges\n");
114                 exit(1);
115         }
116
117         if (getpwnam(NTPD_USER) == NULL) {
118                 fprintf(stderr, "ntpd: unknown user %s\n", NTPD_USER);
119                 exit(1);
120         }
121         endpwent();
122
123         log_init(debug);
124
125         if (!debug)
126                 daemon(1, 0);
127
128         if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_chld) == -1)
129                 fatal("socketpair");
130
131         /* fork children */
132         chld_pid = ntp_main(pipe_chld, &conf);
133
134         setproctitle("[priv]");
135
136         signal(SIGTERM, sighdlr);
137         signal(SIGINT, sighdlr);
138         signal(SIGCHLD, sighdlr);
139         signal(SIGHUP, sighdlr);
140
141         close(pipe_chld[1]);
142
143         imsg_init(&ibuf, pipe_chld[0]);
144
145         while (quit == 0) {
146                 pfd[PFD_PIPE].fd = ibuf.fd;
147                 pfd[PFD_PIPE].events = POLLIN;
148                 if (ibuf.w.queued)
149                         pfd[PFD_PIPE].events |= POLLOUT;
150
151                 if ((nfds = poll(pfd, 1, INFTIM)) == -1)
152                         if (errno != EINTR) {
153                                 log_warn("poll error");
154                                 quit = 1;
155                         }
156
157                 if (nfds > 0 && (pfd[PFD_PIPE].revents & POLLOUT))
158                         if (msgbuf_write(&ibuf.w) < 0) {
159                                 log_warn("pipe write error (to child");
160                                 quit = 1;
161                         }
162
163                 if (nfds > 0 && pfd[PFD_PIPE].revents & POLLIN) {
164                         nfds--;
165                         if (dispatch_imsg() == -1)
166                                 quit = 1;
167                 }
168
169                 if (sigchld) {
170                         if (check_child(chld_pid, "child"))
171                                 quit = 1;
172                         sigchld = 0;
173                 }
174
175         }
176
177         signal(SIGCHLD, SIG_IGN);
178
179         if (chld_pid)
180                 kill(chld_pid, SIGTERM);
181
182         do {
183                 if ((pid = wait(NULL)) == -1 &&
184                     errno != EINTR && errno != ECHILD)
185                         fatal("wait");
186         } while (pid != -1 || (pid == -1 && errno == EINTR));
187
188         log_info("Terminating");
189         return (0);
190 }
191
192 int
193 check_child(pid_t pid, const char *pname)
194 {
195         int     status;
196
197         if (waitpid(pid, &status, WNOHANG) > 0) {
198                 if (WIFEXITED(status)) {
199                         log_warnx("Lost child: %s exited", pname);
200                         return (1);
201                 }
202                 if (WIFSIGNALED(status)) {
203                         log_warnx("Lost child: %s terminated; signal %d",
204                             pname, WTERMSIG(status));
205                         return (1);
206                 }
207         }
208
209         return (0);
210 }
211
212 int
213 dispatch_imsg(void)
214 {
215         struct imsg              imsg;
216         int                      n, cnt;
217         double                   d;
218         char                    *name;
219         struct ntp_addr         *h, *hn;
220         struct buf              *buf;
221
222         if ((n = imsg_read(&ibuf)) == -1)
223                 return (-1);
224
225         if (n == 0) {   /* connection closed */
226                 log_warnx("dispatch_imsg in main: pipe closed");
227                 return (-1);
228         }
229
230         for (;;) {
231                 if ((n = imsg_get(&ibuf, &imsg)) == -1)
232                         return (-1);
233
234                 if (n == 0)
235                         break;
236
237                 switch (imsg.hdr.type) {
238                 case IMSG_ADJTIME:
239                         if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
240                                 fatal("invalid IMSG_ADJTIME received");
241                         memcpy(&d, imsg.data, sizeof(d));
242                         ntpd_adjtime(d);
243                         break;
244                 case IMSG_HOST_DNS:
245                         name = imsg.data;
246                         if (imsg.hdr.len != strlen(name) + 1 + IMSG_HEADER_SIZE)
247                                 fatal("invalid IMSG_HOST_DNS received");
248                         if ((cnt = host_dns(name, &hn)) > 0) {
249                                 buf = imsg_create(&ibuf, IMSG_HOST_DNS,
250                                     imsg.hdr.peerid,
251                                     cnt * sizeof(struct sockaddr_storage));
252                                 if (buf == NULL)
253                                         break;
254                                 for (h = hn; h != NULL; h = h->next) {
255                                         imsg_add(buf, &h->ss, sizeof(h->ss));
256                                 }
257                                 imsg_close(&ibuf, buf);
258                         }
259                         break;
260                 default:
261                         break;
262                 }
263                 imsg_free(&imsg);
264         }
265         return (0);
266 }
267
268 void
269 ntpd_adjtime(double d)
270 {
271         struct timeval  tv;
272
273         d_to_tv(d, &tv);
274         log_info("adjusting local clock by %fs", d);
275         if (adjtime(&tv, NULL) == -1)
276                 log_warn("adjtime failed");
277 }