xref: /plan9/sys/src/cmd/aux/timesync.c (revision be93767fd3d9b466f5cc98d40df522ccdbbcd2c0)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
35d459b5aSDavid du Colombier #include <auth.h>
47dd7cddfSDavid du Colombier #include <ip.h>
580ee5cbfSDavid du Colombier #include <mp.h>
67dd7cddfSDavid du Colombier 
780ee5cbfSDavid du Colombier /* nanosecond times */
87dd7cddfSDavid du Colombier #define SEC 1000000000LL
980ee5cbfSDavid du Colombier #define MIN (60LL*SEC)
1080ee5cbfSDavid du Colombier #define HOUR (60LL*MIN)
1180ee5cbfSDavid du Colombier #define DAY (24LL*HOUR)
127dd7cddfSDavid du Colombier 
137dd7cddfSDavid du Colombier enum {
147dd7cddfSDavid du Colombier 	Fs,
157dd7cddfSDavid du Colombier 	Rtc,
167dd7cddfSDavid du Colombier 	Ntp,
175fab9909SDavid du Colombier 	Utc,
1880ee5cbfSDavid du Colombier 
1980ee5cbfSDavid du Colombier 	HZAvgSecs=	3*60,	/* target averaging period for the frequency in seconds */
2080ee5cbfSDavid du Colombier 	MinSampleSecs=	60,	/* minimum sampling time in seconds */
217dd7cddfSDavid du Colombier };
227dd7cddfSDavid du Colombier 
237dd7cddfSDavid du Colombier 
247dd7cddfSDavid du Colombier char *dir = "/tmp";	// directory sample files live in
257dd7cddfSDavid du Colombier char *logfile = "timesync";
267dd7cddfSDavid du Colombier char *timeserver;
275fab9909SDavid du Colombier char *Rootid;
285fab9909SDavid du Colombier int utcfil;
297dd7cddfSDavid du Colombier int debug;
307dd7cddfSDavid du Colombier int impotent;
317dd7cddfSDavid du Colombier int logging;
327dd7cddfSDavid du Colombier int type;
337dd7cddfSDavid du Colombier int gmtdelta;	// rtc+gmtdelta = gmt
3495fdf19cSDavid du Colombier uvlong avgerr;
357dd7cddfSDavid du Colombier 
367dd7cddfSDavid du Colombier // ntp server info
377dd7cddfSDavid du Colombier int stratum = 14;
3880ee5cbfSDavid du Colombier vlong mydisp, rootdisp;
3980ee5cbfSDavid du Colombier vlong mydelay, rootdelay;
407dd7cddfSDavid du Colombier vlong avgdelay;
411118d624SDavid du Colombier vlong lastutc;
427dd7cddfSDavid du Colombier uchar rootid[4];
437dd7cddfSDavid du Colombier char *sysid;
4467031067SDavid du Colombier int myprec;
457dd7cddfSDavid du Colombier 
467dd7cddfSDavid du Colombier // list of time samples
477dd7cddfSDavid du Colombier typedef struct Sample Sample;
487dd7cddfSDavid du Colombier struct Sample
497dd7cddfSDavid du Colombier {
507dd7cddfSDavid du Colombier 	Sample	*next;
517dd7cddfSDavid du Colombier 	uvlong	ticks;
527dd7cddfSDavid du Colombier 	vlong	ltime;
537dd7cddfSDavid du Colombier 	vlong	stime;
547dd7cddfSDavid du Colombier };
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier // ntp packet
577dd7cddfSDavid du Colombier typedef struct NTPpkt NTPpkt;
587dd7cddfSDavid du Colombier struct NTPpkt
597dd7cddfSDavid du Colombier {
607dd7cddfSDavid du Colombier 	uchar	mode;
617dd7cddfSDavid du Colombier 	uchar	stratum;
627dd7cddfSDavid du Colombier 	uchar	poll;
637dd7cddfSDavid du Colombier 	uchar	precision;
647dd7cddfSDavid du Colombier 	uchar	rootdelay[4];
657dd7cddfSDavid du Colombier 	uchar	rootdisp[4];
667dd7cddfSDavid du Colombier 	uchar	rootid[4];
677dd7cddfSDavid du Colombier 	uchar	refts[8];
687dd7cddfSDavid du Colombier 	uchar	origts[8];		// departed client
697dd7cddfSDavid du Colombier 	uchar	recvts[8];		// arrived at server
707dd7cddfSDavid du Colombier 	uchar	xmitts[8];		// departed server
717dd7cddfSDavid du Colombier 	uchar	keyid[4];
727dd7cddfSDavid du Colombier 	uchar	digest[16];
737dd7cddfSDavid du Colombier };
747dd7cddfSDavid du Colombier 
7580ee5cbfSDavid du Colombier // ntp server
7680ee5cbfSDavid du Colombier typedef struct NTPserver NTPserver;
7780ee5cbfSDavid du Colombier struct NTPserver
7880ee5cbfSDavid du Colombier {
7980ee5cbfSDavid du Colombier 	NTPserver *next;
8080ee5cbfSDavid du Colombier 	char	*name;
8180ee5cbfSDavid du Colombier 	uchar	stratum;
8280ee5cbfSDavid du Colombier 	uchar	precision;
8380ee5cbfSDavid du Colombier 	vlong	rootdelay;
8480ee5cbfSDavid du Colombier 	vlong	rootdisp;
8580ee5cbfSDavid du Colombier 	vlong	rtt;
8680ee5cbfSDavid du Colombier 	vlong	dt;
8780ee5cbfSDavid du Colombier };
8880ee5cbfSDavid du Colombier 
8980ee5cbfSDavid du Colombier NTPserver *ntpservers;
9080ee5cbfSDavid du Colombier 
917dd7cddfSDavid du Colombier enum
927dd7cddfSDavid du Colombier {
937dd7cddfSDavid du Colombier 	NTPSIZE= 	48,		// basic ntp packet
947dd7cddfSDavid du Colombier 	NTPDIGESTSIZE=	20,		// key and digest
957dd7cddfSDavid du Colombier };
967dd7cddfSDavid du Colombier 
97ed250ae1SDavid du Colombier // error bound of last sample
98ed250ae1SDavid du Colombier ulong	ε;
99ed250ae1SDavid du Colombier 
10080ee5cbfSDavid du Colombier static void	addntpserver(char *name);
10180ee5cbfSDavid du Colombier static int	adjustperiod(vlong diff, vlong accuracy, int secs);
10295fdf19cSDavid du Colombier static void	background(void);
10380ee5cbfSDavid du Colombier static int	caperror(vlong dhz, int tsecs, vlong taccuracy);
10480ee5cbfSDavid du Colombier static long	fstime(void);
10580ee5cbfSDavid du Colombier static int	gettime(vlong *nsec, uvlong *ticks, uvlong *hz); // returns time, ticks, hz
10667031067SDavid du Colombier static int	getclockprecision(vlong);
10780ee5cbfSDavid du Colombier static void	hnputts(void *p, vlong nsec);
10880ee5cbfSDavid du Colombier static void	hnputts(void *p, vlong nsec);
1097dd7cddfSDavid du Colombier static void	inittime(void);
11080ee5cbfSDavid du Colombier static vlong	nhgetts(void *p);
11180ee5cbfSDavid du Colombier static vlong	nhgetts(void *p);
1129a747e4fSDavid du Colombier static void	ntpserver(char*);
11380ee5cbfSDavid du Colombier static vlong	ntpsample(void);
11480ee5cbfSDavid du Colombier static int	ntptimediff(NTPserver *ns);
11580ee5cbfSDavid du Colombier static int	openfreqfile(void);
11680ee5cbfSDavid du Colombier static vlong	readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz);
11780ee5cbfSDavid du Colombier static long	rtctime(void);
11880ee5cbfSDavid du Colombier static vlong	sample(long (*get)(void));
1197dd7cddfSDavid du Colombier static void	setpriority(void);
12080ee5cbfSDavid du Colombier static void	setrootid(char *d);
12180ee5cbfSDavid du Colombier static void	settime(vlong now, uvlong hz, vlong delta, int n); // set time, hz, delta, period
1225fab9909SDavid du Colombier static vlong	utcsample(void);
12380ee5cbfSDavid du Colombier static uvlong	vabs(vlong);
12480ee5cbfSDavid du Colombier static uvlong	whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz, vlong dt, vlong ticks, vlong period);
12580ee5cbfSDavid du Colombier static void	writefreqfile(int fd, vlong hz, int secs, vlong diff);
1267dd7cddfSDavid du Colombier 
1277dd7cddfSDavid du Colombier // ((1970-1900)*365 + 17/*leap days*/)*24*60*60
1287dd7cddfSDavid du Colombier #define EPOCHDIFF 2208988800UL
1297dd7cddfSDavid du Colombier 
1307dd7cddfSDavid du Colombier void
1317dd7cddfSDavid du Colombier main(int argc, char **argv)
1327dd7cddfSDavid du Colombier {
13380ee5cbfSDavid du Colombier 	int i;
13480ee5cbfSDavid du Colombier 	int secs;	// sampling period
13580ee5cbfSDavid du Colombier 	int tsecs;	// temporary sampling period
1367dd7cddfSDavid du Colombier 	int t, fd;
1377dd7cddfSDavid du Colombier 	Sample *s, *x, *first, **l;
13880ee5cbfSDavid du Colombier 	vlong diff, accuracy, taccuracy;
13995fdf19cSDavid du Colombier 	uvlong hz, minhz, maxhz, period, nhz;
1409a747e4fSDavid du Colombier 	char *servenet[4];
1419a747e4fSDavid du Colombier 	int nservenet;
1427dd7cddfSDavid du Colombier 	char *a;
1437dd7cddfSDavid du Colombier 	Tm tl, tg;
1447dd7cddfSDavid du Colombier 
14580ee5cbfSDavid du Colombier 	type = Fs;		// by default, sync with the file system
1467dd7cddfSDavid du Colombier 	debug = 0;
1477dd7cddfSDavid du Colombier 	accuracy = 1000000LL;	// default accuracy is 1 millisecond
1489a747e4fSDavid du Colombier 	nservenet = 0;
14980ee5cbfSDavid du Colombier 	tsecs = secs = MinSampleSecs;
1505fab9909SDavid du Colombier 	timeserver = "";
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier 	ARGBEGIN{
1537dd7cddfSDavid du Colombier 	case 'a':
1547dd7cddfSDavid du Colombier 		a = ARGF();
1557dd7cddfSDavid du Colombier 		if(a == nil)
1567dd7cddfSDavid du Colombier 			sysfatal("bad accuracy specified");
1577dd7cddfSDavid du Colombier 		accuracy = strtoll(a, 0, 0);	// accuracy specified in ns
1587dd7cddfSDavid du Colombier 		if(accuracy <= 1LL)
1597dd7cddfSDavid du Colombier 			sysfatal("bad accuracy specified");
1607dd7cddfSDavid du Colombier 		break;
1617dd7cddfSDavid du Colombier 	case 'f':
1627dd7cddfSDavid du Colombier 		type = Fs;
1637dd7cddfSDavid du Colombier 		stratum = 2;
1647dd7cddfSDavid du Colombier 		break;
1657dd7cddfSDavid du Colombier 	case 'r':
1667dd7cddfSDavid du Colombier 		type = Rtc;
16780ee5cbfSDavid du Colombier 		stratum = 0;
1687dd7cddfSDavid du Colombier 		break;
1695fab9909SDavid du Colombier 	case 'U':
1705fab9909SDavid du Colombier 		type = Utc;
1715fab9909SDavid du Colombier 		stratum = 1;
1725fab9909SDavid du Colombier 		timeserver = ARGF();
1735fab9909SDavid du Colombier 		if (timeserver == nil)
1745fab9909SDavid du Colombier 			sysfatal("bad time source");
1755fab9909SDavid du Colombier 		break;
1767dd7cddfSDavid du Colombier 	case 'n':
1777dd7cddfSDavid du Colombier 		type = Ntp;
1787dd7cddfSDavid du Colombier 		break;
1797dd7cddfSDavid du Colombier 	case 'D':
1807dd7cddfSDavid du Colombier 		debug = 1;
1817dd7cddfSDavid du Colombier 		break;
1827dd7cddfSDavid du Colombier 	case 'd':
1837dd7cddfSDavid du Colombier 		dir = ARGF();
1847dd7cddfSDavid du Colombier 		break;
1857dd7cddfSDavid du Colombier 	case 'L':
1867dd7cddfSDavid du Colombier 		//
1877dd7cddfSDavid du Colombier 		// Assume time source in local time rather than GMT.
1887dd7cddfSDavid du Colombier 		// Calculate difference so that rtctime can return GMT.
1897dd7cddfSDavid du Colombier 		// This is useful with the rtc on PC's that run Windows
1907dd7cddfSDavid du Colombier 		// since Windows keeps the local time in the rtc.
1917dd7cddfSDavid du Colombier 		//
1927dd7cddfSDavid du Colombier 		t = time(0);
1937dd7cddfSDavid du Colombier 		tl = *localtime(t);
1947dd7cddfSDavid du Colombier 		tg = *gmtime(t);
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier 		// if the years are different, we're at most a day off, so just rewrite
1977dd7cddfSDavid du Colombier 		if(tl.year < tg.year){
1987dd7cddfSDavid du Colombier 			tg.year--;
1997dd7cddfSDavid du Colombier 			tg.yday = tl.yday + 1;
2007dd7cddfSDavid du Colombier 		}else if(tl.year > tg.year){
2017dd7cddfSDavid du Colombier 			tl.year--;
2027dd7cddfSDavid du Colombier 			tl.yday = tg.yday+1;
2037dd7cddfSDavid du Colombier 		}
2047dd7cddfSDavid du Colombier 		assert(tl.year == tg.year);
2057dd7cddfSDavid du Colombier 
2067dd7cddfSDavid du Colombier 		tg.sec -= tl.sec;
2077dd7cddfSDavid du Colombier 		tg.min -= tl.min;
2087dd7cddfSDavid du Colombier 		tg.hour -= tl.hour;
2097dd7cddfSDavid du Colombier 		tg.yday -= tl.yday;
2107dd7cddfSDavid du Colombier 		gmtdelta = tg.sec+60*(tg.min+60*(tg.hour+tg.yday*24));
2117dd7cddfSDavid du Colombier 
2127dd7cddfSDavid du Colombier 		assert(abs(gmtdelta) <= 24*60*60);
2137dd7cddfSDavid du Colombier 		break;
2147dd7cddfSDavid du Colombier 	case 'i':
2157dd7cddfSDavid du Colombier 		impotent = 1;
2167dd7cddfSDavid du Colombier 		break;
2175fab9909SDavid du Colombier 	case 'I':
2185fab9909SDavid du Colombier 		Rootid = ARGF();
2195fab9909SDavid du Colombier 		break;
22080ee5cbfSDavid du Colombier 	case 's':
2219a747e4fSDavid du Colombier 		if(nservenet >= nelem(servenet))
2229a747e4fSDavid du Colombier 			sysfatal("too many networks to serve on");
2239a747e4fSDavid du Colombier 		a = ARGF();
2249a747e4fSDavid du Colombier 		if(a == nil)
2259a747e4fSDavid du Colombier 			sysfatal("must specify network to serve on");
2269a747e4fSDavid du Colombier 		servenet[nservenet++] = a;
22780ee5cbfSDavid du Colombier 		break;
2287dd7cddfSDavid du Colombier 	case 'l':
2297dd7cddfSDavid du Colombier 		logging = 1;
2307dd7cddfSDavid du Colombier 		break;
2313ff48bf5SDavid du Colombier 	case 'S':
2323ff48bf5SDavid du Colombier 		a = ARGF();
2333ff48bf5SDavid du Colombier 		if(a == nil)
2343ff48bf5SDavid du Colombier 			sysfatal("bad stratum specified");
2353ff48bf5SDavid du Colombier 		stratum = strtoll(a, 0, 0);
2363ff48bf5SDavid du Colombier 		break;
2377dd7cddfSDavid du Colombier 	}ARGEND;
2387dd7cddfSDavid du Colombier 
2399a747e4fSDavid du Colombier 	fmtinstall('E', eipfmt);
2409a747e4fSDavid du Colombier 	fmtinstall('I', eipfmt);
2419a747e4fSDavid du Colombier 	fmtinstall('V', eipfmt);
24280ee5cbfSDavid du Colombier 	sysid = getenv("sysname");
2437dd7cddfSDavid du Colombier 
24495fdf19cSDavid du Colombier 	//  detach from the current namespace
24595fdf19cSDavid du Colombier 	if(debug)
24695fdf19cSDavid du Colombier 		rfork(RFNAMEG);
24795fdf19cSDavid du Colombier 
24880ee5cbfSDavid du Colombier 	switch(type){
24980ee5cbfSDavid du Colombier 	case Fs:
2507dd7cddfSDavid du Colombier 		if(argc > 0)
2517dd7cddfSDavid du Colombier 			timeserver = argv[0];
2527dd7cddfSDavid du Colombier 		else
2537dd7cddfSDavid du Colombier 			timeserver = "/srv/boot";
2547dd7cddfSDavid du Colombier 		break;
2557dd7cddfSDavid du Colombier 	case Ntp:
25680ee5cbfSDavid du Colombier 		if(argc > 0){
25780ee5cbfSDavid du Colombier 			for(i = 0; i <argc; i++)
25880ee5cbfSDavid du Colombier 				addntpserver(argv[i]);
2599a747e4fSDavid du Colombier 		} else {
26080ee5cbfSDavid du Colombier 			addntpserver("$ntp");
2619a747e4fSDavid du Colombier 		}
2627dd7cddfSDavid du Colombier 		break;
2637dd7cddfSDavid du Colombier 	}
2647dd7cddfSDavid du Colombier 
2657dd7cddfSDavid du Colombier 	setpriority();
2667dd7cddfSDavid du Colombier 
26780ee5cbfSDavid du Colombier 	// figure out our time interface and initial frequency
2687dd7cddfSDavid du Colombier 	inittime();
2697dd7cddfSDavid du Colombier 	gettime(0, 0, &hz);
270*be93767fSDavid du Colombier 	minhz = hz/10;
271*be93767fSDavid du Colombier 	maxhz = hz*10;
27267031067SDavid du Colombier 	myprec = getclockprecision(hz);
2737dd7cddfSDavid du Colombier 
27480ee5cbfSDavid du Colombier 	// convert the accuracy from nanoseconds to ticks
27580ee5cbfSDavid du Colombier 	taccuracy = hz*accuracy/SEC;
2767dd7cddfSDavid du Colombier 
2777dd7cddfSDavid du Colombier 	//
2787dd7cddfSDavid du Colombier 	//  bind in clocks
2797dd7cddfSDavid du Colombier 	//
2807dd7cddfSDavid du Colombier 	switch(type){
2817dd7cddfSDavid du Colombier 	case Fs:
2827dd7cddfSDavid du Colombier 		fd = open(timeserver, ORDWR);
2837dd7cddfSDavid du Colombier 		if(fd < 0)
2847dd7cddfSDavid du Colombier 			sysfatal("opening %s: %r\n", timeserver);
2855d459b5aSDavid du Colombier 		if(amount(fd, "/n/boot", MREPL, "") < 0)
2867dd7cddfSDavid du Colombier 			sysfatal("mounting %s: %r\n", timeserver);
2877dd7cddfSDavid du Colombier 		close(fd);
2887dd7cddfSDavid du Colombier 		break;
2897dd7cddfSDavid du Colombier 	case Rtc:
2907dd7cddfSDavid du Colombier 		bind("#r", "/dev", MAFTER);
2917dd7cddfSDavid du Colombier 		if(access("/dev/rtc", AREAD) < 0)
2927dd7cddfSDavid du Colombier 			sysfatal("accessing /dev/rtc: %r\n");
2937dd7cddfSDavid du Colombier 		break;
2945fab9909SDavid du Colombier 	case Utc:
2955fab9909SDavid du Colombier 		fd = open(timeserver, OREAD);
2965fab9909SDavid du Colombier 		if(fd < 0)
2975fab9909SDavid du Colombier 			sysfatal("opening %s: %r\n", timeserver);
2985fab9909SDavid du Colombier 		utcfil = fd;
2995fab9909SDavid du Colombier 		break;
3007dd7cddfSDavid du Colombier 	}
3017dd7cddfSDavid du Colombier 
3027dd7cddfSDavid du Colombier 	//
3039a747e4fSDavid du Colombier 	//  start a local ntp server(s)
3047dd7cddfSDavid du Colombier 	//
3059a747e4fSDavid du Colombier 	for(i = 0; i < nservenet; i++){
3067dd7cddfSDavid du Colombier 		switch(rfork(RFPROC|RFFDG|RFMEM|RFNOWAIT)){
3077dd7cddfSDavid du Colombier 		case -1:
3087dd7cddfSDavid du Colombier 			sysfatal("forking: %r");
3097dd7cddfSDavid du Colombier 			break;
3107dd7cddfSDavid du Colombier 		case 0:
3119a747e4fSDavid du Colombier 			ntpserver(servenet[i]);
3127dd7cddfSDavid du Colombier 			_exits(0);
3137dd7cddfSDavid du Colombier 			break;
3147dd7cddfSDavid du Colombier 		default:
3157dd7cddfSDavid du Colombier 			break;
3167dd7cddfSDavid du Colombier 		}
3177dd7cddfSDavid du Colombier 	}
3187dd7cddfSDavid du Colombier 
3197dd7cddfSDavid du Colombier 	// get the last known frequency from the file
32080ee5cbfSDavid du Colombier 	fd = openfreqfile();
3217dd7cddfSDavid du Colombier 	hz = readfreqfile(fd, hz, minhz, maxhz);
3227dd7cddfSDavid du Colombier 
32380ee5cbfSDavid du Colombier 	// this is the main loop.  it gets a sample, adjusts the
32480ee5cbfSDavid du Colombier 	// clock and computes a sleep period until the next loop.
32580ee5cbfSDavid du Colombier 	// we balance frequency drift against the length of the
32680ee5cbfSDavid du Colombier 	// period to avoid blowing the accuracy limit.
3277dd7cddfSDavid du Colombier 	first = nil;
3287dd7cddfSDavid du Colombier 	l = &first;
32980ee5cbfSDavid du Colombier 	avgerr = accuracy>>1;
33095fdf19cSDavid du Colombier 	for(;; background(),sleep(tsecs*(1000))){
3317dd7cddfSDavid du Colombier 		s = mallocz(sizeof(*s), 1);
3327dd7cddfSDavid du Colombier 		diff = 0;
3337dd7cddfSDavid du Colombier 
3347dd7cddfSDavid du Colombier 		// get times for this sample
335ed250ae1SDavid du Colombier 		ε = ~0;
3367dd7cddfSDavid du Colombier 		switch(type){
3377dd7cddfSDavid du Colombier 		case Fs:
3387dd7cddfSDavid du Colombier 			s->stime = sample(fstime);
3397dd7cddfSDavid du Colombier 			break;
3407dd7cddfSDavid du Colombier 		case Rtc:
3417dd7cddfSDavid du Colombier 			s->stime = sample(rtctime);
3427dd7cddfSDavid du Colombier 			break;
3435fab9909SDavid du Colombier 		case Utc:
3445fab9909SDavid du Colombier 			s->stime = utcsample();
3455fab9909SDavid du Colombier 			if(s->stime == 0LL){
3465fab9909SDavid du Colombier 				if(logging)
3475fab9909SDavid du Colombier 					syslog(0, logfile, "no sample");
3485fab9909SDavid du Colombier 				free(s);
3495fab9909SDavid du Colombier 				if (secs > 60 * 15)
3505fab9909SDavid du Colombier 					tsecs = 60*15;
3515fab9909SDavid du Colombier 				continue;
3525fab9909SDavid du Colombier 			}
3535fab9909SDavid du Colombier 			break;
3547dd7cddfSDavid du Colombier 		case Ntp:
35580ee5cbfSDavid du Colombier 			diff = ntpsample();
35680ee5cbfSDavid du Colombier 			if(diff == 0LL){
35780ee5cbfSDavid du Colombier 				if(logging)
35880ee5cbfSDavid du Colombier 					syslog(0, logfile, "no sample");
35980ee5cbfSDavid du Colombier 				free(s);
36080ee5cbfSDavid du Colombier 				if(secs > 60*15)
36180ee5cbfSDavid du Colombier 					tsecs = 60*15;
36280ee5cbfSDavid du Colombier 				continue;
36380ee5cbfSDavid du Colombier 			}
3647dd7cddfSDavid du Colombier 			break;
3657dd7cddfSDavid du Colombier 		}
3667dd7cddfSDavid du Colombier 
3677dd7cddfSDavid du Colombier 		// use fastest method to read local clock and ticks
3687dd7cddfSDavid du Colombier 		gettime(&s->ltime, &s->ticks, 0);
3697dd7cddfSDavid du Colombier 		if(type == Ntp)
3707dd7cddfSDavid du Colombier 			s->stime = s->ltime + diff;
3717dd7cddfSDavid du Colombier 
372940a793aSDavid du Colombier 		// if the sample was bad, ignore it
373940a793aSDavid du Colombier 		if(s->stime < 0){
37480ee5cbfSDavid du Colombier 			free(s);
37580ee5cbfSDavid du Colombier 			continue;
37680ee5cbfSDavid du Colombier 		}
37780ee5cbfSDavid du Colombier 
37880ee5cbfSDavid du Colombier 		// reset local time
37980ee5cbfSDavid du Colombier 		diff = s->stime - s->ltime;
38080ee5cbfSDavid du Colombier 		if(diff > 10*SEC || diff < -10*SEC){
38180ee5cbfSDavid du Colombier 			// we're way off, just set the time
38280ee5cbfSDavid du Colombier 			secs = MinSampleSecs;
38380ee5cbfSDavid du Colombier 			settime(s->stime, 0, 0, 0);
38480ee5cbfSDavid du Colombier 		} else {
38580ee5cbfSDavid du Colombier 			// keep a running average of the error.
38680ee5cbfSDavid du Colombier 			avgerr = (avgerr>>1) + (vabs(diff)>>1);
38780ee5cbfSDavid du Colombier 
38880ee5cbfSDavid du Colombier 			// the time to next sample depends on how good or
38980ee5cbfSDavid du Colombier 			// bad we're doing.
39080ee5cbfSDavid du Colombier 			tsecs = secs = adjustperiod(diff, accuracy, secs);
39180ee5cbfSDavid du Colombier 
39280ee5cbfSDavid du Colombier 			// work off the fixed difference.  This is done
39380ee5cbfSDavid du Colombier 			// by adding a ramp to the clock.  Each 100th of a
39480ee5cbfSDavid du Colombier 			// second (or so) the kernel will add diff/(4*secs*100)
39580ee5cbfSDavid du Colombier 			// to the clock.  we only do 1/4 of the difference per
39680ee5cbfSDavid du Colombier 			// period to dampen any measurement noise.
397ed250ae1SDavid du Colombier 			//
398ed250ae1SDavid du Colombier 			// any difference greater than ε we work off during the
399ed250ae1SDavid du Colombier 			// sampling period.
400ed250ae1SDavid du Colombier 			if(abs(diff) > ε){
401ed250ae1SDavid du Colombier 				if(diff > 0)
402ed250ae1SDavid du Colombier 					settime(-1, 0, diff-((3*ε)/4), secs);
403ed250ae1SDavid du Colombier 				else
404ed250ae1SDavid du Colombier 					settime(-1, 0, diff+((3*ε)/4), secs);
405ed250ae1SDavid du Colombier 			} else
40680ee5cbfSDavid du Colombier 				settime(-1, 0, diff, 4*secs);
40780ee5cbfSDavid du Colombier 
40880ee5cbfSDavid du Colombier 		}
40980ee5cbfSDavid du Colombier 		if(debug)
41080ee5cbfSDavid du Colombier 			fprint(2, "δ %lld avgδ %lld f %lld\n", diff, avgerr, hz);
41180ee5cbfSDavid du Colombier 
41280ee5cbfSDavid du Colombier 		// dump old samples (keep at least one)
4137dd7cddfSDavid du Colombier 		while(first != nil){
4147dd7cddfSDavid du Colombier 			if(first->next == nil)
4157dd7cddfSDavid du Colombier 				break;
41680ee5cbfSDavid du Colombier 			if(s->stime - first->next->stime < DAY)
4177dd7cddfSDavid du Colombier 				break;
4187dd7cddfSDavid du Colombier 			x = first;
41980ee5cbfSDavid du Colombier 			first = first->next;
4207dd7cddfSDavid du Colombier 			free(x);
4217dd7cddfSDavid du Colombier 		}
4227dd7cddfSDavid du Colombier 
42380ee5cbfSDavid du Colombier 		// The sampling error is limited by the total error.  If
42480ee5cbfSDavid du Colombier 		// we make sure the sampling period is at least 16 million
42580ee5cbfSDavid du Colombier 		// times the average error, we should calculate a frequency
42680ee5cbfSDavid du Colombier 		// with on average a 1e-7 error.
42780ee5cbfSDavid du Colombier 		//
42880ee5cbfSDavid du Colombier 		// So that big hz changes don't blow our accuracy requirement,
42980ee5cbfSDavid du Colombier 		// we shorten the period to make sure that δhz*secs will be
43080ee5cbfSDavid du Colombier 		// greater than the accuracy limit.
43180ee5cbfSDavid du Colombier 		period = avgerr<<24;
43280ee5cbfSDavid du Colombier 		for(x = first; x != nil; x = x->next){
43380ee5cbfSDavid du Colombier 			if(s->stime - x->stime < period)
43480ee5cbfSDavid du Colombier 				break;
43580ee5cbfSDavid du Colombier 			if(x->next == nil || s->stime - x->next->stime < period)
43680ee5cbfSDavid du Colombier 				break;
43780ee5cbfSDavid du Colombier 		}
43880ee5cbfSDavid du Colombier 		if(x != nil){
43980ee5cbfSDavid du Colombier 			nhz = whatisthefrequencykenneth(
44080ee5cbfSDavid du Colombier 				hz, minhz, maxhz,
44180ee5cbfSDavid du Colombier 				s->stime - x->stime,
44280ee5cbfSDavid du Colombier 				s->ticks - x->ticks,
44380ee5cbfSDavid du Colombier 				period);
44480ee5cbfSDavid du Colombier 			tsecs = caperror(vabs(nhz-hz), tsecs, taccuracy);
44580ee5cbfSDavid du Colombier 			hz = nhz;
44680ee5cbfSDavid du Colombier 			writefreqfile(fd, hz, (s->stime - x->stime)/SEC, diff);
4477dd7cddfSDavid du Colombier 		}
4487dd7cddfSDavid du Colombier 
44980ee5cbfSDavid du Colombier 		// add current sample to list.
4507dd7cddfSDavid du Colombier 		*l = s;
4517dd7cddfSDavid du Colombier 		l = &s->next;
45280ee5cbfSDavid du Colombier 
45380ee5cbfSDavid du Colombier 		if(logging)
45480ee5cbfSDavid du Colombier 			syslog(0, logfile, "δ %lld avgδ %lld hz %lld",
45580ee5cbfSDavid du Colombier 				diff, avgerr, hz);
4567dd7cddfSDavid du Colombier 	}
4577dd7cddfSDavid du Colombier }
4587dd7cddfSDavid du Colombier 
45980ee5cbfSDavid du Colombier //
46080ee5cbfSDavid du Colombier // adjust the sampling period with some histeresis
46180ee5cbfSDavid du Colombier //
46280ee5cbfSDavid du Colombier static int
46380ee5cbfSDavid du Colombier adjustperiod(vlong diff, vlong accuracy, int secs)
46480ee5cbfSDavid du Colombier {
46580ee5cbfSDavid du Colombier 	uvlong absdiff;
46680ee5cbfSDavid du Colombier 
46780ee5cbfSDavid du Colombier 	absdiff = vabs(diff);
46880ee5cbfSDavid du Colombier 
46980ee5cbfSDavid du Colombier 	if(absdiff < (accuracy>>1))
47080ee5cbfSDavid du Colombier 		secs += 60;
47180ee5cbfSDavid du Colombier 	else if(absdiff > accuracy)
47280ee5cbfSDavid du Colombier 		secs >>= 1;
47380ee5cbfSDavid du Colombier 	else
47480ee5cbfSDavid du Colombier 		secs -= 60;
47580ee5cbfSDavid du Colombier 	if(secs < MinSampleSecs)
47680ee5cbfSDavid du Colombier 		secs = MinSampleSecs;
47780ee5cbfSDavid du Colombier 	return secs;
47880ee5cbfSDavid du Colombier }
47980ee5cbfSDavid du Colombier 
48080ee5cbfSDavid du Colombier //
48180ee5cbfSDavid du Colombier // adjust the frequency
48280ee5cbfSDavid du Colombier //
48380ee5cbfSDavid du Colombier static uvlong
48480ee5cbfSDavid du Colombier whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz, vlong dt, vlong ticks, vlong period)
48580ee5cbfSDavid du Colombier {
48680ee5cbfSDavid du Colombier 	static mpint *mpdt;
48780ee5cbfSDavid du Colombier 	static mpint *mpticks;
48880ee5cbfSDavid du Colombier 	static mpint *mphz;
48980ee5cbfSDavid du Colombier 	static mpint *mpbillion;
49080ee5cbfSDavid du Colombier 	uvlong ohz = hz;
49180ee5cbfSDavid du Colombier 
49280ee5cbfSDavid du Colombier 	// sanity check
49380ee5cbfSDavid du Colombier 	if(dt <= 0 || ticks <= 0)
49480ee5cbfSDavid du Colombier 		return hz;
49580ee5cbfSDavid du Colombier 
49680ee5cbfSDavid du Colombier 	if(mphz == nil){
49780ee5cbfSDavid du Colombier 		mphz = mpnew(0);
49880ee5cbfSDavid du Colombier 		mpbillion = uvtomp(SEC, nil);
49980ee5cbfSDavid du Colombier 	}
50080ee5cbfSDavid du Colombier 
50180ee5cbfSDavid du Colombier 	// hz = (ticks*SEC)/dt
50280ee5cbfSDavid du Colombier 	mpdt = vtomp(dt, mpdt);
50380ee5cbfSDavid du Colombier 	mpticks = vtomp(ticks, mpticks);
50480ee5cbfSDavid du Colombier 	mpmul(mpticks, mpbillion, mpticks);
50580ee5cbfSDavid du Colombier 	mpdiv(mpticks, mpdt, mphz, nil);
50680ee5cbfSDavid du Colombier 	hz = mptoui(mphz);
50780ee5cbfSDavid du Colombier 
50880ee5cbfSDavid du Colombier 	// sanity
50980ee5cbfSDavid du Colombier 	if(hz < minhz || hz > maxhz)
51080ee5cbfSDavid du Colombier 		return ohz;
51180ee5cbfSDavid du Colombier 
51280ee5cbfSDavid du Colombier 	// damp the change if we're shorter than the target period
51380ee5cbfSDavid du Colombier 	if(period > dt)
51480ee5cbfSDavid du Colombier 		hz = (12ULL*ohz + 4ULL*hz)/16ULL;
51580ee5cbfSDavid du Colombier 
51680ee5cbfSDavid du Colombier 	settime(-1, hz, 0, 0);
51780ee5cbfSDavid du Colombier 	return hz;
51880ee5cbfSDavid du Colombier }
51980ee5cbfSDavid du Colombier 
52080ee5cbfSDavid du Colombier // We may be changing the frequency to match a bad measurement
52180ee5cbfSDavid du Colombier // or to match a condition no longer in effet.  To make sure
52280ee5cbfSDavid du Colombier // that this doesn't blow our error budget over the next measurement
52380ee5cbfSDavid du Colombier // period, shorten the period to make sure that δhz*secs will be
52480ee5cbfSDavid du Colombier // less than the accuracy limit.  Here taccuracy is accuracy converted
52580ee5cbfSDavid du Colombier // from nanoseconds to ticks.
52680ee5cbfSDavid du Colombier static int
52780ee5cbfSDavid du Colombier caperror(vlong dhz, int tsecs, vlong taccuracy)
52880ee5cbfSDavid du Colombier {
5299a747e4fSDavid du Colombier 	if(dhz*tsecs <= taccuracy)
53080ee5cbfSDavid du Colombier 		return tsecs;
53180ee5cbfSDavid du Colombier 
53280ee5cbfSDavid du Colombier 	if(debug)
53380ee5cbfSDavid du Colombier 		fprint(2, "δhz %lld tsecs %d tacc %lld\n", dhz, tsecs, taccuracy);
53480ee5cbfSDavid du Colombier 
53580ee5cbfSDavid du Colombier 	tsecs = taccuracy/dhz;
53680ee5cbfSDavid du Colombier 	if(tsecs < MinSampleSecs)
53780ee5cbfSDavid du Colombier 		tsecs = MinSampleSecs;
53880ee5cbfSDavid du Colombier 	return tsecs;
53980ee5cbfSDavid du Colombier }
5407dd7cddfSDavid du Colombier 
5417dd7cddfSDavid du Colombier //
5427dd7cddfSDavid du Colombier //  kernel interface
5437dd7cddfSDavid du Colombier //
5447dd7cddfSDavid du Colombier enum
5457dd7cddfSDavid du Colombier {
5467dd7cddfSDavid du Colombier 	Ibintime,
5477dd7cddfSDavid du Colombier 	Insec,
5487dd7cddfSDavid du Colombier 	Itiming,
5497dd7cddfSDavid du Colombier };
5507dd7cddfSDavid du Colombier int ifc;
5517dd7cddfSDavid du Colombier int bintimefd = -1;
5527dd7cddfSDavid du Colombier int timingfd = -1;
5537dd7cddfSDavid du Colombier int nsecfd = -1;
5547dd7cddfSDavid du Colombier int fastclockfd = -1;
5557dd7cddfSDavid du Colombier 
5567dd7cddfSDavid du Colombier static void
5577dd7cddfSDavid du Colombier inittime(void)
5587dd7cddfSDavid du Colombier {
5597dd7cddfSDavid du Colombier 	int mode;
5607dd7cddfSDavid du Colombier 
5617dd7cddfSDavid du Colombier 	if(impotent)
5627dd7cddfSDavid du Colombier 		mode = OREAD;
5637dd7cddfSDavid du Colombier 	else
5647dd7cddfSDavid du Colombier 		mode = ORDWR;
5657dd7cddfSDavid du Colombier 
5667dd7cddfSDavid du Colombier 	// bind in clocks
5677dd7cddfSDavid du Colombier 	if(access("/dev/time", 0) < 0)
5687dd7cddfSDavid du Colombier 		bind("#c", "/dev", MAFTER);
5697dd7cddfSDavid du Colombier 	if(access("/dev/rtc", 0) < 0)
5707dd7cddfSDavid du Colombier 		bind("#r", "/dev", MAFTER);
5717dd7cddfSDavid du Colombier 
5727dd7cddfSDavid du Colombier 	// figure out what interface we have
5737dd7cddfSDavid du Colombier 	ifc = Ibintime;
5747dd7cddfSDavid du Colombier 	bintimefd = open("/dev/bintime", mode);
5757dd7cddfSDavid du Colombier 	if(bintimefd >= 0)
5767dd7cddfSDavid du Colombier 		return;
5777dd7cddfSDavid du Colombier 	ifc = Insec;
5787dd7cddfSDavid du Colombier 	nsecfd = open("/dev/nsec", mode);
5797dd7cddfSDavid du Colombier 	if(nsecfd < 0)
5807dd7cddfSDavid du Colombier 		sysfatal("opening /dev/nsec");
5817dd7cddfSDavid du Colombier 	fastclockfd = open("/dev/fastclock", mode);
5827dd7cddfSDavid du Colombier 	if(fastclockfd < 0)
5837dd7cddfSDavid du Colombier 		sysfatal("opening /dev/fastclock");
5847dd7cddfSDavid du Colombier 	timingfd = open("/dev/timing", OREAD);
5857dd7cddfSDavid du Colombier 	if(timingfd < 0)
5867dd7cddfSDavid du Colombier 		return;
5877dd7cddfSDavid du Colombier 	ifc = Itiming;
5887dd7cddfSDavid du Colombier }
5897dd7cddfSDavid du Colombier 
5907dd7cddfSDavid du Colombier //
5917dd7cddfSDavid du Colombier //  convert binary numbers from/to kernel
5927dd7cddfSDavid du Colombier //
5937dd7cddfSDavid du Colombier static uvlong uvorder = 0x0001020304050607ULL;
5947dd7cddfSDavid du Colombier 
5957dd7cddfSDavid du Colombier static uchar*
5967dd7cddfSDavid du Colombier be2vlong(vlong *to, uchar *f)
5977dd7cddfSDavid du Colombier {
5987dd7cddfSDavid du Colombier 	uchar *t, *o;
5997dd7cddfSDavid du Colombier 	int i;
6007dd7cddfSDavid du Colombier 
6017dd7cddfSDavid du Colombier 	t = (uchar*)to;
6027dd7cddfSDavid du Colombier 	o = (uchar*)&uvorder;
6037dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(vlong); i++)
6047dd7cddfSDavid du Colombier 		t[o[i]] = f[i];
6057dd7cddfSDavid du Colombier 	return f+sizeof(vlong);
6067dd7cddfSDavid du Colombier }
6077dd7cddfSDavid du Colombier 
6087dd7cddfSDavid du Colombier static uchar*
6097dd7cddfSDavid du Colombier vlong2be(uchar *t, vlong from)
6107dd7cddfSDavid du Colombier {
6117dd7cddfSDavid du Colombier 	uchar *f, *o;
6127dd7cddfSDavid du Colombier 	int i;
6137dd7cddfSDavid du Colombier 
6147dd7cddfSDavid du Colombier 	f = (uchar*)&from;
6157dd7cddfSDavid du Colombier 	o = (uchar*)&uvorder;
6167dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(vlong); i++)
6177dd7cddfSDavid du Colombier 		t[i] = f[o[i]];
6187dd7cddfSDavid du Colombier 	return t+sizeof(vlong);
6197dd7cddfSDavid du Colombier }
6207dd7cddfSDavid du Colombier 
6217dd7cddfSDavid du Colombier static long order = 0x00010203;
6227dd7cddfSDavid du Colombier 
6237dd7cddfSDavid du Colombier static uchar*
6247dd7cddfSDavid du Colombier be2long(long *to, uchar *f)
6257dd7cddfSDavid du Colombier {
6267dd7cddfSDavid du Colombier 	uchar *t, *o;
6277dd7cddfSDavid du Colombier 	int i;
6287dd7cddfSDavid du Colombier 
6297dd7cddfSDavid du Colombier 	t = (uchar*)to;
6307dd7cddfSDavid du Colombier 	o = (uchar*)&order;
6317dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(long); i++)
6327dd7cddfSDavid du Colombier 		t[o[i]] = f[i];
6337dd7cddfSDavid du Colombier 	return f+sizeof(long);
6347dd7cddfSDavid du Colombier }
6357dd7cddfSDavid du Colombier 
6367dd7cddfSDavid du Colombier static uchar*
6377dd7cddfSDavid du Colombier long2be(uchar *t, long from)
6387dd7cddfSDavid du Colombier {
6397dd7cddfSDavid du Colombier 	uchar *f, *o;
6407dd7cddfSDavid du Colombier 	int i;
6417dd7cddfSDavid du Colombier 
6427dd7cddfSDavid du Colombier 	f = (uchar*)&from;
6437dd7cddfSDavid du Colombier 	o = (uchar*)&order;
6447dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(long); i++)
6457dd7cddfSDavid du Colombier 		t[i] = f[o[i]];
6467dd7cddfSDavid du Colombier 	return t+sizeof(long);
6477dd7cddfSDavid du Colombier }
6487dd7cddfSDavid du Colombier 
6497dd7cddfSDavid du Colombier //
6507dd7cddfSDavid du Colombier // read ticks and local time in nanoseconds
6517dd7cddfSDavid du Colombier //
6527dd7cddfSDavid du Colombier static int
6537dd7cddfSDavid du Colombier gettime(vlong *nsec, uvlong *ticks, uvlong *hz)
6547dd7cddfSDavid du Colombier {
6557dd7cddfSDavid du Colombier 	int i, n;
6567dd7cddfSDavid du Colombier 	uchar ub[3*8], *p;
6577dd7cddfSDavid du Colombier 	char b[2*24+1];
6587dd7cddfSDavid du Colombier 
6597dd7cddfSDavid du Colombier 	switch(ifc){
6607dd7cddfSDavid du Colombier 	case Ibintime:
6617dd7cddfSDavid du Colombier 		n = sizeof(vlong);
6627dd7cddfSDavid du Colombier 		if(hz != nil)
6637dd7cddfSDavid du Colombier 			n = 3*sizeof(vlong);
6647dd7cddfSDavid du Colombier 		if(ticks != nil)
6657dd7cddfSDavid du Colombier 			n = 2*sizeof(vlong);
6667dd7cddfSDavid du Colombier 		i = read(bintimefd, ub, n);
6677dd7cddfSDavid du Colombier 		if(i != n)
6687dd7cddfSDavid du Colombier 			break;
6697dd7cddfSDavid du Colombier 		p = ub;
6707dd7cddfSDavid du Colombier 		if(nsec != nil)
6717dd7cddfSDavid du Colombier 			be2vlong(nsec, ub);
6727dd7cddfSDavid du Colombier 		p += sizeof(vlong);
6737dd7cddfSDavid du Colombier 		if(ticks != nil)
6747dd7cddfSDavid du Colombier 			be2vlong((vlong*)ticks, p);
6757dd7cddfSDavid du Colombier 		p += sizeof(vlong);
6767dd7cddfSDavid du Colombier 		if(hz != nil)
6777dd7cddfSDavid du Colombier 			be2vlong((vlong*)hz, p);
6787dd7cddfSDavid du Colombier 		return 0;
6797dd7cddfSDavid du Colombier 	case Itiming:
6807dd7cddfSDavid du Colombier 		n = sizeof(vlong);
6817dd7cddfSDavid du Colombier 		if(ticks != nil)
6827dd7cddfSDavid du Colombier 			n = 2*sizeof(vlong);
6837dd7cddfSDavid du Colombier 		i = read(timingfd, ub, n);
6847dd7cddfSDavid du Colombier 		if(i != n)
6857dd7cddfSDavid du Colombier 			break;
6867dd7cddfSDavid du Colombier 		p = ub;
6877dd7cddfSDavid du Colombier 		if(nsec != nil)
6887dd7cddfSDavid du Colombier 			be2vlong(nsec, ub);
6897dd7cddfSDavid du Colombier 		p += sizeof(vlong);
6907dd7cddfSDavid du Colombier 		if(ticks != nil)
6917dd7cddfSDavid du Colombier 			be2vlong((vlong*)ticks, p);
6927dd7cddfSDavid du Colombier 		if(hz != nil){
6937dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
6947dd7cddfSDavid du Colombier 			n = read(fastclockfd, b, sizeof(b)-1);
6957dd7cddfSDavid du Colombier 			if(n <= 0)
6967dd7cddfSDavid du Colombier 				break;
6977dd7cddfSDavid du Colombier 			b[n] = 0;
6987dd7cddfSDavid du Colombier 			*hz = strtoll(b+24, 0, 0);
6997dd7cddfSDavid du Colombier 		}
7007dd7cddfSDavid du Colombier 		return 0;
7017dd7cddfSDavid du Colombier 	case Insec:
7027dd7cddfSDavid du Colombier 		if(nsec != nil){
7037dd7cddfSDavid du Colombier 			seek(nsecfd, 0, 0);
7047dd7cddfSDavid du Colombier 			n = read(nsecfd, b, sizeof(b)-1);
7057dd7cddfSDavid du Colombier 			if(n <= 0)
7067dd7cddfSDavid du Colombier 				break;
7077dd7cddfSDavid du Colombier 			b[n] = 0;
7087dd7cddfSDavid du Colombier 			*nsec = strtoll(b, 0, 0);
7097dd7cddfSDavid du Colombier 		}
7107dd7cddfSDavid du Colombier 		if(ticks != nil){
7117dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
7127dd7cddfSDavid du Colombier 			n = read(fastclockfd, b, sizeof(b)-1);
7137dd7cddfSDavid du Colombier 			if(n <= 0)
7147dd7cddfSDavid du Colombier 				break;
7157dd7cddfSDavid du Colombier 			b[n] = 0;
7167dd7cddfSDavid du Colombier 			*ticks = strtoll(b, 0, 0);
7177dd7cddfSDavid du Colombier 		}
7187dd7cddfSDavid du Colombier 		if(hz != nil){
7197dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
7207dd7cddfSDavid du Colombier 			n = read(fastclockfd, b, sizeof(b)-1);
7217dd7cddfSDavid du Colombier 			if(n <= 24)
7227dd7cddfSDavid du Colombier 				break;
7237dd7cddfSDavid du Colombier 			b[n] = 0;
7247dd7cddfSDavid du Colombier 			*hz = strtoll(b+24, 0, 0);
7257dd7cddfSDavid du Colombier 		}
7267dd7cddfSDavid du Colombier 		return 0;
7277dd7cddfSDavid du Colombier 	}
7287dd7cddfSDavid du Colombier 	return -1;
7297dd7cddfSDavid du Colombier }
7307dd7cddfSDavid du Colombier 
7317dd7cddfSDavid du Colombier static void
7327dd7cddfSDavid du Colombier settime(vlong now, uvlong hz, vlong delta, int n)
7337dd7cddfSDavid du Colombier {
7347dd7cddfSDavid du Colombier 	uchar b[1+sizeof(vlong)+sizeof(long)], *p;
7357dd7cddfSDavid du Colombier 
7367dd7cddfSDavid du Colombier 	if(debug)
7377dd7cddfSDavid du Colombier 		fprint(2, "settime(now=%lld, hz=%llud, delta=%lld, period=%d)\n", now, hz, delta, n);
7387dd7cddfSDavid du Colombier 	if(impotent)
7397dd7cddfSDavid du Colombier 		return;
7407dd7cddfSDavid du Colombier 	switch(ifc){
7417dd7cddfSDavid du Colombier 	case Ibintime:
7427dd7cddfSDavid du Colombier 		if(now >= 0){
7437dd7cddfSDavid du Colombier 			p = b;
7447dd7cddfSDavid du Colombier 			*p++ = 'n';
7457dd7cddfSDavid du Colombier 			p = vlong2be(p, now);
7467dd7cddfSDavid du Colombier 			if(write(bintimefd, b, p-b) < 0)
7477dd7cddfSDavid du Colombier 				sysfatal("writing /dev/bintime: %r");
7487dd7cddfSDavid du Colombier 		}
7497dd7cddfSDavid du Colombier 		if(delta != 0){
7507dd7cddfSDavid du Colombier 			p = b;
7517dd7cddfSDavid du Colombier 			*p++ = 'd';
7527dd7cddfSDavid du Colombier 			p = vlong2be(p, delta);
7537dd7cddfSDavid du Colombier 			p = long2be(p, n);
7547dd7cddfSDavid du Colombier 			if(write(bintimefd, b, p-b) < 0)
7557dd7cddfSDavid du Colombier 				sysfatal("writing /dev/bintime: %r");
7567dd7cddfSDavid du Colombier 		}
7577dd7cddfSDavid du Colombier 		if(hz != 0){
7587dd7cddfSDavid du Colombier 			p = b;
7597dd7cddfSDavid du Colombier 			*p++ = 'f';
7607dd7cddfSDavid du Colombier 			p = vlong2be(p, hz);
7617dd7cddfSDavid du Colombier 			if(write(bintimefd, b, p-b) < 0)
7627dd7cddfSDavid du Colombier 				sysfatal("writing /dev/bintime: %r");
7637dd7cddfSDavid du Colombier 		}
7647dd7cddfSDavid du Colombier 		break;
7657dd7cddfSDavid du Colombier 	case Itiming:
7667dd7cddfSDavid du Colombier 	case Insec:
7677dd7cddfSDavid du Colombier 		seek(nsecfd, 0, 0);
7687dd7cddfSDavid du Colombier 		if(now >= 0 || delta != 0){
7697dd7cddfSDavid du Colombier 			if(fprint(nsecfd, "%lld %lld %d", now, delta, n) < 0)
7707dd7cddfSDavid du Colombier 				sysfatal("writing /dev/nsec: %r");
7717dd7cddfSDavid du Colombier 		}
7727dd7cddfSDavid du Colombier 		if(hz > 0){
7737dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
7747dd7cddfSDavid du Colombier 			if(fprint(fastclockfd, "%lld", hz) < 0)
7757dd7cddfSDavid du Colombier 				sysfatal("writing /dev/fastclock: %r");
7767dd7cddfSDavid du Colombier 		}
7777dd7cddfSDavid du Colombier 	}
7787dd7cddfSDavid du Colombier }
7797dd7cddfSDavid du Colombier 
7807dd7cddfSDavid du Colombier //
78180ee5cbfSDavid du Colombier //  set priority high and wire process to a processor
7827dd7cddfSDavid du Colombier //
7837dd7cddfSDavid du Colombier static void
7847dd7cddfSDavid du Colombier setpriority(void)
7857dd7cddfSDavid du Colombier {
7867dd7cddfSDavid du Colombier 	int fd;
7877dd7cddfSDavid du Colombier 	char buf[32];
7887dd7cddfSDavid du Colombier 
7897dd7cddfSDavid du Colombier 	sprint(buf, "/proc/%d/ctl", getpid());
7907dd7cddfSDavid du Colombier 	fd = open(buf, ORDWR);
7917dd7cddfSDavid du Colombier 	if(fd < 0){
7927dd7cddfSDavid du Colombier 		fprint(2, "can't set priority\n");
7937dd7cddfSDavid du Colombier 		return;
7947dd7cddfSDavid du Colombier 	}
7957dd7cddfSDavid du Colombier 	if(fprint(fd, "pri 100") < 0)
7967dd7cddfSDavid du Colombier 		fprint(2, "can't set priority\n");
79780ee5cbfSDavid du Colombier 	if(fprint(fd, "wired 2") < 0)
79880ee5cbfSDavid du Colombier 		fprint(2, "can't wire process\n");
7997dd7cddfSDavid du Colombier 	close(fd);
8007dd7cddfSDavid du Colombier }
80180ee5cbfSDavid du Colombier 
80280ee5cbfSDavid du Colombier // convert to ntp timestamps
80380ee5cbfSDavid du Colombier static void
80480ee5cbfSDavid du Colombier hnputts(void *p, vlong nsec)
80580ee5cbfSDavid du Colombier {
80680ee5cbfSDavid du Colombier 	uchar *a;
80780ee5cbfSDavid du Colombier 	ulong tsh;
80880ee5cbfSDavid du Colombier 	ulong tsl;
80980ee5cbfSDavid du Colombier 
81080ee5cbfSDavid du Colombier 	a = p;
81180ee5cbfSDavid du Colombier 
81280ee5cbfSDavid du Colombier 	// zero is a special case
81380ee5cbfSDavid du Colombier 	if(nsec == 0)
81480ee5cbfSDavid du Colombier 		return;
81580ee5cbfSDavid du Colombier 
81680ee5cbfSDavid du Colombier 	tsh = (nsec/SEC);
81780ee5cbfSDavid du Colombier 	nsec -= tsh*SEC;
81880ee5cbfSDavid du Colombier 	tsl = (nsec<<32)/SEC;
81980ee5cbfSDavid du Colombier 	hnputl(a, tsh+EPOCHDIFF);
82080ee5cbfSDavid du Colombier 	hnputl(a+4, tsl);
82180ee5cbfSDavid du Colombier }
82280ee5cbfSDavid du Colombier 
82380ee5cbfSDavid du Colombier // convert from ntp timestamps
82480ee5cbfSDavid du Colombier static vlong
82580ee5cbfSDavid du Colombier nhgetts(void *p)
82680ee5cbfSDavid du Colombier {
82780ee5cbfSDavid du Colombier 	uchar *a;
82880ee5cbfSDavid du Colombier 	ulong tsh, tsl;
82980ee5cbfSDavid du Colombier 	vlong nsec;
83080ee5cbfSDavid du Colombier 
83180ee5cbfSDavid du Colombier 	a = p;
83280ee5cbfSDavid du Colombier 	tsh = nhgetl(a);
83380ee5cbfSDavid du Colombier 	tsl = nhgetl(a+4);
83480ee5cbfSDavid du Colombier 	nsec = tsl*SEC;
83580ee5cbfSDavid du Colombier 	nsec >>= 32;
83680ee5cbfSDavid du Colombier 	nsec += (tsh - EPOCHDIFF)*SEC;
83780ee5cbfSDavid du Colombier 	return nsec;
83880ee5cbfSDavid du Colombier }
83980ee5cbfSDavid du Colombier 
84080ee5cbfSDavid du Colombier // convert to ntp 32 bit fixed point
84180ee5cbfSDavid du Colombier static void
84280ee5cbfSDavid du Colombier hnputfp(void *p, vlong nsec)
84380ee5cbfSDavid du Colombier {
84480ee5cbfSDavid du Colombier 	uchar *a;
84580ee5cbfSDavid du Colombier 	ulong fp;
84680ee5cbfSDavid du Colombier 
84780ee5cbfSDavid du Colombier 	a = p;
84880ee5cbfSDavid du Colombier 
84980ee5cbfSDavid du Colombier 	fp = nsec/(SEC/((vlong)(1<<16)));
85080ee5cbfSDavid du Colombier 	hnputl(a, fp);
85180ee5cbfSDavid du Colombier }
85280ee5cbfSDavid du Colombier 
85380ee5cbfSDavid du Colombier // convert from ntp fixed point to nanosecs
85480ee5cbfSDavid du Colombier static vlong
85580ee5cbfSDavid du Colombier nhgetfp(void *p)
85680ee5cbfSDavid du Colombier {
85780ee5cbfSDavid du Colombier 	uchar *a;
85880ee5cbfSDavid du Colombier 	ulong fp;
85980ee5cbfSDavid du Colombier 	vlong nsec;
86080ee5cbfSDavid du Colombier 
86180ee5cbfSDavid du Colombier 	a = p;
86280ee5cbfSDavid du Colombier 	fp = nhgetl(a);
86380ee5cbfSDavid du Colombier 	nsec = ((vlong)fp)*(SEC/((vlong)(1<<16)));
86480ee5cbfSDavid du Colombier 	return nsec;
86580ee5cbfSDavid du Colombier }
86680ee5cbfSDavid du Colombier 
86780ee5cbfSDavid du Colombier // get network address of the server
86880ee5cbfSDavid du Colombier static void
86980ee5cbfSDavid du Colombier setrootid(char *d)
87080ee5cbfSDavid du Colombier {
87180ee5cbfSDavid du Colombier 	char buf[128];
87280ee5cbfSDavid du Colombier 	int fd, n;
87380ee5cbfSDavid du Colombier 	char *p;
87480ee5cbfSDavid du Colombier 
87580ee5cbfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/remote", d);
87680ee5cbfSDavid du Colombier 	fd = open(buf, OREAD);
87780ee5cbfSDavid du Colombier 	if(fd < 0)
87880ee5cbfSDavid du Colombier 		return;
87980ee5cbfSDavid du Colombier 	n = read(fd, buf, sizeof buf);
88080ee5cbfSDavid du Colombier 	close(fd);
88180ee5cbfSDavid du Colombier 	if(n <= 0)
88280ee5cbfSDavid du Colombier 		return;
88380ee5cbfSDavid du Colombier 	p = strchr(buf, '!');
88480ee5cbfSDavid du Colombier 	if(p != nil)
88580ee5cbfSDavid du Colombier 		*p = 0;
88680ee5cbfSDavid du Colombier 	v4parseip(rootid, buf);
88780ee5cbfSDavid du Colombier }
88880ee5cbfSDavid du Colombier 
88980ee5cbfSDavid du Colombier static void
89080ee5cbfSDavid du Colombier ding(void*, char *s)
89180ee5cbfSDavid du Colombier {
89280ee5cbfSDavid du Colombier 	if(strstr(s, "alarm") != nil)
89380ee5cbfSDavid du Colombier 		noted(NCONT);
89480ee5cbfSDavid du Colombier 	noted(NDFLT);
89580ee5cbfSDavid du Colombier }
89680ee5cbfSDavid du Colombier 
89780ee5cbfSDavid du Colombier static void
89880ee5cbfSDavid du Colombier addntpserver(char *name)
89980ee5cbfSDavid du Colombier {
90080ee5cbfSDavid du Colombier 	NTPserver *ns, **l;
90180ee5cbfSDavid du Colombier 
90280ee5cbfSDavid du Colombier 	ns = mallocz(sizeof(NTPserver), 1);
90380ee5cbfSDavid du Colombier 	if(ns == nil)
90480ee5cbfSDavid du Colombier 		sysfatal("addntpserver: %r");
9059a747e4fSDavid du Colombier 	timeserver = strdup(name);
90680ee5cbfSDavid du Colombier 	ns->name = name;
90780ee5cbfSDavid du Colombier 	for(l = &ntpservers; *l != nil; l = &(*l)->next)
90880ee5cbfSDavid du Colombier 		;
90980ee5cbfSDavid du Colombier 	*l = ns;
91080ee5cbfSDavid du Colombier }
91180ee5cbfSDavid du Colombier 
91280ee5cbfSDavid du Colombier //
91380ee5cbfSDavid du Colombier //  sntp client, we keep calling if the delay seems
91480ee5cbfSDavid du Colombier //  unusually high, i.e., 30% longer than avg.
91580ee5cbfSDavid du Colombier //
91680ee5cbfSDavid du Colombier static int
91780ee5cbfSDavid du Colombier ntptimediff(NTPserver *ns)
91880ee5cbfSDavid du Colombier {
91980ee5cbfSDavid du Colombier 	int fd, tries, n;
92080ee5cbfSDavid du Colombier 	NTPpkt ntpin, ntpout;
92180ee5cbfSDavid du Colombier 	vlong dt, recvts, origts, xmitts, destts, x;
92280ee5cbfSDavid du Colombier 	char dir[64];
92380ee5cbfSDavid du Colombier 
9249a747e4fSDavid du Colombier 	fd = dial(netmkaddr(ns->name, "udp", "ntp"), 0, dir, 0);
92580ee5cbfSDavid du Colombier 	if(fd < 0){
92680ee5cbfSDavid du Colombier 		syslog(0, logfile, "can't reach %s: %r", ns->name);
92780ee5cbfSDavid du Colombier 		return -1;
92880ee5cbfSDavid du Colombier 	}
92980ee5cbfSDavid du Colombier 	setrootid(dir);
93080ee5cbfSDavid du Colombier 	notify(ding);
93180ee5cbfSDavid du Colombier 
93280ee5cbfSDavid du Colombier 	memset(&ntpout, 0, sizeof(ntpout));
93380ee5cbfSDavid du Colombier 	ntpout.mode = 3 | (3 << 3);
93480ee5cbfSDavid du Colombier 
93580ee5cbfSDavid du Colombier 	for(tries = 0; tries < 3; tries++){
93680ee5cbfSDavid du Colombier 		alarm(2*1000);
93780ee5cbfSDavid du Colombier 
93880ee5cbfSDavid du Colombier 		gettime(&x, 0, 0);
93980ee5cbfSDavid du Colombier 		hnputts(ntpout.xmitts, x);
94080ee5cbfSDavid du Colombier 		if(write(fd, &ntpout, NTPSIZE) < 0){
94180ee5cbfSDavid du Colombier 			alarm(0);
94280ee5cbfSDavid du Colombier 			continue;
94380ee5cbfSDavid du Colombier 		}
94480ee5cbfSDavid du Colombier 
94580ee5cbfSDavid du Colombier 		n = read(fd, &ntpin, sizeof(ntpin));
94680ee5cbfSDavid du Colombier 		alarm(0);
94780ee5cbfSDavid du Colombier 		gettime(&destts, 0, 0);
94880ee5cbfSDavid du Colombier 		if(n >= NTPSIZE){
94980ee5cbfSDavid du Colombier 			close(fd);
95080ee5cbfSDavid du Colombier 
95180ee5cbfSDavid du Colombier 			// we got one, use it
95280ee5cbfSDavid du Colombier 			recvts = nhgetts(ntpin.recvts);
95380ee5cbfSDavid du Colombier 			origts = nhgetts(ntpin.origts);
95480ee5cbfSDavid du Colombier 			xmitts = nhgetts(ntpin.xmitts);
95580ee5cbfSDavid du Colombier 			dt = ((recvts - origts) + (xmitts - destts))/2;
95680ee5cbfSDavid du Colombier 
95780ee5cbfSDavid du Colombier 			// save results
95880ee5cbfSDavid du Colombier 			ns->rtt = ((destts - origts) - (xmitts - recvts))/2;
95980ee5cbfSDavid du Colombier 			ns->dt = dt;
96080ee5cbfSDavid du Colombier 			ns->stratum = ntpin.stratum;
96180ee5cbfSDavid du Colombier 			ns->precision = ntpin.precision;
96280ee5cbfSDavid du Colombier 			ns->rootdelay = nhgetfp(ntpin.rootdelay);
96380ee5cbfSDavid du Colombier 			ns->rootdisp = nhgetfp(ntpin.rootdisp);
96480ee5cbfSDavid du Colombier 
96580ee5cbfSDavid du Colombier 			if(debug)
96680ee5cbfSDavid du Colombier 				fprint(2, "ntp %s stratum %d ntpdelay(%lld)\n",
96780ee5cbfSDavid du Colombier 					ns->name, ntpin.stratum, ns->rtt);
96880ee5cbfSDavid du Colombier 			return 0;
96980ee5cbfSDavid du Colombier 		}
97080ee5cbfSDavid du Colombier 
97180ee5cbfSDavid du Colombier 		// try again
97280ee5cbfSDavid du Colombier 		sleep(250);
97380ee5cbfSDavid du Colombier 	}
97480ee5cbfSDavid du Colombier 	close(fd);
97580ee5cbfSDavid du Colombier 	return -1;
97680ee5cbfSDavid du Colombier }
97780ee5cbfSDavid du Colombier 
97880ee5cbfSDavid du Colombier static vlong
97980ee5cbfSDavid du Colombier ntpsample(void)
98080ee5cbfSDavid du Colombier {
98180ee5cbfSDavid du Colombier 	NTPserver *tns, *ns;
98280ee5cbfSDavid du Colombier 	vlong metric, x;
98380ee5cbfSDavid du Colombier 
98480ee5cbfSDavid du Colombier 	metric = 1000LL*SEC;
98580ee5cbfSDavid du Colombier 	ns = nil;
98680ee5cbfSDavid du Colombier 	for(tns = ntpservers; tns != nil; tns = tns->next){
98780ee5cbfSDavid du Colombier 		if(ntptimediff(tns) < 0)
98880ee5cbfSDavid du Colombier 			continue;
98967031067SDavid du Colombier 		x = vabs(tns->rootdisp) + (vabs(tns->rtt+tns->rootdelay)>>1);
99095fdf19cSDavid du Colombier 		if(debug)
99195fdf19cSDavid du Colombier 			fprint(2, "ntp %s rootdelay %lld rootdisp %lld metric %lld\n",
99295fdf19cSDavid du Colombier 				tns->name, tns->rootdelay, tns->rootdisp, x);
99380ee5cbfSDavid du Colombier 		if(x < metric){
99480ee5cbfSDavid du Colombier 			metric = x;
99580ee5cbfSDavid du Colombier 			ns = tns;
99680ee5cbfSDavid du Colombier 		}
99780ee5cbfSDavid du Colombier 	}
99880ee5cbfSDavid du Colombier 
99980ee5cbfSDavid du Colombier 	if(ns == nil)
100080ee5cbfSDavid du Colombier 		return 0LL;
100180ee5cbfSDavid du Colombier 
100280ee5cbfSDavid du Colombier 	// save data for our server
100380ee5cbfSDavid du Colombier 	rootdisp = ns->rootdisp;
100467031067SDavid du Colombier 	rootdelay = ns->rootdelay;
100580ee5cbfSDavid du Colombier 	mydelay = ns->rtt;
100667031067SDavid du Colombier 	mydisp = avgerr;
100780ee5cbfSDavid du Colombier 	if(ns->stratum == 0)
100880ee5cbfSDavid du Colombier 		stratum = 0;
100980ee5cbfSDavid du Colombier 	else
101080ee5cbfSDavid du Colombier 		stratum = ns->stratum + 1;
101180ee5cbfSDavid du Colombier 
1012ed250ae1SDavid du Colombier 	ε = abs(ns->rtt/2);
101380ee5cbfSDavid du Colombier 	return ns->dt;
101480ee5cbfSDavid du Colombier }
101580ee5cbfSDavid du Colombier 
101680ee5cbfSDavid du Colombier //
10175fab9909SDavid du Colombier // sample the utc file
10185fab9909SDavid du Colombier //
10195fab9909SDavid du Colombier static vlong
10205fab9909SDavid du Colombier utcsample(void)
10215fab9909SDavid du Colombier {
10225fab9909SDavid du Colombier 	vlong	s;
10235fab9909SDavid du Colombier 	int	n;
10245fab9909SDavid du Colombier 	char	*v[2], buf[128];
10255fab9909SDavid du Colombier 
10265fab9909SDavid du Colombier 	s = 0;
10275fab9909SDavid du Colombier 	seek(utcfil, 0, 0);
10285fab9909SDavid du Colombier 	n = read(utcfil, buf, sizeof buf - 1);
10295fab9909SDavid du Colombier 	if (n <= 0)
10305fab9909SDavid du Colombier 		return(0LL);
10315fab9909SDavid du Colombier 	buf[n] = 0;
10325fab9909SDavid du Colombier 	n = tokenize(buf, v, nelem(v));
10335fab9909SDavid du Colombier 	if (strcmp(v[0], "0") == 0)
10345fab9909SDavid du Colombier 		return(0LL);
10355fab9909SDavid du Colombier 	if (n == 2) {
10365fab9909SDavid du Colombier 		gettime(&s, nil, nil);
10375fab9909SDavid du Colombier 		s -= atoll(v[1]);
10385fab9909SDavid du Colombier 	}
10391118d624SDavid du Colombier 	lastutc = atoll(v[0]) + s;
10401118d624SDavid du Colombier 	return(lastutc);
10415fab9909SDavid du Colombier }
10425fab9909SDavid du Colombier 
10435fab9909SDavid du Colombier //
104480ee5cbfSDavid du Colombier //  sntp server
104580ee5cbfSDavid du Colombier //
104680ee5cbfSDavid du Colombier static int
104780ee5cbfSDavid du Colombier openlisten(char *net)
104880ee5cbfSDavid du Colombier {
104980ee5cbfSDavid du Colombier 	int fd, cfd;
105080ee5cbfSDavid du Colombier 	char data[128];
105180ee5cbfSDavid du Colombier 	char devdir[40];
105280ee5cbfSDavid du Colombier 
105380ee5cbfSDavid du Colombier 	sprint(data, "%s/udp!*!ntp", net);
105480ee5cbfSDavid du Colombier 	cfd = announce(data, devdir);
105580ee5cbfSDavid du Colombier 	if(cfd < 0)
105680ee5cbfSDavid du Colombier 		sysfatal("can't announce");
105780ee5cbfSDavid du Colombier 	if(fprint(cfd, "headers") < 0)
105880ee5cbfSDavid du Colombier 		sysfatal("can't set header mode");
1059d9dc5dd1SDavid du Colombier 	fprint(cfd, "oldheaders");
106080ee5cbfSDavid du Colombier 
106180ee5cbfSDavid du Colombier 	sprint(data, "%s/data", devdir);
106280ee5cbfSDavid du Colombier 
106380ee5cbfSDavid du Colombier 	fd = open(data, ORDWR);
106480ee5cbfSDavid du Colombier 	if(fd < 0)
106580ee5cbfSDavid du Colombier 		sysfatal("open udp data");
106680ee5cbfSDavid du Colombier 	return fd;
106780ee5cbfSDavid du Colombier }
106880ee5cbfSDavid du Colombier static void
10699a747e4fSDavid du Colombier ntpserver(char *servenet)
107080ee5cbfSDavid du Colombier {
107180ee5cbfSDavid du Colombier 	int fd, n;
107280ee5cbfSDavid du Colombier 	NTPpkt *ntp;
107380ee5cbfSDavid du Colombier 	char buf[512];
107480ee5cbfSDavid du Colombier 	int vers, mode;
107580ee5cbfSDavid du Colombier 	vlong recvts, x;
107680ee5cbfSDavid du Colombier 
10779a747e4fSDavid du Colombier 	fd = openlisten(servenet);
107880ee5cbfSDavid du Colombier 
10795fab9909SDavid du Colombier 	if (Rootid == nil)
108080ee5cbfSDavid du Colombier 		switch(type){
108180ee5cbfSDavid du Colombier 		case Fs:
10825fab9909SDavid du Colombier 			Rootid = "WWV";
108380ee5cbfSDavid du Colombier 			break;
108480ee5cbfSDavid du Colombier 		case Rtc:
10855fab9909SDavid du Colombier 			Rootid = "LOCL";
10865fab9909SDavid du Colombier 			break;
10875fab9909SDavid du Colombier 		case Utc:
10885fab9909SDavid du Colombier 			Rootid = "UTC";
108980ee5cbfSDavid du Colombier 			break;
109080ee5cbfSDavid du Colombier 		case Ntp:
109180ee5cbfSDavid du Colombier 			/* set by the ntp client */
109280ee5cbfSDavid du Colombier 			break;
109380ee5cbfSDavid du Colombier 		}
10945fab9909SDavid du Colombier 	if (Rootid != nil)
10955fab9909SDavid du Colombier 		memmove(rootid, Rootid, strlen(Rootid) > 4 ? 4 : strlen(Rootid));
109680ee5cbfSDavid du Colombier 
109780ee5cbfSDavid du Colombier 	for(;;){
109880ee5cbfSDavid du Colombier 		n = read(fd, buf, sizeof(buf));
109980ee5cbfSDavid du Colombier 		gettime(&recvts, 0, 0);
110080ee5cbfSDavid du Colombier 		if(n < 0)
110180ee5cbfSDavid du Colombier 			return;
1102d9dc5dd1SDavid du Colombier 		if(n < OUdphdrsize + NTPSIZE)
110380ee5cbfSDavid du Colombier 			continue;
110480ee5cbfSDavid du Colombier 
1105d9dc5dd1SDavid du Colombier 		ntp = (NTPpkt*)(buf+OUdphdrsize);
110680ee5cbfSDavid du Colombier 		mode = ntp->mode & 7;
110780ee5cbfSDavid du Colombier 		vers = (ntp->mode>>3) & 7;
110880ee5cbfSDavid du Colombier 		if(mode != 3)
110980ee5cbfSDavid du Colombier 			continue;
111080ee5cbfSDavid du Colombier 
111180ee5cbfSDavid du Colombier 		ntp->mode = (vers<<3)|4;
111280ee5cbfSDavid du Colombier 		ntp->stratum = stratum;
111367031067SDavid du Colombier 		ntp->precision = myprec;
111480ee5cbfSDavid du Colombier 		hnputfp(ntp->rootdelay, rootdelay + mydelay);
111580ee5cbfSDavid du Colombier 		hnputfp(ntp->rootdisp, rootdisp + mydisp);
11161118d624SDavid du Colombier 		hnputts(ntp->refts, lastutc);
111780ee5cbfSDavid du Colombier 		memmove(ntp->origts, ntp->xmitts, sizeof(ntp->origts));
111880ee5cbfSDavid du Colombier 		hnputts(ntp->recvts, recvts);
111980ee5cbfSDavid du Colombier 		memmove(ntp->rootid, rootid, sizeof(ntp->rootid));
112080ee5cbfSDavid du Colombier 		gettime(&x, 0, 0);
112180ee5cbfSDavid du Colombier 		hnputts(ntp->xmitts, x);
1122d9dc5dd1SDavid du Colombier 		write(fd, buf, NTPSIZE+OUdphdrsize);
112380ee5cbfSDavid du Colombier 	}
112480ee5cbfSDavid du Colombier }
112580ee5cbfSDavid du Colombier 
112680ee5cbfSDavid du Colombier //
112780ee5cbfSDavid du Colombier //  get the current time from the file system
112880ee5cbfSDavid du Colombier //
112980ee5cbfSDavid du Colombier static long
113080ee5cbfSDavid du Colombier fstime(void)
113180ee5cbfSDavid du Colombier {
11329a747e4fSDavid du Colombier 	Dir *d;
11339a747e4fSDavid du Colombier 	ulong t;
113480ee5cbfSDavid du Colombier 
11359a747e4fSDavid du Colombier 	d = dirstat("/n/boot");
11369a747e4fSDavid du Colombier 	if(d != nil){
11379a747e4fSDavid du Colombier 		t = d->atime;
11389a747e4fSDavid du Colombier 		free(d);
11399a747e4fSDavid du Colombier 	} else
11409a747e4fSDavid du Colombier 		t = 0;
11419a747e4fSDavid du Colombier 	return t;
114280ee5cbfSDavid du Colombier }
114380ee5cbfSDavid du Colombier 
114480ee5cbfSDavid du Colombier //
114580ee5cbfSDavid du Colombier //  get the current time from the real time clock
114680ee5cbfSDavid du Colombier //
114780ee5cbfSDavid du Colombier static long
114880ee5cbfSDavid du Colombier rtctime(void)
114980ee5cbfSDavid du Colombier {
115080ee5cbfSDavid du Colombier 	char b[20];
115180ee5cbfSDavid du Colombier 	static int f = -1;
115280ee5cbfSDavid du Colombier 	int i, retries;
115380ee5cbfSDavid du Colombier 
115480ee5cbfSDavid du Colombier 	memset(b, 0, sizeof(b));
115580ee5cbfSDavid du Colombier 	for(retries = 0; retries < 100; retries++){
115680ee5cbfSDavid du Colombier 		if(f < 0)
115780ee5cbfSDavid du Colombier 			f = open("/dev/rtc", OREAD|OCEXEC);
115880ee5cbfSDavid du Colombier 		if(f < 0)
115980ee5cbfSDavid du Colombier 			break;
116080ee5cbfSDavid du Colombier 		if(seek(f, 0, 0) < 0 || (i = read(f, b, sizeof(b))) < 0){
116180ee5cbfSDavid du Colombier 			close(f);
116280ee5cbfSDavid du Colombier 			f = -1;
116380ee5cbfSDavid du Colombier 		} else {
116480ee5cbfSDavid du Colombier 			if(i != 0)
116580ee5cbfSDavid du Colombier 				break;
116680ee5cbfSDavid du Colombier 		}
116780ee5cbfSDavid du Colombier 	}
116880ee5cbfSDavid du Colombier 	return strtoul(b, 0, 10)+gmtdelta;
116980ee5cbfSDavid du Colombier }
117080ee5cbfSDavid du Colombier 
117180ee5cbfSDavid du Colombier 
117280ee5cbfSDavid du Colombier //
117380ee5cbfSDavid du Colombier //  Sample a clock.  We wait for the clock to always
117480ee5cbfSDavid du Colombier //  be at the leading edge of a clock period.
117580ee5cbfSDavid du Colombier //
117680ee5cbfSDavid du Colombier static vlong
117780ee5cbfSDavid du Colombier sample(long (*get)(void))
117880ee5cbfSDavid du Colombier {
117980ee5cbfSDavid du Colombier 	long this, last;
118080ee5cbfSDavid du Colombier 	vlong start, end;
118180ee5cbfSDavid du Colombier 
118280ee5cbfSDavid du Colombier 	/*
118380ee5cbfSDavid du Colombier 	 *  wait for the second to change
118480ee5cbfSDavid du Colombier 	 */
118580ee5cbfSDavid du Colombier 	last = (*get)();
118680ee5cbfSDavid du Colombier 	for(;;){
118780ee5cbfSDavid du Colombier 		gettime(&start, 0, 0);
118880ee5cbfSDavid du Colombier 		this = (*get)();
118980ee5cbfSDavid du Colombier 		gettime(&end, 0, 0);
119080ee5cbfSDavid du Colombier 		if(this != last)
119180ee5cbfSDavid du Colombier 			break;
119280ee5cbfSDavid du Colombier 		last = this;
119380ee5cbfSDavid du Colombier 	}
119480ee5cbfSDavid du Colombier 	return SEC*this - (end-start)/2;
119580ee5cbfSDavid du Colombier }
119680ee5cbfSDavid du Colombier 
119780ee5cbfSDavid du Colombier //
119880ee5cbfSDavid du Colombier // the name of the frequency file has the method and possibly the
119980ee5cbfSDavid du Colombier // server name encoded in it.
120080ee5cbfSDavid du Colombier //
120180ee5cbfSDavid du Colombier static int
120280ee5cbfSDavid du Colombier openfreqfile(void)
120380ee5cbfSDavid du Colombier {
12049a747e4fSDavid du Colombier 	char buf[29];
120580ee5cbfSDavid du Colombier 	int fd;
120680ee5cbfSDavid du Colombier 
120780ee5cbfSDavid du Colombier 	if(sysid == nil)
120880ee5cbfSDavid du Colombier 		return -1;
120980ee5cbfSDavid du Colombier 
121080ee5cbfSDavid du Colombier 	switch(type){
121180ee5cbfSDavid du Colombier 	case Ntp:
121280ee5cbfSDavid du Colombier 		snprint(buf, sizeof buf, "%s/ts.%s.%d.%s", dir, sysid, type, timeserver);
121380ee5cbfSDavid du Colombier 		break;
121480ee5cbfSDavid du Colombier 	default:
121580ee5cbfSDavid du Colombier 		snprint(buf, sizeof buf, "%s/ts.%s.%d", dir, sysid, type);
121680ee5cbfSDavid du Colombier 		break;
121780ee5cbfSDavid du Colombier 	}
121880ee5cbfSDavid du Colombier 	fd = open(buf, ORDWR);
121980ee5cbfSDavid du Colombier 	if(fd < 0)
122080ee5cbfSDavid du Colombier 		fd = create(buf, ORDWR, 0666);
122180ee5cbfSDavid du Colombier 	if(fd < 0)
122280ee5cbfSDavid du Colombier 		return -1;
122380ee5cbfSDavid du Colombier 	return fd;
122480ee5cbfSDavid du Colombier }
122580ee5cbfSDavid du Colombier 
122680ee5cbfSDavid du Colombier //
122780ee5cbfSDavid du Colombier //  the file contains the last known frequency and the
122880ee5cbfSDavid du Colombier //  number of seconds it was sampled over
122980ee5cbfSDavid du Colombier //
123080ee5cbfSDavid du Colombier static vlong
123180ee5cbfSDavid du Colombier readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz)
123280ee5cbfSDavid du Colombier {
123380ee5cbfSDavid du Colombier 	int n;
123480ee5cbfSDavid du Colombier 	char buf[128];
123580ee5cbfSDavid du Colombier 	vlong hz;
123680ee5cbfSDavid du Colombier 
123780ee5cbfSDavid du Colombier 	n = read(fd, buf, sizeof(buf)-1);
123880ee5cbfSDavid du Colombier 	if(n <= 0)
123980ee5cbfSDavid du Colombier 		return ohz;
124080ee5cbfSDavid du Colombier 	buf[n] = 0;
124180ee5cbfSDavid du Colombier 	hz = strtoll(buf, nil, 0);
124280ee5cbfSDavid du Colombier 
124380ee5cbfSDavid du Colombier 	if(hz > maxhz || hz < minhz)
124480ee5cbfSDavid du Colombier 		return ohz;
124580ee5cbfSDavid du Colombier 
124680ee5cbfSDavid du Colombier 	settime(-1, hz, 0, 0);
124780ee5cbfSDavid du Colombier 	return hz;
124880ee5cbfSDavid du Colombier }
124980ee5cbfSDavid du Colombier 
125080ee5cbfSDavid du Colombier //
125180ee5cbfSDavid du Colombier //  remember hz and averaging period
125280ee5cbfSDavid du Colombier //
125380ee5cbfSDavid du Colombier static void
125480ee5cbfSDavid du Colombier writefreqfile(int fd, vlong hz, int secs, vlong diff)
125580ee5cbfSDavid du Colombier {
125680ee5cbfSDavid du Colombier 	long now;
125780ee5cbfSDavid du Colombier 	static long last;
125880ee5cbfSDavid du Colombier 
125980ee5cbfSDavid du Colombier 	if(fd < 0)
126080ee5cbfSDavid du Colombier 		return;
126180ee5cbfSDavid du Colombier 	now = time(0);
12629a747e4fSDavid du Colombier 	if(now - last < 10*60)
126380ee5cbfSDavid du Colombier 		return;
126480ee5cbfSDavid du Colombier 	last = now;
126580ee5cbfSDavid du Colombier 	if(seek(fd, 0, 0) < 0)
126680ee5cbfSDavid du Colombier 		return;
126780ee5cbfSDavid du Colombier 	fprint(fd, "%lld %d %d %lld\n", hz, secs, type, diff);
126880ee5cbfSDavid du Colombier }
126980ee5cbfSDavid du Colombier 
127080ee5cbfSDavid du Colombier static uvlong
127180ee5cbfSDavid du Colombier vabs(vlong x)
127280ee5cbfSDavid du Colombier {
127380ee5cbfSDavid du Colombier 	if(x < 0LL)
127480ee5cbfSDavid du Colombier 		return (uvlong)-x;
127580ee5cbfSDavid du Colombier 	else
127680ee5cbfSDavid du Colombier 		return (uvlong)x;
127780ee5cbfSDavid du Colombier }
127895fdf19cSDavid du Colombier 
127995fdf19cSDavid du Colombier static void
128095fdf19cSDavid du Colombier background(void)
128195fdf19cSDavid du Colombier {
128295fdf19cSDavid du Colombier 	static int inbackground;
128395fdf19cSDavid du Colombier 
128495fdf19cSDavid du Colombier 	if(inbackground)
128595fdf19cSDavid du Colombier 		return;
128695fdf19cSDavid du Colombier 
128795fdf19cSDavid du Colombier 	if(!debug) {
128895fdf19cSDavid du Colombier 		switch(rfork(RFPROC|RFFDG|RFNAMEG|RFNOTEG|RFNOWAIT)){
128995fdf19cSDavid du Colombier 		case -1:
129095fdf19cSDavid du Colombier 			sysfatal("forking: %r");
129195fdf19cSDavid du Colombier 			break;
129295fdf19cSDavid du Colombier 		case 0:
129395fdf19cSDavid du Colombier 			break;
129495fdf19cSDavid du Colombier 		default:
129595fdf19cSDavid du Colombier 			exits(0);
129695fdf19cSDavid du Colombier 		}
129795fdf19cSDavid du Colombier 	}
129895fdf19cSDavid du Colombier 	inbackground = 1;
129995fdf19cSDavid du Colombier }
130067031067SDavid du Colombier 
130167031067SDavid du Colombier static int
130267031067SDavid du Colombier getclockprecision(vlong hz)
130367031067SDavid du Colombier {
130467031067SDavid du Colombier 	int i;
130567031067SDavid du Colombier 
130667031067SDavid du Colombier 	i = 8;
130767031067SDavid du Colombier 	while(hz > 0){
130867031067SDavid du Colombier 		i--;
130967031067SDavid du Colombier 		hz >>= 1;
131067031067SDavid du Colombier 	}
131167031067SDavid du Colombier 	return i;
131267031067SDavid du Colombier }
1313