xref: /dragonfly/usr.sbin/dntpd/main.c (revision 2038fb68)
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:
10  *
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.
20  *
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.
33  *
34  * $DragonFly: src/usr.sbin/dntpd/main.c,v 1.11 2007/06/26 02:40:20 dillon Exp $
35  */
36 
37 #include "defs.h"
38 
39 static void usage(const char *av0);
40 static void dotest(const char *target);
41 static void add_server(const char *target);
42 static void process_config_file(const char *path);
43 static pid_t check_pid(void);
44 static void set_pid(const char *av0);
45 static void sigint_handler(int signo __unused);
46 
47 static struct server_info **servers;
48 static int nservers;
49 static int maxservers;
50 
51 int daemon_opt = 1;
52 int debug_opt = 0;
53 int debug_level = -1;		/* (set to default later) */
54 int quickset_opt = 0;		/* immediately set time of day on startup */
55 int no_update_opt = 0;		/* do not make any actual updates */
56 int min_sleep_opt = 5;		/* 5 seconds minimum poll interval */
57 int nom_sleep_opt = 300;	/* 5 minutes nominal poll interval */
58 int max_sleep_opt = 1800;	/* 30 minutes maximum poll interval */
59 int family = PF_UNSPEC;		/* Address family */
60 double insane_deviation = 0.5;	/* 0.5 seconds of deviation == insane */
61 const char *config_opt;		/* config file */
62 const char *pid_opt = "/var/run/dntpd.pid";
63 
64 int
65 main(int ac, char **av)
66 {
67     int test_opt = 0;
68     pid_t pid;
69     int rc;
70     int ch;
71     int i;
72 
73     /*
74      * Really randomize
75      */
76     srandomdev();
77     rc = 0;
78 
79     /*
80      * Process Options
81      */
82     while ((ch = getopt(ac, av, "46df:i:l:np:qstFL:QST:")) != -1) {
83 	switch(ch) {
84 	case '4':
85 	    family = PF_INET;
86 	    break;
87 	case '6':
88 	    family = PF_INET6;
89 	    break;
90 	case 'd':
91 	    debug_opt = 1;
92 	    daemon_opt = 0;
93 	    if (debug_level < 0)
94 		debug_level = 99;
95 	    if (config_opt == NULL)
96 		config_opt = "/dev/null";
97 	    break;
98 	case 'p':
99 	    pid_opt = optarg;
100 	    break;
101 	case 'f':
102 	    config_opt = optarg;
103 	    break;
104 	case 'i':
105 	    insane_deviation = strtod(optarg, NULL);
106 	    break;
107 	case 'l':
108 	    debug_level = strtol(optarg, NULL, 0);
109 	    break;
110 	case 'n':
111 	    no_update_opt = 1;
112 	    break;
113 	case 'q':
114 	    debug_level = 0;
115 	    break;
116 	case 's':
117 	    quickset_opt = 1;
118 	    break;
119 	case 'S':
120 	    quickset_opt = 0;
121 	    break;
122 	case 't':
123 	    test_opt = 1;
124 	    debug_opt = 1;
125 	    daemon_opt = 0;
126 	    if (debug_level < 0)
127 		debug_level = 99;
128 	    if (config_opt == NULL)
129 		config_opt = "/dev/null";
130 	    break;
131 	case 'F':
132 	    daemon_opt = 0;
133 	    break;
134 	case 'L':
135 	    max_sleep_opt = strtol(optarg, NULL, 0);
136 	    break;
137 	case 'T':
138 	    nom_sleep_opt = strtol(optarg, NULL, 0);
139 	    if (nom_sleep_opt < 1) {
140 		fprintf(stderr, "Warning: nominal poll interval too small, "
141 				"limiting to 1 second\n");
142 		nom_sleep_opt = 1;
143 	    }
144 	    if (nom_sleep_opt > 24 * 60 * 60) {
145 		fprintf(stderr, "Warning: nominal poll interval too large, "
146 				"limiting to 24 hours\n");
147 		nom_sleep_opt = 24 * 60 * 60;
148 	    }
149 	    if (min_sleep_opt > nom_sleep_opt)
150 		min_sleep_opt = nom_sleep_opt;
151 	    if (max_sleep_opt < nom_sleep_opt * 5)
152 		max_sleep_opt = nom_sleep_opt * 5;
153 	    break;
154 	case 'Q':
155 	    if ((pid = check_pid()) != 0) {
156 		fprintf(stderr, "%s: killing old daemon\n", av[0]);
157 		kill(pid, SIGINT);
158 		usleep(100000);
159 		if (check_pid())
160 		    sleep(1);
161 		if (check_pid())
162 		    sleep(9);
163 		if (check_pid()) {
164 		    fprintf(stderr, "%s: Unable to kill running daemon.\n", av[0]);
165 		} else {
166 		    fprintf(stderr, "%s: Running daemon has been terminated.\n", av[0]);
167 		}
168 	    } else {
169 		fprintf(stderr, "%s: There is no daemon running to kill.\n", av[0]);
170 	    }
171 	    exit(0);
172 	    break;
173 	case 'h':
174 	default:
175 	    usage(av[0]);
176 	    /* not reached */
177 	}
178     }
179 
180     /*
181      * Make sure min and nom intervals are less then or equal to the maximum
182      * interval.
183      */
184     if (min_sleep_opt > max_sleep_opt)
185 	min_sleep_opt = max_sleep_opt;
186     if (nom_sleep_opt > max_sleep_opt)
187 	nom_sleep_opt = max_sleep_opt;
188 
189     /*
190      * Set default config file
191      */
192     if (config_opt == NULL) {
193 	if (optind != ac)
194 	    config_opt = "/dev/null";
195 	else
196 	    config_opt = "/etc/dntpd.conf";
197     }
198 
199     if (debug_level < 0)
200 	debug_level = 1;
201 
202     process_config_file(config_opt);
203 
204     if (debug_opt == 0)
205 	openlog("dntpd", LOG_CONS|LOG_PID, LOG_DAEMON);
206 
207     if (test_opt) {
208 	if (optind != ac - 1)
209 	    usage(av[0]);
210 	dotest(av[optind]);
211 	/* not reached */
212     }
213 
214     /*
215      * Add additional hosts.
216      */
217     for (i = optind; i < ac; ++i) {
218 	add_server(av[i]);
219     }
220     if (nservers == 0) {
221 	usage(av[0]);
222 	/* not reached */
223     }
224 
225     /*
226      * Do an initial course time setting if requested using the first
227      * host successfully polled.
228      */
229     /* XXX */
230 
231     /*
232      * Daemonize, stop logging to stderr.
233      */
234     if (daemon_opt) {
235 	if ((pid = check_pid()) != 0) {
236 	    logerrstr("%s: NOTE: killing old daemon and starting a new one",
237 			av[0]);
238 	    kill(pid, SIGINT);
239 	    usleep(100000);
240 	    if (check_pid())
241 		sleep(1);
242 	    if (check_pid())
243 		sleep(9);
244 	    if (check_pid()) {
245 		logerrstr("%s: Unable to kill running daemon, exiting", av[0]);
246 		exit(1);
247 	    }
248 	}
249 	daemon(0, 0);
250     } else if (check_pid() != 0) {
251 	logerrstr("%s: A background dntpd is running, you must kill it first",
252 		av[0]);
253 	exit(1);
254     }
255     if (debug_opt == 0) {
256 	log_stderr = 0;
257 	set_pid(av[0]);
258 	signal(SIGINT, sigint_handler);
259 	logdebug(0, "dntpd version %s started\n", DNTPD_VERSION);
260     }
261 
262     /*
263      * And go.
264      */
265     sysntp_clear_alternative_corrections();
266     client_init();
267     client_check_duplicate_ips(servers, nservers);
268     rc = client_main(servers, nservers);
269     return(rc);
270 }
271 
272 static
273 void
274 usage(const char *av0)
275 {
276     fprintf(stderr, "%s [-dnqstFSQ] [-f config_file] [-l log_level] [-T poll_interval] [-L poll_limit] [additional_targets]\n", av0);
277     fprintf(stderr,
278 	"\t-d\tDebugging mode, implies -F, -l 99, and logs to stderr\n"
279 	"\t-f file\tSpecify the config file (/etc/dntpd.conf)\n"
280 	"\t-l int\tSet log level (0-4), default 1\n"
281 	"\t-n\tNo-update mode.  No offset or frequency corrections are made\n"
282 	"\t-q\tQuiet-mode, same as -L 0\n"
283 	"\t-s\tSet the time immediately on startup\n"
284 	"\t-t\tTest mode, implies -F, -l 99, -n, logs to stderr\n"
285 	"\t-F\tRun in foreground (log still goes to syslog)\n"
286 	"\t-L int\tMaximum polling interval\n"
287 	"\t-S\tDo not set the time immediately on startup\n"
288 	"\t-T int\tNominal polling interval\n"
289 	"\t-Q\tTerminate any running background daemon\n"
290 	"\n"
291 	"\t\tNOTE: in debug and test modes -f must be specified if\n"
292 	"\t\tyou want to use a config file.\n"
293     );
294     exit(1);
295 }
296 
297 static
298 void
299 dotest(const char *target)
300 {
301     struct server_info info;
302 
303     bzero(&info, sizeof(info));
304     info.sam = (struct sockaddr *)&info.sam_st;
305     info.fd = udp_socket(target, 123, info.sam, LOG_DNS_ERROR);
306     if (info.fd < 0) {
307 	logerrstr("unable to create UDP socket for %s", target);
308 	return;
309     }
310     info.target = strdup(target);
311     client_init();
312 
313     fprintf(stderr,
314 	    "Will run %d-second polls until interrupted.\n", nom_sleep_opt);
315 
316     for (;;) {
317 	client_poll(&info, nom_sleep_opt, 1);
318 	sleep(nom_sleep_opt);
319     }
320     /* not reached */
321 }
322 
323 static char *
324 myaddr2ascii(struct sockaddr *sa)
325 {
326 	static char str[INET6_ADDRSTRLEN];
327 	struct sockaddr_in *soin;
328 	struct sockaddr_in6 *sin6;
329 
330 	switch (sa->sa_family) {
331 	case AF_INET:
332 		soin = (struct sockaddr_in *) sa;
333 		inet_ntop(AF_INET, &soin->sin_addr, str, sizeof(str));
334 		break;
335 	case AF_INET6:
336 		sin6 = (struct sockaddr_in6 *) sa;
337 		inet_ntop(AF_INET6, &sin6->sin6_addr, str, sizeof(str));
338 		break;
339 	}
340 	return (str);
341 }
342 
343 static void
344 add_server(const char *target)
345 {
346     server_info_t info;
347 
348     if (nservers == maxservers) {
349 	maxservers += 16;
350 	servers = realloc(servers, maxservers * sizeof(server_info_t));
351 	assert(servers != NULL);
352     }
353     info = malloc(sizeof(struct server_info));
354     servers[nservers] = info;
355     bzero(info, sizeof(struct server_info));
356     info->sam = (struct sockaddr *)&info->sam_st;
357     info->target = strdup(target);
358     /*
359      * Postpone socket opening and server name resolution until we are in main
360      * loop to avoid hang during init if network down.
361      */
362     info->fd = -1;
363     info->server_state = -1;
364     ++nservers;
365 }
366 
367 void
368 disconnect_server(server_info_t info)
369 {
370     if (info->fd >= 0)
371 	close(info->fd);
372     info->fd = -1;
373     if (info->ipstr) {
374 	free(info->ipstr);
375 	info->ipstr = NULL;
376     }
377 }
378 
379 void
380 reconnect_server(server_info_t info)
381 {
382     const char *ipstr;
383     dns_error_policy_t policy;
384 
385     /*
386      * Ignore DNS errors if never connected before to handle the case where
387      * we're started before network up.
388      */
389     policy = IGNORE_DNS_ERROR;
390     if (info->fd >= 0) {
391 	close(info->fd);
392 	policy = LOG_DNS_ERROR;
393     }
394     if (info->ipstr) {
395 	free(info->ipstr);
396 	info->ipstr = NULL;
397     }
398     info->sam = (struct sockaddr *)&info->sam_st;
399     info->fd = udp_socket(info->target, 123, info->sam, policy);
400     if (info->fd >= 0) {
401 	ipstr = myaddr2ascii(info->sam);
402 	info->ipstr = strdup(ipstr);
403     }
404 }
405 
406 static void
407 process_config_file(const char *path)
408 {
409     const char *ws = " \t\r\n";
410     char buf[1024];
411     char *keyword;
412     char *data;
413     int line;
414     FILE *fi;
415 
416     if ((fi = fopen(path, "r")) != NULL) {
417 	line = 1;
418 	while (fgets(buf, sizeof(buf), fi) != NULL) {
419 	    if (strchr(buf, '#'))
420 		*strchr(buf, '#') = 0;
421 	    if ((keyword = strtok(buf, ws)) != NULL) {
422 		data = strtok(NULL, ws);
423 		if (strcmp(keyword, "server") == 0) {
424 		    if (data == NULL) {
425 			logerr("%s:%d server missing host specification",
426 				path, line);
427 		    } else {
428 			add_server(data);
429 		    }
430 		} else {
431 		    logerr("%s:%d unknown keyword %s", path, line, keyword);
432 		}
433 	    }
434 	    ++line;
435 	}
436 	fclose(fi);
437     } else {
438 	logerr("Unable to open %s", path);
439 	exit(1);
440     }
441 }
442 
443 static
444 pid_t
445 check_pid(void)
446 {
447     char buf[32];
448     pid_t pid;
449     FILE *fi;
450 
451     pid = 0;
452     if ((fi = fopen(pid_opt, "r")) != NULL) {
453 	if (fgets(buf, sizeof(buf), fi) != NULL) {
454 	    pid = strtol(buf, NULL, 0);
455 	    if (kill(pid, 0) != 0)
456 		pid = 0;
457 	}
458 	fclose(fi);
459     }
460     return(pid);
461 }
462 
463 static
464 void
465 set_pid(const char *av0)
466 {
467     pid_t pid;
468     FILE *fo;
469 
470     pid = getpid();
471     if ((fo = fopen(pid_opt, "w")) != NULL) {
472 	fprintf(fo, "%d\n", (int)pid);
473 	fclose(fo);
474     } else {
475 	logerr("%s: Unable to create %s, continuing anyway.", av0, pid_opt);
476     }
477 }
478 
479 static
480 void
481 sigint_handler(int signo __unused)
482 {
483     remove(pid_opt);
484     /* dangerous, but we are exiting anyway so pray... */
485     logdebug(0, "dntpd version %s stopped\n", DNTPD_VERSION);
486     exit(0);
487 }
488 
489