1387 lines · 25.8 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <auth.h>#include <ip.h>#include <mp.h> /* nanosecond times */#define SEC 1000000000LL#define MIN (60LL*SEC)#define HOUR (60LL*MIN)#define DAY (24LL*HOUR) enum {	Fs,	Rtc,	Ntp,	Utc,	Gps, 	HZAvgSecs= 3*60,  /* target averaging period for frequency in seconds */	MinSampleSecs= 60,	/* minimum sampling time in seconds */};  char *dir = "/tmp";	/* directory sample files live in */char *logfile = "timesync";char *timeserver;char *Rootid;int utcfil;int gpsfil;int debug;int impotent;int logging;int type;int gmtdelta;		/* rtc+gmtdelta = gmt */uvlong avgerr; /* ntp server info */int stratum = 14;vlong mydisp, rootdisp;vlong mydelay, rootdelay;vlong avgdelay;vlong lastutc;uchar rootid[4];char *sysid;int myprec; /* list of time samples */typedef struct Sample Sample;struct Sample{	Sample	*next;	uvlong	ticks;	vlong	ltime;	vlong	stime;}; /* ntp packet */typedef struct NTPpkt NTPpkt;struct NTPpkt{	uchar	mode;	uchar	stratum;	uchar	poll;	uchar	precision;	uchar	rootdelay[4];	uchar	rootdisp[4];	uchar	rootid[4];	uchar	refts[8];	uchar	origts[8];	/* departed client */	uchar	recvts[8];	/* arrived at server */	uchar	xmitts[8];	/* departed server */	uchar	keyid[4];	uchar	digest[16];}; /* ntp server */typedef struct NTPserver NTPserver;struct NTPserver{	NTPserver *next;	char	*name;	uchar	stratum;	uchar	precision;	vlong	rootdelay;	vlong	rootdisp;	vlong	rtt;	vlong	dt;}; NTPserver *ntpservers; enum{	NTPSIZE= 	48,	/* basic ntp packet */	NTPDIGESTSIZE=	20,	/* key and digest */}; /* error bound of last sample */ulong	ε; static void	addntpserver(char *name);static int	adjustperiod(vlong diff, vlong accuracy, int secs);static void	background(void);static int	caperror(vlong dhz, int tsecs, vlong taccuracy);static long	fstime(void);static int	gettime(vlong *nsec, uvlong *ticks, uvlong *hz); /* returns time, ticks, hz */static int	getclockprecision(vlong);static vlong	gpssample(void);static void	hnputts(void *p, vlong nsec);static void	hnputts(void *p, vlong nsec);static void	inittime(void);static vlong	nhgetts(void *p);static vlong	nhgetts(void *p);static void	ntpserver(char*);static vlong	ntpsample(void);static int	ntptimediff(NTPserver *ns);static int	openfreqfile(void);static vlong	readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz);static long	rtctime(void);static vlong	sample(long (*get)(void));static void	setpriority(void);static void	setrootid(char *d);static void	settime(vlong now, uvlong hz, vlong delta, int n); /* set time, hz, delta, period */static vlong	utcsample(void);static uvlong	vabs(vlong);static uvlong	whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz,			vlong dt, vlong ticks, vlong period);static void	writefreqfile(int fd, vlong hz, int secs, vlong diff); // ((1970-1900)*365 + 17 /*leap days*/)*24*60*60#define EPOCHDIFF 2208988800UL static voidusage(void){	fprint(2, "usage: %s [-a accuracy][-d dir][-I rootid][-s net]"		"[-S stratum][-DfGilLnrU] timesource ...\n", argv0);	exits("usage");} voidmain(int argc, char **argv){	int i, t, fd, nservenet;	int secs;		/* sampling period */	int tsecs;		/* temporary sampling period */	uvlong hz, minhz, maxhz, period, nhz;	vlong diff, accuracy, taccuracy;	char *servenet[4];	Sample *s, *x, *first, **l;	Tm tl, tg; 	type = Fs;		/* by default, sync with the file system */	debug = 0;	accuracy = 1000000LL;	/* default accuracy is 1 millisecond */	nservenet = 0;	tsecs = secs = MinSampleSecs;	timeserver = ""; 	ARGBEGIN{	case 'a':		accuracy = strtoll(EARGF(usage()), 0, 0); /* specified in ns */		if(accuracy <= 1)			sysfatal("bad accuracy specified");		break;	case 'd':		dir = EARGF(usage());		break;	case 'D':		debug = 1;		break;	case 'f':		type = Fs;		stratum = 2;		break;	case 'G':		type = Gps;		stratum = 1;		break;	case 'i':		impotent = 1;		break;	case 'I':		Rootid = EARGF(usage());		break;	case 'l':		logging = 1;		break;	case 'L':		/*		 * Assume time source in local time rather than GMT.		 * Calculate difference so that rtctime can return GMT.		 * This is useful with the rtc on PC's that run Windows		 * since Windows keeps the local time in the rtc.		 */		t = time(0);		tl = *localtime(t);		tg = *gmtime(t); 		/*		 * if the years are different, we're at most a day off,		 * so just rewrite		 */		if(tl.year < tg.year){			tg.year--;			tg.yday = tl.yday + 1;		}else if(tl.year > tg.year){			tl.year--;			tl.yday = tg.yday+1;		}		assert(tl.year == tg.year); 		tg.sec -= tl.sec;		tg.min -= tl.min;		tg.hour -= tl.hour;		tg.yday -= tl.yday;		gmtdelta = tg.sec+60*(tg.min+60*(tg.hour+tg.yday*24)); 		assert(abs(gmtdelta) <= 24*60*60);		break;	case 'n':		type = Ntp;		break;	case 'r':		type = Rtc;		stratum = 0;		break;	case 'U':		type = Utc;		stratum = 1;		break;	case 's':		if(nservenet >= nelem(servenet))			sysfatal("too many networks to serve on");		servenet[nservenet++] = EARGF(usage());		break;	case 'S':		stratum = strtoll(EARGF(usage()), 0, 0);		break;	default:		usage();	}ARGEND; 	fmtinstall('E', eipfmt);	fmtinstall('I', eipfmt);	fmtinstall('V', eipfmt);	sysid = getenv("sysname"); 	/* detach from the current namespace */	if(debug)		rfork(RFNAMEG); 	switch(type){	case Utc:		if(argc > 0)			timeserver = argv[0];		else			sysfatal("bad time source");		break;	case Gps:		if(argc > 0)			timeserver = argv[0];		else			timeserver = "/mnt/gps/time";		break;	case Fs:		if(argc > 0)			timeserver = argv[0];		else			timeserver = "/srv/boot";		break;	case Ntp:		if(argc > 0)			for(i = 0; i < argc; i++)				addntpserver(argv[i]);		else			addntpserver("$ntp");		break;	} 	setpriority(); 	/* figure out our time interface and initial frequency */	inittime();	gettime(0, 0, &hz);	minhz = hz/10;	maxhz = hz*10;	myprec = getclockprecision(hz); 	/* convert the accuracy from nanoseconds to ticks */	taccuracy = hz*accuracy/SEC; 	/*	 * bind in clocks	 */	switch(type){	case Fs:		fd = open(timeserver, ORDWR);		if(fd < 0)			sysfatal("opening %s: %r", timeserver);		if(amount(fd, "/n/boot", MREPL, "") < 0)			sysfatal("mounting %s: %r", timeserver);		close(fd);		break;	case Rtc:		bind("#r", "/dev", MAFTER);		if(access("/dev/rtc", AREAD) < 0)			sysfatal("accessing /dev/rtc: %r");		break;	case Utc:		fd = open(timeserver, OREAD);		if(fd < 0)			sysfatal("opening %s: %r", timeserver);		utcfil = fd;		break;	case Gps:		fd = open(timeserver, OREAD);		if(fd < 0)			sysfatal("opening %s: %r", timeserver);		gpsfil = fd;		break;	} 	/*	 * start a local ntp server(s)	 */	for(i = 0; i < nservenet; i++)		switch(rfork(RFPROC|RFFDG|RFMEM|RFNOWAIT)){		case -1:			sysfatal("forking: %r");		case 0:			ntpserver(servenet[i]);			_exits(0);		} 	/* get the last known frequency from the file */	fd = openfreqfile();	hz = readfreqfile(fd, hz, minhz, maxhz); 	/*	 * this is the main loop.  it gets a sample, adjusts the	 * clock and computes a sleep period until the next loop.	 * we balance frequency drift against the length of the	 * period to avoid blowing the accuracy limit.	 */	first = nil;	l = &first;	avgerr = accuracy >> 1;	for(;; background(), sleep(tsecs*1000)){		s = mallocz(sizeof *s, 1);		diff = 0; 		/* get times for this sample */		ε = ~0;		switch(type){		case Fs:			s->stime = sample(fstime);			break;		case Rtc:			s->stime = sample(rtctime);			break;		case Utc:			s->stime = utcsample();			if(s->stime == 0LL){				if(logging)					syslog(0, logfile, "no sample");				free(s);				if (secs > 60 * 15)					tsecs = 60*15;				continue;			}			break;		case Ntp:			diff = ntpsample();			if(diff == 0LL){				if(logging)					syslog(0, logfile, "no sample");				free(s);				if(secs > 60*15)					tsecs = 60*15;				continue;			}			break;		case Gps:			diff = gpssample();			if(diff == 0LL){				if(logging)					syslog(0, logfile, "no sample");				free(s);				if(secs > 60*15)					tsecs = 60*15;				continue;			}		} 		/* use fastest method to read local clock and ticks */		gettime(&s->ltime, &s->ticks, 0);		if(type == Ntp || type == Gps)			s->stime = s->ltime + diff; 		/* if the sample was bad, ignore it */		if(s->stime < 0){			free(s);			continue;		} 		/* reset local time */		diff = s->stime - s->ltime;		if(diff > 10*SEC || diff < -10*SEC){			/* we're way off, just set the time */			secs = MinSampleSecs;			settime(s->stime, 0, 0, 0);		} else {			/* keep a running average of the error. */			avgerr = (avgerr>>1) + (vabs(diff)>>1); 			/*			 * the time to next sample depends on how good or			 * bad we're doing.			 */			tsecs = secs = adjustperiod(diff, accuracy, secs); 			/*			 * work off the fixed difference.  This is done			 * by adding a ramp to the clock.  Each 100th of a			 * second (or so) the kernel will add diff/(4*secs*100)			 * to the clock.  we only do 1/4 of the difference per			 * period to dampen any measurement noise.			 *			 * any difference greater than ε we work off during the			 * sampling period.			 */			if(abs(diff) > ε)				if(diff > 0)					settime(-1, 0, diff-((3*ε)/4), secs);				else					settime(-1, 0, diff+((3*ε)/4), secs);			else				settime(-1, 0, diff, 4*secs); 		}		if(debug)			fprint(2, "δ %lld avgδ %lld f %lld\n", diff, avgerr, hz); 		/* dump old samples (keep at least one) */		while(first != nil){			if(first->next == nil)				break;			if(s->stime - first->next->stime < DAY)				break;			x = first;			first = first->next;			free(x);		} 		/*		 * The sampling error is limited by the total error.  If		 * we make sure the sampling period is at least 16 million		 * times the average error, we should calculate a frequency		 * with on average a 1e-7 error.		 *		 * So that big hz changes don't blow our accuracy requirement,		 * we shorten the period to make sure that δhz*secs will be		 * greater than the accuracy limit.		 */		period = avgerr << 24;		for(x = first; x != nil; x = x->next)			if(s->stime - x->stime < period ||			   x->next == nil || s->stime - x->next->stime < period)				break;		if(x != nil){			nhz = whatisthefrequencykenneth(				hz, minhz, maxhz,				s->stime - x->stime,				s->ticks - x->ticks,				period);			tsecs = caperror(vabs(nhz-hz), tsecs, taccuracy);			hz = nhz;			writefreqfile(fd, hz, (s->stime - x->stime)/SEC, diff);		} 		/* add current sample to list. */		*l = s;		l = &s->next; 		if(logging)			syslog(0, logfile, "δ %lld avgδ %lld hz %lld",				diff, avgerr, hz);	}}

Lines 1–500 of 1387.