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 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"; 62 63 int 64 main(int ac, char **av) 65 { 66 int test_opt = 0; 67 pid_t pid; 68 int rc; 69 int ch; 70 int i; 71 72 /* 73 * Really randomize 74 */ 75 srandomdev(); 76 rc = 0; 77 78 /* 79 * Process Options 80 */ 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; 136 } 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; 141 } 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]); 160 } 161 } else { 162 fprintf(stderr, "%s: There is no daemon running to kill.\n", av[0]); 163 } 164 exit(0); 165 break; 166 case 'h': 167 default: 168 usage(av[0]); 169 /* not reached */ 170 } 171 } 172 173 /* 174 * Make sure min and nom intervals are less then or equal to the maximum 175 * interval. 176 */ 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; 181 182 /* 183 * Set default config file 184 */ 185 if (config_opt == NULL) { 186 if (optind != ac) 187 config_opt = "/dev/null"; 188 else 189 config_opt = "/etc/dntpd.conf"; 190 } 191 192 if (debug_level < 0) 193 debug_level = 1; 194 195 process_config_file(config_opt); 196 197 if (debug_opt == 0) 198 openlog("dntpd", LOG_CONS|LOG_PID, LOG_DAEMON); 199 200 if (test_opt) { 201 if (optind != ac - 1) 202 usage(av[0]); 203 dotest(av[optind]); 204 /* not reached */ 205 } 206 207 /* 208 * Add additional hosts. 209 */ 210 for (i = optind; i < ac; ++i) { 211 add_server(av[i]); 212 } 213 if (nservers == 0) { 214 usage(av[0]); 215 /* not reached */ 216 } 217 218 /* 219 * Do an initial course time setting if requested using the first 220 * host successfully polled. 221 */ 222 /* XXX */ 223 224 /* 225 * Daemonize, stop logging to stderr. 226 */ 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); 240 } 241 } 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); 247 } 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); 253 } 254 255 /* 256 * And go. 257 */ 258 sysntp_clear_alternative_corrections(); 259 client_init(); 260 client_check_duplicate_ips(servers, nservers); 261 rc = client_main(servers, nservers); 262 return(rc); 263 } 264 265 static 266 void 267 usage(const char *av0) 268 { 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" 286 ); 287 exit(1); 288 } 289 290 static 291 void 292 dotest(const char *target) 293 { 294 struct server_info info; 295 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; 301 } 302 info.target = strdup(target); 303 client_init(); 304 305 fprintf(stderr, 306 "Will run %d-second polls until interrupted.\n", nom_sleep_opt); 307 308 for (;;) { 309 client_poll(&info, nom_sleep_opt, 1); 310 sleep(nom_sleep_opt); 311 } 312 /* not reached */ 313 } 314 315 static void 316 add_server(const char *target) 317 { 318 server_info_t info; 319 const char *ipstr; 320 321 if (nservers == maxservers) { 322 maxservers += 16; 323 servers = realloc(servers, maxservers * sizeof(server_info_t)); 324 assert(servers != NULL); 325 } 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"); 337 } 338 ++nservers; 339 } 340 341 void 342 disconnect_server(server_info_t info) 343 { 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; 350 } 351 } 352 353 void 354 reconnect_server(server_info_t info) 355 { 356 const char *ipstr; 357 358 if (info->fd >= 0) 359 close(info->fd); 360 if (info->ipstr) { 361 free(info->ipstr); 362 info->ipstr = NULL; 363 } 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); 369 } 370 } 371 372 static void 373 process_config_file(const char *path) 374 { 375 const char *ws = " \t\r\n"; 376 char buf[1024]; 377 char *keyword; 378 char *data; 379 int line; 380 FILE *fi; 381 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); 395 } 396 } else { 397 logerr("%s:%d unknown keyword %s", path, line, keyword); 398 } 399 } 400 ++line; 401 } 402 fclose(fi); 403 } else { 404 logerr("Unable to open %s", path); 405 exit(1); 406 } 407 } 408 409 static 410 pid_t 411 check_pid(void) 412 { 413 char buf[32]; 414 pid_t pid; 415 FILE *fi; 416 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; 423 } 424 fclose(fi); 425 } 426 return(pid); 427 } 428 429 static 430 void 431 set_pid(const char *av0) 432 { 433 pid_t pid; 434 FILE *fo; 435 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); 442 } 443 } 444 445 static 446 void 447 sigint_handler(int signo __unused) 448 { 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); 453 } 454 455