kernel - Fix auto port assignment collision in network code
[dragonfly.git] / usr.sbin / dntpd / main.c
blobb43aab006aedd4d3a23d7f6e08358af45d645b83
1 /*
2 * Copyright (c) 2005 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
35 #include "defs.h"
37 static void usage(const char *av0);
38 static void dotest(const char *target);
39 static void add_server(const char *target);
40 static void process_config_file(const char *path);
41 static pid_t check_pid(void);
42 static void set_pid(const char *av0);
43 static void sigint_handler(int signo __unused);
45 static struct server_info **servers;
46 static int nservers;
47 static int maxservers;
49 int daemon_opt = 1;
50 int debug_opt = 0;
51 int debug_level = -1; /* (set to default later) */
52 int quickset_opt = 0; /* immediately set time of day on startup */
53 int no_update_opt = 0; /* do not make any actual updates */
54 int min_sleep_opt = 5; /* 5 seconds minimum poll interval */
55 int nom_sleep_opt = 300; /* 5 minutes nominal poll interval */
56 int max_sleep_opt = 1800; /* 30 minutes maximum poll interval */
57 int family = PF_UNSPEC; /* Address family */
58 double insane_deviation = 0.5; /* 0.5 seconds of deviation == insane */
59 const char *config_opt; /* config file */
60 const char *pid_opt = "/var/run/dntpd.pid";
62 int
63 main(int ac, char **av)
65 int test_opt = 0;
66 pid_t pid;
67 int ch;
68 int i;
71 * Really randomize
73 srandomdev();
76 * Process Options
78 while ((ch = getopt(ac, av, "46df:i:l:np:qstFL:QST:")) != -1) {
79 switch(ch) {
80 case '4':
81 family = PF_INET;
82 break;
83 case '6':
84 family = PF_INET6;
85 break;
86 case 'd':
87 debug_opt = 1;
88 daemon_opt = 0;
89 if (debug_level < 0)
90 debug_level = 99;
91 if (config_opt == NULL)
92 config_opt = "/dev/null";
93 break;
94 case 'p':
95 pid_opt = optarg;
96 break;
97 case 'f':
98 config_opt = optarg;
99 break;
100 case 'i':
101 insane_deviation = strtod(optarg, NULL);
102 break;
103 case 'l':
104 debug_level = strtol(optarg, NULL, 0);
105 break;
106 case 'n':
107 no_update_opt = 1;
108 break;
109 case 'q':
110 debug_level = 0;
111 break;
112 case 's':
113 quickset_opt = 1;
114 break;
115 case 'S':
116 quickset_opt = 0;
117 break;
118 case 't':
119 test_opt = 1;
120 debug_opt = 1;
121 daemon_opt = 0;
122 if (debug_level < 0)
123 debug_level = 99;
124 if (config_opt == NULL)
125 config_opt = "/dev/null";
126 break;
127 case 'F':
128 daemon_opt = 0;
129 break;
130 case 'L':
131 max_sleep_opt = strtol(optarg, NULL, 0);
132 break;
133 case 'T':
134 nom_sleep_opt = strtol(optarg, NULL, 0);
135 if (nom_sleep_opt < 1) {
136 fprintf(stderr, "Warning: nominal poll interval too small, "
137 "limiting to 1 second\n");
138 nom_sleep_opt = 1;
140 if (nom_sleep_opt > 24 * 60 * 60) {
141 fprintf(stderr, "Warning: nominal poll interval too large, "
142 "limiting to 24 hours\n");
143 nom_sleep_opt = 24 * 60 * 60;
145 if (min_sleep_opt > nom_sleep_opt)
146 min_sleep_opt = nom_sleep_opt;
147 if (max_sleep_opt < nom_sleep_opt * 5)
148 max_sleep_opt = nom_sleep_opt * 5;
149 break;
150 case 'Q':
151 if ((pid = check_pid()) != 0) {
152 fprintf(stderr, "%s: killing old daemon\n", av[0]);
153 kill(pid, SIGINT);
154 usleep(100000);
155 if (check_pid())
156 sleep(1);
157 if (check_pid())
158 sleep(9);
159 if (check_pid()) {
160 fprintf(stderr, "%s: Unable to kill running daemon.\n", av[0]);
161 } else {
162 fprintf(stderr, "%s: Running daemon has been terminated.\n", av[0]);
164 } else {
165 fprintf(stderr, "%s: There is no daemon running to kill.\n", av[0]);
167 exit(0);
168 break;
169 case 'h':
170 default:
171 usage(av[0]);
172 /* not reached */
177 * Make sure min and nom intervals are less then or equal to the maximum
178 * interval.
180 if (min_sleep_opt > max_sleep_opt)
181 min_sleep_opt = max_sleep_opt;
182 if (nom_sleep_opt > max_sleep_opt)
183 nom_sleep_opt = max_sleep_opt;
186 * Set default config file
188 if (config_opt == NULL) {
189 if (optind != ac)
190 config_opt = "/dev/null";
191 else
192 config_opt = "/etc/dntpd.conf";
195 if (debug_level < 0)
196 debug_level = 1;
198 process_config_file(config_opt);
200 if (debug_opt == 0)
201 openlog("dntpd", LOG_CONS|LOG_PID, LOG_DAEMON);
203 if (test_opt) {
204 if (optind != ac - 1)
205 usage(av[0]);
206 dotest(av[optind]);
207 /* not reached */
211 * Add additional hosts.
213 for (i = optind; i < ac; ++i) {
214 add_server(av[i]);
216 if (nservers == 0) {
217 usage(av[0]);
218 /* not reached */
222 * Do an initial course time setting if requested using the first
223 * host successfully polled.
225 /* XXX */
228 * Daemonize, stop logging to stderr.
230 if (daemon_opt) {
231 if ((pid = check_pid()) != 0) {
232 logerrstr("%s: NOTE: killing old daemon and starting a new one",
233 av[0]);
234 kill(pid, SIGINT);
235 usleep(100000);
236 if (check_pid())
237 sleep(1);
238 if (check_pid())
239 sleep(9);
240 if (check_pid()) {
241 logerrstr("%s: Unable to kill running daemon, exiting", av[0]);
242 exit(1);
245 daemon(0, 0);
246 } else if (check_pid() != 0) {
247 logerrstr("%s: A background dntpd is running, you must kill it first",
248 av[0]);
249 exit(1);
251 if (debug_opt == 0) {
252 log_stderr = 0;
253 set_pid(av[0]);
254 signal(SIGINT, sigint_handler);
255 logdebug(0, "dntpd version %s started\n", DNTPD_VERSION);
259 * And go.
261 sysntp_clear_alternative_corrections();
262 client_init();
263 client_check_duplicate_ips(servers, nservers);
264 client_main(servers, nservers);
265 return(0);
268 static
269 void
270 usage(const char *av0)
272 fprintf(stderr, "%s [-dnqstFSQ] [-f config_file] [-l log_level] [-T poll_interval] [-L poll_limit] [additional_targets]\n", av0);
273 fprintf(stderr,
274 "\t-d\tDebugging mode, implies -F, -l 99, and logs to stderr\n"
275 "\t-f file\tSpecify the config file (/etc/dntpd.conf)\n"
276 "\t-l int\tSet log level (0-4), default 1\n"
277 "\t-n\tNo-update mode. No offset or frequency corrections are made\n"
278 "\t-q\tQuiet-mode, same as -L 0\n"
279 "\t-s\tSet the time immediately on startup\n"
280 "\t-t\tTest mode, implies -F, -l 99, -n, logs to stderr\n"
281 "\t-F\tRun in foreground (log still goes to syslog)\n"
282 "\t-L int\tMaximum polling interval\n"
283 "\t-S\tDo not set the time immediately on startup\n"
284 "\t-T int\tNominal polling interval\n"
285 "\t-Q\tTerminate any running background daemon\n"
286 "\n"
287 "\t\tNOTE: in debug and test modes -f must be specified if\n"
288 "\t\tyou want to use a config file.\n"
290 exit(1);
293 static
294 void
295 dotest(const char *target)
297 struct server_info info;
299 bzero(&info, sizeof(info));
300 info.sam = (struct sockaddr *)&info.sam_st;
301 info.fd = udp_socket(target, 123, info.sam, LOG_DNS_ERROR);
302 if (info.fd < 0) {
303 logerrstr("unable to create UDP socket for %s", target);
304 return;
306 info.target = strdup(target);
307 client_init();
309 fprintf(stderr,
310 "Will run %d-second polls until interrupted.\n", nom_sleep_opt);
312 for (;;) {
313 client_poll(&info, nom_sleep_opt, 1);
314 sleep(nom_sleep_opt);
316 /* not reached */
319 static char *
320 myaddr2ascii(struct sockaddr *sa)
322 static char str[INET6_ADDRSTRLEN];
323 struct sockaddr_in *soin;
324 struct sockaddr_in6 *sin6;
326 switch (sa->sa_family) {
327 case AF_INET:
328 soin = (struct sockaddr_in *) sa;
329 inet_ntop(AF_INET, &soin->sin_addr, str, sizeof(str));
330 break;
331 case AF_INET6:
332 sin6 = (struct sockaddr_in6 *) sa;
333 inet_ntop(AF_INET6, &sin6->sin6_addr, str, sizeof(str));
334 break;
336 return (str);
339 static void
340 add_server(const char *target)
342 server_info_t info;
344 if (nservers == maxservers) {
345 maxservers += 16;
346 servers = realloc(servers, maxservers * sizeof(server_info_t));
347 assert(servers != NULL);
349 info = malloc(sizeof(struct server_info));
350 servers[nservers] = info;
351 bzero(info, sizeof(struct server_info));
352 info->sam = (struct sockaddr *)&info->sam_st;
353 info->target = strdup(target);
355 * Postpone socket opening and server name resolution until we are in main
356 * loop to avoid hang during init if network down.
358 info->fd = -1;
359 info->server_state = -1;
360 ++nservers;
363 void
364 disconnect_server(server_info_t info)
366 if (info->fd >= 0)
367 close(info->fd);
368 info->fd = -1;
369 if (info->ipstr) {
370 free(info->ipstr);
371 info->ipstr = NULL;
375 void
376 reconnect_server(server_info_t info)
378 const char *ipstr;
379 dns_error_policy_t policy;
382 * Ignore DNS errors if never connected before to handle the case where
383 * we're started before network up.
385 policy = IGNORE_DNS_ERROR;
386 if (info->fd >= 0) {
387 close(info->fd);
388 policy = LOG_DNS_ERROR;
390 if (info->ipstr) {
391 free(info->ipstr);
392 info->ipstr = NULL;
394 info->sam = (struct sockaddr *)&info->sam_st;
395 info->fd = udp_socket(info->target, 123, info->sam, policy);
396 if (info->fd >= 0) {
397 ipstr = myaddr2ascii(info->sam);
398 info->ipstr = strdup(ipstr);
402 static void
403 process_config_file(const char *path)
405 const char *ws = " \t\r\n";
406 char buf[1024];
407 char *keyword;
408 char *data;
409 int line;
410 FILE *fi;
412 if ((fi = fopen(path, "r")) != NULL) {
413 line = 1;
414 while (fgets(buf, sizeof(buf), fi) != NULL) {
415 if (strchr(buf, '#'))
416 *strchr(buf, '#') = 0;
417 if ((keyword = strtok(buf, ws)) != NULL) {
418 data = strtok(NULL, ws);
419 if (strcmp(keyword, "server") == 0) {
420 if (data == NULL) {
421 logerr("%s:%d server missing host specification",
422 path, line);
423 } else {
424 add_server(data);
426 } else {
427 logerr("%s:%d unknown keyword %s", path, line, keyword);
430 ++line;
432 fclose(fi);
433 } else {
434 logerr("Unable to open %s", path);
435 exit(1);
439 static
440 pid_t
441 check_pid(void)
443 char buf[32];
444 pid_t pid;
445 FILE *fi;
447 pid = 0;
448 if ((fi = fopen(pid_opt, "r")) != NULL) {
449 if (fgets(buf, sizeof(buf), fi) != NULL) {
450 pid = strtol(buf, NULL, 0);
451 if (kill(pid, 0) != 0)
452 pid = 0;
454 fclose(fi);
456 return(pid);
459 static
460 void
461 set_pid(const char *av0)
463 pid_t pid;
464 FILE *fo;
466 pid = getpid();
467 if ((fo = fopen(pid_opt, "w")) != NULL) {
468 fprintf(fo, "%d\n", (int)pid);
469 fclose(fo);
470 } else {
471 logerr("%s: Unable to create %s, continuing anyway.", av0, pid_opt);
475 static
476 void
477 sigint_handler(int signo __unused)
479 remove(pid_opt);
480 /* dangerous, but we are exiting anyway so pray... */
481 logdebug(0, "dntpd version %s stopped\n", DNTPD_VERSION);
482 exit(0);