/*
 * Copyright 2000, International Business Machines Corporation and others.
 * All Rights Reserved.
 * 
 * This software has been released under the terms of the IBM Public
 * License.  For details, see the LICENSE file in the top-level source
 * directory or online at http://www.openafs.org/dl/license10.html
 */

#include <afsconfig.h>
#include <afs/param.h>

RCSID("$Header: /cvs/openafs/src/ntp/ntpd.c,v 1.5 2001/08/08 00:03:53 shadow Exp $");

#if defined(AFS_SGI_ENV)
#define NOSWAP 1
#endif
#include <stdio.h>
#ifdef	AFS_AIX32_ENV
#include <signal.h>
#endif
#ifdef	AFS_SUN5_ENV
#include <signal.h>
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/time.h>
#ifdef	AFS_SUN5_ENV
#define BSD_COMP
#endif
#include <sys/ioctl.h>
#include <sys/resource.h>
#ifdef	AFS_SUN5_ENV
#include <fcntl.h>
#endif
#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>
#ifndef	LOG_NTP
#define	LOG_NTP	LOG_DAEMON
#endif
#ifdef	AFS_SUN5_ENV
#include <libelf.h>
#endif
#include <nlist.h>

#ifdef AFS_AIX32_ENV
#include <sys/select.h>
#include <sys/signal.h>
#endif

#ifdef	AFS_HPUX_ENV
#include <signal.h>
#endif

#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

int	tickadj = 0;
int	dotickadj = 0;
int	dosynctodr = 0;

#ifdef	NOSWAP
int	noswap = 0;
#endif

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

double WayTooBig = WAYTOOBIG;
afs_uint32 clock_watchdog;

struct ntpdata ntpframe;
struct sysdata sys;

extern int errno;
extern char *malloc(), *ntoa();
extern double s_fixed_to_double(), ul_fixed_to_double();

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

extern void init_logical_clock();

#if defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV)
#define SETTICKADJ
#endif

#if !defined(AFS_SUN5_ENV)
#define INIT_KERN_VARS
#endif

#if defined(INIT_KERN_VARS)
void init_kern_vars();
#endif	/* INIT_KERN_VARS */

#include "AFS_component_version_number.c"

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;
	int dontFork = 0;
	register int i;
	extern char *optarg;
	extern int atoi();

#if	defined(DEBUG) && defined(SIGUSR1) && defined(SIGUSR2)
	void incdebug(), decdebug();
#endif

#ifdef	AFS_AIX32_ENV
	/*
	 * The following signal action for AIX is necessary so that in case of a 
	 * crash (i.e. core is generated) we can include the user's data section 
	 * in the core dump. Unfortunately, by default, only a partial core is
	 * generated which, in many cases, isn't too useful.
	 */
	struct sigaction nsa;
    
	sigemptyset(&nsa.sa_mask);
	nsa.sa_handler = SIG_DFL;
	nsa.sa_flags = SA_FULLDUMP;
	sigaction(SIGSEGV, &nsa, NULL);
#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:lstrnf")) != 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':
#ifdef	SETTICKADJ
			dotickadj++;
#else
			fprintf(stderr, "%s: not compiled to set tickadj\n",
				prog_name);
#endif
			break;

		case 'r':
#ifdef	SETTICKADJ
			dosynctodr++;
#else
			fprintf(stderr, "%s: not compiled to set kernel variables\n",
				prog_name);
#endif
			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;

		case 'f':
			dontFork = 1;
			break;

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

#ifdef	DEBUG
	if (!debug && !dontFork)
#else
 	if (!dontFork)
#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) {
#ifndef	AFS_HPUX_ENV
				(void) ioctl(s, (afs_uint32) TIOCNOTTY, (char *) 0);
#endif
				(void) close(s);
			}
		}
	}

	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));

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

#ifdef	DEBUG
	if (debug)
		printf("%s version $Revision: 1.5 $ patchlevel %d\n",
		       prog_name, PATCHLEVEL);
#endif
#if defined(AFS_AIX_ENV)
	(void) nice(-10);
#else
#if defined(AFS_HPUX_ENV)
	(void) rtprio (0, 90);
#else
	(void) setpriority(PRIO_PROCESS, 0, -10);
#endif
#endif

#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);
#if defined(INIT_KERN_VARS)
	init_kern_vars();
#endif	/* 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; 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_ERR, "recvfrom: %m");
#ifdef	DEBUG
					if(debug > 2)
						perror("recvfrom");
#endif
				}
				continue;
			}

			/* hpux seems to generate these zero's randomly */
			if (cc == 0) continue;

			if (cc < sizeof(*pkt)) {
#ifdef	DEBUG
				if (debug)
					printf("Runt packet from %s, got %d bytes, should be %d\n",
					       ntoa(dst->sin_addr),
					       cc, sizeof(*pkt));
#endif
				continue;
			} else if (cc > sizeof(*pkt)) {
#ifdef	DEBUG
				if (debug)
					printf("too many chars returned by recvfrom.  Host %s, got %d bytes, should be %d\n",
					       ntoa(dst->sin_addr),
					       cc, sizeof(*pkt));
#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 = (afs_uint32) 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 %f\n",
	       ntohl(pkt->reftime.int_part),
	       ntohl(pkt->reftime.fraction),
	       ul_fixed_to_double(&pkt->reftime));
	printf("Originate Timestamp is %08lx.%08lx %f\n",
	       ntohl(pkt->org.int_part),
	       ntohl(pkt->org.fraction),
	       ul_fixed_to_double(&pkt->org));
	printf("Receive Timestamp is   %08lx.%08lx %f\n",
	       ntohl(pkt->rec.int_part),
	       ntohl(pkt->rec.fraction),
	       ul_fixed_to_double(&pkt->rec));
	printf("Transmit Timestamp is  %08lx.%08lx %f\n",
	       ntohl(pkt->xmt.int_part),
	       ntohl(pkt->xmt.fraction),
	       ul_fixed_to_double(&pkt->xmt));
	if(peer != NULL)
		printf("Input Timestamp is     %08lx.%08lx %f\n",
		       ntohl(peer->rec.int_part),
		       ntohl(peer->rec.fraction),
		       ul_fixed_to_double(&peer->rec));
	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.
 */
void 
tock(x) {
	ticked = 1;

	/* re-enable self (we're not BSD any more) */
	(void) signal(SIGALRM, tock);
}

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;

	memset((char *) &sin, 0, 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	SETTICKADJ
		} else if (strcmp(name, "tickadj") == 0) {
			if (fscanf(fp, "%d", &i) != 1)
				error = TRUE;
			else tickadj = i;
		} else if (strcmp(name, "settickadj") == 0) {
			if (fscanf(fp, "%s", name) != 1)
				error = TRUE;
			else {
				if (*name == 'Y' || *name == 'y') {
					dotickadj = 1;
				} else if (*name == 'N' || *name == 'n') {
					dotickadj = 0;
				} else
					dotickadj = atoi(name);
			}
		} else if (strcmp(name, "setdosynctodr") == 0) {
			if (fscanf(fp, "%s", name) != 1)
				error = TRUE;
			else {
				if (*name == 'Y' || *name == 'y') {
					dosynctodr = 1;
				} else if (*name == 'N' || *name == 'n') {
					dosynctodr = 0;
				} else
					dosynctodr = atoi(name);
			}
#endif
#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);
#ifdef DEBUG
				if (debug > 1)
					printf("Peer %s mode %d refid %.4s stratum %d precision %d\n",
					       name,
					       peer->hmode,
					       (char *)&peer->refid,
					       stratum, precision);
#endif
				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.sin_addr));
#ifdef	DEBUG
					if(debug)
						printf("Duplicate peer %s in in config file\n",
						       inet_ntoa(sin.sin_addr));
#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 defined(AFS_SUN_ENV) || defined(AFS_HPUX_ENV)
#define BOGUS_DRIFT_COMPENSATION 10.0
#else
#define BOGUS_DRIFT_COMPENSATION 1.0
#endif
	if ((fp = fopen(driftcomp_file, "r")) != NULL) {
		if (fscanf(fp, "%lf", &j) == 1 &&
		    j > -BOGUS_DRIFT_COMPENSATION &&
		    j < BOGUS_DRIFT_COMPENSATION) {
			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_tickadj, kern_hz, kern_tick, kern_dosynctodr;

#ifdef	SETTICKADJ
int SetKernel (kmem, nl, newValue)
  int kmem;
  struct nlist *nl;
{
    static char	*memory = "/dev/kmem";
    if (lseek(kmem, (long)nl->n_value, L_SET) == -1) {
	syslog(LOG_ERR, "%s: lseek fails: %m", memory);
	perror ("setting kernel variable");
	return errno;
    }
    if (write(kmem, &newValue, sizeof(int)) != sizeof(int)) {
	syslog(LOG_ERR, "%s: kernel variable assignment fails: %m", memory);
	printf("SetKernel variable %s fails\n", nl->n_name);
	perror ("setting kernel variable");
	return errno;
    } 
    syslog(LOG_INFO, "System %s SET to %d", nl->n_name, newValue);
    printf("System %s SET to %d\n", nl->n_name, newValue);
}
#endif

#if defined(INIT_KERN_VARS)
void
init_kern_vars()
{
	int kmem;
	static char	*memory = "/dev/kmem";
	static struct nlist nl[] = {
#ifndef	NeXT
		{"_tickadj"},
		{"_hz"},
		{"_tick"},
#ifdef AFS_SUN_ENV
		{"_dosynctodr"},
#endif
		{""},
#else
		{{"_tickadj"}},
		{{"_hz"}},
		{{"_tick"}},
		{{""}},
#endif
	};
	static int *kern_vars[] =
	    {&kern_tickadj, &kern_hz, &kern_tick, &kern_dosynctodr};
	int i;
	kmem = open(memory, O_RDONLY);
	if (kmem < 0) {
		syslog(LOG_ERR, "Can't open %s for reading: %m", memory);
#ifdef	DEBUG
		if (debug)
			perror(memory);
#endif
		return;
	}

	nlist("/vmunix", nl);

	for (i = 0; i < (sizeof(kern_vars)/sizeof(kern_vars[0])); i++) {
		long where;

		if ((where = nl[i].n_value) == 0) {
			syslog(LOG_ERR, "Unknown kernal var %s",
#ifdef	NeXT
			       nl[i].n_un.n_name
#else
			       nl[i].n_name
#endif
			       );
			continue;
		}
		if (lseek(kmem, where, L_SET) == -1) {
			syslog(LOG_ERR, "lseek for %s fails: %m",
#ifdef	NeXT
			       nl[i].n_un.n_name
#else
			       nl[i].n_name
#endif
			       );
			continue;
		}
		if (read(kmem, kern_vars[i], sizeof(int)) != sizeof(int)) {
			syslog(LOG_ERR, "read for %s fails: %m",
#ifdef	NeXT
			       nl[i].n_un.n_name
#else
			       nl[i].n_name
#endif
			       );

			*kern_vars[i] = 0;
		}
	}

	/*
	 *  If desired value of tickadj is not specified in the configuration
	 *  file, compute a "reasonable" value here, based on the assumption 
	 *  that we don't have to slew more than 2ms every 4 seconds.
	 *
	 *  TODO: the 500 needs to be parameterized.
	 */
	if (tickadj == 0 && kern_hz)
		tickadj = 500/kern_hz;

#ifdef	DEBUG
	if (debug) {
		printf("kernel vars: tickadj = %d, hz = %d, tick = %d\n",
		       kern_tickadj, kern_hz, kern_tick);
#ifdef AFS_SUN_ENV
		if (kern_dosynctodr) printf ("dosynctodr is on\n");
#endif
		printf("desired tickadj = %d, dotickadj = %d\n", tickadj,
		       dotickadj);
	}
#endif
	close(kmem);

#ifdef	SETTICKADJ
	{   int set_tickadj, set_dosynctodr;
	    int code;
	    set_tickadj = (dotickadj && tickadj && (tickadj != kern_tickadj)
			   /* do some plausibility checks on the value... */
			   && (kern_tickadj > 0) && (kern_tickadj < 20000));
	    set_dosynctodr = (dosynctodr && (kern_dosynctodr == 1));
	    if (set_tickadj || set_dosynctodr) {
		if ((kmem = open(memory, O_RDWR)) >= 0) {
		    if (set_tickadj) {
			code = SetKernel (kmem, &nl[0], tickadj);
			if (code == 0) kern_tickadj = tickadj;
		    }
		    if (set_dosynctodr) {
			code = SetKernel (kmem, &nl[3], 0);
			if (code == 0) kern_dosynctodr = 0;
		    }
		    close (kmem);
		} else {
		    syslog(LOG_ERR, "Can't open %s: %m", memory);
		    printf("Can't open %s\n", memory);
		}
	    }
	}
#endif	/* SETTICKADJ */

	/*
	 *  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) {
	    char msg[128];
	    int interval = 0;
	    sys.precision = MeasurePrecision(&interval);
	    if (sys.precision) {
		sprintf (msg, "sys.precision set to %d from measured minimum clock interval of %d usec",
			 sys.precision, interval);
	    } else {
		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;
		sprintf (msg,
			 "sys.precision set to %d from sys clock of %d HZ",
			 sys.precision, kern_hz);
	    }
	    syslog(LOG_INFO, msg);
	    printf ("%s\n", msg);
	}
}
#endif	/* INIT_KERN_VARS */


/*
 * 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;
{
	afs_int32 HostAddr;
	struct hostent *hp;

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

	if (hp = gethostbyname(name)) {
		if (hp->h_addrtype != AF_INET)
			return (0);
		memcpy((char *) &sin->sin_addr, (char *) hp->h_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 clockinfo)))

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 clockinfo *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 clockinfo *)&nip[1];

	while (peer != NULL) {
		cip->net_address = peer->src.sin_addr.s_addr;
		if ((peer->sock < 0) || (peer->sock >= nintf))
			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 = peer->precision;
		cip->hpoll = peer->hpoll;
		cip->reach = htons(peer->reach & NTP_WINDOW_SHIFT_MASK);
		cip->estdisp = htonl((afs_int32) (peer->estdisp * 1000.0));
		cip->estdelay = htonl((afs_int32) (peer->estdelay * 1000.0));
		cip->estoffset = htonl((afs_int32) (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((afs_int32)(peer->filter.offset[i] * 1000.0));
			cip->info_filter.delay[i] =
				htonl((afs_int32)(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 clockinfo *)&nip[1];
		}
		peer = peer->next;
	}
	if (nip->count) {
		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", 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(x)
{
	/* re-enable self (we're not BSD any more) */
	(void) signal(SIGUSR1, 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(x)
{
	/* re-enable self (we're not BSD any more) */
	(void) signal(SIGUSR2, 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 {
#ifdef DEBUG
		if (debug) printf("Unknown reference type\n"); else
#endif
		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
