2.11BSD/src/usr.sbin/ntp/ntpd.c

Compare this file to the similar file:
Show the results in this format:

#if	!defined(lint) && defined(DOSCCS)
static char *rcsid = "$Source: /usr/src/new/ntp/ntpd.c,v $ $Revision: 3.4.1.12 $ $Date: 95/07/1 20:35:17 $";
#endif	lint

/*
 *  $Log:	ntpd.c,v $
 * Revision 3.4.1.11 95/01/31 20:35:17 sms
 * Fix missing initializer before calling sysctl().
 *
 * Revision 3.4.1.10 95/01/27 17:20:17 sms
 * 2.11BSD - remove SETTICKADJ from ntpd.c.  This was done for several reasons:
 * 1) tickadj does not (and never has) existed, 2) this is an old version and
 * not going to be ported to a system with tickadj, 3) with 'securelevel' 
 * (see sysctl(3), init(8) and sysctl(8)) set to 1 or 2 /dev/{k}mem can not
 * be written even by root.  If tickadj were ever added it would need an
 * extension to sysctl(3) to have a chance of working.
 *
 * Revision 3.4.1.9  89/05/18  18:30:17  louie
 * Changes in ntpd.c for reference clock support.  Also, a few diddles to
 * accomodate the NeXT computer system that has a slightly different nlist.h
 * 
 * Revision 3.4.1.8  89/05/03  15:16:17  louie
 * Add code to save the value of the drift compensation register to a file every
 * hour.  Add additional configuration file directives which can specify the same
 * information as on the command line.
 * 
 * Revision 3.4.1.7  89/04/10  15:58:45  louie
 * Add -l option to enable logging of clock adjust messages.
 * 
 * Revision 3.4.1.6  89/04/07  19:09:04  louie
 * Added NOSWAP code for Ultrix systems to lock NTP process in memory.  Deleted
 * unused variable in ntpd.c
 * 
 * Revision 3.4.1.5  89/03/31  16:37:49  louie
 * Add support for "trusting" directive in NTP configuration file.  It allows 
 * you to specify at run time if non-configured peers will be synced to.
 * 
 * Revision 3.4.1.4  89/03/29  12:30:46  louie
 * peer->mode has been renamed peer->hmode.  Drop PEER_FL_SYNC since the
 * PEER_FL_CONFIG flag means much the same thing.
 * 
 * Revision 3.4.1.3  89/03/22  18:29:41  louie
 * patch3: Use new RCS headers.
 * 
 * Revision 3.4.1.2  89/03/22  18:03:17  louie
 * The peer->refid field was being htonl()'ed when it was already in network
 * byte order.
 * 
 * Revision 3.4.1.1  89/03/20  00:12:10  louie
 * patch1: Diddle syslog messages a bit.  Handle case of udp/ntp not being
 * patch1: defined in /etc/services.  Compute default value for tickadj if
 * patch1: the change-kernel-tickadj flag is set, but no tickadj directive
 * patch1: is present in the configuration file.
 * 
 * Revision 3.4  89/03/17  18:37:11  louie
 * Latest test release.
 * 
 * Revision 3.3.1.1  89/03/17  18:26:32  louie
 * 1
 * 
 * Revision 3.3  89/03/15  14:19:56  louie
 * New baseline for next release.
 * 
 * Revision 3.2.1.2  89/03/15  13:59:50  louie
 * Initialize random number generator.  The ntpdc query_mode() routine has been
 * revised to send more peers per packet, a count of the total number of peers
 * which will be transmited, the number of packets to be transmitted, and a 
 * sequence number for each packet.  There is a new version number for the
 * ntpdc query packets, which is now symbolically defined in ntp.h
 * 
 * Revision 3.2.1.1  89/03/10  12:27:41  louie
 * Removed reference to HUGE, and replaced it by a suitable large number.  Added
 * some #ifdef DEBUG .. #endif around some debug code that was missing.  Display
 * patchlevel along with version.
 * 
 * Revision 3.2  89/03/07  18:26:30  louie
 * New version of the UNIX NTP daemon based on the 6 March 1989 draft of the
 * new NTP protcol spec.  A bunch of cosmetic changes.  The peer list is
 * now doublely linked, and a subroutine (enqueue()) replaces the ENQUEUE
 * macro used previously.
 * 
 * Revision 3.1.1.1  89/02/15  08:58:46  louie
 * Bugfixes to released version.
 * 
 * 
 * Revision 3.1  89/01/30  14:43:14  louie
 * Second UNIX NTP test release.
 * 
 * Revision 3.0  88/12/12  15:56:38  louie
 * Test release of new UNIX NTP software.  This version should conform to the
 * revised NTP protocol specification.
 * 
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/file.h>
#ifdef NOSWAP
#include <sys/lock.h>
#endif

#include <net/if.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/udp.h>

#include <arpa/inet.h>
#include <netdb.h>
#include <strings.h>
#include <errno.h>
#include <syslog.h>

#include "ntp.h"
#include "patchlevel.h"

#define	TRUE	1
#define	FALSE	0

struct sockaddr_in dst_sock = {AF_INET};

struct servent *servp;
struct list peer_list;

struct itimerval it;
struct itimerval *itp = &it;
struct timeval tv;
char *prog_name;

char *conf = NTPINITFILE;
char *driftcomp_file = NTPDRIFTCOMP;
static int drift_fd = -1;

#ifdef	DEBUG
int debug = 0;
#endif

#ifdef	NOSWAP
int	noswap = 0;
#endif

int doset = 1;
int ticked;
int selfds;
int trusting = 1;
int logstats;

double WayTooBig = WAYTOOBIG;
u_long clock_watchdog;

struct ntpdata ntpframe;
struct sysdata sys;

extern int errno;
extern char *malloc(), *ntoa();

void finish(), timeout(), tock(), make_new_peer(), init_ntp(), initialize(),
	init_kern_vars(), hourly();
extern void transmit(), process_packet(), clock_update(),
	clear(), clock_filter(), select_clock();

extern void init_logical_clock();

main(argc, argv)
	int argc;
	char *argv[];
{
	struct sockaddr_in *dst = &dst_sock;
	struct ntpdata *pkt = &ntpframe;
	fd_set readfds, tmpmask;
	int dstlen = sizeof(struct sockaddr_in);
	int cc;
	register int i;
	extern char *optarg;
	extern int atoi();

#if	defined(DEBUG) && defined(SIGUSR1) && defined(SIGUSR2)
	void incdebug(), decdebug();
#endif
	initialize();		/* call NTP protocol initialization first,
				   then allow others to override default
				   values */
	prog_name = argv[0];
	while ((cc = getopt(argc, argv, "a:c:dD:lstn")) != EOF) {
		switch (cc) {
		case 'a':
			if (strcmp(optarg, "any") == 0)
				WayTooBig = 10e15;
			else
				WayTooBig = atof(optarg);
			break;

		case 'd':
#ifdef	DEBUG
			debug++;
#else
			fprintf(stderr, "%s: not compiled with DEBUG\n",
				prog_name);
#endif
			break;

		case 'D':
#ifdef	DEBUG
			debug = atoi(optarg);
#else
			fprintf(stderr, "%s: not compiled with DEBUG\n",
				prog_name);
#endif
			break;

		case 's':
			doset = 0;
			break;

		case 't':
			fprintf(stderr, "%s: tickadj not supported\n",
				prog_name);
			break;

		case 'n':
#ifdef	NOSWAP
			noswap = 1;
#else
			fprintf(stderr, "%s: not compiled for noswap\n",
				prog_name);
#endif
			break;

		case 'l':
			logstats = 1;
			break;

		case 'c':
			conf = optarg;
			break;

		default:
			fprintf(stderr, "ntpd: -%c: unknown option\n", cc);
			break;
		}
	}

#ifdef	DEBUG
	if (!debug) {
#endif
		if (fork())
			exit(0);

		{
			int s;
			for (s = getdtablesize(); s >= 0; s--)
				(void) close(s);
			(void) open("/", 0);
			(void) dup2(0, 1);
			(void) dup2(0, 2);
			(void) setpgrp(0, getpid());
			s = open("/dev/tty", 2);
			if (s >= 0) {
				(void) ioctl(s, (u_long) TIOCNOTTY, (char *) 0);
				(void) close(s);
			}
		}
#ifdef	DEBUG
	}
#endif

#ifndef	LOG_DAEMON
	openlog("ntpd", LOG_PID);
#else

#ifndef	LOG_NTP
#define	LOG_NTP	LOG_DAEMON
#endif
	openlog("ntpd", LOG_PID | LOG_NDELAY, LOG_NTP);
#ifdef	DEBUG
	if (debug)
		setlogmask(LOG_UPTO(LOG_DEBUG));
	else
#endif	/* DEBUG */
		setlogmask(LOG_UPTO(LOG_INFO));
#endif	/* LOG_DAEMON */

	syslog(LOG_NOTICE, "%s version $Revision: 3.4.1.9 $", prog_name);
	syslog(LOG_NOTICE, "patchlevel %d", PATCHLEVEL);

#ifdef	DEBUG
	if (debug)
		printf("%s version $Revision: 3.4.1.9 $ patchlevel %d\n",
		       prog_name, PATCHLEVEL);
#endif
	(void) setpriority(PRIO_PROCESS, 0, -10);

#ifdef	NOSWAP
	if (noswap)
		if (plock(PROCLOCK) != 0)  {
			syslog(LOG_ERR, "plock() failed: %m");
#ifdef	DEBUG
			if (debug)
				perror("plock() failed");
#endif
		}
#endif

	servp = getservbyname("ntp", "udp");
	if (servp == NULL) {
		syslog(LOG_CRIT, "udp/ntp: service unknown, using default %d",
		       NTP_PORT);
		(void) create_sockets(htons(NTP_PORT));
	} else
		(void) create_sockets(servp->s_port);


	peer_list.head = peer_list.tail = NULL;
	peer_list.members = 0;

	init_ntp(conf);
	init_kern_vars();
	init_logical_clock();

	/*
	 * Attempt to open for writing the file for storing the drift comp
	 * register.  File must already exist for snapshots to be taken.
	 */
	if ((i = open(driftcomp_file, O_WRONLY|O_CREAT, 0644)) >= 0) {
		drift_fd = i;
	}
	(void) gettimeofday(&tv, (struct timezone *) 0);
	srand(tv.tv_sec);

	FD_ZERO(&tmpmask);
	for (i = 0; i < nintf; i++) {
		FD_SET(addrs[i].fd, &tmpmask);
#ifdef	DEBUG
		if (debug>2) {
			if (addrs[i].if_flags & IFF_BROADCAST)
				printf("Addr %d: %s fd %d %s broadcast %s\n",
				       i, addrs[i].name, addrs[i].fd,
				       ntoa(addrs[i].sin.sin_addr),
				       ntoa(addrs[i].bcast.sin_addr));
			else
				printf("Addr %d: %s fd %d %s\n", i,
				       addrs[i].name, addrs[i].fd,
				       ntoa(addrs[i].sin.sin_addr));
		}
#endif
	}

	(void) signal(SIGINT, finish);
	(void) signal(SIGTERM, finish);
	(void) signal(SIGALRM, tock);
#if	defined(DEBUG) && defined(SIGUSR1) && defined(SIGUSR2)
	(void) signal(SIGUSR1, incdebug);
	(void) signal(SIGUSR2, decdebug);
#endif
	itp->it_interval.tv_sec = (1<<CLOCK_ADJ);
	itp->it_interval.tv_usec = 0;
	itp->it_value.tv_sec = 1;
	itp->it_value.tv_usec = 0;

	/*
	 * Find highest fd in use.  This might save a few microseconds in
	 * the select system call.
	 */
	for (selfds = FD_SETSIZE - 1; selfds; selfds--)
		if (FD_ISSET(selfds, &tmpmask))
			break;
#ifdef	DEBUG
	if (debug > 2)
		printf("Highest fd in use is %d\n", selfds);
	if (!selfds) abort();
#endif
	selfds++;

	(void) setitimer(ITIMER_REAL, itp, (struct itimerval *) 0);

	for (;;) {		/* go into a finite but hopefully very long
				 * loop */
		int nfds;

		readfds = tmpmask;
		nfds = select(selfds, &readfds, (fd_set *) 0, (fd_set *) 0,
						(struct timeval *) 0);
		(void) gettimeofday(&tv, (struct timezone *) 0);

		for(i = 0; i < nintf && nfds > 0; i++) {
			if (!FD_ISSET(addrs[i].fd, &readfds))
				continue;
			addrs[i].uses++;
			dstlen = sizeof(struct sockaddr_in);
			if ((cc = 
			     recvfrom(addrs[i].fd, (char *) pkt, 
				      sizeof(ntpframe), 0, 
				      (struct sockaddr *) dst, &dstlen)) < 0) {

				if (errno != EWOULDBLOCK) {
					syslog(LOG_NOTICE, "recvfrom: %m");
#ifdef	DEBUG
					if(debug > 2)
						perror("recvfrom");
#endif
				}
				continue;
			}

			if (cc < sizeof(*pkt)) {
#ifdef	DEBUG
				if (debug)
					printf("Runt packet from %s\n",
					       ntoa(dst->sin_addr));
#endif
				continue;
			}

			if (pkt->stratum == INFO_QUERY || 
			    pkt->stratum == INFO_REPLY) {
				query_mode(dst, pkt, i);
				continue;
			}
#ifdef	DEBUG
			if (debug > 3) {
				printf("\nInput ");
				dump_pkt(dst, pkt, NULL);
			}
#endif
			if ((pkt->status & VERSIONMASK) != NTPVERSION_1)
				continue;

			receive(dst, pkt, &tv, i);
		}
		if (ticked) {
			ticked = 0;
			timeout();
		}
	}			/* end of forever loop */
}

struct ntp_peer *
check_peer(dst, sock)
	struct sockaddr_in *dst;
	int sock;
{
	register struct ntp_peer *peer = peer_list.head;

	while (peer != NULL) {
		if ((peer->src.sin_addr.s_addr == dst->sin_addr.s_addr) &&
		    (peer->src.sin_port == dst->sin_port) &&
		    ((peer->sock == sock) || (peer->sock == -1)))
			return peer;
		peer = peer->next;
	}
	return ((struct ntp_peer *) NULL);
}

#ifdef	DEBUG
dump_pkt(dst, pkt, peer)
	struct sockaddr_in *dst;
	struct ntpdata *pkt;
	struct ntp_peer *peer;
{
	struct in_addr clock_host;

	printf("Packet: [%s](%d)\n", inet_ntoa(dst->sin_addr),
	       (int) htons(dst->sin_port));
	printf("Leap %d, version %d, mode %d, poll %d, precision %d stratum %d",
	       (pkt->status & LEAPMASK) >> 6, (pkt->status & VERSIONMASK) >> 3,
	       pkt->status & MODEMASK, pkt->ppoll, pkt->precision,
	       pkt->stratum);
	switch (pkt->stratum) {
	case 0:
	case 1:
		printf(" (%.4s)\n", (char *)&pkt->refid);
		break;
	default:
		clock_host.s_addr = (u_long) pkt->refid;
		printf(" [%s]\n", inet_ntoa(clock_host));
		break;
	}
	printf("Synch Dist is %04x.%04x  Synch Dispersion is %04x.%04x\n",
	       ntohs((u_short) pkt->distance.int_part),
	       ntohs((u_short) pkt->distance.fraction),
	       ntohs((u_short) pkt->dispersion.int_part),
	       ntohs((u_short) pkt->dispersion.fraction));
	printf("Reference Timestamp is %08lx.%08lx\n",
	       ntohl(pkt->reftime.int_part),
	       ntohl(pkt->reftime.fraction));
	printf("Originate Timestamp is %08lx.%08lx\n",
	       ntohl(pkt->org.int_part),
	       ntohl(pkt->org.fraction));
	printf("Receive Timestamp is   %08lx.%08lx\n",
	       ntohl(pkt->rec.int_part),
	       ntohl(pkt->rec.fraction));
	printf("Transmit Timestamp is  %08lx.%08lx\n",
	       ntohl(pkt->xmt.int_part),
	       ntohl(pkt->xmt.fraction));
	if(peer != NULL)
		printf("Input Timestamp is     %08lx.%08lx\n",
		       ntohl(peer->rec.int_part),
		       ntohl(peer->rec.fraction));
	putchar('\n');
}
#endif

void
make_new_peer(peer)
	struct ntp_peer *peer;
{
	int i;

	/*
	 * initialize peer data fields 
	 */
	peer->src.sin_family = AF_INET;
	peer->src.sin_port = 0;
	peer->src.sin_addr.s_addr = 0;
	peer->hmode = MODE_SYM_PAS;	/* default: symmetric passive mode */
	peer->flags = 0;
	peer->timer = 1 << NTP_MINPOLL;
	peer->stopwatch = 0;
	peer->hpoll = NTP_MINPOLL;
	double_to_s_fixed(&peer->dispersion, PEER_MAXDISP);
	peer->reach = 0;
	peer->estoffset = 0.0;
	peer->estdelay = 0.0;
	peer->org.int_part = peer->org.fraction = 0;
	peer->rec.int_part = peer->rec.fraction = 0;
	peer->filter.samples = 0;
	for (i = 0; i < NTP_WINDOW; i++) {
		peer->filter.offset[i] = 0.0;
		peer->filter.delay[i] = 0.0;
	}
	peer->pkt_sent = 0;
	peer->pkt_rcvd = 0;
	peer->pkt_dropped = 0;
}

/*
 *  This procedure is called to delete a peer from our list of peers.
 */
void
demobilize(l, peer)
	struct list *l;
	struct ntp_peer *peer;
{
	extern struct ntp_peer dummy_peer;

	if (peer == &dummy_peer)
#ifdef	DEBUG
		abort();
#else
		return;
#endif

#ifdef	DEBUG
	if ((peer->next == NULL && peer->prev == NULL) ||
	    l->tail == NULL || l->head == NULL)
		abort();
#endif

	/* delete only peer in list? */
	if (l->head == l->tail) {
#ifdef	DEBUG
		if (l->head != peer) abort();
#endif
		l->head = l->tail = NULL;
		goto dropit;
	}

	/* delete first peer? */
	if (l->head == peer) {
		l->head = peer->next;
		l->head->prev = NULL;
		goto dropit;
	}

	/* delete last peer? */
	if (l->tail == peer) {
		l->tail = peer->prev;
		l->tail->next = NULL;
		goto dropit;
	}

	/* drop peer in middle */
	peer->prev->next = peer->next;
	peer->next->prev = peer->prev;

 dropit:
#ifdef	DEBUG
	/* just some sanity checking */
	if ((l->members < 0) || 
	    (l->members && l->tail == NULL) ||
	    (l->members == 0 && l->tail != NULL)) {
		syslog(LOG_ERR, "List hosed (demobilize)");
		abort();
	}
	peer->next = peer->prev = NULL;
#endif
	free((char *) peer);
	l->members--;

	return;
}

enqueue(l, peer)
	register struct list *l;
	struct ntp_peer *peer;
{
	l->members++;
	if (l->tail == NULL) {
		/* insertion into empty list */
		l->tail = l->head = peer;
		peer->next = peer->prev = NULL;
		return;
	}

	/* insert at end of list */
	l->tail->next = peer;
	peer->next = NULL;
	peer->prev = l->tail;
	l->tail = peer;
}

/* XXX */
/*
 *  Trivial signal handler.  Assumes you have Berkeley flavored signals which
 *  re-enable themselves.
 */
void 
tock() {
	ticked = 1;
}

void
timeout()
{
	static int periodic = 0;
	register struct ntp_peer *peer = peer_list.head, *next;
#ifndef	XADJTIME2
	extern void adj_host_clock();

	adj_host_clock();
#endif
	/*
	 * Count down sys.hold if necessary.
	 */
	if (sys.hold) {
		if (sys.hold <= (1<<CLOCK_ADJ))
			sys.hold = 0;
		else
			sys.hold -= (1<<CLOCK_ADJ);
	}
	/*
	 * If interval has expired blast off an NTP to that host.
	 */
	while (peer != NULL) {
#ifdef	DEBUG
		if (peer->next == NULL && peer != peer_list.tail) {
			printf("Broken peer list\n");
			syslog(LOG_ERR, "Broken peer list");
			abort();
		}
#endif
		next = peer->next;
		if (peer->reach != 0 || peer->hmode != MODE_SERVER) {
			peer->stopwatch +=(1<<CLOCK_ADJ);
			if (peer->timer <= peer->stopwatch) {
				transmit(peer);
				peer->stopwatch = 0;
			}
		}
		peer = next;
	}

	periodic += (1<<CLOCK_ADJ);
	if (periodic >= 60*60) {
		periodic = 0;
		hourly();
	}

	clock_watchdog += (1 << CLOCK_ADJ);
	if (clock_watchdog >= NTP_MAXAGE) {
		/* woof, woof - barking dogs bite! */
		sys.leap = ALARM;
		if (clock_watchdog < NTP_MAXAGE + (1 << CLOCK_ADJ)) {
			syslog(LOG_ERR,
			       "logical clock adjust timeout (%d seconds)",
			       NTP_MAXAGE);
#ifdef	DEBUG
			if (debug)
			 printf("logical clock adjust timeout (%d seconds)\n",
				NTP_MAXAGE);
#endif
		}
	}

#ifdef	DEBUG
	if (debug)
		(void) fflush(stdout);
#endif
}


/*
 * init_ntp() reads NTP daemon configuration information from disk file.
 */
void
init_ntp(config)
	char *config;
{
	struct sockaddr_in sin;
	char ref_clock[5];
	char name[81];
	FILE *fp;
	int error = FALSE, c;
	struct ntp_peer *peer;
	int precision;
	int stratum;
	int i;
	int debuglevel;
	int stagger = 0;
	double j;
	extern double drift_comp;

	bzero((char *) &sin, sizeof(sin));
	fp = fopen(config, "r");
	if (fp == NULL) {
		fprintf(stderr,"Problem opening NTP initialization file %s\n",
			config);
		syslog(LOG_ERR,"Problem opening NTP initialization file %s",
			config);
		exit(1);
	}

	while (fscanf(fp, "%s", name) != EOF) {	/* read first word of line
						 * and compare to key words */
		if (strcmp(name, "maxpeers") == 0) {
			if (fscanf(fp, "%d", &sys.maxpeers) != 1)
				error = TRUE;
		} else if (strcmp(name, "trusting") == 0) {
			if (fscanf(fp, "%s", name) != 1)
				error = TRUE;
			else {
				if (*name == 'Y' || *name == 'y') {
					trusting = 1;
				} else if (*name == 'N' || *name == 'n') {
					trusting = 0;
				} else
					trusting = atoi(name);
			}
		} else if (strcmp(name, "logclock") == 0) {
			if (fscanf(fp, "%s", name) != 1)
				error = TRUE;
			else {
				if (*name == 'Y' || *name == 'y') {
					logstats = 1;
				} else if (*name == 'N' || *name == 'n') {
					logstats = 0;
				} else
					logstats = atoi(name);
			}
		} else if (strcmp(name, "driftfile") == 0) {
			if (fscanf(fp, "%s", name) != 1)
				error = TRUE;
			else {
				if (driftcomp_file = malloc(strlen(name)+1))
					strcpy(driftcomp_file, name);
			}
		} else if (strcmp(name, "waytoobig") == 0 ||
			   strcmp(name, "setthreshold") == 0) {
			if (fscanf(fp, "%s", name) != 1)
				error = TRUE;
			else {
				if (strcmp(name, "any") == 0)
					WayTooBig = 10e15;
				else
					WayTooBig = atof(name);
			}
		} else if (strncmp(name, "debuglevel", 5) == 0) {
			if (fscanf(fp, "%d", &debuglevel) != 1)
				error = TRUE;
#ifdef	DEBUG
			else debug += debuglevel;
#endif
		} else if (strcmp(name, "stratum") == 0) {
			fprintf(stderr, "Obsolete command 'stratum'\n");
			error = TRUE;
		} else if (strcmp(name, "precision") == 0) {
			if (fscanf(fp, "%d", &precision) != 1)
				error = TRUE;
			else sys.precision = (char) precision;
#ifdef	NOSWAP
		} else if (strcmp(name, "noswap") == 0) {
			noswap = 1;
#endif
#ifdef	BROADCAST_NTP
		} else if (strcmp(name, "broadcast") == 0) {
			if (fscanf(fp, "%s", name) != 1) {
				error = TRUE;
				goto skipit;
			}
			for (i = 0; i < nintf; i++)
				if (strcmp(addrs[i].name, name) == 0)
					break;
			if (i == nintf) {
				syslog(LOG_ERR, "config file: %s not a known interface");
				error = TRUE;
				goto skipit;
			}
			if ((addrs[i].if_flags & IFF_BROADCAST) == 0) {
				syslog(LOG_ERR, "config file: %s doesn't support broadcast", name);
				error = TRUE;
				goto skipit;
			}
			if (peer = check_peer(&addrs[i].bcast, -1)) {
				syslog(LOG_ERR, "config file: duplicate broadcast for %s",
				       name);
				error = TRUE;
				goto skipit;
			}
			peer = (struct ntp_peer *) malloc(sizeof(struct ntp_peer));
			if (peer == NULL) {
				error = TRUE;
				syslog(LOG_ERR, "No memory");
				goto skipit;
			}
			make_new_peer(peer);
			peer->flags = PEER_FL_BCAST;
			peer->hmode = MODE_BROADCAST;
			peer->src = addrs[i].bcast;
			peer->sock = i;
#endif	/* BROADCAST_NTP */
		} else if ((strcmp(name, "peer") == 0) || 
			   (strcmp(name, "passive") == 0) ||
			   (strcmp(name, "server") == 0)) {
			int mode = 0;

			if (strcmp(name, "peer") == 0) {
				mode = MODE_SYM_ACT;
			} else if (strcmp(name, "server") == 0) {
				mode = MODE_CLIENT;
			} else if (strcmp(name, "passive") == 0) {
				mode = MODE_SYM_PAS;
			} else {
				printf("can't happen\n");
				abort();
			}
			if (fscanf(fp, "%s", name) != 1)
				error = TRUE;
#ifdef REFCLOCK
			else if (name[0] == '/') {
				int stratum, precision;
				char clk_type[20];

				if (fscanf(fp, "%4s", ref_clock) != 1) {
					error = TRUE;
					syslog(LOG_ERR, "reference id missing");
					goto skipit;
				}
				if (fscanf(fp, "%4d", &stratum) != 1) {
					error = TRUE;
					syslog(LOG_ERR, "reference stratum missing");
					goto skipit;
				}
				if (fscanf(fp, "%4d", &precision) != 1) {
					error = TRUE;
					syslog(LOG_ERR, "reference precision missing");
					goto skipit;
				}
				if (fscanf(fp, "%19s", clk_type) != 1) {
					error = TRUE;
					syslog(LOG_ERR, "reference type missing");
					goto skipit;
				}

				if((i = init_clock(name, clk_type)) < 0) {
					/* If we could not initialize clock line */
#ifdef DEBUG
					if (debug)
						printf("Could not init reference source %s (type %s)\n",
							name, clk_type);
					else
#endif DEBUG
						syslog(LOG_ERR, "Could not init reference source %s (type %s)",
							name, clk_type);
					error = TRUE;
					goto skipit;
				}
				peer = (struct ntp_peer *)
					malloc(sizeof(struct ntp_peer));
				if (peer == NULL) {
					close(i);
					error = TRUE;
					goto skipit;
				}
				make_new_peer(peer);
				ref_clock[4] = 0;
				(void) strncpy((char *) &peer->refid,
						ref_clock, 4);
				peer->flags = PEER_FL_CONFIG|PEER_FL_REFCLOCK;
				peer->hmode = MODE_SYM_ACT;
				peer->stopwatch = stagger;
				stagger += (1<<CLOCK_ADJ);
				peer->flags |= PEER_FL_SYNC;
				peer->sock = i;
				peer->stratum = stratum;
				peer->precision = precision;
				clear(peer);
				enqueue(&peer_list, peer);
				if (debug > 1)
					printf("Peer %s mode %d refid %.4s stratum %d precision %d\n",
					       name,
					       peer->hmode,
					       (char *)&peer->refid,
					       stratum, precision);
				transmit(peer);	/* head start for REFCLOCK */
			}
#endif REFCLOCK
			else if (GetHostName(name, &sin) == 0)
				syslog(LOG_ERR, "%s: unknown host", name);
			else {
				for (i=0; i<nintf; i++)
					if (addrs[i].sin.sin_addr.s_addr ==
					    sin.sin_addr.s_addr)
						goto skipit;

				if (servp)
					sin.sin_port = servp->s_port;
				else
					sin.sin_port = htons(NTP_PORT);

				peer = check_peer(&sin, -1);
				if (peer == NULL) {
					peer = (struct ntp_peer *)
						malloc(sizeof(struct ntp_peer));
					if (peer == NULL)
						error = TRUE;
					else {
						make_new_peer(peer);
						peer->flags = PEER_FL_CONFIG;
						switch (mode) {
						case MODE_SYM_ACT:	/* "peer" */
							peer->hmode = MODE_SYM_ACT;
							peer->stopwatch = stagger;
							stagger += (1<<CLOCK_ADJ);
							peer->flags |= PEER_FL_SYNC;
							break;
						case MODE_CLIENT:	/* "server" */
							peer->hmode = MODE_CLIENT;
							peer->stopwatch = stagger;
							stagger += (1<<CLOCK_ADJ);
							peer->flags |= PEER_FL_SYNC;
							break;
						case MODE_SYM_PAS:	/* "passive" */
							peer->hmode = MODE_SYM_PAS;
							peer->flags |= PEER_FL_SYNC;
							break;
						default:
							printf("can't happen\n");
							abort();
						}
						peer->src = sin;
						peer->sock = -1;
						clear(peer);
						enqueue(&peer_list, peer);
#ifdef	DEBUG
						if (debug > 1)
							printf("Peer %s/%d af %d mode %d\n",
							       inet_ntoa(peer->src.sin_addr),
							       ntohs(peer->src.sin_port),
							       peer->src.sin_family,
							       peer->hmode);
#endif
					}
				} else {
					syslog(LOG_WARNING,
				     "Duplicate peer %s in in config file",
					       inet_ntoa(sin));
#ifdef	DEBUG
					if(debug)
						printf("Duplicate peer %s in in config file\n",
						       inet_ntoa(sin));
#endif
				}
			}
			skipit:;
		} else if( *name != '#' ) {
			syslog(LOG_ERR, "config file: %s not recognized", name);
#ifdef DEBUG
			if(debug)
				printf("unrecognized option in config file: %s\n", name);
#endif
		}
		do
			c = fgetc(fp);
		while (c != '\n' && c != EOF);	/* next line */
	}			/* end while */
	if (error) {
		fprintf(stderr, "init_ntp: %s: initialization error\n", config);
		syslog(LOG_ERR, "init_ntp: %s: initialization error", config);

		exit(1);
	}
	/*
	 *  Read saved drift compensation register value.
	 */
	if ((fp = fopen(driftcomp_file, "r")) != NULL) {
		if (fscanf(fp, "%lf", &j) == 1 && j > -1.0 && j < 1.0) {
			drift_comp = j;
			syslog(LOG_INFO,
			       "Drift compensation value initialized to %f", j);
		} else {
			fprintf(stderr,
				"init_ntp: bad drift compensation value\n");
			syslog(LOG_ERR,
			       "init_ntp: bad drift compensation value\n");
		}
		fclose(fp);
	}
}

int kern_hz;

#include <sys/sysctl.h>

void
init_kern_vars() {
	int size, mib[2];
	struct clockinfo cinfo;

	mib[0] = CTL_KERN;
	mib[1] = KERN_CLOCKRATE;
	size = sizeof (struct clockinfo);
	if	(sysctl(mib, 2, &cinfo, &size, NULL, 0) < 0)
		{
		syslog(LOG_ERR, "sysctl() for kern.clockrate: %m\n");
		return;
		}
	kern_hz = cinfo.hz;

	/*
	 *  If we have successfully discovered `hz' from the kernel, then we
	 *  can set sys.precision, if it has not already been specified.  If
	 *  no value of `hz' is available, then use default (-6)
	 */
	if (sys.precision == 0) {
		if (kern_hz <= 64)
			sys.precision = -6;
		else if (kern_hz <= 128)
			sys.precision = -7;
		else if (kern_hz <= 256)
			sys.precision = -8;
		else if (kern_hz <= 512)
			sys.precision = -9;
		else if (kern_hz <= 1024)
			sys.precision = -10;
		else sys.precision = -11;
		syslog(LOG_INFO, "sys.precision set to %d from sys clock of %d HZ",
		       sys.precision, kern_hz);
	}
}


/*
 * Given host or net name or internet address in dot notation assign the
 * internet address in byte format. source is ../routed/startup.c with minor
 * changes to detect syntax errors. 
 *
 * We now try to interpret the name as in address before we go off and bother
 * the domain name servers.
 *
 * Unfortunately the library routine inet_addr() does not detect mal formed
 * addresses that have characters or byte values > 255. 
 */

GetHostName(name, sin)
	char *name;
	struct sockaddr_in *sin;
{
	long HostAddr;
	struct hostent *hp;

	if ((HostAddr = inet_addr(name)) != -1) {
		sin->sin_addr.s_addr = (u_long) HostAddr;
		sin->sin_family = AF_INET;
		return (1);
	}

	if (hp = gethostbyname(name)) {
		if (hp->h_addrtype != AF_INET)
			return (0);
		bcopy((char *) hp->h_addr, (char *) &sin->sin_addr,
		      hp->h_length);
		sin->sin_family = hp->h_addrtype;
		return (1);
	}
	return (0);
}

#define	PKTBUF_SIZE	536

/* number of clocks per packet */
#define	N_NTP_PKTS \
      ((PKTBUF_SIZE - sizeof(struct ntpinfo))/(sizeof(struct xclockinfo)))

query_mode(dst, ntp, sock)
	struct sockaddr_in *dst;
	struct ntpdata *ntp;
	int sock;		/* which socket packet arrived on */
{
	char packet[PKTBUF_SIZE];
	register struct ntpinfo *nip = (struct ntpinfo *) packet;
	register struct ntp_peer *peer = peer_list.head;
	struct xclockinfo *cip;
	int seq = 0;
	int i;

	if (ntp->stratum != INFO_QUERY)
		return;
	nip->version = NTPDC_VERSION;
	nip->type = INFO_REPLY;
	nip->seq = 0;
	nip->npkts = peer_list.members/N_NTP_PKTS;
	if (peer_list.members % N_NTP_PKTS)
		nip->npkts++;
	nip->peers = peer_list.members;
	nip->count = 0;
	cip = (struct xclockinfo *)&nip[1];

	while (peer != NULL) {
		cip->net_address = peer->src.sin_addr.s_addr;
		if (peer->sock < 0)
			cip->my_address = htonl(0);
		else
			cip->my_address = addrs[peer->sock].sin.sin_addr.s_addr;
		cip->port = peer->src.sin_port;	/* already in network order */
		cip->flags = htons(peer->flags);
		if (sys.peer == peer)
			cip->flags |= htons(PEER_FL_SELECTED);
		cip->pkt_sent = htonl(peer->pkt_sent);
		cip->pkt_rcvd = htonl(peer->pkt_rcvd);
		cip->pkt_dropped = htonl(peer->pkt_dropped);
		cip->timer = htonl(peer->timer);
		cip->leap = peer->leap;
		cip->stratum = peer->stratum;
		cip->ppoll = peer->ppoll;
		cip->precision = (int) peer->precision;
		cip->hpoll = peer->hpoll;
		cip->reach = htons((u_short)peer->reach & NTP_WINDOW_SHIFT_MASK);
		cip->estdisp = htonl((long) (peer->estdisp * 1000.0));
		cip->estdelay = htonl((long) (peer->estdelay * 1000.0));
		cip->estoffset = htonl((long) (peer->estoffset * 1000.0));
		cip->refid = peer->refid;
		cip->reftime.int_part = htonl(peer->reftime.int_part);
		cip->reftime.fraction = htonl(peer->reftime.fraction);

		cip->info_filter.index = htons(peer->filter.samples);
		for (i = 0; i < PEER_SHIFT; i++) {
			cip->info_filter.offset[i] =
				htonl((long)(peer->filter.offset[i] * 1000.0));
			cip->info_filter.delay[i] =
				htonl((long)(peer->filter.delay[i] * 1000.0));
		}
		cip++;
		if (nip->count++ >= N_NTP_PKTS - 1) {
			nip->seq =seq++;
			if ((sendto(addrs[sock].fd, (char *) packet, 
				    sizeof(packet), 0,
				    (struct sockaddr *) dst,
				    sizeof(struct sockaddr_in))) < 0) {
				syslog(LOG_ERR, "sendto: %s  %m",
				       inet_ntoa(dst->sin_addr));
			}
			nip->type = INFO_REPLY;
			nip->count = 0;
			cip = (struct xclockinfo *)&nip[1];
		}
		peer = peer->next;
	}
	if (nip->count) {
		nip->seq = seq;
		if ((sendto(addrs[sock].fd, (char *) packet, sizeof(packet), 0,
			    dst, sizeof(struct sockaddr_in))) < 0) {
			syslog(LOG_ERR, "sendto: %s  %m", ntoa(dst->sin_addr));
		}
	}
}

/* every hour, dump some useful information to the log */
void
hourly() {
	char buf[200];
	register int p = 0;
	static double drifts[5] = { 0.0, 0.0, 0.0, 0.0, 0.0 };
	static int drift_count = 0;
	extern double drift_comp, compliance;
	extern int peer_switches, peer_sw_inhibited;

	(void) sprintf(buf, "stats: dc %f comp %f peersw %d inh %d",
		       drift_comp, compliance, peer_switches,
		       peer_sw_inhibited);

	if (sys.peer == NULL) {
		strcat(buf, " UNSYNC");
#ifdef	REFCLOCK
	} else if (sys.peer->flags & PEER_FL_REFCLOCK) {
		p = strlen(buf);
		(void) sprintf(buf + p, " off %f SYNC %.4s %d",
			       sys.peer->estoffset,
			       (char *)&sys.peer->refid,
			       sys.peer->stratum);
#endif
	} else {
		p = strlen(buf);
		(void) sprintf(buf + p, " off %f SYNC %s %d",
			       sys.peer->estoffset,
			       ntoa(sys.peer->src.sin_addr),
			       sys.peer->stratum);
	}
	syslog(LOG_INFO, buf);
#ifdef	DEBUG
	if (debug)
		puts(buf);
#endif
	/*
	 *  If the drift compensation snapshot file is open, then write
	 *  the current value to it.  Since there's only one block in the
	 *  file, and no one else is reading it, we'll just keep the file
	 *  open and write to it.
	 */
	if (drift_fd >= 0) {
		drifts[drift_count % 5] = drift_comp;
		/* works out to be 70 bytes */
		(void) sprintf(buf,
		     "%12.10f %12.10f %12.10f %12.10f %12.10f %4d\n",
			       drifts[drift_count % 5],
			       drifts[(drift_count+4) % 5],
			       drifts[(drift_count+3) % 5],
			       drifts[(drift_count+2) % 5],
			       drifts[(drift_count+1) % 5],
			       drift_count + 1);

		(void) lseek(drift_fd, 0L, L_SET);
		if (write(drift_fd, buf, strlen(buf)) < 0) {
			syslog(LOG_ERR, "Error writing drift comp file: %m");
		}
		drift_count++;
	}
}

#if	defined(DEBUG) && defined(SIGUSR1) && defined(SIGUSR2)
void
incdebug()
{
	if (debug == 255)
		return;
	debug++;
	printf("DEBUG LEVEL %d\n", debug);
#ifdef	LOG_DAEMON
	(void) setlogmask(LOG_UPTO(LOG_DEBUG));
#endif
	syslog(LOG_DEBUG, "DEBUG LEVEL %d", debug);
}

void
decdebug()
{
	if (debug == 0)
		return;
	debug--;
	printf("DEBUG LEVEL %d\n", debug);
	syslog(LOG_DEBUG, "DEBUG LEVEL %d", debug);
#ifdef	LOG_DAEMON
	if (debug == 0)
		(void) setlogmask(LOG_UPTO(LOG_INFO));
#endif
}
#endif

void
finish(sig)
	int sig;
{
	syslog(LOG_NOTICE, "terminated: (sig %d)", sig);
#ifdef	DEBUG
	if (debug)
		printf("ntpd terminated (sig %d)\n", sig);
#endif
	exit(1);
}

#ifdef	REFCLOCK
struct refclock {
	int fd;
	int (*reader)();
	struct refclock *next;
} *refclocks = NULL;

int init_clock_local(), read_clock_local();
#ifdef PSTI
int init_clock_psti(), read_clock_psti();
#endif PSTI

init_clock(name, type)
char *name, *type;
{
	struct refclock *r;
	int (*reader)();
	int cfd;

	if (strcmp(type, "local") == 0) {
		reader = read_clock_local;
		cfd = init_clock_local(name);
	}
#ifdef PSTI
	else if (strcmp(type, "psti") == 0) {
		reader = read_clock_psti;
		cfd = init_clock_psti(name);
	}
#endif PSTI
	else {
		if (debug) printf("Unknown reference type\n"); else
		syslog(LOG_ERR, "Unknown reference clock type (%s)\n", type);
		return(-1);
	}
	if (cfd >= 0) {
		r = (struct refclock *)malloc(sizeof(struct refclock));
		r->fd = cfd;
		r->reader = reader;
		r->next = refclocks;
		refclocks = r;
	}
	return(cfd);
}

read_clock(cfd, tvpp, otvpp)
int cfd;
struct timeval **tvpp, **otvpp;
{
	struct refclock *r;

	for (r = refclocks; r; r = r->next)
		if(r->fd == cfd)
			return((r->reader)(cfd, tvpp, otvpp));
	return(1); /* Can't happen */
}
#endif