systat - clarify 'Free' in systat -vm 1
[dragonfly.git] / usr.sbin / dntpd / main.c
blob0759fcf75306dbcbecf1e511b0a665587e938380
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 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";
64 int
65 main(int ac, char **av)
67 int test_opt = 0;
68 pid_t pid;
69 int rc;
70 int ch;
71 int i;
74 * Really randomize
76 srandomdev();
77 rc = 0;
80 * Process Options
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;
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;
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]);
168 } else {
169 fprintf(stderr, "%s: There is no daemon running to kill.\n", av[0]);
171 exit(0);
172 break;
173 case 'h':
174 default:
175 usage(av[0]);
176 /* not reached */
181 * Make sure min and nom intervals are less then or equal to the maximum
182 * interval.
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;
190 * Set default config file
192 if (config_opt == NULL) {
193 if (optind != ac)
194 config_opt = "/dev/null";
195 else
196 config_opt = "/etc/dntpd.conf";
199 if (debug_level < 0)
200 debug_level = 1;
202 process_config_file(config_opt);
204 if (debug_opt == 0)
205 openlog("dntpd", LOG_CONS|LOG_PID, LOG_DAEMON);
207 if (test_opt) {
208 if (optind != ac - 1)
209 usage(av[0]);
210 dotest(av[optind]);
211 /* not reached */
215 * Add additional hosts.
217 for (i = optind; i < ac; ++i) {
218 add_server(av[i]);
220 if (nservers == 0) {
221 usage(av[0]);
222 /* not reached */
226 * Do an initial course time setting if requested using the first
227 * host successfully polled.
229 /* XXX */
232 * Daemonize, stop logging to stderr.
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);
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);
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);
263 * And go.
265 sysntp_clear_alternative_corrections();
266 client_init();
267 client_check_duplicate_ips(servers, nservers);
268 rc = client_main(servers, nservers);
269 return(rc);
272 static
273 void
274 usage(const char *av0)
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"
294 exit(1);
297 static
298 void
299 dotest(const char *target)
301 struct server_info info;
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;
310 info.target = strdup(target);
311 client_init();
313 fprintf(stderr,
314 "Will run %d-second polls until interrupted.\n", nom_sleep_opt);
316 for (;;) {
317 client_poll(&info, nom_sleep_opt, 1);
318 sleep(nom_sleep_opt);
320 /* not reached */
323 static char *
324 myaddr2ascii(struct sockaddr *sa)
326 static char str[INET6_ADDRSTRLEN];
327 struct sockaddr_in *soin;
328 struct sockaddr_in6 *sin6;
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;
340 return (str);
343 static void
344 add_server(const char *target)
346 server_info_t info;
348 if (nservers == maxservers) {
349 maxservers += 16;
350 servers = realloc(servers, maxservers * sizeof(server_info_t));
351 assert(servers != NULL);
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);
359 * Postpone socket opening and server name resolution until we are in main
360 * loop to avoid hang during init if network down.
362 info->fd = -1;
363 info->server_state = -1;
364 ++nservers;
367 void
368 disconnect_server(server_info_t info)
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;
379 void
380 reconnect_server(server_info_t info)
382 const char *ipstr;
383 dns_error_policy_t policy;
386 * Ignore DNS errors if never connected before to handle the case where
387 * we're started before network up.
389 policy = IGNORE_DNS_ERROR;
390 if (info->fd >= 0) {
391 close(info->fd);
392 policy = LOG_DNS_ERROR;
394 if (info->ipstr) {
395 free(info->ipstr);
396 info->ipstr = NULL;
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);
406 static void
407 process_config_file(const char *path)
409 const char *ws = " \t\r\n";
410 char buf[1024];
411 char *keyword;
412 char *data;
413 int line;
414 FILE *fi;
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);
430 } else {
431 logerr("%s:%d unknown keyword %s", path, line, keyword);
434 ++line;
436 fclose(fi);
437 } else {
438 logerr("Unable to open %s", path);
439 exit(1);
443 static
444 pid_t
445 check_pid(void)
447 char buf[32];
448 pid_t pid;
449 FILE *fi;
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;
458 fclose(fi);
460 return(pid);
463 static
464 void
465 set_pid(const char *av0)
467 pid_t pid;
468 FILE *fo;
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);
479 static
480 void
481 sigint_handler(int signo __unused)
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);