diff options
Diffstat (limited to 'contrib/ntp/ntpd/ntp_config.c')
-rw-r--r-- | contrib/ntp/ntpd/ntp_config.c | 2405 |
1 files changed, 2405 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/ntp_config.c b/contrib/ntp/ntpd/ntp_config.c new file mode 100644 index 0000000..e21f5b0 --- /dev/null +++ b/contrib/ntp/ntpd/ntp_config.c @@ -0,0 +1,2405 @@ +/* + * ntp_config.c - read and apply configuration information + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <ctype.h> +#include <sys/param.h> +#include <sys/types.h> +#include <signal.h> +#ifndef SIGCHLD +#define SIGCHLD SIGCLD +#endif +#if !defined(VMS) +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#endif /* VMS */ +#include <sys/time.h> + +#ifdef HAVE_NETINFO +#include <netinfo/ni.h> +#endif + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntp_refclock.h" +#include "ntp_filegen.h" +#include "ntp_stdlib.h" + +#ifdef SYS_WINNT +#include <io.h> +extern HANDLE ResolverThreadHandle; +#endif /* SYS_WINNT */ + +/* + * These routines are used to read the configuration file at + * startup time. An entry in the file must fit on a single line. + * Entries are processed as multiple tokens separated by white space + * Lines are considered terminated when a '#' is encountered. Blank + * lines are ignored. + */ + +/* + * Configuration file name + */ +#ifndef CONFIG_FILE +# ifndef SYS_WINNT +# define CONFIG_FILE "/etc/ntp.conf" +# else /* SYS_WINNT */ +# define CONFIG_FILE "%windir%\\system32\\drivers\\etc\\ntp.conf" +# define ALT_CONFIG_FILE "%windir%\\ntp.conf" +# endif /* SYS_WINNT */ +#endif /* not CONFIG_FILE */ + +/* + * We understand the following configuration entries and defaults. + * + * peer [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ] + * server [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ] + * broadcast [ addr ] [ version 3 ] [ key 0 ] [ ttl 1 ] + * broadcastclient + * multicastclient [ 224.0.1.1 ] + * manycastclient [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ] + * manycastserver [ 224.0.1.1 ] + * broadcastdelay 0.0102 + * restrict [ addr ] [ mask 255.255.255.0 ] ignore|noserve|notrust|noquery + * driftfile file_name + * keys file_name + * statsdir /var/NTP/ + * filegen peerstats [ file peerstats ] [ type day ] [ link ] + * clientlimit [ n ] + * clientperiod [ 3600 ] + * trustedkey [ key ] + * requestkey [ key] + * controlkey [ key ] + * trap [ addr ] + * fudge [ addr ] [ stratum ] [ refid ] ... + * pidfile [ ] + * setvar [ ] + * logfile logfile + * logconfig [+|-|=][{sync|sys|peer|clock}{{,all}{info|statistics|events|status}}]... + * enable auth|bclient|pll|kernel|monitor|stats + * disable auth|bclient|pll|kernel|monitor|stats + * phone ... + * pps device [assert|clear] [hardpps] + */ + +/* + * Types of entries we understand. + */ +#define CONFIG_UNKNOWN 0 + +#define CONFIG_PEER 1 +#define CONFIG_SERVER 2 +#define CONFIG_AUTOMAX 3 +#define CONFIG_DRIFTFILE 4 +#define CONFIG_BROADCAST 5 +#define CONFIG_BROADCASTCLIENT 6 +#define CONFIG_AUTHENTICATE 7 +#define CONFIG_KEYS 8 +#define CONFIG_REVOKE 9 +#define CONFIG_PPS 10 +#define CONFIG_RESTRICT 11 +#define CONFIG_BDELAY 12 +#define CONFIG_TRUSTEDKEY 13 +#define CONFIG_REQUESTKEY 14 +#define CONFIG_CONTROLKEY 15 +#define CONFIG_TRAP 16 +#define CONFIG_FUDGE 17 +#define CONFIG_18 18 /* unused */ +#define CONFIG_STATSDIR 19 +#define CONFIG_FILEGEN 20 +#define CONFIG_STATISTICS 21 +#define CONFIG_PIDFILE 22 +#define CONFIG_SETVAR 23 +#define CONFIG_CLIENTLIMIT 24 +#define CONFIG_CLIENTPERIOD 25 +#define CONFIG_MULTICASTCLIENT 26 +#define CONFIG_ENABLE 27 +#define CONFIG_DISABLE 28 +#define CONFIG_PHONE 29 +#define CONFIG_LOGFILE 30 +#define CONFIG_LOGCONFIG 31 +#define CONFIG_MANYCASTCLIENT 32 +#define CONFIG_MANYCASTSERVER 33 + +#define CONF_MOD_VERSION 1 +#define CONF_MOD_KEY 2 +#define CONF_MOD_MINPOLL 3 +#define CONF_MOD_MAXPOLL 4 +#define CONF_MOD_PREFER 5 +#define CONF_MOD_BURST 6 +#define CONF_MOD_SKEY 7 +#define CONF_MOD_TTL 8 +#define CONF_MOD_MODE 9 +#define CONF_MOD_NOSELECT 10 + +#define CONF_RES_MASK 1 +#define CONF_RES_IGNORE 2 +#define CONF_RES_NOSERVE 3 +#define CONF_RES_NOTRUST 4 +#define CONF_RES_NOQUERY 5 +#define CONF_RES_NOMODIFY 6 +#define CONF_RES_NOPEER 7 +#define CONF_RES_NOTRAP 8 +#define CONF_RES_LPTRAP 9 +#define CONF_RES_NTPPORT 10 +#define CONF_RES_LIMITED 11 + +#define CONF_TRAP_PORT 1 +#define CONF_TRAP_INTERFACE 2 + +#define CONF_FDG_TIME1 1 +#define CONF_FDG_TIME2 2 +#define CONF_FDG_STRATUM 3 +#define CONF_FDG_REFID 4 +#define CONF_FDG_FLAG1 5 +#define CONF_FDG_FLAG2 6 +#define CONF_FDG_FLAG3 7 +#define CONF_FDG_FLAG4 8 + +#define CONF_FGEN_FILE 1 +#define CONF_FGEN_TYPE 2 +#define CONF_FGEN_FLAG_LINK 3 +#define CONF_FGEN_FLAG_NOLINK 4 +#define CONF_FGEN_FLAG_ENABLE 5 +#define CONF_FGEN_FLAG_DISABLE 6 + +#define CONF_PPS_ASSERT 1 +#define CONF_PPS_CLEAR 2 +#define CONF_PPS_HARDPPS 3 + +/* + * Translation table - keywords to function index + */ +struct keyword { + const char *text; + int keytype; +}; + +/* + * Command keywords + */ +static struct keyword keywords[] = { + { "peer", CONFIG_PEER }, + { "server", CONFIG_SERVER }, + { "driftfile", CONFIG_DRIFTFILE }, + { "broadcast", CONFIG_BROADCAST }, + { "broadcastclient", CONFIG_BROADCASTCLIENT }, + { "multicastclient", CONFIG_MULTICASTCLIENT }, + { "manycastclient", CONFIG_MANYCASTCLIENT }, + { "manycastserver", CONFIG_MANYCASTSERVER }, + { "authenticate", CONFIG_AUTHENTICATE }, + { "keys", CONFIG_KEYS }, + { "revoke", CONFIG_REVOKE }, + { "pps", CONFIG_PPS }, + { "automax", CONFIG_AUTOMAX }, + { "restrict", CONFIG_RESTRICT }, + { "broadcastdelay", CONFIG_BDELAY }, + { "trustedkey", CONFIG_TRUSTEDKEY }, + { "requestkey", CONFIG_REQUESTKEY }, + { "controlkey", CONFIG_CONTROLKEY }, + { "trap", CONFIG_TRAP }, + { "fudge", CONFIG_FUDGE }, + { "statsdir", CONFIG_STATSDIR }, + { "filegen", CONFIG_FILEGEN }, + { "statistics", CONFIG_STATISTICS }, + { "pidfile", CONFIG_PIDFILE }, + { "setvar", CONFIG_SETVAR }, + { "clientlimit", CONFIG_CLIENTLIMIT }, + { "clientperiod", CONFIG_CLIENTPERIOD }, + { "enable", CONFIG_ENABLE }, + { "disable", CONFIG_DISABLE }, + { "phone", CONFIG_PHONE }, + { "logfile", CONFIG_LOGFILE }, + { "logconfig", CONFIG_LOGCONFIG }, + { "", CONFIG_UNKNOWN } +}; + +/* + * "peer", "server", "broadcast" modifier keywords + */ +static struct keyword mod_keywords[] = { + { "version", CONF_MOD_VERSION }, + { "key", CONF_MOD_KEY }, + { "minpoll", CONF_MOD_MINPOLL }, + { "maxpoll", CONF_MOD_MAXPOLL }, + { "prefer", CONF_MOD_PREFER }, + { "noselect", CONF_MOD_NOSELECT }, + { "burst", CONF_MOD_BURST }, + { "autokey", CONF_MOD_SKEY }, + { "mode", CONF_MOD_MODE }, /* reference clocks */ + { "ttl", CONF_MOD_TTL }, /* NTP peers */ + { "", CONFIG_UNKNOWN } +}; + +/* + * "restrict" modifier keywords + */ +static struct keyword res_keywords[] = { + { "mask", CONF_RES_MASK }, + { "ignore", CONF_RES_IGNORE }, + { "noserve", CONF_RES_NOSERVE }, + { "notrust", CONF_RES_NOTRUST }, + { "noquery", CONF_RES_NOQUERY }, + { "nomodify", CONF_RES_NOMODIFY }, + { "nopeer", CONF_RES_NOPEER }, + { "notrap", CONF_RES_NOTRAP }, + { "lowpriotrap", CONF_RES_LPTRAP }, + { "ntpport", CONF_RES_NTPPORT }, + { "limited", CONF_RES_LIMITED }, + { "", CONFIG_UNKNOWN } +}; + +/* + * "trap" modifier keywords + */ +static struct keyword trap_keywords[] = { + { "port", CONF_TRAP_PORT }, + { "interface", CONF_TRAP_INTERFACE }, + { "", CONFIG_UNKNOWN } +}; + + +/* + * "fudge" modifier keywords + */ +static struct keyword fudge_keywords[] = { + { "time1", CONF_FDG_TIME1 }, + { "time2", CONF_FDG_TIME2 }, + { "stratum", CONF_FDG_STRATUM }, + { "refid", CONF_FDG_REFID }, + { "flag1", CONF_FDG_FLAG1 }, + { "flag2", CONF_FDG_FLAG2 }, + { "flag3", CONF_FDG_FLAG3 }, + { "flag4", CONF_FDG_FLAG4 }, + { "", CONFIG_UNKNOWN } +}; + + +/* + * "filegen" modifier keywords + */ +static struct keyword filegen_keywords[] = { + { "file", CONF_FGEN_FILE }, + { "type", CONF_FGEN_TYPE }, + { "link", CONF_FGEN_FLAG_LINK }, + { "nolink", CONF_FGEN_FLAG_NOLINK }, + { "enable", CONF_FGEN_FLAG_ENABLE }, + { "disable", CONF_FGEN_FLAG_DISABLE }, + { "", CONFIG_UNKNOWN } +}; + +/* + * "type" modifier keywords + */ +static struct keyword fgen_types[] = { + { "none", FILEGEN_NONE }, + { "pid", FILEGEN_PID }, + { "day", FILEGEN_DAY }, + { "week", FILEGEN_WEEK }, + { "month", FILEGEN_MONTH }, + { "year", FILEGEN_YEAR }, + { "age", FILEGEN_AGE }, + { "", CONFIG_UNKNOWN} +}; + +/* + * "enable", "disable" modifier keywords + */ +static struct keyword flags_keywords[] = { + { "auth", PROTO_AUTHENTICATE }, + { "bclient", PROTO_BROADCLIENT }, + { "ntp", PROTO_NTP }, + { "kernel", PROTO_KERNEL }, + { "monitor", PROTO_MONITOR }, + { "stats", PROTO_FILEGEN }, + { "", CONFIG_UNKNOWN } +}; + +/* + * pps modifier keywords + */ +static struct keyword pps_keywords[] = { + { "assert", CONF_PPS_ASSERT }, + { "clear", CONF_PPS_CLEAR }, + { "hardpps", CONF_PPS_HARDPPS }, + { "", CONFIG_UNKNOWN } +}; + +/* + * "logconfig" building blocks + */ +struct masks { + const char *name; + unsigned long mask; +}; + +static struct masks logcfg_class[] = { + { "sys", NLOG_OSYS }, + { "peer", NLOG_OPEER }, + { "clock", NLOG_OCLOCK }, + { "sync", NLOG_OSYNC }, + { (char *)0, 0 } +}; + +static struct masks logcfg_item[] = { + { "info", NLOG_INFO }, + { "allinfo", NLOG_SYSINFO|NLOG_PEERINFO|NLOG_CLOCKINFO|NLOG_SYNCINFO }, + { "events", NLOG_EVENT }, + { "allevents", NLOG_SYSEVENT|NLOG_PEEREVENT|NLOG_CLOCKEVENT|NLOG_SYNCEVENT }, + { "status", NLOG_STATUS }, + { "allstatus", NLOG_SYSSTATUS|NLOG_PEERSTATUS|NLOG_CLOCKSTATUS|NLOG_SYNCSTATUS }, + { "statistics", NLOG_STATIST }, + { "allstatistics", NLOG_SYSSTATIST|NLOG_PEERSTATIST|NLOG_CLOCKSTATIST|NLOG_SYNCSTATIST }, + { "allclock", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OCLOCK }, + { "allpeer", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OPEER }, + { "allsys", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OSYS }, + { "allsync", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OSYNC }, + { "all", NLOG_SYSMASK|NLOG_PEERMASK|NLOG_CLOCKMASK|NLOG_SYNCMASK }, + { (char *)0, 0 } +}; + +/* + * Limits on things + */ +#define MAXTOKENS 20 /* 20 tokens on line */ +#define MAXLINE 1024 /* maximum length of line */ +#define MAXPHONE 5 /* maximum number of phone strings */ +#define MAXPPS 20 /* maximum length of PPS device string */ +#define MAXFILENAME 128 /* maximum length of a file name (alloca()?) */ + + +/* + * Miscellaneous macros + */ +#define STRSAME(s1, s2) (*(s1) == *(s2) && strcmp((s1), (s2)) == 0) +#define ISEOL(c) ((c) == '#' || (c) == '\n' || (c) == '\0') +#define ISSPACE(c) ((c) == ' ' || (c) == '\t') +#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) + +/* + * File descriptor used by the resolver save routines, and temporary file + * name. + */ +static FILE *res_fp; +#ifndef SYS_WINNT +static char res_file[20]; /* enough for /tmp/ntpXXXXXX\0 */ +#define RES_TEMPFILE "/tmp/ntpXXXXXX" +#else +static char res_file[MAX_PATH]; +#endif /* SYS_WINNT */ + +/* + * Definitions of things either imported from or exported to outside + */ +char const *progname; +char sys_phone[MAXPHONE][MAXDIAL]; /* ACTS phone numbers */ +char pps_device[MAXPPS + 1]; /* PPS device name */ +int pps_assert = 1; +int pps_hardpps; +int listen_to_virtual_ips = 0; +#if defined(HAVE_SCHED_SETSCHEDULER) +int config_priority_override = 0; +int config_priority; +#endif + +static const char *ntp_options = "aAbc:dD:f:gk:l:Lmnp:P:r:s:t:v:V:x"; + +#ifdef HAVE_NETINFO +/* + * NetInfo configuration state + */ +struct netinfo_config_state { + void *domain; /* domain with config */ + ni_id config_dir; /* ID config dir */ + int prop_index; /* current property */ + int val_index; /* current value */ + char **val_list; /* value list */ +}; +#endif + +/* + * Function prototypes + */ +static unsigned long get_pfxmatch P((char **, struct masks *)); +static unsigned long get_match P((char *, struct masks *)); +static unsigned long get_logmask P((char *)); +#ifdef HAVE_NETINFO +static struct netinfo_config_state *get_netinfo_config P((void)); +static void free_netinfo_config P((struct netinfo_config_state *)); +static int gettokens_netinfo P((struct netinfo_config_state *, char **, int *)); +#endif +static int gettokens P((FILE *, char *, char **, int *)); +static int matchkey P((char *, struct keyword *)); +static int getnetnum P((const char *, struct sockaddr_in *, int)); +static void save_resolve P((char *, int, int, int, int, int, int, u_long)); +static void do_resolve_internal P((void)); +static void abort_resolve P((void)); +#if !defined(VMS) +static RETSIGTYPE catchchild P((int)); +#endif /* VMS */ + +/* + * get_pfxmatch - find value for prefixmatch + * and update char * accordingly + */ +static unsigned long +get_pfxmatch( + char ** s, + struct masks *m + ) +{ + while (m->name) { + if (strncmp(*s, m->name, strlen(m->name)) == 0) { + *s += strlen(m->name); + return m->mask; + } else { + m++; + } + } + return 0; +} + +/* + * get_match - find logmask value + */ +static unsigned long +get_match( + char *s, + struct masks *m + ) +{ + while (m->name) { + if (strcmp(s, m->name) == 0) { + return m->mask; + } else { + m++; + } + } + return 0; +} + +/* + * get_logmask - build bitmask for ntp_syslogmask + */ +static unsigned long +get_logmask( + char *s + ) +{ + char *t; + unsigned long offset; + unsigned long mask; + + t = s; + offset = get_pfxmatch(&t, logcfg_class); + mask = get_match(t, logcfg_item); + + if (mask) + return mask << offset; + else + msyslog(LOG_ERR, "logconfig: illegal argument %s - ignored", s); + + return 0; +} + +/* + * getstartup - search through the options looking for a debugging flag + */ +void +getstartup( + int argc, + char *argv[] + ) +{ + int errflg; + int c; + +#ifdef DEBUG + debug = 0; /* no debugging by default */ +#endif + + /* + * This is a big hack. We don't really want to read command line + * configuration until everything else is initialized, since + * the ability to configure the system may depend on storage + * and the like having been initialized. Except that we also + * don't want to initialize anything until after detaching from + * the terminal, but we won't know to do that until we've + * parsed the command line. Do that now, crudely, and do it + * again later. Our ntp_getopt() is explicitly reusable, by the + * way. Your own mileage may vary. + * + * This hack is even called twice (to allow complete logging to file) + */ + errflg = 0; + progname = argv[0]; + + /* + * Decode argument list + */ + while ((c = ntp_getopt(argc, argv, ntp_options)) != EOF) + switch (c) { +#ifdef DEBUG + case 'd': + ++debug; + break; + case 'D': + debug = (int)atol(ntp_optarg); + printf("Debug1: %s -> %x = %d\n", ntp_optarg, debug, debug); + break; +#else + case 'd': + case 'D': + msyslog(LOG_ERR, "ntpd not compiled with -DDEBUG option - no DEBUG support"); + fprintf(stderr, "ntpd not compiled with -DDEBUG option - no DEBUG support"); + ++errflg; + break; +#endif + case 'L': + listen_to_virtual_ips = 1; + break; + case 'l': + { + FILE *new_file; + + new_file = fopen(ntp_optarg, "a"); + if (new_file != NULL) { + NLOG(NLOG_SYSINFO) + msyslog(LOG_NOTICE, "logging to file %s", ntp_optarg); + if (syslog_file != NULL && + fileno(syslog_file) != fileno(new_file)) + (void)fclose(syslog_file); + + syslog_file = new_file; + syslogit = 0; + } + else + msyslog(LOG_ERR, + "Cannot open log file %s", + ntp_optarg); + } + break; + + case 'n': + ++nofork; + break; + + case '?': + ++errflg; + break; + + default: + break; + } + + if (errflg || ntp_optind != argc) { + (void) fprintf(stderr, "usage: %s [ -abdgmnx ] [ -c config_file ] [ -e e_delay ]\n", progname); + (void) fprintf(stderr, "\t\t[ -f freq_file ] [ -k key_file ] [ -l log_file ]\n"); + (void) fprintf(stderr, "\t\t[ -p pid_file ] [ -r broad_delay ] [ -s statdir ]\n"); + (void) fprintf(stderr, "\t\t[ -t trust_key ] [ -v sys_var ] [ -V default_sysvar ]\n"); +#if defined(HAVE_SCHED_SETSCHEDULER) + (void) fprintf(stderr, "\t\t[ -P fixed_process_priority ]\n"); +#endif + exit(2); + } + ntp_optind = 0; /* reset ntp_optind to restart ntp_getopt */ + +#ifdef DEBUG + if (debug) { +#ifdef HAVE_SETVBUF + static char buf[BUFSIZ]; + setvbuf(stdout, buf, _IOLBF, BUFSIZ); +#else + setlinebuf(stdout); +#endif + } +#endif +} + +/* + * getconfig - get command line options and read the configuration file + */ +void +getconfig( + int argc, + char *argv[] + ) +{ + register int i; + int c; + int errflg; + int peerversion; + int minpoll; + int maxpoll; + int ttl; + u_long peerkey; + u_long lpeerkey; + int peerflags; + int hmode; + struct sockaddr_in peeraddr; + struct sockaddr_in maskaddr; + FILE *fp; + char line[MAXLINE]; + char *(tokens[MAXTOKENS]); + int ntokens; + int tok = CONFIG_UNKNOWN; + struct interface *localaddr; + const char *config_file; +#ifdef HAVE_NETINFO + struct netinfo_config_state *config_netinfo = NULL; + int check_netinfo = 1; +#endif /* HAVE_NETINFO */ +#ifdef SYS_WINNT + char *alt_config_file; + LPTSTR temp; + char config_file_storage[MAX_PATH]; + char alt_config_file_storage[MAX_PATH]; +#endif /* SYS_WINNT */ + struct refclockstat clock_stat; + FILEGEN *filegen; + + /* + * Initialize, initialize + */ + errflg = 0; +#ifdef DEBUG + debug = 0; +#endif /* DEBUG */ +#ifndef SYS_WINNT + config_file = CONFIG_FILE; +#else + temp = CONFIG_FILE; + if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)config_file_storage, (DWORD)sizeof(config_file_storage))) { + msyslog(LOG_ERR, "ExpandEnvironmentStrings CONFIG_FILE failed: %m\n"); + exit(1); + } + config_file = config_file_storage; + + temp = ALT_CONFIG_FILE; + if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)alt_config_file_storage, (DWORD)sizeof(alt_config_file_storage))) { + msyslog(LOG_ERR, "ExpandEnvironmentStrings ALT_CONFIG_FILE failed: %m\n"); + exit(1); + } + alt_config_file = alt_config_file_storage; + +#endif /* SYS_WINNT */ + progname = argv[0]; + res_fp = NULL; + memset((char *)sys_phone, 0, sizeof(sys_phone)); + ntp_syslogmask = NLOG_SYNCMASK; /* set more via logconfig */ + + /* + * install a non default variable with this daemon version + */ + (void) sprintf(line, "daemon_version=\"%s\"", Version); + set_sys_var(line, strlen(line)+1, RO); + + /* + * Say how we're setting the time of day + */ + (void) sprintf(line, "settimeofday=\"%s\"", set_tod_using); + set_sys_var(line, strlen(line)+1, RO); + + /* + * Initialize the loop. + */ + loop_config(LOOP_DRIFTINIT, 0.); + + /* + * Decode argument list + */ + while ((c = ntp_getopt(argc, argv, ntp_options)) != EOF) { + switch (c) { + case 'a': + proto_config(PROTO_AUTHENTICATE, 1, 0.); + break; + + case 'A': + proto_config(PROTO_AUTHENTICATE, 0, 0.); + break; + + case 'b': + proto_config(PROTO_BROADCLIENT, 1, 0.); + break; + + case 'c': + config_file = ntp_optarg; +#ifdef HAVE_NETINFO + check_netinfo = 0; +#endif + break; + + case 'd': +#ifdef DEBUG + debug++; +#else + errflg++; +#endif /* DEBUG */ + break; + + case 'D': +#ifdef DEBUG + debug = (int)atol(ntp_optarg); + printf("Debug2: %s -> %x = %d\n", ntp_optarg, debug, debug); +#else + errflg++; +#endif /* DEBUG */ + break; + + case 'f': + stats_config(STATS_FREQ_FILE, ntp_optarg); + break; + + case 'g': + correct_any = TRUE; + break; + + case 'k': + getauthkeys(ntp_optarg); + break; + + case 'L': /* already done at pre-scan */ + case 'l': /* already done at pre-scan */ + break; + + case 'm': + proto_config(PROTO_MULTICAST_ADD, htonl(INADDR_NTP), 0.); + sys_bclient = 1; + break; + + case 'n': /* already done at pre-scan */ + break; + + case 'p': + stats_config(STATS_PID_FILE, ntp_optarg); + break; + + case 'P': +#if defined(HAVE_SCHED_SETSCHEDULER) + config_priority = (int)atol(ntp_optarg); + config_priority_override = 1; +#else + errflg++; +#endif + break; + + case 'r': + do { + double tmp; + + if (sscanf(ntp_optarg, "%lf", &tmp) != 1) { + msyslog(LOG_ERR, + "command line broadcast delay value %s undecodable", + ntp_optarg); + } else { + proto_config(PROTO_BROADDELAY, 0, tmp); + } + } while (0); + break; + + case 's': + stats_config(STATS_STATSDIR, ntp_optarg); + break; + + case 't': + do { + u_long tkey; + + tkey = (int)atol(ntp_optarg); + if (tkey <= 0 || tkey > NTP_MAXKEY) { + msyslog(LOG_ERR, + "command line trusted key %s is invalid", + ntp_optarg); + } else { + authtrust(tkey, 1); + } + } while (0); + break; + + case 'v': + case 'V': + set_sys_var(ntp_optarg, strlen(ntp_optarg)+1, + RW | ((c == 'V') ? DEF : 0)); + break; + + case 'x': + allow_set_backward = FALSE; + break; + + default: + errflg++; + break; + } + } + + if (errflg || ntp_optind != argc) { + (void) fprintf(stderr, "usage: %s [ -abdgmnx ] [ -c config_file ] [ -e e_delay ]\n", progname); + (void) fprintf(stderr, "\t\t[ -f freq_file ] [ -k key_file ] [ -l log_file ]\n"); + (void) fprintf(stderr, "\t\t[ -p pid_file ] [ -r broad_delay ] [ -s statdir ]\n"); + (void) fprintf(stderr, "\t\t[ -t trust_key ] [ -v sys_var ] [ -V default_sysvar ]\n"); +#if defined(HAVE_SCHED_SETSCHEDULER) + (void) fprintf(stderr, "\t\t[ -P fixed_process_priority ]\n"); +#endif + exit(2); + } + + if ( + (fp = fopen(FindConfig(config_file), "r")) == NULL +#ifdef HAVE_NETINFO + /* If there is no config_file, try NetInfo. */ + && check_netinfo && !(config_netinfo = get_netinfo_config()) +#endif /* HAVE_NETINFO */ + ) { + fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(config_file)); + msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(config_file)); +#ifdef SYS_WINNT + /* Under WinNT try alternate_config_file name, first NTP.CONF, then NTP.INI */ + + if ((fp = fopen(FindConfig(alt_config_file), "r")) == NULL) { + + /* + * Broadcast clients can sometimes run without + * a configuration file. + */ + + fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(alt_config_file)); + msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(alt_config_file)); + return; + } +#else /* not SYS_WINNT */ + return; +#endif /* not SYS_WINNT */ + } + + for (;;) { + if (fp) + tok = gettokens(fp, line, tokens, &ntokens); +#ifdef HAVE_NETINFO + else + tok = gettokens_netinfo(config_netinfo, tokens, &ntokens); +#endif /* HAVE_NETINFO */ + + if (tok == CONFIG_UNKNOWN) break; + + switch(tok) { + case CONFIG_PEER: + case CONFIG_SERVER: + case CONFIG_MANYCASTCLIENT: + case CONFIG_BROADCAST: + if (tok == CONFIG_PEER) + hmode = MODE_ACTIVE; + else if (tok == CONFIG_SERVER) + hmode = MODE_CLIENT; + else if (tok == CONFIG_MANYCASTCLIENT) + hmode = MODE_CLIENT; + else + hmode = MODE_BROADCAST; + + if (ntokens < 2) { + msyslog(LOG_ERR, + "No address for %s, line ignored", + tokens[0]); + break; + } + + if (!getnetnum(tokens[1], &peeraddr, 0)) { + errflg = -1; + } else { + errflg = 0; + + if ( +#ifdef REFCLOCK + !ISREFCLOCKADR(&peeraddr) && +#endif + ISBADADR(&peeraddr)) { + msyslog(LOG_ERR, + "attempt to configure invalid address %s", + ntoa(&peeraddr)); + break; + } + /* + * Shouldn't be able to specify multicast + * address for server/peer! + * and unicast address for manycastclient! + */ + if (((tok == CONFIG_SERVER) || + (tok == CONFIG_PEER)) && +#ifdef REFCLOCK + !ISREFCLOCKADR(&peeraddr) && +#endif + IN_CLASSD(ntohl(peeraddr.sin_addr.s_addr))) { + msyslog(LOG_ERR, + "attempt to configure invalid address %s", + ntoa(&peeraddr)); + break; + } + if ((tok == CONFIG_MANYCASTCLIENT) && + !IN_CLASSD(ntohl(peeraddr.sin_addr.s_addr))) { + msyslog(LOG_ERR, + "attempt to configure invalid address %s", + ntoa(&peeraddr)); + break; + } + } + + peerversion = NTP_VERSION; + minpoll = NTP_MINDPOLL; + maxpoll = NTP_MAXDPOLL; + peerkey = 0; + peerflags = 0; + ttl = 0; + for (i = 2; i < ntokens; i++) + switch (matchkey(tokens[i], mod_keywords)) { + case CONF_MOD_VERSION: + if (i >= ntokens-1) { + msyslog(LOG_ERR, + "peer/server version requires an argument"); + errflg = 1; + break; + } + peerversion = atoi(tokens[++i]); + if ((u_char)peerversion > NTP_VERSION + || (u_char)peerversion < NTP_OLDVERSION) { + msyslog(LOG_ERR, + "inappropriate version number %s, line ignored", + tokens[i]); + errflg = 1; + } + break; + + case CONF_MOD_KEY: + if (i >= ntokens-1) { + msyslog(LOG_ERR, + "key: argument required"); + errflg = 1; + break; + } + peerkey = (int)atol(tokens[++i]); + peerflags |= FLAG_AUTHENABLE; + break; + + case CONF_MOD_MINPOLL: + if (i >= ntokens-1) { + msyslog(LOG_ERR, + "minpoll: argument required"); + errflg = 1; + break; + } + minpoll = atoi(tokens[++i]); + if (minpoll < NTP_MINPOLL) + minpoll = NTP_MINPOLL; + break; + + case CONF_MOD_MAXPOLL: + if (i >= ntokens-1) { + msyslog(LOG_ERR, + "maxpoll: argument required" + ); + errflg = 1; + break; + } + maxpoll = atoi(tokens[++i]); + if (maxpoll > NTP_MAXPOLL) + maxpoll = NTP_MAXPOLL; + break; + + case CONF_MOD_PREFER: + peerflags |= FLAG_PREFER; + break; + + case CONF_MOD_NOSELECT: + peerflags |= FLAG_NOSELECT; + break; + + case CONF_MOD_BURST: + peerflags |= FLAG_BURST; + break; + + case CONF_MOD_SKEY: + peerflags |= FLAG_SKEY | FLAG_AUTHENABLE; + break; + + case CONF_MOD_TTL: + if (i >= ntokens-1) { + msyslog(LOG_ERR, + "ttl: argument required"); + errflg = 1; + break; + } + ttl = atoi(tokens[++i]); + break; + + case CONF_MOD_MODE: + if (i >= ntokens-1) { + msyslog(LOG_ERR, + "mode: argument required"); + errflg = 1; + break; + } + ttl = atoi(tokens[++i]); + break; + + case CONFIG_UNKNOWN: + errflg = 1; + break; + } + if (minpoll > maxpoll) { + msyslog(LOG_ERR, "config error: minpoll > maxpoll"); + errflg = 1; + } + if (errflg == 0) { + if (peer_config(&peeraddr, + (struct interface *)0, hmode, + peerversion, minpoll, maxpoll, + peerflags, ttl, peerkey) + == 0) { + msyslog(LOG_ERR, + "configuration of %s failed", + ntoa(&peeraddr)); + } + } else if (errflg == -1) { + save_resolve(tokens[1], hmode, peerversion, + minpoll, maxpoll, peerflags, ttl, + peerkey); + } + break; + + case CONFIG_DRIFTFILE: + if (ntokens >= 2) + stats_config(STATS_FREQ_FILE, tokens[1]); + else + stats_config(STATS_FREQ_FILE, (char *)0); + break; + + case CONFIG_PIDFILE: + if (ntokens >= 2) + stats_config(STATS_PID_FILE, tokens[1]); + else + stats_config(STATS_PID_FILE, (char *)0); + break; + + case CONFIG_LOGFILE: + if (ntokens >= 2) { + FILE *new_file; + new_file = fopen(tokens[1], "a"); + if (new_file != NULL) { + NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, "logging to file %s", tokens[1]); + if (syslog_file != NULL && + fileno(syslog_file) != fileno(new_file)) + (void)fclose(syslog_file); + + syslog_file = new_file; + syslogit = 0; + } + else + msyslog(LOG_ERR, + "Cannot open log file %s", + tokens[1]); + } + else + msyslog(LOG_ERR, "logfile needs one argument"); + break; + + case CONFIG_LOGCONFIG: + for (i = 1; i < ntokens; i++) + { + int add = 1; + int equals = 0; + char * s = &tokens[i][0]; + + switch (*s) { + case '+': + case '-': + case '=': + add = *s == '+'; + equals = *s == '='; + s++; + break; + + default: + break; + } + if (equals) { + ntp_syslogmask = get_logmask(s); + } else { + if (add) { + ntp_syslogmask |= get_logmask(s); + } else { + ntp_syslogmask &= ~get_logmask(s); + } + } +#ifdef DEBUG + if (debug) + printf("ntp_syslogmask = 0x%08lx (%s)\n", ntp_syslogmask, tokens[i]); +#endif + } + break; + + case CONFIG_BROADCASTCLIENT: + proto_config(PROTO_BROADCLIENT, 1, 0.); + break; + + case CONFIG_MULTICASTCLIENT: + case CONFIG_MANYCASTSERVER: + if (ntokens > 1) { + for (i = 1; i < ntokens; i++) { + if (getnetnum(tokens[i], &peeraddr, 1)) + proto_config(PROTO_MULTICAST_ADD, + peeraddr.sin_addr.s_addr, 0.); + } + } else + proto_config(PROTO_MULTICAST_ADD, + htonl(INADDR_NTP), 0.); + if (tok == CONFIG_MULTICASTCLIENT) { + sys_bclient = 1; +#ifdef DEBUG + if (debug) + printf("sys_bclient\n"); +#endif /* DEBUG */ + } + else if (tok == CONFIG_MANYCASTSERVER) { + sys_manycastserver = 1; +#ifdef DEBUG + if (debug) + printf("sys_manycastserver\n"); +#endif /* DEBUG */ + } + break; + + case CONFIG_AUTHENTICATE: + errflg = 0; + if (ntokens >= 2) { + if (STREQ(tokens[1], "yes")) + proto_config(PROTO_AUTHENTICATE, 1, 0.); + else if (STREQ(tokens[1], "no")) + proto_config(PROTO_AUTHENTICATE, 0, 0.); + else + errflg++; + } else { + errflg++; + } + + if (errflg) + msyslog(LOG_ERR, + "should be `authenticate yes|no'"); + break; + + case CONFIG_KEYS: + if (ntokens >= 2) { + getauthkeys(tokens[1]); + } + break; + + case CONFIG_REVOKE: + if (ntokens >= 2) { + sys_revoke = 1 << max(atoi(tokens[1]), 10); + } + break; + + case CONFIG_AUTOMAX: + if (ntokens >= 2) { + sys_automax = 1 << max(atoi(tokens[1]), 10); + } + break; + + case CONFIG_RESTRICT: + if (ntokens < 2) { + msyslog(LOG_ERR, "restrict requires an address"); + break; + } + if (STREQ(tokens[1], "default")) + peeraddr.sin_addr.s_addr = htonl(INADDR_ANY); + else if (!getnetnum(tokens[1], &peeraddr, 1)) + break; + + /* + * Use peerversion as flags, peerkey as mflags. Ick. + */ + peerversion = 0; + peerkey = 0; + errflg = 0; + maskaddr.sin_addr.s_addr = ~(u_int32)0; + for (i = 2; i < ntokens; i++) { + switch (matchkey(tokens[i], res_keywords)) { + case CONF_RES_MASK: + if (i >= ntokens-1) { + msyslog(LOG_ERR, + "mask keyword needs argument"); + errflg++; + break; + } + i++; + if (!getnetnum(tokens[i], &maskaddr, 1)) + errflg++; + break; + + case CONF_RES_IGNORE: + peerversion |= RES_IGNORE; + break; + + case CONF_RES_NOSERVE: + peerversion |= RES_DONTSERVE; + break; + + case CONF_RES_NOTRUST: + peerversion |= RES_DONTTRUST; + break; + + case CONF_RES_NOQUERY: + peerversion |= RES_NOQUERY; + break; + + case CONF_RES_NOMODIFY: + peerversion |= RES_NOMODIFY; + break; + + case CONF_RES_NOPEER: + peerversion |= RES_NOPEER; + break; + + case CONF_RES_NOTRAP: + peerversion |= RES_NOTRAP; + break; + + case CONF_RES_LPTRAP: + peerversion |= RES_LPTRAP; + break; + + case CONF_RES_NTPPORT: + peerkey |= RESM_NTPONLY; + break; + + case CONF_RES_LIMITED: + peerversion |= RES_LIMITED; + break; + + case CONFIG_UNKNOWN: + errflg++; + break; + } + } + if (SRCADR(&peeraddr) == htonl(INADDR_ANY)) + maskaddr.sin_addr.s_addr = 0; + if (!errflg) + hack_restrict(RESTRICT_FLAGS, &peeraddr, &maskaddr, + (int)peerkey, peerversion); + break; + + case CONFIG_BDELAY: + if (ntokens >= 2) { + double tmp; + + if (sscanf(tokens[1], "%lf", &tmp) != 1) { + msyslog(LOG_ERR, + "broadcastdelay value %s undecodable", + tokens[1]); + } else { + proto_config(PROTO_BROADDELAY, 0, tmp); + } + } + break; + + case CONFIG_TRUSTEDKEY: + for (i = 1; i < ntokens; i++) { + u_long tkey; + + tkey = atol(tokens[i]); + if (tkey == 0) { + msyslog(LOG_ERR, + "trusted key %s unlikely", + tokens[i]); + } else { + authtrust(tkey, 1); + } + } + break; + + case CONFIG_REQUESTKEY: + if (ntokens >= 2) { + u_long rkey; + + if (!atouint(tokens[1], &rkey)) { + msyslog(LOG_ERR, + "%s is undecodable as request key", + tokens[1]); + } else if (rkey == 0) { + msyslog(LOG_ERR, + "%s makes a poor request keyid", + tokens[1]); + } else { +#ifdef DEBUG + if (debug > 3) + printf( + "set info_auth_key to %lu\n", rkey); +#endif + info_auth_keyid = rkey; + } + } + break; + + case CONFIG_CONTROLKEY: + if (ntokens >= 2) { + u_long ckey; + + ckey = atol(tokens[1]); + if (ckey == 0) { + msyslog(LOG_ERR, + "%s makes a poor control keyid", + tokens[1]); + } else { + ctl_auth_keyid = ckey; + } + } + break; + + case CONFIG_TRAP: + if (ntokens < 2) { + msyslog(LOG_ERR, + "no address for trap command, line ignored"); + break; + } + if (!getnetnum(tokens[1], &peeraddr, 1)) + break; + + /* + * Use peerversion for port number. Barf. + */ + errflg = 0; + peerversion = 0; + localaddr = 0; + for (i = 2; i < ntokens-1; i++) + switch (matchkey(tokens[i], trap_keywords)) { + case CONF_TRAP_PORT: + if (i >= ntokens-1) { + msyslog(LOG_ERR, + "trap port requires an argument"); + errflg = 1; + break; + } + peerversion = atoi(tokens[++i]); + if (peerversion <= 0 + || peerversion > 32767) { + msyslog(LOG_ERR, + "invalid port number %s, trap ignored", + tokens[i]); + errflg = 1; + } + break; + + case CONF_TRAP_INTERFACE: + if (i >= ntokens-1) { + msyslog(LOG_ERR, + "trap interface requires an argument"); + errflg = 1; + break; + } + + if (!getnetnum(tokens[++i], + &maskaddr, 1)) { + errflg = 1; + break; + } + + localaddr = findinterface(&maskaddr); + if (localaddr == NULL) { + msyslog(LOG_ERR, + "can't find interface with address %s", + ntoa(&maskaddr)); + errflg = 1; + } + break; + + case CONFIG_UNKNOWN: + errflg++; + break; + } + + if (!errflg) { + if (peerversion != 0) + peeraddr.sin_port = htons( (u_short) peerversion); + else + peeraddr.sin_port = htons(TRAPPORT); + if (localaddr == NULL) + localaddr = any_interface; + if (!ctlsettrap(&peeraddr, localaddr, 0, + NTP_VERSION)) + msyslog(LOG_ERR, + "can't set trap for %s, no resources", + ntoa(&peeraddr)); + } + break; + + case CONFIG_FUDGE: + if (ntokens < 2) { + msyslog(LOG_ERR, + "no address for fudge command, line ignored"); + break; + } + if (!getnetnum(tokens[1], &peeraddr, 1)) + break; + + if (!ISREFCLOCKADR(&peeraddr)) { + msyslog(LOG_ERR, + "%s is inappropriate address for the fudge command, line ignored", + ntoa(&peeraddr)); + break; + } + + memset((void *)&clock_stat, 0, sizeof clock_stat); + errflg = 0; + for (i = 2; i < ntokens-1; i++) { + switch (c = matchkey(tokens[i], + fudge_keywords)) { + case CONF_FDG_TIME1: + if (sscanf(tokens[++i], "%lf", + &clock_stat.fudgetime1) != 1) { + msyslog(LOG_ERR, + "fudge %s time1 value in error", + ntoa(&peeraddr)); + errflg = i; + break; + } + clock_stat.haveflags |= CLK_HAVETIME1; + break; + + case CONF_FDG_TIME2: + if (sscanf(tokens[++i], "%lf", + &clock_stat.fudgetime2) != 1) { + msyslog(LOG_ERR, + "fudge %s time2 value in error", + ntoa(&peeraddr)); + errflg = i; + break; + } + clock_stat.haveflags |= CLK_HAVETIME2; + break; + + + case CONF_FDG_STRATUM: + /* HMS: the (long *)_ may be trouble */ + if (!atoint(tokens[++i], + (long *)&clock_stat.fudgeval1)) + { + msyslog(LOG_ERR, + "fudge %s stratum value in error", + ntoa(&peeraddr)); + errflg = i; + break; + } + clock_stat.haveflags |= CLK_HAVEVAL1; + break; + + case CONF_FDG_REFID: + /* HMS: Endianness and 0 bytes? */ + /* XXX */ + strncpy((char *)&clock_stat.fudgeval2, + tokens[++i], 4); + clock_stat.haveflags |= CLK_HAVEVAL2; + break; + + case CONF_FDG_FLAG1: + case CONF_FDG_FLAG2: + case CONF_FDG_FLAG3: + case CONF_FDG_FLAG4: + if (!atouint(tokens[++i], &lpeerkey) + || lpeerkey > 1) { + msyslog(LOG_ERR, + "fudge %s flag value in error", + ntoa(&peeraddr)); + peerkey = lpeerkey; + errflg = i; + break; + } + peerkey = lpeerkey; + switch(c) { + case CONF_FDG_FLAG1: + c = CLK_FLAG1; + clock_stat.haveflags|=CLK_HAVEFLAG1; + break; + case CONF_FDG_FLAG2: + c = CLK_FLAG2; + clock_stat.haveflags|=CLK_HAVEFLAG2; + break; + case CONF_FDG_FLAG3: + c = CLK_FLAG3; + clock_stat.haveflags|=CLK_HAVEFLAG3; + break; + case CONF_FDG_FLAG4: + c = CLK_FLAG4; + clock_stat.haveflags|=CLK_HAVEFLAG4; + break; + } + if (peerkey == 0) + clock_stat.flags &= ~c; + else + clock_stat.flags |= c; + break; + + case CONFIG_UNKNOWN: + errflg = -1; + break; + } + } + +#ifdef REFCLOCK + /* + * If reference clock support isn't defined the + * fudge line will still be accepted and syntax + * checked, but will essentially do nothing. + */ + if (!errflg) { + refclock_control(&peeraddr, &clock_stat, + (struct refclockstat *)0); + } +#endif + break; + + case CONFIG_STATSDIR: + if (ntokens >= 2) { + stats_config(STATS_STATSDIR,tokens[1]); + } + break; + + case CONFIG_STATISTICS: + for (i = 1; i < ntokens; i++) { + filegen = filegen_get(tokens[i]); + + if (filegen == NULL) { + msyslog(LOG_ERR, + "no statistics named %s available", + tokens[i]); + continue; + } +#ifdef DEBUG + if (debug > 3) + printf("enabling filegen for %s statistics \"%s%s\"\n", + tokens[i], filegen->prefix, filegen->basename); +#endif + filegen->flag |= FGEN_FLAG_ENABLED; + } + break; + + case CONFIG_FILEGEN: + if (ntokens < 2) { + msyslog(LOG_ERR, + "no id for filegen command, line ignored"); + break; + } + + filegen = filegen_get(tokens[1]); + if (filegen == NULL) { + msyslog(LOG_ERR, + "unknown filegen \"%s\" ignored", + tokens[1]); + break; + } + /* + * peerversion is (ab)used for filegen file (index) + * peerkey is (ab)used for filegen type + * peerflags is (ab)used for filegen flags + */ + peerversion = 0; + peerkey = filegen->type; + peerflags = filegen->flag; + errflg = 0; + + for (i = 2; i < ntokens; i++) { + switch (matchkey(tokens[i], filegen_keywords)) { + case CONF_FGEN_FILE: + if (i >= ntokens - 1) { + msyslog(LOG_ERR, + "filegen %s file requires argument", + tokens[1]); + errflg = i; + break; + } + peerversion = ++i; + break; + case CONF_FGEN_TYPE: + if (i >= ntokens -1) { + msyslog(LOG_ERR, + "filegen %s type requires argument", + tokens[1]); + errflg = i; + break; + } + peerkey = matchkey(tokens[++i], fgen_types); + if (peerkey == CONFIG_UNKNOWN) { + msyslog(LOG_ERR, + "filegen %s unknown type \"%s\"", + tokens[1], tokens[i]); + errflg = i; + break; + } + break; + + case CONF_FGEN_FLAG_LINK: + peerflags |= FGEN_FLAG_LINK; + break; + + case CONF_FGEN_FLAG_NOLINK: + peerflags &= ~FGEN_FLAG_LINK; + break; + + case CONF_FGEN_FLAG_ENABLE: + peerflags |= FGEN_FLAG_ENABLED; + break; + + case CONF_FGEN_FLAG_DISABLE: + peerflags &= ~FGEN_FLAG_ENABLED; + break; + } + } + if (!errflg) { + filegen_config(filegen, tokens[peerversion], + (u_char)peerkey, (u_char)peerflags); + } + break; + + case CONFIG_SETVAR: + if (ntokens < 2) { + msyslog(LOG_ERR, + "no value for setvar command - line ignored"); + } else { + set_sys_var(tokens[1], strlen(tokens[1])+1, + RW | + ((((ntokens > 2) + && !strcmp(tokens[2], + "default"))) + ? DEF + : 0)); + } + break; + + case CONFIG_CLIENTLIMIT: + if (ntokens < 2) { + msyslog(LOG_ERR, + "no value for clientlimit command - line ignored"); + } else { + u_long ui; + + if (!atouint(tokens[1], &ui) || !ui) { + msyslog(LOG_ERR, + "illegal value for clientlimit command - line ignored"); + } else { + char bp[80]; + +#ifdef DEBUG + if (debug) + sprintf(bp, "client_limit=%lu", ui); +#endif + set_sys_var(bp, strlen(bp)+1, RO); + client_limit = ui; + } + } + break; + + case CONFIG_CLIENTPERIOD: + if (ntokens < 2) { + msyslog(LOG_ERR, + "no value for clientperiod command - line ignored"); + } else { + u_long ui; + + if (!atouint(tokens[1], &ui) || ui < 64) { + msyslog(LOG_ERR, + "illegal value for clientperiod command - line ignored"); + } else { + char bp[80]; + + sprintf(bp, "client_limit_period=%ld", ui); + set_sys_var(bp, strlen(bp)+1, RO); + client_limit_period = ui; + } + } + break; + + case CONFIG_ENABLE: + for (i = 1; i < ntokens; i++) { + int flag; + + flag = matchkey(tokens[i], flags_keywords); + if (flag == CONFIG_UNKNOWN) { + msyslog(LOG_ERR, + "enable unknown flag %s", + tokens[i]); + errflg = 1; + break; + } + proto_config(flag, 1, 0.); + } + break; + + case CONFIG_DISABLE: + for (i = 1; i < ntokens; i++) { + int flag; + + flag = matchkey(tokens[i], flags_keywords); + if (flag == CONFIG_UNKNOWN) { + msyslog(LOG_ERR, + "disable unknown flag %s", + tokens[i]); + errflg = 1; + break; + } + proto_config(flag, 0, 0.); + } + break; + + case CONFIG_PHONE: + for (i = 1; i < ntokens && i < MAXPHONE; i++) { + (void)strncpy(sys_phone[i - 1], + tokens[i], MAXDIAL); + } + sys_phone[i - 1][0] = '\0'; + break; + + case CONFIG_PPS: + if (ntokens < 2) { + msyslog(LOG_ERR, + "pps missing device name"); + break; + } + (void)strncpy(pps_device, tokens[1], MAXPPS); + for (i = 2; i < ntokens; i++) { + int flag; + + flag = matchkey(tokens[i], pps_keywords); + switch(flag) { + case CONF_PPS_ASSERT: + pps_assert = 1; + break; + case CONF_PPS_CLEAR: + pps_assert = 0; + break; + case CONF_PPS_HARDPPS: + pps_hardpps = 1; + break; + default: + msyslog(LOG_ERR, + "pps unknown flag %s", + tokens[i]); + errflg = 1; + break; + } + if(errflg) + break; + } + break; + } + } + if (fp) (void)fclose(fp); +#ifdef HAVE_NETINFO + if (config_netinfo) free_netinfo_config(config_netinfo); +#endif /* HAVE_NETINFO */ + + if (res_fp != NULL) { + /* + * Need name resolution + */ + do_resolve_internal(); + } +} + + +#ifdef HAVE_NETINFO + +/* + * get_netinfo_config - find the nearest NetInfo domain with an ntp + * configuration and initialize the configuration state. + */ +static struct netinfo_config_state * +get_netinfo_config() +{ + ni_status status; + void *domain; + ni_id config_dir; + struct netinfo_config_state *config; + + if (ni_open(NULL, ".", &domain) != NI_OK) return NULL; + + while ((status = ni_pathsearch(domain, &config_dir, NETINFO_CONFIG_DIR)) == NI_NODIR) { + void *next_domain; + if (ni_open(domain, "..", &next_domain) != NI_OK) { + ni_free(next_domain); + break; + } + ni_free(domain); + domain = next_domain; + } + if (status != NI_OK) { + ni_free(domain); + return NULL; + } + + config = (struct netinfo_config_state *)malloc(sizeof(struct netinfo_config_state)); + config->domain = domain; + config->config_dir = config_dir; + config->prop_index = 0; + config->val_index = 0; + config->val_list = NULL; + + return config; +} + + + +/* + * free_netinfo_config - release NetInfo configuration state + */ +static void +free_netinfo_config(struct netinfo_config_state *config) +{ + ni_free(config->domain); + free(config); +} + + + +/* + * gettokens_netinfo - return tokens from NetInfo + */ +static int +gettokens_netinfo ( + struct netinfo_config_state *config, + char **tokenlist, + int *ntokens + ) +{ + int prop_index = config->prop_index; + int val_index = config->val_index; + char **val_list = config->val_list; + + /* + * Iterate through each keyword and look for a property that matches it. + */ + again: + if (!val_list) { + for (; prop_index < (sizeof(keywords)/sizeof(keywords[0])); prop_index++) + { + ni_namelist namelist; + struct keyword current_prop = keywords[prop_index]; + + /* + * For each value associated in the property, we're going to return + * a separate line. We squirrel away the values in the config state + * so the next time through, we don't need to do this lookup. + */ + NI_INIT(&namelist); + if (ni_lookupprop(config->domain, &config->config_dir, current_prop.text, &namelist) == NI_OK) { + ni_index index; + + /* Found the property, but it has no values */ + if (namelist.ni_namelist_len == 0) continue; + + if (! (val_list = config->val_list = (char**)malloc(sizeof(char*) * (namelist.ni_namelist_len + 1)))) + { msyslog(LOG_ERR, "out of memory while configuring"); break; } + + for (index = 0; index < namelist.ni_namelist_len; index++) { + char *value = namelist.ni_namelist_val[index]; + + if (! (val_list[index] = (char*)malloc(strlen(value+1)))) + { msyslog(LOG_ERR, "out of memory while configuring"); break; } + + strcpy(val_list[index], value); + } + val_list[index] = NULL; + + break; + } + ni_namelist_free(&namelist); + } + config->prop_index = prop_index; + } + + /* No list; we're done here. */ + if (!val_list) return CONFIG_UNKNOWN; + + /* + * We have a list of values for the current property. + * Iterate through them and return each in order. + */ + if (val_list[val_index]) + { + int ntok = 1; + int quoted = 0; + char *tokens = val_list[val_index]; + + msyslog(LOG_INFO, "%s %s", keywords[prop_index].text, val_list[val_index]); + + (const char*)tokenlist[0] = keywords[prop_index].text; + for (ntok = 1; ntok < MAXTOKENS; ntok++) { + tokenlist[ntok] = tokens; + while (!ISEOL(*tokens) && (!ISSPACE(*tokens) || quoted)) + quoted ^= (*tokens++ == '"'); + + if (ISEOL(*tokens)) { + *tokens = '\0'; + break; + } else { /* must be space */ + *tokens++ = '\0'; + while (ISSPACE(*tokens)) tokens++; + if (ISEOL(*tokens)) break; + } + } + *ntokens = ntok + 1; + + config->val_index++; + + return keywords[prop_index].keytype; + } + + /* We're done with the current property. */ + prop_index = ++config->prop_index; + + /* Free val_list and reset counters. */ + for (val_index = 0; val_list[val_index]; val_index++) + free(val_list[val_index]); + free(val_list); val_list = config->val_list = NULL; val_index = config->val_index = 0; + + goto again; +} + +#endif /* HAVE_NETINFO */ + + +/* + * gettokens - read a line and return tokens + */ +static int +gettokens ( + FILE *fp, + char *line, + char **tokenlist, + int *ntokens + ) +{ + register char *cp; + register int ntok; + register int quoted = 0; + + /* + * Find start of first token + */ + again: + while ((cp = fgets(line, MAXLINE, fp)) != NULL) { + cp = line; + while (ISSPACE(*cp)) + cp++; + if (!ISEOL(*cp)) + break; + } + if (cp == NULL) { + *ntokens = 0; + return CONFIG_UNKNOWN; /* hack. Is recognized as EOF */ + } + + /* + * Now separate out the tokens + */ + for (ntok = 0; ntok < MAXTOKENS; ntok++) { + tokenlist[ntok] = cp; + while (!ISEOL(*cp) && (!ISSPACE(*cp) || quoted)) + quoted ^= (*cp++ == '"'); + + if (ISEOL(*cp)) { + *cp = '\0'; + break; + } else { /* must be space */ + *cp++ = '\0'; + while (ISSPACE(*cp)) + cp++; + if (ISEOL(*cp)) + break; + } + } + + /* + * Return the match + */ + *ntokens = ntok + 1; + ntok = matchkey(tokenlist[0], keywords); + if (ntok == CONFIG_UNKNOWN) + goto again; + return ntok; +} + + + +/* + * matchkey - match a keyword to a list + */ +static int +matchkey( + register char *word, + register struct keyword *keys + ) +{ + for (;;) { + if (keys->keytype == CONFIG_UNKNOWN) { + msyslog(LOG_ERR, + "configure: keyword \"%s\" unknown, line ignored", + word); + return CONFIG_UNKNOWN; + } + if (STRSAME(word, keys->text)) + return keys->keytype; + keys++; + } +} + + +/* + * getnetnum - return a net number (this is crude, but careful) + */ +static int +getnetnum( + const char *num, + struct sockaddr_in *addr, + int complain + ) +{ + register const char *cp; + register char *bp; + register int i; + register int temp; + char buf[80]; /* will core dump on really stupid stuff */ + u_int32 netnum; + + /* XXX ELIMINATE replace with decodenetnum */ + cp = num; + netnum = 0; + for (i = 0; i < 4; i++) { + bp = buf; + while (isdigit((int)*cp)) + *bp++ = *cp++; + if (bp == buf) + break; + + if (i < 3) { + if (*cp++ != '.') + break; + } else if (*cp != '\0') + break; + + *bp = '\0'; + temp = atoi(buf); + if (temp > 255) + break; + netnum <<= 8; + netnum += temp; +#ifdef DEBUG + if (debug > 3) + printf("getnetnum %s step %d buf %s temp %d netnum %lu\n", + num, i, buf, temp, (u_long)netnum); +#endif + } + + if (i < 4) { + if (complain) + msyslog(LOG_ERR, + "getnetnum: \"%s\" invalid host number, line ignored", + num); +#ifdef DEBUG + if (debug > 3) + printf( + "getnetnum: \"%s\" invalid host number, line ignored\n", + num); +#endif + return 0; + } + + /* + * make up socket address. Clear it out for neatness. + */ + memset((void *)addr, 0, sizeof(struct sockaddr_in)); + addr->sin_family = AF_INET; + addr->sin_port = htons(NTP_PORT); + addr->sin_addr.s_addr = htonl(netnum); +#ifdef DEBUG + if (debug > 1) + printf("getnetnum given %s, got %s (%lx)\n", + num, ntoa(addr), (u_long)netnum); +#endif + return 1; +} + + +#if !defined(VMS) +/* + * catchchild - receive the resolver's exit status + */ +static RETSIGTYPE +catchchild( + int sig + ) +{ + /* + * We only start up one child, and if we're here + * it should have already exited. Hence the following + * shouldn't hang. If it does, please tell me. + */ +#if !defined (SYS_WINNT) && !defined(SYS_VXWORKS) + (void) wait(0); +#endif /* SYS_WINNT && VXWORKS*/ +} +#endif /* VMS */ + + +/* + * save_resolve - save configuration info into a file for later name resolution + */ +static void +save_resolve( + char *name, + int mode, + int version, + int minpoll, + int maxpoll, + int flags, + int ttl, + u_long keyid + ) +{ +#ifndef SYS_VXWORKS + if (res_fp == NULL) { +#ifndef SYS_WINNT + (void) strcpy(res_file, RES_TEMPFILE); +#else + /* no /tmp directory under NT */ + { + DWORD len; + if(!(len = GetTempPath((DWORD)MAX_PATH, (LPTSTR)res_file))) { + msyslog(LOG_ERR, "cannot get pathname for temporary directory: %m"); + return; + } + (void) strcat(res_file, "ntpdXXXXXX"); + } +#endif /* SYS_WINNT */ +#ifdef HAVE_MKSTEMP + { + int fd; + + res_fp = NULL; + if ((fd = mkstemp(res_file)) != -1) + res_fp = fdopen(fd, "w"); + } +#else + (void) mktemp(res_file); + res_fp = fopen(res_file, "w"); +#endif + if (res_fp == NULL) { + msyslog(LOG_ERR, "open failed for %s: %m", res_file); + return; + } + } +#ifdef DEBUG + if (debug) { + printf("resolving %s\n", name); + } +#endif + + (void) fprintf(res_fp, "%s %d %d %d %d %d %d %lu\n", name, mode, + version, minpoll, maxpoll, flags, ttl, keyid); +#else /* SYS_VXWORKS */ + /* save resolve info to a struct */ +#endif /* SYS_VXWORKS */ +} + + +/* + * abort_resolve - terminate the resolver stuff and delete the file + */ +static void +abort_resolve(void) +{ + /* + * In an ideal world we would might reread the file and + * log the hosts which aren't getting configured. Since + * this is too much work, however, just close and delete + * the temp file. + */ + if (res_fp != NULL) + (void) fclose(res_fp); + res_fp = NULL; + +#ifndef SYS_VXWORKS /* we don't open the file to begin with */ +#if !defined(VMS) + (void) unlink(res_file); +#else + (void) delete(res_file); +#endif /* VMS */ +#endif /* SYS_VXWORKS */ +} + + +#define KEY_TYPE_MD5 4 + +/* + * do_resolve_internal - start up the resolver function (not program) + */ +/* + * On VMS, this routine will simply refuse to resolve anything. + * + * Possible implementation: keep `res_file' in memory, do async + * name resolution via QIO, update from within completion AST. + * I'm unlikely to find the time for doing this, though. -wjm + */ +static void +do_resolve_internal(void) +{ + int i; + + if (res_fp == NULL) { + /* belch */ + msyslog(LOG_ERR, + "internal error in do_resolve_internal: res_fp == NULL"); + exit(1); + } + + /* we are done with this now */ + (void) fclose(res_fp); + res_fp = NULL; + +#if !defined(VMS) && !defined (SYS_VXWORKS) + /* find a keyid */ + if (info_auth_keyid == 0) + req_keyid = 65535; + else + req_keyid = info_auth_keyid; + + /* if doesn't exist, make up one at random */ + if (!authhavekey(req_keyid)) { + char rankey[8]; + + for (i = 0; i < 8; i++) + rankey[i] = RANDOM & 0xff; + authusekey(req_keyid, KEY_TYPE_MD5, (u_char *)rankey); + authtrust(req_keyid, 1); + } + + /* save keyid so we will accept config requests with it */ + info_auth_keyid = req_keyid; + req_file = res_file; /* set up pointer to res file */ +#ifndef SYS_WINNT + (void) signal_no_reset(SIGCHLD, catchchild); + +#ifndef SYS_VXWORKS + i = fork(); + if (i == 0) { + /* + * this used to close everything + * I don't think this is necessary + */ + /* + * To the unknown commenter above: + * Well, I think it's better to clean up + * after oneself. I have had problems with + * refclock-io when intres was running - things + * where fine again when ntpintres was gone. + * So some systems react erratic at least. + * + * Frank Kardel + * + * 94-11-16: + * Further debugging has proven that the above is + * absolutely harmful. The internal resolver + * is still in the SIGIO process group and the lingering + * async io information causes it to process requests from + * all file decriptor causing a race between the NTP daemon + * and the resolver. which then eats data when it wins 8-(. + * It is absolutly necessary to kill ane io associations + * shared with the NTP daemon. I currently don't want + * + * we also block SIGIO (currently no portes means to + * disable the signal handle for IO). + * + * Thanks to wgstuken@informatik.uni-erlangen.de to notice + * that it is the ntp-resolver child running into trouble. + * + * THUS: + */ + + closelog(); + kill_asyncio(); + + (void) signal_no_reset(SIGCHLD, SIG_DFL); + +#ifdef DEBUG + if (0) + debug = 2; +#endif + +# ifndef LOG_DAEMON + openlog("ntpd_initres", LOG_PID); +# else /* LOG_DAEMON */ + +# ifndef LOG_NTP +# define LOG_NTP LOG_DAEMON +# endif + openlog("ntpd_initres", LOG_PID | LOG_NDELAY, LOG_NTP); +#ifndef SYS_CYGWIN32 +# ifdef DEBUG + if (debug) + setlogmask(LOG_UPTO(LOG_DEBUG)); + else +# endif /* DEBUG */ + setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */ +# endif /* LOG_DAEMON */ +#endif + + ntp_intres(); + + /* + * If we got here, the intres code screwed up. + * Print something so we don't die without complaint + */ + msyslog(LOG_ERR, "call to ntp_intres lost"); + abort_resolve(); + exit(1); + } +#else + /* vxWorks spawns a thread... -casey */ + i = sp (ntp_intres); + /*i = taskSpawn("ntp_intres",100,VX_FP_TASK,20000,ntp_intres);*/ +#endif + if (i == -1) { + msyslog(LOG_ERR, "fork() failed, can't start ntp_intres: %m"); + (void) signal_no_reset(SIGCHLD, SIG_DFL); + abort_resolve(); + } +#else /* SYS_WINNT */ + { + /* NT's equivalent of fork() is _spawn(), but the start point + * of the new process is an executable filename rather than + * a function name as desired here. + */ + DWORD dwThreadId; + fflush(stdout); + if (!(ResolverThreadHandle = CreateThread( + NULL, /* no security attributes */ + 0, /* use default stack size */ + (LPTHREAD_START_ROUTINE) ntp_intres, /* thread function */ + NULL, /* argument to thread function */ + 0, /* use default creation flags */ + &dwThreadId))) { /* returns the thread identifier */ + msyslog(LOG_ERR, "CreateThread() failed, can't start ntp_intres"); + abort_resolve(); + } + } +#endif /* SYS_WINNT */ +#else /* VMS VX_WORKS */ + msyslog(LOG_ERR, + "Name resolution not implemented for VMS - use numeric addresses"); + abort_resolve(); +#endif /* VMS VX_WORKS */ +} |