xref: /dragonfly/usr.sbin/dntpd/main.c (revision 5062ee70)
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 
35 #include "defs.h"
36 
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);
44 
45 static struct server_info **servers;
46 static int nservers;
47 static int maxservers;
48 
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";
61 
62 int
63 main(int ac, char **av)
64 {
65     int test_opt = 0;
66     pid_t pid;
67     int ch;
68     int i;
69 
70     /*
71      * Really randomize
72      */
73     srandomdev();
74 
75     /*
76      * Process Options
77      */
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;
139 	    }
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;
144 	    }
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]);
163 		}
164 	    } else {
165 		fprintf(stderr, "%s: There is no daemon running to kill.\n", av[0]);
166 	    }
167 	    exit(0);
168 	    break;
169 	case 'h':
170 	default:
171 	    usage(av[0]);
172 	    /* not reached */
173 	}
174     }
175 
176     /*
177      * Make sure min and nom intervals are less then or equal to the maximum
178      * interval.
179      */
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;
184 
185     /*
186      * Set default config file
187      */
188     if (config_opt == NULL) {
189 	if (optind != ac)
190 	    config_opt = "/dev/null";
191 	else
192 	    config_opt = "/etc/dntpd.conf";
193     }
194 
195     if (debug_level < 0)
196 	debug_level = 1;
197 
198     process_config_file(config_opt);
199 
200     if (debug_opt == 0)
201 	openlog("dntpd", LOG_CONS|LOG_PID, LOG_DAEMON);
202 
203     if (test_opt) {
204 	if (optind != ac - 1)
205 	    usage(av[0]);
206 	dotest(av[optind]);
207 	/* not reached */
208     }
209 
210     /*
211      * Add additional hosts.
212      */
213     for (i = optind; i < ac; ++i) {
214 	add_server(av[i]);
215     }
216     if (nservers == 0) {
217 	usage(av[0]);
218 	/* not reached */
219     }
220 
221     /*
222      * Do an initial course time setting if requested using the first
223      * host successfully polled.
224      */
225     /* XXX */
226 
227     /*
228      * Daemonize, stop logging to stderr.
229      */
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);
243 	    }
244 	}
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);
250     }
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);
256     }
257 
258     /*
259      * And go.
260      */
261     sysntp_clear_alternative_corrections();
262     client_init();
263     client_check_duplicate_ips(servers, nservers);
264     client_main(servers, nservers);
265     return(0);
266 }
267 
268 static
269 void
270 usage(const char *av0)
271 {
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"
289     );
290     exit(1);
291 }
292 
293 static
294 void
295 dotest(const char *target)
296 {
297     struct server_info info;
298 
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;
305     }
306     info.target = strdup(target);
307     client_init();
308 
309     fprintf(stderr,
310 	    "Will run %d-second polls until interrupted.\n", nom_sleep_opt);
311 
312     for (;;) {
313 	client_poll(&info, nom_sleep_opt, 1);
314 	sleep(nom_sleep_opt);
315     }
316     /* not reached */
317 }
318 
319 static char *
320 myaddr2ascii(struct sockaddr *sa)
321 {
322 	static char str[INET6_ADDRSTRLEN];
323 	struct sockaddr_in *soin;
324 	struct sockaddr_in6 *sin6;
325 
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;
335 	}
336 	return (str);
337 }
338 
339 static void
340 add_server(const char *target)
341 {
342     server_info_t info;
343 
344     if (nservers == maxservers) {
345 	maxservers += 16;
346 	servers = realloc(servers, maxservers * sizeof(server_info_t));
347 	assert(servers != NULL);
348     }
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);
354     /*
355      * Postpone socket opening and server name resolution until we are in main
356      * loop to avoid hang during init if network down.
357      */
358     info->fd = -1;
359     info->server_state = -1;
360     ++nservers;
361 }
362 
363 void
364 disconnect_server(server_info_t info)
365 {
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;
372     }
373 }
374 
375 void
376 reconnect_server(server_info_t info)
377 {
378     const char *ipstr;
379     dns_error_policy_t policy;
380 
381     /*
382      * Ignore DNS errors if never connected before to handle the case where
383      * we're started before network up.
384      */
385     policy = IGNORE_DNS_ERROR;
386     if (info->fd >= 0) {
387 	close(info->fd);
388 	policy = LOG_DNS_ERROR;
389     }
390     if (info->ipstr) {
391 	free(info->ipstr);
392 	info->ipstr = NULL;
393     }
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);
399     }
400 }
401 
402 static void
403 process_config_file(const char *path)
404 {
405     const char *ws = " \t\r\n";
406     char buf[1024];
407     char *keyword;
408     char *data;
409     int line;
410     FILE *fi;
411 
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);
425 		    }
426 		} else {
427 		    logerr("%s:%d unknown keyword %s", path, line, keyword);
428 		}
429 	    }
430 	    ++line;
431 	}
432 	fclose(fi);
433     } else {
434 	logerr("Unable to open %s", path);
435 	exit(1);
436     }
437 }
438 
439 static
440 pid_t
441 check_pid(void)
442 {
443     char buf[32];
444     pid_t pid;
445     FILE *fi;
446 
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;
453 	}
454 	fclose(fi);
455     }
456     return(pid);
457 }
458 
459 static
460 void
461 set_pid(const char *av0)
462 {
463     pid_t pid;
464     FILE *fo;
465 
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);
472     }
473 }
474 
475 static
476 void
477 sigint_handler(int signo __unused)
478 {
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);
483 }
484 
485