/* * Copyright (c) 2005 The DragonFly Project. All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Matthew Dillon * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of The DragonFly Project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific, prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $DragonFly: src/usr.sbin/dntpd/main.c,v 1.9 2005/04/27 00:42:13 dillon Exp $ */ #include "defs.h" static void usage(const char *av0); static void dotest(const char *target); static void add_server(const char *target); static void process_config_file(const char *path); static pid_t check_pid(void); static void set_pid(const char *av0); static void sigint_handler(int signo __unused); static struct server_info **servers; static int nservers; static int maxservers; int daemon_opt = 1; int debug_opt = 0; int debug_level = -1; /* (set to default later) */ int quickset_opt = 0; /* immediately set time of day on startup */ int no_update_opt = 0; /* do not make any actual updates */ int min_sleep_opt = 5; /* 5 seconds minimum poll interval */ int nom_sleep_opt = 300; /* 5 minutes nominal poll interval */ int max_sleep_opt = 1800; /* 30 minutes maximum poll interval */ const char *config_opt; /* config file */ const char *pid_opt = "/var/run/dntpd.pid"; int main(int ac, char **av) { int test_opt = 0; pid_t pid; int rc; int ch; int i; /* * Really randomize */ srandomdev(); rc = 0; /* * Process Options */ while ((ch = getopt(ac, av, "df:l:np:qstFL:QST:")) != -1) { switch(ch) { case 'd': debug_opt = 1; daemon_opt = 0; if (debug_level < 0) debug_level = 99; if (config_opt == NULL) config_opt = "/dev/null"; break; case 'p': pid_opt = optarg; break; case 'f': config_opt = optarg; break; case 'l': debug_level = strtol(optarg, NULL, 0); break; case 'n': no_update_opt = 1; break; case 'q': debug_level = 0; break; case 's': quickset_opt = 1; break; case 'S': quickset_opt = 0; break; case 't': test_opt = 1; debug_opt = 1; daemon_opt = 0; if (debug_level < 0) debug_level = 99; if (config_opt == NULL) config_opt = "/dev/null"; break; case 'F': daemon_opt = 0; break; case 'L': max_sleep_opt = strtol(optarg, NULL, 0); break; case 'T': nom_sleep_opt = strtol(optarg, NULL, 0); if (nom_sleep_opt < 1) { fprintf(stderr, "Warning: nominal poll interval too small, " "limiting to 1 second\n"); nom_sleep_opt = 1; } if (nom_sleep_opt > 24 * 60 * 60) { fprintf(stderr, "Warning: nominal poll interval too large, " "limiting to 24 hours\n"); nom_sleep_opt = 24 * 60 * 60; } if (min_sleep_opt > nom_sleep_opt) min_sleep_opt = nom_sleep_opt; if (max_sleep_opt < nom_sleep_opt * 5) max_sleep_opt = nom_sleep_opt * 5; break; case 'Q': if ((pid = check_pid()) != 0) { fprintf(stderr, "%s: killing old daemon\n", av[0]); kill(pid, SIGINT); usleep(100000); if (check_pid()) sleep(1); if (check_pid()) sleep(9); if (check_pid()) { fprintf(stderr, "%s: Unable to kill running daemon.\n", av[0]); } else { fprintf(stderr, "%s: Running daemon has been terminated.\n", av[0]); } } else { fprintf(stderr, "%s: There is no daemon running to kill.\n", av[0]); } exit(0); break; case 'h': default: usage(av[0]); /* not reached */ } } if (config_opt == NULL) { if (optind != ac) config_opt = "/dev/null"; else config_opt = "/etc/dntpd.conf"; } if (debug_level < 0) debug_level = 1; process_config_file(config_opt); if (debug_opt == 0) openlog("dntpd", LOG_CONS|LOG_PID, LOG_DAEMON); if (test_opt) { if (optind != ac - 1) usage(av[0]); dotest(av[optind]); /* not reached */ } /* * Add additional hosts. */ for (i = optind; i < ac; ++i) { add_server(av[i]); } if (nservers == 0) { usage(av[0]); /* not reached */ } /* * Do an initial course time setting if requested using the first * host successfully polled. */ /* XXX */ /* * Daemonize, stop logging to stderr. */ if (daemon_opt) { if ((pid = check_pid()) != 0) { logerrstr("%s: NOTE: killing old daemon and starting a new one", av[0]); kill(pid, SIGINT); usleep(100000); if (check_pid()) sleep(1); if (check_pid()) sleep(9); if (check_pid()) { logerrstr("%s: Unable to kill running daemon, exiting", av[0]); exit(1); } } daemon(0, 0); } else if (check_pid() != 0) { logerrstr("%s: A background dntpd is running, you must kill it first", av[0]); exit(1); } if (debug_opt == 0) { log_stderr = 0; set_pid(av[0]); signal(SIGINT, sigint_handler); logdebug(0, "dntpd version %s started\n", DNTPD_VERSION); } /* * And go. */ sysntp_clear_alternative_corrections(); client_init(); rc = client_main(servers, nservers); return(rc); } static void usage(const char *av0) { fprintf(stderr, "%s [-dnqstFSQ] [-f config_file] [-l log_level] [-T poll_interval] [-L poll_limit] [additional_targets]\n", av0); fprintf(stderr, "\t-d\tDebugging mode, implies -F, -l 99, and logs to stderr\n" "\t-f file\tSpecify the config file (/etc/dntpd.conf)\n" "\t-l int\tSet log level (0-4), default 1\n" "\t-n\tNo-update mode. No offset or frequency corrections are made\n" "\t-q\tQuiet-mode, same as -L 0\n" "\t-s\tSet the time immediately on startup\n" "\t-t\tTest mode, implies -F, -l 99, -n, logs to stderr\n" "\t-F\tRun in foreground (log still goes to syslog)\n" "\t-L int\tMaximum polling interval\n" "\t-S\tDo not set the time immediately on startup\n" "\t-T int\tNominal polling interval\n" "\t-Q\tTerminate any running background daemon\n" "\n" "\t\tNOTE: in debug and test modes -f must be specified if\n" "\t\tyou want to use a config file.\n" ); exit(1); } static void dotest(const char *target) { struct server_info info; bzero(&info, sizeof(info)); info.fd = udp_socket(target, 123); if (info.fd < 0) { logerrstr("unable to create UDP socket for %s", target); return; } info.target = strdup(target); client_init(); fprintf(stderr, "Will run %d-second polls until interrupted.\n", nom_sleep_opt); for (;;) { client_poll(&info, nom_sleep_opt, 1); sleep(nom_sleep_opt); } /* not reached */ } static void add_server(const char *target) { server_info_t info; if (nservers == maxservers) { maxservers += 16; servers = realloc(servers, maxservers * sizeof(server_info_t)); assert(servers != NULL); } info = malloc(sizeof(struct server_info)); servers[nservers] = info; bzero(info, sizeof(struct server_info)); info->fd = udp_socket(target, 123); if (info->fd < 0) { logerrstr("Unable to add server %s", target); } else { info->target = strdup(target); ++nservers; } } static void process_config_file(const char *path) { const char *ws = " \t\r\n"; char buf[1024]; char *keyword; char *data; int line; FILE *fi; if ((fi = fopen(path, "r")) != NULL) { line = 1; while (fgets(buf, sizeof(buf), fi) != NULL) { if (strchr(buf, '#')) *strchr(buf, '#') = 0; if ((keyword = strtok(buf, ws)) != NULL) { data = strtok(NULL, ws); if (strcmp(keyword, "server") == 0) { if (data == NULL) { logerr("%s:%d server missing host specification", path, line); } else { add_server(data); } } else { logerr("%s:%d unknown keyword %s", path, line, keyword); } } ++line; } fclose(fi); } else { logerr("Unable to open %s", path); exit(1); } } static pid_t check_pid(void) { char buf[32]; pid_t pid; FILE *fi; pid = 0; if ((fi = fopen(pid_opt, "r")) != NULL) { if (fgets(buf, sizeof(buf), fi) != NULL) { pid = strtol(buf, NULL, 0); if (kill(pid, 0) != 0) pid = 0; } fclose(fi); } return(pid); } static void set_pid(const char *av0) { pid_t pid; FILE *fo; pid = getpid(); if ((fo = fopen(pid_opt, "w")) != NULL) { fprintf(fo, "%d\n", (int)pid); fclose(fo); } else { logerr("%s: Unable to create %s, continuing anyway.", av0, pid_opt); } } static void sigint_handler(int signo __unused) { remove(pid_opt); /* dangerous, but we are exiting anyway so pray... */ logdebug(0, "dntpd version %s stopped\n", DNTPD_VERSION); exit(0); }