Sync with FreeBSD (adds reload).
[dragonfly.git] / usr.sbin / dntpd / main.c
blobc48e61abbf4d7b193606ecfdc4a0a2aee3f9c9d5
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.
34 * $DragonFly: src/usr.sbin/dntpd/main.c,v 1.11 2007/06/26 02:40:20 dillon Exp $
37 #include "defs.h"
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);
47 static struct server_info **servers;
48 static int nservers;
49 static int maxservers;
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 double insane_deviation = 0.5; /* 0.5 seconds of deviation == insane */
60 const char *config_opt; /* config file */
61 const char *pid_opt = "/var/run/dntpd.pid";
63 int
64 main(int ac, char **av)
66 int test_opt = 0;
67 pid_t pid;
68 int rc;
69 int ch;
70 int i;
73 * Really randomize
75 srandomdev();
76 rc = 0;
79 * Process Options
81 while ((ch = getopt(ac, av, "df:i:l:np:qstFL:QST:")) != -1) {
82 switch(ch) {
83 case 'd':
84 debug_opt = 1;
85 daemon_opt = 0;
86 if (debug_level < 0)
87 debug_level = 99;
88 if (config_opt == NULL)
89 config_opt = "/dev/null";
90 break;
91 case 'p':
92 pid_opt = optarg;
93 break;
94 case 'f':
95 config_opt = optarg;
96 break;
97 case 'i':
98 insane_deviation = strtod(optarg, NULL);
99 break;
100 case 'l':
101 debug_level = strtol(optarg, NULL, 0);
102 break;
103 case 'n':
104 no_update_opt = 1;
105 break;
106 case 'q':
107 debug_level = 0;
108 break;
109 case 's':
110 quickset_opt = 1;
111 break;
112 case 'S':
113 quickset_opt = 0;
114 break;
115 case 't':
116 test_opt = 1;
117 debug_opt = 1;
118 daemon_opt = 0;
119 if (debug_level < 0)
120 debug_level = 99;
121 if (config_opt == NULL)
122 config_opt = "/dev/null";
123 break;
124 case 'F':
125 daemon_opt = 0;
126 break;
127 case 'L':
128 max_sleep_opt = strtol(optarg, NULL, 0);
129 break;
130 case 'T':
131 nom_sleep_opt = strtol(optarg, NULL, 0);
132 if (nom_sleep_opt < 1) {
133 fprintf(stderr, "Warning: nominal poll interval too small, "
134 "limiting to 1 second\n");
135 nom_sleep_opt = 1;
137 if (nom_sleep_opt > 24 * 60 * 60) {
138 fprintf(stderr, "Warning: nominal poll interval too large, "
139 "limiting to 24 hours\n");
140 nom_sleep_opt = 24 * 60 * 60;
142 if (min_sleep_opt > nom_sleep_opt)
143 min_sleep_opt = nom_sleep_opt;
144 if (max_sleep_opt < nom_sleep_opt * 5)
145 max_sleep_opt = nom_sleep_opt * 5;
146 break;
147 case 'Q':
148 if ((pid = check_pid()) != 0) {
149 fprintf(stderr, "%s: killing old daemon\n", av[0]);
150 kill(pid, SIGINT);
151 usleep(100000);
152 if (check_pid())
153 sleep(1);
154 if (check_pid())
155 sleep(9);
156 if (check_pid()) {
157 fprintf(stderr, "%s: Unable to kill running daemon.\n", av[0]);
158 } else {
159 fprintf(stderr, "%s: Running daemon has been terminated.\n", av[0]);
161 } else {
162 fprintf(stderr, "%s: There is no daemon running to kill.\n", av[0]);
164 exit(0);
165 break;
166 case 'h':
167 default:
168 usage(av[0]);
169 /* not reached */
174 * Make sure min and nom intervals are less then or equal to the maximum
175 * interval.
177 if (min_sleep_opt > max_sleep_opt)
178 min_sleep_opt = max_sleep_opt;
179 if (nom_sleep_opt > max_sleep_opt)
180 nom_sleep_opt = max_sleep_opt;
183 * Set default config file
185 if (config_opt == NULL) {
186 if (optind != ac)
187 config_opt = "/dev/null";
188 else
189 config_opt = "/etc/dntpd.conf";
192 if (debug_level < 0)
193 debug_level = 1;
195 process_config_file(config_opt);
197 if (debug_opt == 0)
198 openlog("dntpd", LOG_CONS|LOG_PID, LOG_DAEMON);
200 if (test_opt) {
201 if (optind != ac - 1)
202 usage(av[0]);
203 dotest(av[optind]);
204 /* not reached */
208 * Add additional hosts.
210 for (i = optind; i < ac; ++i) {
211 add_server(av[i]);
213 if (nservers == 0) {
214 usage(av[0]);
215 /* not reached */
219 * Do an initial course time setting if requested using the first
220 * host successfully polled.
222 /* XXX */
225 * Daemonize, stop logging to stderr.
227 if (daemon_opt) {
228 if ((pid = check_pid()) != 0) {
229 logerrstr("%s: NOTE: killing old daemon and starting a new one",
230 av[0]);
231 kill(pid, SIGINT);
232 usleep(100000);
233 if (check_pid())
234 sleep(1);
235 if (check_pid())
236 sleep(9);
237 if (check_pid()) {
238 logerrstr("%s: Unable to kill running daemon, exiting", av[0]);
239 exit(1);
242 daemon(0, 0);
243 } else if (check_pid() != 0) {
244 logerrstr("%s: A background dntpd is running, you must kill it first",
245 av[0]);
246 exit(1);
248 if (debug_opt == 0) {
249 log_stderr = 0;
250 set_pid(av[0]);
251 signal(SIGINT, sigint_handler);
252 logdebug(0, "dntpd version %s started\n", DNTPD_VERSION);
256 * And go.
258 sysntp_clear_alternative_corrections();
259 client_init();
260 client_check_duplicate_ips(servers, nservers);
261 rc = client_main(servers, nservers);
262 return(rc);
265 static
266 void
267 usage(const char *av0)
269 fprintf(stderr, "%s [-dnqstFSQ] [-f config_file] [-l log_level] [-T poll_interval] [-L poll_limit] [additional_targets]\n", av0);
270 fprintf(stderr,
271 "\t-d\tDebugging mode, implies -F, -l 99, and logs to stderr\n"
272 "\t-f file\tSpecify the config file (/etc/dntpd.conf)\n"
273 "\t-l int\tSet log level (0-4), default 1\n"
274 "\t-n\tNo-update mode. No offset or frequency corrections are made\n"
275 "\t-q\tQuiet-mode, same as -L 0\n"
276 "\t-s\tSet the time immediately on startup\n"
277 "\t-t\tTest mode, implies -F, -l 99, -n, logs to stderr\n"
278 "\t-F\tRun in foreground (log still goes to syslog)\n"
279 "\t-L int\tMaximum polling interval\n"
280 "\t-S\tDo not set the time immediately on startup\n"
281 "\t-T int\tNominal polling interval\n"
282 "\t-Q\tTerminate any running background daemon\n"
283 "\n"
284 "\t\tNOTE: in debug and test modes -f must be specified if\n"
285 "\t\tyou want to use a config file.\n"
287 exit(1);
290 static
291 void
292 dotest(const char *target)
294 struct server_info info;
296 bzero(&info, sizeof(info));
297 info.fd = udp_socket(target, 123, &info.sam);
298 if (info.fd < 0) {
299 logerrstr("unable to create UDP socket for %s", target);
300 return;
302 info.target = strdup(target);
303 client_init();
305 fprintf(stderr,
306 "Will run %d-second polls until interrupted.\n", nom_sleep_opt);
308 for (;;) {
309 client_poll(&info, nom_sleep_opt, 1);
310 sleep(nom_sleep_opt);
312 /* not reached */
315 static void
316 add_server(const char *target)
318 server_info_t info;
319 const char *ipstr;
321 if (nservers == maxservers) {
322 maxservers += 16;
323 servers = realloc(servers, maxservers * sizeof(server_info_t));
324 assert(servers != NULL);
326 info = malloc(sizeof(struct server_info));
327 servers[nservers] = info;
328 bzero(info, sizeof(struct server_info));
329 info->fd = udp_socket(target, 123, &info->sam);
330 info->target = strdup(target);
331 if (info->fd >= 0) {
332 ipstr = addr2ascii(AF_INET, &info->sam.sin_addr,
333 sizeof(info->sam.sin_addr), NULL);
334 info->ipstr = strdup(ipstr);
335 } else {
336 client_setserverstate(info, -1, "DNS or IP lookup failure");
338 ++nservers;
341 void
342 disconnect_server(server_info_t info)
344 if (info->fd >= 0)
345 close(info->fd);
346 info->fd = -1;
347 if (info->ipstr) {
348 free(info->ipstr);
349 info->ipstr = NULL;
353 void
354 reconnect_server(server_info_t info)
356 const char *ipstr;
358 if (info->fd >= 0)
359 close(info->fd);
360 if (info->ipstr) {
361 free(info->ipstr);
362 info->ipstr = NULL;
364 info->fd = udp_socket(info->target, 123, &info->sam);
365 if (info->fd >= 0) {
366 ipstr = addr2ascii(AF_INET, &info->sam.sin_addr,
367 sizeof(info->sam.sin_addr), NULL);
368 info->ipstr = strdup(ipstr);
372 static void
373 process_config_file(const char *path)
375 const char *ws = " \t\r\n";
376 char buf[1024];
377 char *keyword;
378 char *data;
379 int line;
380 FILE *fi;
382 if ((fi = fopen(path, "r")) != NULL) {
383 line = 1;
384 while (fgets(buf, sizeof(buf), fi) != NULL) {
385 if (strchr(buf, '#'))
386 *strchr(buf, '#') = 0;
387 if ((keyword = strtok(buf, ws)) != NULL) {
388 data = strtok(NULL, ws);
389 if (strcmp(keyword, "server") == 0) {
390 if (data == NULL) {
391 logerr("%s:%d server missing host specification",
392 path, line);
393 } else {
394 add_server(data);
396 } else {
397 logerr("%s:%d unknown keyword %s", path, line, keyword);
400 ++line;
402 fclose(fi);
403 } else {
404 logerr("Unable to open %s", path);
405 exit(1);
409 static
410 pid_t
411 check_pid(void)
413 char buf[32];
414 pid_t pid;
415 FILE *fi;
417 pid = 0;
418 if ((fi = fopen(pid_opt, "r")) != NULL) {
419 if (fgets(buf, sizeof(buf), fi) != NULL) {
420 pid = strtol(buf, NULL, 0);
421 if (kill(pid, 0) != 0)
422 pid = 0;
424 fclose(fi);
426 return(pid);
429 static
430 void
431 set_pid(const char *av0)
433 pid_t pid;
434 FILE *fo;
436 pid = getpid();
437 if ((fo = fopen(pid_opt, "w")) != NULL) {
438 fprintf(fo, "%d\n", (int)pid);
439 fclose(fo);
440 } else {
441 logerr("%s: Unable to create %s, continuing anyway.", av0, pid_opt);
445 static
446 void
447 sigint_handler(int signo __unused)
449 remove(pid_opt);
450 /* dangerous, but we are exiting anyway so pray... */
451 logdebug(0, "dntpd version %s stopped\n", DNTPD_VERSION);
452 exit(0);