xref: /plan9/sys/src/cmd/aux/timesync.c (revision 66d9869129ceef534e7ca537d062f37c0d2cc439)
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,
187c881178SDavid du Colombier 	Gps,
1980ee5cbfSDavid du Colombier 
203f9c8393SDavid du Colombier 	HZAvgSecs= 3*60,  /* target averaging period for frequency in seconds */
2180ee5cbfSDavid du Colombier 	MinSampleSecs= 60,	/* minimum sampling time in seconds */
227dd7cddfSDavid du Colombier };
237dd7cddfSDavid du Colombier 
247dd7cddfSDavid du Colombier 
253f9c8393SDavid du Colombier char *dir = "/tmp";	/* directory sample files live in */
267dd7cddfSDavid du Colombier char *logfile = "timesync";
277dd7cddfSDavid du Colombier char *timeserver;
285fab9909SDavid du Colombier char *Rootid;
295fab9909SDavid du Colombier int utcfil;
307c881178SDavid du Colombier int gpsfil;
317dd7cddfSDavid du Colombier int debug;
327dd7cddfSDavid du Colombier int impotent;
337dd7cddfSDavid du Colombier int logging;
347dd7cddfSDavid du Colombier int type;
353f9c8393SDavid du Colombier int gmtdelta;		/* rtc+gmtdelta = gmt */
3695fdf19cSDavid du Colombier uvlong avgerr;
377dd7cddfSDavid du Colombier 
383f9c8393SDavid du Colombier /* ntp server info */
397dd7cddfSDavid du Colombier int stratum = 14;
4080ee5cbfSDavid du Colombier vlong mydisp, rootdisp;
4180ee5cbfSDavid du Colombier vlong mydelay, rootdelay;
427dd7cddfSDavid du Colombier vlong avgdelay;
431118d624SDavid du Colombier vlong lastutc;
447dd7cddfSDavid du Colombier uchar rootid[4];
457dd7cddfSDavid du Colombier char *sysid;
4667031067SDavid du Colombier int myprec;
477dd7cddfSDavid du Colombier 
483f9c8393SDavid du Colombier /* list of time samples */
497dd7cddfSDavid du Colombier typedef struct Sample Sample;
507dd7cddfSDavid du Colombier struct Sample
517dd7cddfSDavid du Colombier {
527dd7cddfSDavid du Colombier 	Sample	*next;
537dd7cddfSDavid du Colombier 	uvlong	ticks;
547dd7cddfSDavid du Colombier 	vlong	ltime;
557dd7cddfSDavid du Colombier 	vlong	stime;
567dd7cddfSDavid du Colombier };
577dd7cddfSDavid du Colombier 
583f9c8393SDavid du Colombier /* ntp packet */
597dd7cddfSDavid du Colombier typedef struct NTPpkt NTPpkt;
607dd7cddfSDavid du Colombier struct NTPpkt
617dd7cddfSDavid du Colombier {
627dd7cddfSDavid du Colombier 	uchar	mode;
637dd7cddfSDavid du Colombier 	uchar	stratum;
647dd7cddfSDavid du Colombier 	uchar	poll;
657dd7cddfSDavid du Colombier 	uchar	precision;
667dd7cddfSDavid du Colombier 	uchar	rootdelay[4];
677dd7cddfSDavid du Colombier 	uchar	rootdisp[4];
687dd7cddfSDavid du Colombier 	uchar	rootid[4];
697dd7cddfSDavid du Colombier 	uchar	refts[8];
703f9c8393SDavid du Colombier 	uchar	origts[8];	/* departed client */
713f9c8393SDavid du Colombier 	uchar	recvts[8];	/* arrived at server */
723f9c8393SDavid du Colombier 	uchar	xmitts[8];	/* departed server */
737dd7cddfSDavid du Colombier 	uchar	keyid[4];
747dd7cddfSDavid du Colombier 	uchar	digest[16];
757dd7cddfSDavid du Colombier };
767dd7cddfSDavid du Colombier 
773f9c8393SDavid du Colombier /* ntp server */
7880ee5cbfSDavid du Colombier typedef struct NTPserver NTPserver;
7980ee5cbfSDavid du Colombier struct NTPserver
8080ee5cbfSDavid du Colombier {
8180ee5cbfSDavid du Colombier 	NTPserver *next;
8280ee5cbfSDavid du Colombier 	char	*name;
8380ee5cbfSDavid du Colombier 	uchar	stratum;
8480ee5cbfSDavid du Colombier 	uchar	precision;
8580ee5cbfSDavid du Colombier 	vlong	rootdelay;
8680ee5cbfSDavid du Colombier 	vlong	rootdisp;
8780ee5cbfSDavid du Colombier 	vlong	rtt;
8880ee5cbfSDavid du Colombier 	vlong	dt;
8980ee5cbfSDavid du Colombier };
9080ee5cbfSDavid du Colombier 
9180ee5cbfSDavid du Colombier NTPserver *ntpservers;
9280ee5cbfSDavid du Colombier 
937dd7cddfSDavid du Colombier enum
947dd7cddfSDavid du Colombier {
953f9c8393SDavid du Colombier 	NTPSIZE= 	48,	/* basic ntp packet */
963f9c8393SDavid du Colombier 	NTPDIGESTSIZE=	20,	/* key and digest */
977dd7cddfSDavid du Colombier };
987dd7cddfSDavid du Colombier 
993f9c8393SDavid du Colombier /* error bound of last sample */
100ed250ae1SDavid du Colombier ulong	ε;
101ed250ae1SDavid du Colombier 
10280ee5cbfSDavid du Colombier static void	addntpserver(char *name);
10380ee5cbfSDavid du Colombier static int	adjustperiod(vlong diff, vlong accuracy, int secs);
10495fdf19cSDavid du Colombier static void	background(void);
10580ee5cbfSDavid du Colombier static int	caperror(vlong dhz, int tsecs, vlong taccuracy);
10680ee5cbfSDavid du Colombier static long	fstime(void);
1073f9c8393SDavid du Colombier static int	gettime(vlong *nsec, uvlong *ticks, uvlong *hz); /* returns time, ticks, hz */
10867031067SDavid du Colombier static int	getclockprecision(vlong);
1097c881178SDavid du Colombier static vlong	gpssample(void);
11080ee5cbfSDavid du Colombier static void	hnputts(void *p, vlong nsec);
11180ee5cbfSDavid du Colombier static void	hnputts(void *p, vlong nsec);
1127dd7cddfSDavid du Colombier static void	inittime(void);
11380ee5cbfSDavid du Colombier static vlong	nhgetts(void *p);
11480ee5cbfSDavid du Colombier static vlong	nhgetts(void *p);
1159a747e4fSDavid du Colombier static void	ntpserver(char*);
11680ee5cbfSDavid du Colombier static vlong	ntpsample(void);
11780ee5cbfSDavid du Colombier static int	ntptimediff(NTPserver *ns);
11880ee5cbfSDavid du Colombier static int	openfreqfile(void);
11980ee5cbfSDavid du Colombier static vlong	readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz);
12080ee5cbfSDavid du Colombier static long	rtctime(void);
12180ee5cbfSDavid du Colombier static vlong	sample(long (*get)(void));
1227dd7cddfSDavid du Colombier static void	setpriority(void);
12380ee5cbfSDavid du Colombier static void	setrootid(char *d);
1243f9c8393SDavid du Colombier static void	settime(vlong now, uvlong hz, vlong delta, int n); /* set time, hz, delta, period */
1255fab9909SDavid du Colombier static vlong	utcsample(void);
12680ee5cbfSDavid du Colombier static uvlong	vabs(vlong);
1273f9c8393SDavid du Colombier static uvlong	whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz,
1283f9c8393SDavid du Colombier 			vlong dt, vlong ticks, vlong period);
12980ee5cbfSDavid du Colombier static void	writefreqfile(int fd, vlong hz, int secs, vlong diff);
1307dd7cddfSDavid du Colombier 
1317dd7cddfSDavid du Colombier // ((1970-1900)*365 + 17 /*leap days*/)*24*60*60
1327dd7cddfSDavid du Colombier #define EPOCHDIFF 2208988800UL
1337dd7cddfSDavid du Colombier 
1343f9c8393SDavid du Colombier static void
usage(void)1353f9c8393SDavid du Colombier usage(void)
1363f9c8393SDavid du Colombier {
1373f9c8393SDavid du Colombier 	fprint(2, "usage: %s [-a accuracy][-d dir][-I rootid][-s net]"
1383f9c8393SDavid du Colombier 		"[-S stratum][-DfGilLnrU] timesource ...\n", argv0);
1393f9c8393SDavid du Colombier 	exits("usage");
1403f9c8393SDavid du Colombier }
1413f9c8393SDavid du Colombier 
1427dd7cddfSDavid du Colombier void
main(int argc,char ** argv)1437dd7cddfSDavid du Colombier main(int argc, char **argv)
1447dd7cddfSDavid du Colombier {
1453f9c8393SDavid du Colombier 	int i, t, fd, nservenet;
1463f9c8393SDavid du Colombier 	int secs;		/* sampling period */
1473f9c8393SDavid du Colombier 	int tsecs;		/* temporary sampling period */
14895fdf19cSDavid du Colombier 	uvlong hz, minhz, maxhz, period, nhz;
1493f9c8393SDavid du Colombier 	vlong diff, accuracy, taccuracy;
1509a747e4fSDavid du Colombier 	char *servenet[4];
1513f9c8393SDavid du Colombier 	Sample *s, *x, *first, **l;
1527dd7cddfSDavid du Colombier 	Tm tl, tg;
1537dd7cddfSDavid du Colombier 
1543f9c8393SDavid du Colombier 	type = Fs;		/* by default, sync with the file system */
1557dd7cddfSDavid du Colombier 	debug = 0;
1563f9c8393SDavid du Colombier 	accuracy = 1000000LL;	/* default accuracy is 1 millisecond */
1579a747e4fSDavid du Colombier 	nservenet = 0;
15880ee5cbfSDavid du Colombier 	tsecs = secs = MinSampleSecs;
1595fab9909SDavid du Colombier 	timeserver = "";
1607dd7cddfSDavid du Colombier 
1617dd7cddfSDavid du Colombier 	ARGBEGIN{
1627dd7cddfSDavid du Colombier 	case 'a':
1633f9c8393SDavid du Colombier 		accuracy = strtoll(EARGF(usage()), 0, 0); /* specified in ns */
1643f9c8393SDavid du Colombier 		if(accuracy <= 1)
1657dd7cddfSDavid du Colombier 			sysfatal("bad accuracy specified");
1663f9c8393SDavid du Colombier 		break;
1673f9c8393SDavid du Colombier 	case 'd':
1683f9c8393SDavid du Colombier 		dir = EARGF(usage());
1693f9c8393SDavid du Colombier 		break;
1703f9c8393SDavid du Colombier 	case 'D':
1713f9c8393SDavid du Colombier 		debug = 1;
1727dd7cddfSDavid du Colombier 		break;
1737dd7cddfSDavid du Colombier 	case 'f':
1747dd7cddfSDavid du Colombier 		type = Fs;
1757dd7cddfSDavid du Colombier 		stratum = 2;
1767dd7cddfSDavid du Colombier 		break;
1777c881178SDavid du Colombier 	case 'G':
1787c881178SDavid du Colombier 		type = Gps;
1797c881178SDavid du Colombier 		stratum = 1;
1805fab9909SDavid du Colombier 		break;
1813f9c8393SDavid du Colombier 	case 'i':
1823f9c8393SDavid du Colombier 		impotent = 1;
1837dd7cddfSDavid du Colombier 		break;
1843f9c8393SDavid du Colombier 	case 'I':
1853f9c8393SDavid du Colombier 		Rootid = EARGF(usage());
1867dd7cddfSDavid du Colombier 		break;
1873f9c8393SDavid du Colombier 	case 'l':
1883f9c8393SDavid du Colombier 		logging = 1;
1897dd7cddfSDavid du Colombier 		break;
1907dd7cddfSDavid du Colombier 	case 'L':
1913f9c8393SDavid du Colombier 		/*
1923f9c8393SDavid du Colombier 		 * Assume time source in local time rather than GMT.
1933f9c8393SDavid du Colombier 		 * Calculate difference so that rtctime can return GMT.
1943f9c8393SDavid du Colombier 		 * This is useful with the rtc on PC's that run Windows
1953f9c8393SDavid du Colombier 		 * since Windows keeps the local time in the rtc.
1963f9c8393SDavid du Colombier 		 */
1977dd7cddfSDavid du Colombier 		t = time(0);
1987dd7cddfSDavid du Colombier 		tl = *localtime(t);
1997dd7cddfSDavid du Colombier 		tg = *gmtime(t);
2007dd7cddfSDavid du Colombier 
2013f9c8393SDavid du Colombier 		/*
2023f9c8393SDavid du Colombier 		 * if the years are different, we're at most a day off,
2033f9c8393SDavid du Colombier 		 * so just rewrite
2043f9c8393SDavid du Colombier 		 */
2057dd7cddfSDavid du Colombier 		if(tl.year < tg.year){
2067dd7cddfSDavid du Colombier 			tg.year--;
2077dd7cddfSDavid du Colombier 			tg.yday = tl.yday + 1;
2087dd7cddfSDavid du Colombier 		}else if(tl.year > tg.year){
2097dd7cddfSDavid du Colombier 			tl.year--;
2107dd7cddfSDavid du Colombier 			tl.yday = tg.yday+1;
2117dd7cddfSDavid du Colombier 		}
2127dd7cddfSDavid du Colombier 		assert(tl.year == tg.year);
2137dd7cddfSDavid du Colombier 
2147dd7cddfSDavid du Colombier 		tg.sec -= tl.sec;
2157dd7cddfSDavid du Colombier 		tg.min -= tl.min;
2167dd7cddfSDavid du Colombier 		tg.hour -= tl.hour;
2177dd7cddfSDavid du Colombier 		tg.yday -= tl.yday;
2187dd7cddfSDavid du Colombier 		gmtdelta = tg.sec+60*(tg.min+60*(tg.hour+tg.yday*24));
2197dd7cddfSDavid du Colombier 
2207dd7cddfSDavid du Colombier 		assert(abs(gmtdelta) <= 24*60*60);
2217dd7cddfSDavid du Colombier 		break;
2223f9c8393SDavid du Colombier 	case 'n':
2233f9c8393SDavid du Colombier 		type = Ntp;
2247dd7cddfSDavid du Colombier 		break;
2253f9c8393SDavid du Colombier 	case 'r':
2263f9c8393SDavid du Colombier 		type = Rtc;
2273f9c8393SDavid du Colombier 		stratum = 0;
2283f9c8393SDavid du Colombier 		break;
2293f9c8393SDavid du Colombier 	case 'U':
2303f9c8393SDavid du Colombier 		type = Utc;
2313f9c8393SDavid du Colombier 		stratum = 1;
2325fab9909SDavid du Colombier 		break;
23380ee5cbfSDavid du Colombier 	case 's':
2349a747e4fSDavid du Colombier 		if(nservenet >= nelem(servenet))
2359a747e4fSDavid du Colombier 			sysfatal("too many networks to serve on");
2363f9c8393SDavid du Colombier 		servenet[nservenet++] = EARGF(usage());
2377dd7cddfSDavid du Colombier 		break;
2383ff48bf5SDavid du Colombier 	case 'S':
2393f9c8393SDavid du Colombier 		stratum = strtoll(EARGF(usage()), 0, 0);
2403ff48bf5SDavid du Colombier 		break;
2413f9c8393SDavid du Colombier 	default:
2423f9c8393SDavid du Colombier 		usage();
2437dd7cddfSDavid du Colombier 	}ARGEND;
2447dd7cddfSDavid du Colombier 
2459a747e4fSDavid du Colombier 	fmtinstall('E', eipfmt);
2469a747e4fSDavid du Colombier 	fmtinstall('I', eipfmt);
2479a747e4fSDavid du Colombier 	fmtinstall('V', eipfmt);
24880ee5cbfSDavid du Colombier 	sysid = getenv("sysname");
2497dd7cddfSDavid du Colombier 
2503f9c8393SDavid du Colombier 	/* detach from the current namespace */
25195fdf19cSDavid du Colombier 	if(debug)
25295fdf19cSDavid du Colombier 		rfork(RFNAMEG);
25395fdf19cSDavid du Colombier 
25480ee5cbfSDavid du Colombier 	switch(type){
2557c881178SDavid du Colombier 	case Utc:
2567c881178SDavid du Colombier 		if(argc > 0)
2577c881178SDavid du Colombier 			timeserver = argv[0];
2587c881178SDavid du Colombier 		else
2597c881178SDavid du Colombier 			sysfatal("bad time source");
2607c881178SDavid du Colombier 		break;
2617c881178SDavid du Colombier 	case Gps:
2627c881178SDavid du Colombier 		if(argc > 0)
2637c881178SDavid du Colombier 			timeserver = argv[0];
2647c881178SDavid du Colombier 		else
2657c881178SDavid du Colombier 			timeserver = "/mnt/gps/time";
2667c881178SDavid du Colombier 		break;
26780ee5cbfSDavid du Colombier 	case Fs:
2687dd7cddfSDavid du Colombier 		if(argc > 0)
2697dd7cddfSDavid du Colombier 			timeserver = argv[0];
2707dd7cddfSDavid du Colombier 		else
2717dd7cddfSDavid du Colombier 			timeserver = "/srv/boot";
2727dd7cddfSDavid du Colombier 		break;
2737dd7cddfSDavid du Colombier 	case Ntp:
2743f9c8393SDavid du Colombier 		if(argc > 0)
27580ee5cbfSDavid du Colombier 			for(i = 0; i < argc; i++)
27680ee5cbfSDavid du Colombier 				addntpserver(argv[i]);
2773f9c8393SDavid du Colombier 		else
27880ee5cbfSDavid du Colombier 			addntpserver("$ntp");
2797dd7cddfSDavid du Colombier 		break;
2807dd7cddfSDavid du Colombier 	}
2817dd7cddfSDavid du Colombier 
2827dd7cddfSDavid du Colombier 	setpriority();
2837dd7cddfSDavid du Colombier 
2843f9c8393SDavid du Colombier 	/* figure out our time interface and initial frequency */
2857dd7cddfSDavid du Colombier 	inittime();
2867dd7cddfSDavid du Colombier 	gettime(0, 0, &hz);
287be93767fSDavid du Colombier 	minhz = hz/10;
288be93767fSDavid du Colombier 	maxhz = hz*10;
28967031067SDavid du Colombier 	myprec = getclockprecision(hz);
2907dd7cddfSDavid du Colombier 
2913f9c8393SDavid du Colombier 	/* convert the accuracy from nanoseconds to ticks */
29280ee5cbfSDavid du Colombier 	taccuracy = hz*accuracy/SEC;
2937dd7cddfSDavid du Colombier 
2943f9c8393SDavid du Colombier 	/*
2953f9c8393SDavid du Colombier 	 * bind in clocks
2963f9c8393SDavid du Colombier 	 */
2977dd7cddfSDavid du Colombier 	switch(type){
2987dd7cddfSDavid du Colombier 	case Fs:
2997dd7cddfSDavid du Colombier 		fd = open(timeserver, ORDWR);
3007dd7cddfSDavid du Colombier 		if(fd < 0)
30114cc0f53SDavid du Colombier 			sysfatal("opening %s: %r", timeserver);
3025d459b5aSDavid du Colombier 		if(amount(fd, "/n/boot", MREPL, "") < 0)
30314cc0f53SDavid du Colombier 			sysfatal("mounting %s: %r", timeserver);
3047dd7cddfSDavid du Colombier 		close(fd);
3057dd7cddfSDavid du Colombier 		break;
3067dd7cddfSDavid du Colombier 	case Rtc:
3077dd7cddfSDavid du Colombier 		bind("#r", "/dev", MAFTER);
3087dd7cddfSDavid du Colombier 		if(access("/dev/rtc", AREAD) < 0)
30914cc0f53SDavid du Colombier 			sysfatal("accessing /dev/rtc: %r");
3107dd7cddfSDavid du Colombier 		break;
3115fab9909SDavid du Colombier 	case Utc:
3125fab9909SDavid du Colombier 		fd = open(timeserver, OREAD);
3135fab9909SDavid du Colombier 		if(fd < 0)
31414cc0f53SDavid du Colombier 			sysfatal("opening %s: %r", timeserver);
3155fab9909SDavid du Colombier 		utcfil = fd;
3165fab9909SDavid du Colombier 		break;
3177c881178SDavid du Colombier 	case Gps:
3187c881178SDavid du Colombier 		fd = open(timeserver, OREAD);
3197c881178SDavid du Colombier 		if(fd < 0)
32014cc0f53SDavid du Colombier 			sysfatal("opening %s: %r", timeserver);
3217c881178SDavid du Colombier 		gpsfil = fd;
3227c881178SDavid du Colombier 		break;
3237dd7cddfSDavid du Colombier 	}
3247dd7cddfSDavid du Colombier 
3253f9c8393SDavid du Colombier 	/*
3263f9c8393SDavid du Colombier 	 * start a local ntp server(s)
3273f9c8393SDavid du Colombier 	 */
3283f9c8393SDavid du Colombier 	for(i = 0; i < nservenet; i++)
3297dd7cddfSDavid du Colombier 		switch(rfork(RFPROC|RFFDG|RFMEM|RFNOWAIT)){
3307dd7cddfSDavid du Colombier 		case -1:
3317dd7cddfSDavid du Colombier 			sysfatal("forking: %r");
3327dd7cddfSDavid du Colombier 		case 0:
3339a747e4fSDavid du Colombier 			ntpserver(servenet[i]);
3347dd7cddfSDavid du Colombier 			_exits(0);
3357dd7cddfSDavid du Colombier 		}
3367dd7cddfSDavid du Colombier 
3373f9c8393SDavid du Colombier 	/* get the last known frequency from the file */
33880ee5cbfSDavid du Colombier 	fd = openfreqfile();
3397dd7cddfSDavid du Colombier 	hz = readfreqfile(fd, hz, minhz, maxhz);
3407dd7cddfSDavid du Colombier 
3413f9c8393SDavid du Colombier 	/*
3423f9c8393SDavid du Colombier 	 * this is the main loop.  it gets a sample, adjusts the
3433f9c8393SDavid du Colombier 	 * clock and computes a sleep period until the next loop.
3443f9c8393SDavid du Colombier 	 * we balance frequency drift against the length of the
3453f9c8393SDavid du Colombier 	 * period to avoid blowing the accuracy limit.
3463f9c8393SDavid du Colombier 	 */
3477dd7cddfSDavid du Colombier 	first = nil;
3487dd7cddfSDavid du Colombier 	l = &first;
34980ee5cbfSDavid du Colombier 	avgerr = accuracy >> 1;
3503f9c8393SDavid du Colombier 	for(;; background(), sleep(tsecs*1000)){
3513f9c8393SDavid du Colombier 		s = mallocz(sizeof *s, 1);
3527dd7cddfSDavid du Colombier 		diff = 0;
3537dd7cddfSDavid du Colombier 
3543f9c8393SDavid du Colombier 		/* get times for this sample */
355ed250ae1SDavid du Colombier 		ε = ~0;
3567dd7cddfSDavid du Colombier 		switch(type){
3577dd7cddfSDavid du Colombier 		case Fs:
3587dd7cddfSDavid du Colombier 			s->stime = sample(fstime);
3597dd7cddfSDavid du Colombier 			break;
3607dd7cddfSDavid du Colombier 		case Rtc:
3617dd7cddfSDavid du Colombier 			s->stime = sample(rtctime);
3627dd7cddfSDavid du Colombier 			break;
3635fab9909SDavid du Colombier 		case Utc:
3645fab9909SDavid du Colombier 			s->stime = utcsample();
3655fab9909SDavid du Colombier 			if(s->stime == 0LL){
3665fab9909SDavid du Colombier 				if(logging)
3675fab9909SDavid du Colombier 					syslog(0, logfile, "no sample");
3685fab9909SDavid du Colombier 				free(s);
3695fab9909SDavid du Colombier 				if (secs > 60 * 15)
3705fab9909SDavid du Colombier 					tsecs = 60*15;
3715fab9909SDavid du Colombier 				continue;
3725fab9909SDavid du Colombier 			}
3735fab9909SDavid du Colombier 			break;
3747dd7cddfSDavid du Colombier 		case Ntp:
37580ee5cbfSDavid du Colombier 			diff = ntpsample();
37680ee5cbfSDavid du Colombier 			if(diff == 0LL){
37780ee5cbfSDavid du Colombier 				if(logging)
37880ee5cbfSDavid du Colombier 					syslog(0, logfile, "no sample");
37980ee5cbfSDavid du Colombier 				free(s);
38080ee5cbfSDavid du Colombier 				if(secs > 60*15)
38180ee5cbfSDavid du Colombier 					tsecs = 60*15;
38280ee5cbfSDavid du Colombier 				continue;
38380ee5cbfSDavid du Colombier 			}
3847dd7cddfSDavid du Colombier 			break;
3857c881178SDavid du Colombier 		case Gps:
3867c881178SDavid du Colombier 			diff = gpssample();
3877c881178SDavid du Colombier 			if(diff == 0LL){
3887c881178SDavid du Colombier 				if(logging)
3897c881178SDavid du Colombier 					syslog(0, logfile, "no sample");
3907c881178SDavid du Colombier 				free(s);
3917c881178SDavid du Colombier 				if(secs > 60*15)
3927c881178SDavid du Colombier 					tsecs = 60*15;
3937c881178SDavid du Colombier 				continue;
3947c881178SDavid du Colombier 			}
3957dd7cddfSDavid du Colombier 		}
3967dd7cddfSDavid du Colombier 
3973f9c8393SDavid du Colombier 		/* use fastest method to read local clock and ticks */
3987dd7cddfSDavid du Colombier 		gettime(&s->ltime, &s->ticks, 0);
3997c881178SDavid du Colombier 		if(type == Ntp || type == Gps)
4007dd7cddfSDavid du Colombier 			s->stime = s->ltime + diff;
4017dd7cddfSDavid du Colombier 
4023f9c8393SDavid du Colombier 		/* if the sample was bad, ignore it */
403940a793aSDavid du Colombier 		if(s->stime < 0){
40480ee5cbfSDavid du Colombier 			free(s);
40580ee5cbfSDavid du Colombier 			continue;
40680ee5cbfSDavid du Colombier 		}
40780ee5cbfSDavid du Colombier 
4083f9c8393SDavid du Colombier 		/* reset local time */
40980ee5cbfSDavid du Colombier 		diff = s->stime - s->ltime;
41080ee5cbfSDavid du Colombier 		if(diff > 10*SEC || diff < -10*SEC){
4113f9c8393SDavid du Colombier 			/* we're way off, just set the time */
41280ee5cbfSDavid du Colombier 			secs = MinSampleSecs;
41380ee5cbfSDavid du Colombier 			settime(s->stime, 0, 0, 0);
41480ee5cbfSDavid du Colombier 		} else {
4153f9c8393SDavid du Colombier 			/* keep a running average of the error. */
41680ee5cbfSDavid du Colombier 			avgerr = (avgerr>>1) + (vabs(diff)>>1);
41780ee5cbfSDavid du Colombier 
4183f9c8393SDavid du Colombier 			/*
4193f9c8393SDavid du Colombier 			 * the time to next sample depends on how good or
4203f9c8393SDavid du Colombier 			 * bad we're doing.
4213f9c8393SDavid du Colombier 			 */
42280ee5cbfSDavid du Colombier 			tsecs = secs = adjustperiod(diff, accuracy, secs);
42380ee5cbfSDavid du Colombier 
4243f9c8393SDavid du Colombier 			/*
4253f9c8393SDavid du Colombier 			 * work off the fixed difference.  This is done
4263f9c8393SDavid du Colombier 			 * by adding a ramp to the clock.  Each 100th of a
4273f9c8393SDavid du Colombier 			 * second (or so) the kernel will add diff/(4*secs*100)
4283f9c8393SDavid du Colombier 			 * to the clock.  we only do 1/4 of the difference per
4293f9c8393SDavid du Colombier 			 * period to dampen any measurement noise.
4303f9c8393SDavid du Colombier 			 *
4313f9c8393SDavid du Colombier 			 * any difference greater than ε we work off during the
4323f9c8393SDavid du Colombier 			 * sampling period.
4333f9c8393SDavid du Colombier 			 */
4343f9c8393SDavid du Colombier 			if(abs(diff) > ε)
435ed250ae1SDavid du Colombier 				if(diff > 0)
436ed250ae1SDavid du Colombier 					settime(-1, 0, diff-((3*ε)/4), secs);
437ed250ae1SDavid du Colombier 				else
438ed250ae1SDavid du Colombier 					settime(-1, 0, diff+((3*ε)/4), secs);
4393f9c8393SDavid du Colombier 			else
44080ee5cbfSDavid du Colombier 				settime(-1, 0, diff, 4*secs);
44180ee5cbfSDavid du Colombier 
44280ee5cbfSDavid du Colombier 		}
44380ee5cbfSDavid du Colombier 		if(debug)
44480ee5cbfSDavid du Colombier 			fprint(2, "δ %lld avgδ %lld f %lld\n", diff, avgerr, hz);
44580ee5cbfSDavid du Colombier 
4463f9c8393SDavid du Colombier 		/* dump old samples (keep at least one) */
4477dd7cddfSDavid du Colombier 		while(first != nil){
4487dd7cddfSDavid du Colombier 			if(first->next == nil)
4497dd7cddfSDavid du Colombier 				break;
45080ee5cbfSDavid du Colombier 			if(s->stime - first->next->stime < DAY)
4517dd7cddfSDavid du Colombier 				break;
4527dd7cddfSDavid du Colombier 			x = first;
45380ee5cbfSDavid du Colombier 			first = first->next;
4547dd7cddfSDavid du Colombier 			free(x);
4557dd7cddfSDavid du Colombier 		}
4567dd7cddfSDavid du Colombier 
4573f9c8393SDavid du Colombier 		/*
4583f9c8393SDavid du Colombier 		 * The sampling error is limited by the total error.  If
4593f9c8393SDavid du Colombier 		 * we make sure the sampling period is at least 16 million
4603f9c8393SDavid du Colombier 		 * times the average error, we should calculate a frequency
4613f9c8393SDavid du Colombier 		 * with on average a 1e-7 error.
4623f9c8393SDavid du Colombier 		 *
4633f9c8393SDavid du Colombier 		 * So that big hz changes don't blow our accuracy requirement,
4643f9c8393SDavid du Colombier 		 * we shorten the period to make sure that δhz*secs will be
4653f9c8393SDavid du Colombier 		 * greater than the accuracy limit.
4663f9c8393SDavid du Colombier 		 */
46780ee5cbfSDavid du Colombier 		period = avgerr << 24;
4683f9c8393SDavid du Colombier 		for(x = first; x != nil; x = x->next)
4693f9c8393SDavid du Colombier 			if(s->stime - x->stime < period ||
4703f9c8393SDavid du Colombier 			   x->next == nil || s->stime - x->next->stime < period)
47180ee5cbfSDavid du Colombier 				break;
47280ee5cbfSDavid du Colombier 		if(x != nil){
47380ee5cbfSDavid du Colombier 			nhz = whatisthefrequencykenneth(
47480ee5cbfSDavid du Colombier 				hz, minhz, maxhz,
47580ee5cbfSDavid du Colombier 				s->stime - x->stime,
47680ee5cbfSDavid du Colombier 				s->ticks - x->ticks,
47780ee5cbfSDavid du Colombier 				period);
47880ee5cbfSDavid du Colombier 			tsecs = caperror(vabs(nhz-hz), tsecs, taccuracy);
47980ee5cbfSDavid du Colombier 			hz = nhz;
48080ee5cbfSDavid du Colombier 			writefreqfile(fd, hz, (s->stime - x->stime)/SEC, diff);
4817dd7cddfSDavid du Colombier 		}
4827dd7cddfSDavid du Colombier 
4833f9c8393SDavid du Colombier 		/* add current sample to list. */
4847dd7cddfSDavid du Colombier 		*l = s;
4857dd7cddfSDavid du Colombier 		l = &s->next;
48680ee5cbfSDavid du Colombier 
48780ee5cbfSDavid du Colombier 		if(logging)
48880ee5cbfSDavid du Colombier 			syslog(0, logfile, "δ %lld avgδ %lld hz %lld",
48980ee5cbfSDavid du Colombier 				diff, avgerr, hz);
4907dd7cddfSDavid du Colombier 	}
4917dd7cddfSDavid du Colombier }
4927dd7cddfSDavid du Colombier 
4933f9c8393SDavid du Colombier /*
4943f9c8393SDavid du Colombier  * adjust the sampling period with some histeresis
4953f9c8393SDavid du Colombier  */
49680ee5cbfSDavid du Colombier static int
adjustperiod(vlong diff,vlong accuracy,int secs)49780ee5cbfSDavid du Colombier adjustperiod(vlong diff, vlong accuracy, int secs)
49880ee5cbfSDavid du Colombier {
49980ee5cbfSDavid du Colombier 	uvlong absdiff;
50080ee5cbfSDavid du Colombier 
50180ee5cbfSDavid du Colombier 	absdiff = vabs(diff);
50280ee5cbfSDavid du Colombier 
50380ee5cbfSDavid du Colombier 	if(absdiff < (accuracy>>1))
50480ee5cbfSDavid du Colombier 		secs += 60;
50580ee5cbfSDavid du Colombier 	else if(absdiff > accuracy)
50680ee5cbfSDavid du Colombier 		secs >>= 1;
50780ee5cbfSDavid du Colombier 	else
50880ee5cbfSDavid du Colombier 		secs -= 60;
50980ee5cbfSDavid du Colombier 	if(secs < MinSampleSecs)
51080ee5cbfSDavid du Colombier 		secs = MinSampleSecs;
51180ee5cbfSDavid du Colombier 	return secs;
51280ee5cbfSDavid du Colombier }
51380ee5cbfSDavid du Colombier 
5143f9c8393SDavid du Colombier /*
5153f9c8393SDavid du Colombier  * adjust the frequency
5163f9c8393SDavid du Colombier  */
51780ee5cbfSDavid du Colombier static uvlong
whatisthefrequencykenneth(uvlong hz,uvlong minhz,uvlong maxhz,vlong dt,vlong ticks,vlong period)5183f9c8393SDavid du Colombier whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz, vlong dt,
5193f9c8393SDavid du Colombier 	vlong ticks, vlong period)
52080ee5cbfSDavid du Colombier {
52180ee5cbfSDavid du Colombier 	uvlong ohz = hz;
5223f9c8393SDavid du Colombier 	static mpint *mpdt, *mpticks, *mphz, *mpbillion;
52380ee5cbfSDavid du Colombier 
5243f9c8393SDavid du Colombier 	/* sanity check */
52580ee5cbfSDavid du Colombier 	if(dt <= 0 || ticks <= 0)
52680ee5cbfSDavid du Colombier 		return hz;
52780ee5cbfSDavid du Colombier 
52880ee5cbfSDavid du Colombier 	if(mphz == nil){
52980ee5cbfSDavid du Colombier 		mphz = mpnew(0);
53080ee5cbfSDavid du Colombier 		mpbillion = uvtomp(SEC, nil);
53180ee5cbfSDavid du Colombier 	}
53280ee5cbfSDavid du Colombier 
5333f9c8393SDavid du Colombier 	/* hz = (ticks*SEC)/dt */
53480ee5cbfSDavid du Colombier 	mpdt = vtomp(dt, mpdt);
53580ee5cbfSDavid du Colombier 	mpticks = vtomp(ticks, mpticks);
53680ee5cbfSDavid du Colombier 	mpmul(mpticks, mpbillion, mpticks);
53780ee5cbfSDavid du Colombier 	mpdiv(mpticks, mpdt, mphz, nil);
53880ee5cbfSDavid du Colombier 	hz = mptoui(mphz);
53980ee5cbfSDavid du Colombier 
5403f9c8393SDavid du Colombier 	/* sanity */
54180ee5cbfSDavid du Colombier 	if(hz < minhz || hz > maxhz)
54280ee5cbfSDavid du Colombier 		return ohz;
54380ee5cbfSDavid du Colombier 
5443f9c8393SDavid du Colombier 	/* damp the change if we're shorter than the target period */
54580ee5cbfSDavid du Colombier 	if(period > dt)
54680ee5cbfSDavid du Colombier 		hz = (12ULL*ohz + 4ULL*hz)/16ULL;
54780ee5cbfSDavid du Colombier 
54880ee5cbfSDavid du Colombier 	settime(-1, hz, 0, 0);
54980ee5cbfSDavid du Colombier 	return hz;
55080ee5cbfSDavid du Colombier }
55180ee5cbfSDavid du Colombier 
5523f9c8393SDavid du Colombier /*
5533f9c8393SDavid du Colombier  * We may be changing the frequency to match a bad measurement
5543f9c8393SDavid du Colombier  * or to match a condition no longer in effect.  To make sure
5553f9c8393SDavid du Colombier  * that this doesn't blow our error budget over the next measurement
5563f9c8393SDavid du Colombier  * period, shorten the period to make sure that δhz*secs will be
5573f9c8393SDavid du Colombier  * less than the accuracy limit.  Here taccuracy is accuracy converted
5583f9c8393SDavid du Colombier  * from nanoseconds to ticks.
5593f9c8393SDavid du Colombier  */
56080ee5cbfSDavid du Colombier static int
caperror(vlong dhz,int tsecs,vlong taccuracy)56180ee5cbfSDavid du Colombier caperror(vlong dhz, int tsecs, vlong taccuracy)
56280ee5cbfSDavid du Colombier {
5639a747e4fSDavid du Colombier 	if(dhz*tsecs <= taccuracy)
56480ee5cbfSDavid du Colombier 		return tsecs;
56580ee5cbfSDavid du Colombier 
56680ee5cbfSDavid du Colombier 	if(debug)
56780ee5cbfSDavid du Colombier 		fprint(2, "δhz %lld tsecs %d tacc %lld\n", dhz, tsecs, taccuracy);
56880ee5cbfSDavid du Colombier 
56980ee5cbfSDavid du Colombier 	tsecs = taccuracy/dhz;
57080ee5cbfSDavid du Colombier 	if(tsecs < MinSampleSecs)
57180ee5cbfSDavid du Colombier 		tsecs = MinSampleSecs;
57280ee5cbfSDavid du Colombier 	return tsecs;
57380ee5cbfSDavid du Colombier }
5747dd7cddfSDavid du Colombier 
5753f9c8393SDavid du Colombier /*
5763f9c8393SDavid du Colombier  *  kernel interface
5773f9c8393SDavid du Colombier  */
5787dd7cddfSDavid du Colombier enum
5797dd7cddfSDavid du Colombier {
5807dd7cddfSDavid du Colombier 	Ibintime,
5817dd7cddfSDavid du Colombier 	Insec,
5827dd7cddfSDavid du Colombier 	Itiming,
5837dd7cddfSDavid du Colombier };
5847dd7cddfSDavid du Colombier int ifc;
5857dd7cddfSDavid du Colombier int bintimefd = -1;
5867dd7cddfSDavid du Colombier int timingfd = -1;
5877dd7cddfSDavid du Colombier int nsecfd = -1;
5887dd7cddfSDavid du Colombier int fastclockfd = -1;
5897dd7cddfSDavid du Colombier 
5907dd7cddfSDavid du Colombier static void
inittime(void)5917dd7cddfSDavid du Colombier inittime(void)
5927dd7cddfSDavid du Colombier {
5937dd7cddfSDavid du Colombier 	int mode;
5947dd7cddfSDavid du Colombier 
5957dd7cddfSDavid du Colombier 	if(impotent)
5967dd7cddfSDavid du Colombier 		mode = OREAD;
5977dd7cddfSDavid du Colombier 	else
5987dd7cddfSDavid du Colombier 		mode = ORDWR;
5997dd7cddfSDavid du Colombier 
6003f9c8393SDavid du Colombier 	/* bind in clocks */
6017dd7cddfSDavid du Colombier 	if(access("/dev/time", 0) < 0)
6027dd7cddfSDavid du Colombier 		bind("#c", "/dev", MAFTER);
6037dd7cddfSDavid du Colombier 	if(access("/dev/rtc", 0) < 0)
6047dd7cddfSDavid du Colombier 		bind("#r", "/dev", MAFTER);
6057dd7cddfSDavid du Colombier 
6063f9c8393SDavid du Colombier 	/* figure out what interface we have */
6077dd7cddfSDavid du Colombier 	ifc = Ibintime;
6087dd7cddfSDavid du Colombier 	bintimefd = open("/dev/bintime", mode);
6097dd7cddfSDavid du Colombier 	if(bintimefd >= 0)
6107dd7cddfSDavid du Colombier 		return;
6117dd7cddfSDavid du Colombier 	ifc = Insec;
6127dd7cddfSDavid du Colombier 	nsecfd = open("/dev/nsec", mode);
6137dd7cddfSDavid du Colombier 	if(nsecfd < 0)
6147dd7cddfSDavid du Colombier 		sysfatal("opening /dev/nsec");
6157dd7cddfSDavid du Colombier 	fastclockfd = open("/dev/fastclock", mode);
6167dd7cddfSDavid du Colombier 	if(fastclockfd < 0)
6177dd7cddfSDavid du Colombier 		sysfatal("opening /dev/fastclock");
6187dd7cddfSDavid du Colombier 	timingfd = open("/dev/timing", OREAD);
6197dd7cddfSDavid du Colombier 	if(timingfd < 0)
6207dd7cddfSDavid du Colombier 		return;
6217dd7cddfSDavid du Colombier 	ifc = Itiming;
6227dd7cddfSDavid du Colombier }
6237dd7cddfSDavid du Colombier 
6243f9c8393SDavid du Colombier /*
6253f9c8393SDavid du Colombier  *  convert binary numbers from/to kernel
6263f9c8393SDavid du Colombier  */
6277dd7cddfSDavid du Colombier static uvlong uvorder = 0x0001020304050607ULL;
6287dd7cddfSDavid du Colombier 
6297dd7cddfSDavid du Colombier static uchar*
be2vlong(vlong * to,uchar * f)6307dd7cddfSDavid du Colombier be2vlong(vlong *to, uchar *f)
6317dd7cddfSDavid du Colombier {
6327dd7cddfSDavid du Colombier 	uchar *t, *o;
6337dd7cddfSDavid du Colombier 	int i;
6347dd7cddfSDavid du Colombier 
6357dd7cddfSDavid du Colombier 	t = (uchar*)to;
6367dd7cddfSDavid du Colombier 	o = (uchar*)&uvorder;
6377dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(vlong); i++)
6387dd7cddfSDavid du Colombier 		t[o[i]] = f[i];
6397dd7cddfSDavid du Colombier 	return f+sizeof(vlong);
6407dd7cddfSDavid du Colombier }
6417dd7cddfSDavid du Colombier 
6427dd7cddfSDavid du Colombier static uchar*
vlong2be(uchar * t,vlong from)6437dd7cddfSDavid du Colombier vlong2be(uchar *t, vlong from)
6447dd7cddfSDavid du Colombier {
6457dd7cddfSDavid du Colombier 	uchar *f, *o;
6467dd7cddfSDavid du Colombier 	int i;
6477dd7cddfSDavid du Colombier 
6487dd7cddfSDavid du Colombier 	f = (uchar*)&from;
6497dd7cddfSDavid du Colombier 	o = (uchar*)&uvorder;
6507dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(vlong); i++)
6517dd7cddfSDavid du Colombier 		t[i] = f[o[i]];
6527dd7cddfSDavid du Colombier 	return t+sizeof(vlong);
6537dd7cddfSDavid du Colombier }
6547dd7cddfSDavid du Colombier 
6557dd7cddfSDavid du Colombier static long order = 0x00010203;
6567dd7cddfSDavid du Colombier 
6577dd7cddfSDavid du Colombier static uchar*
be2long(long * to,uchar * f)6587dd7cddfSDavid du Colombier be2long(long *to, uchar *f)
6597dd7cddfSDavid du Colombier {
6607dd7cddfSDavid du Colombier 	uchar *t, *o;
6617dd7cddfSDavid du Colombier 	int i;
6627dd7cddfSDavid du Colombier 
6637dd7cddfSDavid du Colombier 	t = (uchar*)to;
6647dd7cddfSDavid du Colombier 	o = (uchar*)&order;
6657dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(long); i++)
6667dd7cddfSDavid du Colombier 		t[o[i]] = f[i];
6677dd7cddfSDavid du Colombier 	return f+sizeof(long);
6687dd7cddfSDavid du Colombier }
6697dd7cddfSDavid du Colombier 
6707dd7cddfSDavid du Colombier static uchar*
long2be(uchar * t,long from)6717dd7cddfSDavid du Colombier long2be(uchar *t, long from)
6727dd7cddfSDavid du Colombier {
6737dd7cddfSDavid du Colombier 	uchar *f, *o;
6747dd7cddfSDavid du Colombier 	int i;
6757dd7cddfSDavid du Colombier 
6767dd7cddfSDavid du Colombier 	f = (uchar*)&from;
6777dd7cddfSDavid du Colombier 	o = (uchar*)&order;
6787dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(long); i++)
6797dd7cddfSDavid du Colombier 		t[i] = f[o[i]];
6807dd7cddfSDavid du Colombier 	return t+sizeof(long);
6817dd7cddfSDavid du Colombier }
6827dd7cddfSDavid du Colombier 
6833f9c8393SDavid du Colombier /*
6843f9c8393SDavid du Colombier  * read ticks and local time in nanoseconds
6853f9c8393SDavid du Colombier  */
6867dd7cddfSDavid du Colombier static int
gettime(vlong * nsec,uvlong * ticks,uvlong * hz)6877dd7cddfSDavid du Colombier gettime(vlong *nsec, uvlong *ticks, uvlong *hz)
6887dd7cddfSDavid du Colombier {
6897dd7cddfSDavid du Colombier 	int i, n;
6907dd7cddfSDavid du Colombier 	uchar ub[3*8], *p;
6917dd7cddfSDavid du Colombier 	char b[2*24+1];
6927dd7cddfSDavid du Colombier 
6937dd7cddfSDavid du Colombier 	switch(ifc){
6947dd7cddfSDavid du Colombier 	case Ibintime:
6957dd7cddfSDavid du Colombier 		n = sizeof(vlong);
6967dd7cddfSDavid du Colombier 		if(hz != nil)
6977dd7cddfSDavid du Colombier 			n = 3*sizeof(vlong);
6987dd7cddfSDavid du Colombier 		if(ticks != nil)
6997dd7cddfSDavid du Colombier 			n = 2*sizeof(vlong);
7007dd7cddfSDavid du Colombier 		i = read(bintimefd, ub, n);
7017dd7cddfSDavid du Colombier 		if(i != n)
7027dd7cddfSDavid du Colombier 			break;
7037dd7cddfSDavid du Colombier 		p = ub;
7047dd7cddfSDavid du Colombier 		if(nsec != nil)
7057dd7cddfSDavid du Colombier 			be2vlong(nsec, ub);
7067dd7cddfSDavid du Colombier 		p += sizeof(vlong);
7077dd7cddfSDavid du Colombier 		if(ticks != nil)
7087dd7cddfSDavid du Colombier 			be2vlong((vlong*)ticks, p);
7097dd7cddfSDavid du Colombier 		p += sizeof(vlong);
7107dd7cddfSDavid du Colombier 		if(hz != nil)
7117dd7cddfSDavid du Colombier 			be2vlong((vlong*)hz, p);
7127dd7cddfSDavid du Colombier 		return 0;
7137dd7cddfSDavid du Colombier 	case Itiming:
7147dd7cddfSDavid du Colombier 		n = sizeof(vlong);
7157dd7cddfSDavid du Colombier 		if(ticks != nil)
7167dd7cddfSDavid du Colombier 			n = 2*sizeof(vlong);
7177dd7cddfSDavid du Colombier 		i = read(timingfd, ub, n);
7187dd7cddfSDavid du Colombier 		if(i != n)
7197dd7cddfSDavid du Colombier 			break;
7207dd7cddfSDavid du Colombier 		p = ub;
7217dd7cddfSDavid du Colombier 		if(nsec != nil)
7227dd7cddfSDavid du Colombier 			be2vlong(nsec, ub);
7237dd7cddfSDavid du Colombier 		p += sizeof(vlong);
7247dd7cddfSDavid du Colombier 		if(ticks != nil)
7257dd7cddfSDavid du Colombier 			be2vlong((vlong*)ticks, p);
7267dd7cddfSDavid du Colombier 		if(hz != nil){
7277dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
7287dd7cddfSDavid du Colombier 			n = read(fastclockfd, b, sizeof(b)-1);
7297dd7cddfSDavid du Colombier 			if(n <= 0)
7307dd7cddfSDavid du Colombier 				break;
7317dd7cddfSDavid du Colombier 			b[n] = 0;
7327dd7cddfSDavid du Colombier 			*hz = strtoll(b+24, 0, 0);
7337dd7cddfSDavid du Colombier 		}
7347dd7cddfSDavid du Colombier 		return 0;
7357dd7cddfSDavid du Colombier 	case Insec:
7367dd7cddfSDavid du Colombier 		if(nsec != nil){
7377dd7cddfSDavid du Colombier 			seek(nsecfd, 0, 0);
7387dd7cddfSDavid du Colombier 			n = read(nsecfd, b, sizeof(b)-1);
7397dd7cddfSDavid du Colombier 			if(n <= 0)
7407dd7cddfSDavid du Colombier 				break;
7417dd7cddfSDavid du Colombier 			b[n] = 0;
7427dd7cddfSDavid du Colombier 			*nsec = strtoll(b, 0, 0);
7437dd7cddfSDavid du Colombier 		}
7447dd7cddfSDavid du Colombier 		if(ticks != nil){
7457dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
7467dd7cddfSDavid du Colombier 			n = read(fastclockfd, b, sizeof(b)-1);
7477dd7cddfSDavid du Colombier 			if(n <= 0)
7487dd7cddfSDavid du Colombier 				break;
7497dd7cddfSDavid du Colombier 			b[n] = 0;
7507dd7cddfSDavid du Colombier 			*ticks = strtoll(b, 0, 0);
7517dd7cddfSDavid du Colombier 		}
7527dd7cddfSDavid du Colombier 		if(hz != nil){
7537dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
7547dd7cddfSDavid du Colombier 			n = read(fastclockfd, b, sizeof(b)-1);
7557dd7cddfSDavid du Colombier 			if(n <= 24)
7567dd7cddfSDavid du Colombier 				break;
7577dd7cddfSDavid du Colombier 			b[n] = 0;
7587dd7cddfSDavid du Colombier 			*hz = strtoll(b+24, 0, 0);
7597dd7cddfSDavid du Colombier 		}
7607dd7cddfSDavid du Colombier 		return 0;
7617dd7cddfSDavid du Colombier 	}
7627dd7cddfSDavid du Colombier 	return -1;
7637dd7cddfSDavid du Colombier }
7647dd7cddfSDavid du Colombier 
7657dd7cddfSDavid du Colombier static void
settime(vlong now,uvlong hz,vlong delta,int n)7667dd7cddfSDavid du Colombier settime(vlong now, uvlong hz, vlong delta, int n)
7677dd7cddfSDavid du Colombier {
7687dd7cddfSDavid du Colombier 	uchar b[1+sizeof(vlong)+sizeof(long)], *p;
7697dd7cddfSDavid du Colombier 
7707dd7cddfSDavid du Colombier 	if(debug)
7713f9c8393SDavid du Colombier 		fprint(2, "settime(now=%lld, hz=%llud, delta=%lld, period=%d)\n",
7723f9c8393SDavid du Colombier 			now, hz, delta, n);
7737dd7cddfSDavid du Colombier 	if(impotent)
7747dd7cddfSDavid du Colombier 		return;
7757dd7cddfSDavid du Colombier 	switch(ifc){
7767dd7cddfSDavid du Colombier 	case Ibintime:
7777dd7cddfSDavid du Colombier 		if(now >= 0){
7787dd7cddfSDavid du Colombier 			p = b;
7797dd7cddfSDavid du Colombier 			*p++ = 'n';
7807dd7cddfSDavid du Colombier 			p = vlong2be(p, now);
7817dd7cddfSDavid du Colombier 			if(write(bintimefd, b, p-b) < 0)
7827dd7cddfSDavid du Colombier 				sysfatal("writing /dev/bintime: %r");
7837dd7cddfSDavid du Colombier 		}
7847dd7cddfSDavid du Colombier 		if(delta != 0){
7857dd7cddfSDavid du Colombier 			p = b;
7867dd7cddfSDavid du Colombier 			*p++ = 'd';
7877dd7cddfSDavid du Colombier 			p = vlong2be(p, delta);
7887dd7cddfSDavid du Colombier 			p = long2be(p, n);
7897dd7cddfSDavid du Colombier 			if(write(bintimefd, b, p-b) < 0)
7907dd7cddfSDavid du Colombier 				sysfatal("writing /dev/bintime: %r");
7917dd7cddfSDavid du Colombier 		}
7927dd7cddfSDavid du Colombier 		if(hz != 0){
7937dd7cddfSDavid du Colombier 			p = b;
7947dd7cddfSDavid du Colombier 			*p++ = 'f';
7957dd7cddfSDavid du Colombier 			p = vlong2be(p, hz);
7967dd7cddfSDavid du Colombier 			if(write(bintimefd, b, p-b) < 0)
7977dd7cddfSDavid du Colombier 				sysfatal("writing /dev/bintime: %r");
7987dd7cddfSDavid du Colombier 		}
7997dd7cddfSDavid du Colombier 		break;
8007dd7cddfSDavid du Colombier 	case Itiming:
8017dd7cddfSDavid du Colombier 	case Insec:
8027dd7cddfSDavid du Colombier 		seek(nsecfd, 0, 0);
8037dd7cddfSDavid du Colombier 		if(now >= 0 || delta != 0){
8047dd7cddfSDavid du Colombier 			if(fprint(nsecfd, "%lld %lld %d", now, delta, n) < 0)
8057dd7cddfSDavid du Colombier 				sysfatal("writing /dev/nsec: %r");
8067dd7cddfSDavid du Colombier 		}
8077dd7cddfSDavid du Colombier 		if(hz > 0){
8087dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
8097dd7cddfSDavid du Colombier 			if(fprint(fastclockfd, "%lld", hz) < 0)
8107dd7cddfSDavid du Colombier 				sysfatal("writing /dev/fastclock: %r");
8117dd7cddfSDavid du Colombier 		}
8127dd7cddfSDavid du Colombier 	}
8137dd7cddfSDavid du Colombier }
8147dd7cddfSDavid du Colombier 
8153f9c8393SDavid du Colombier /*
8163f9c8393SDavid du Colombier  *  set priority high and wire process to a processor
8173f9c8393SDavid du Colombier  */
8187dd7cddfSDavid du Colombier static void
setpriority(void)8197dd7cddfSDavid du Colombier setpriority(void)
8207dd7cddfSDavid du Colombier {
8217dd7cddfSDavid du Colombier 	int fd;
8227dd7cddfSDavid du Colombier 	char buf[32];
8237dd7cddfSDavid du Colombier 
8247dd7cddfSDavid du Colombier 	sprint(buf, "/proc/%d/ctl", getpid());
8257dd7cddfSDavid du Colombier 	fd = open(buf, ORDWR);
8267dd7cddfSDavid du Colombier 	if(fd < 0){
8277dd7cddfSDavid du Colombier 		fprint(2, "can't set priority\n");
8287dd7cddfSDavid du Colombier 		return;
8297dd7cddfSDavid du Colombier 	}
8307dd7cddfSDavid du Colombier 	if(fprint(fd, "pri 100") < 0)
8317dd7cddfSDavid du Colombier 		fprint(2, "can't set priority\n");
83280ee5cbfSDavid du Colombier 	if(fprint(fd, "wired 2") < 0)
83380ee5cbfSDavid du Colombier 		fprint(2, "can't wire process\n");
8347dd7cddfSDavid du Colombier 	close(fd);
8357dd7cddfSDavid du Colombier }
83680ee5cbfSDavid du Colombier 
8373f9c8393SDavid du Colombier /* convert to ntp timestamps */
83880ee5cbfSDavid du Colombier static void
hnputts(void * p,vlong nsec)83980ee5cbfSDavid du Colombier hnputts(void *p, vlong nsec)
84080ee5cbfSDavid du Colombier {
84180ee5cbfSDavid du Colombier 	uchar *a;
8423f9c8393SDavid du Colombier 	ulong tsh, tsl;
84380ee5cbfSDavid du Colombier 
84480ee5cbfSDavid du Colombier 	a = p;
84580ee5cbfSDavid du Colombier 
8463f9c8393SDavid du Colombier 	/* zero is a special case */
84780ee5cbfSDavid du Colombier 	if(nsec == 0)
84880ee5cbfSDavid du Colombier 		return;
84980ee5cbfSDavid du Colombier 
8503f9c8393SDavid du Colombier 	tsh = nsec/SEC;
85180ee5cbfSDavid du Colombier 	nsec -= tsh*SEC;
85280ee5cbfSDavid du Colombier 	tsl = (nsec<<32)/SEC;
85380ee5cbfSDavid du Colombier 	hnputl(a, tsh+EPOCHDIFF);
85480ee5cbfSDavid du Colombier 	hnputl(a+4, tsl);
85580ee5cbfSDavid du Colombier }
85680ee5cbfSDavid du Colombier 
8573f9c8393SDavid du Colombier /* convert from ntp timestamps */
85880ee5cbfSDavid du Colombier static vlong
nhgetts(void * p)85980ee5cbfSDavid du Colombier nhgetts(void *p)
86080ee5cbfSDavid du Colombier {
86180ee5cbfSDavid du Colombier 	uchar *a;
86280ee5cbfSDavid du Colombier 	ulong tsh, tsl;
86380ee5cbfSDavid du Colombier 	vlong nsec;
86480ee5cbfSDavid du Colombier 
86580ee5cbfSDavid du Colombier 	a = p;
86680ee5cbfSDavid du Colombier 	tsh = nhgetl(a);
86780ee5cbfSDavid du Colombier 	tsl = nhgetl(a+4);
86880ee5cbfSDavid du Colombier 	nsec = tsl*SEC;
86980ee5cbfSDavid du Colombier 	nsec >>= 32;
87080ee5cbfSDavid du Colombier 	nsec += (tsh - EPOCHDIFF)*SEC;
87180ee5cbfSDavid du Colombier 	return nsec;
87280ee5cbfSDavid du Colombier }
87380ee5cbfSDavid du Colombier 
8743f9c8393SDavid du Colombier /* convert to ntp 32 bit fixed point */
87580ee5cbfSDavid du Colombier static void
hnputfp(void * p,vlong nsec)87680ee5cbfSDavid du Colombier hnputfp(void *p, vlong nsec)
87780ee5cbfSDavid du Colombier {
87880ee5cbfSDavid du Colombier 	uchar *a;
87980ee5cbfSDavid du Colombier 	ulong fp;
88080ee5cbfSDavid du Colombier 
88180ee5cbfSDavid du Colombier 	a = p;
88280ee5cbfSDavid du Colombier 
88380ee5cbfSDavid du Colombier 	fp = nsec/(SEC/((vlong)(1<<16)));
88480ee5cbfSDavid du Colombier 	hnputl(a, fp);
88580ee5cbfSDavid du Colombier }
88680ee5cbfSDavid du Colombier 
8873f9c8393SDavid du Colombier /* convert from ntp fixed point to nanosecs */
88880ee5cbfSDavid du Colombier static vlong
nhgetfp(void * p)88980ee5cbfSDavid du Colombier nhgetfp(void *p)
89080ee5cbfSDavid du Colombier {
89180ee5cbfSDavid du Colombier 	uchar *a;
89280ee5cbfSDavid du Colombier 	ulong fp;
89380ee5cbfSDavid du Colombier 	vlong nsec;
89480ee5cbfSDavid du Colombier 
89580ee5cbfSDavid du Colombier 	a = p;
89680ee5cbfSDavid du Colombier 	fp = nhgetl(a);
89780ee5cbfSDavid du Colombier 	nsec = ((vlong)fp)*(SEC/((vlong)(1<<16)));
89880ee5cbfSDavid du Colombier 	return nsec;
89980ee5cbfSDavid du Colombier }
90080ee5cbfSDavid du Colombier 
9013f9c8393SDavid du Colombier /* get network address of the server */
90280ee5cbfSDavid du Colombier static void
setrootid(char * d)90380ee5cbfSDavid du Colombier setrootid(char *d)
90480ee5cbfSDavid du Colombier {
90580ee5cbfSDavid du Colombier 	char buf[128];
90680ee5cbfSDavid du Colombier 	int fd, n;
90780ee5cbfSDavid du Colombier 	char *p;
90880ee5cbfSDavid du Colombier 
9093f9c8393SDavid du Colombier 	snprint(buf, sizeof buf, "%s/remote", d);
91080ee5cbfSDavid du Colombier 	fd = open(buf, OREAD);
91180ee5cbfSDavid du Colombier 	if(fd < 0)
91280ee5cbfSDavid du Colombier 		return;
91380ee5cbfSDavid du Colombier 	n = read(fd, buf, sizeof buf);
91480ee5cbfSDavid du Colombier 	close(fd);
91580ee5cbfSDavid du Colombier 	if(n <= 0)
91680ee5cbfSDavid du Colombier 		return;
91780ee5cbfSDavid du Colombier 	p = strchr(buf, '!');
91880ee5cbfSDavid du Colombier 	if(p != nil)
91980ee5cbfSDavid du Colombier 		*p = 0;
92080ee5cbfSDavid du Colombier 	v4parseip(rootid, buf);
92180ee5cbfSDavid du Colombier }
92280ee5cbfSDavid du Colombier 
92380ee5cbfSDavid du Colombier static void
ding(void *,char * s)92480ee5cbfSDavid du Colombier ding(void*, char *s)
92580ee5cbfSDavid du Colombier {
92680ee5cbfSDavid du Colombier 	if(strstr(s, "alarm") != nil)
92780ee5cbfSDavid du Colombier 		noted(NCONT);
92880ee5cbfSDavid du Colombier 	noted(NDFLT);
92980ee5cbfSDavid du Colombier }
93080ee5cbfSDavid du Colombier 
93180ee5cbfSDavid du Colombier static void
addntpserver(char * name)93280ee5cbfSDavid du Colombier addntpserver(char *name)
93380ee5cbfSDavid du Colombier {
93480ee5cbfSDavid du Colombier 	NTPserver *ns, **l;
93580ee5cbfSDavid du Colombier 
93680ee5cbfSDavid du Colombier 	ns = mallocz(sizeof(NTPserver), 1);
93780ee5cbfSDavid du Colombier 	if(ns == nil)
93880ee5cbfSDavid du Colombier 		sysfatal("addntpserver: %r");
9399a747e4fSDavid du Colombier 	timeserver = strdup(name);
94080ee5cbfSDavid du Colombier 	ns->name = name;
94180ee5cbfSDavid du Colombier 	for(l = &ntpservers; *l != nil; l = &(*l)->next)
94280ee5cbfSDavid du Colombier 		;
94380ee5cbfSDavid du Colombier 	*l = ns;
94480ee5cbfSDavid du Colombier }
94580ee5cbfSDavid du Colombier 
9463f9c8393SDavid du Colombier /*
9473f9c8393SDavid du Colombier  *  sntp client, we keep calling if the delay seems
9483f9c8393SDavid du Colombier  *  unusually high, i.e., 30% longer than avg.
9493f9c8393SDavid du Colombier  */
95080ee5cbfSDavid du Colombier static int
ntptimediff(NTPserver * ns)95180ee5cbfSDavid du Colombier ntptimediff(NTPserver *ns)
95280ee5cbfSDavid du Colombier {
95380ee5cbfSDavid du Colombier 	int fd, tries, n;
95480ee5cbfSDavid du Colombier 	NTPpkt ntpin, ntpout;
95580ee5cbfSDavid du Colombier 	vlong dt, recvts, origts, xmitts, destts, x;
95680ee5cbfSDavid du Colombier 	char dir[64];
95713fec586SDavid du Colombier 	static int whined;
95880ee5cbfSDavid du Colombier 
9593f9c8393SDavid du Colombier 	notify(ding);
9603f9c8393SDavid du Colombier 	alarm(30*1000);	/* don't wait forever if ns->name is unreachable */
9619a747e4fSDavid du Colombier 	fd = dial(netmkaddr(ns->name, "udp", "ntp"), 0, dir, 0);
962*66d98691SDavid du Colombier 	alarm(0);
96380ee5cbfSDavid du Colombier 	if(fd < 0){
96413fec586SDavid du Colombier 		if (!whined++)
96580ee5cbfSDavid du Colombier 			syslog(0, logfile, "can't reach %s: %r", ns->name);
96680ee5cbfSDavid du Colombier 		return -1;
96780ee5cbfSDavid du Colombier 	}
96880ee5cbfSDavid du Colombier 	setrootid(dir);
96980ee5cbfSDavid du Colombier 
97080ee5cbfSDavid du Colombier 	memset(&ntpout, 0, sizeof(ntpout));
97180ee5cbfSDavid du Colombier 	ntpout.mode = 3 | (3 << 3);
97280ee5cbfSDavid du Colombier 
97380ee5cbfSDavid du Colombier 	for(tries = 0; tries < 3; tries++){
97480ee5cbfSDavid du Colombier 		alarm(2*1000);
97580ee5cbfSDavid du Colombier 
97680ee5cbfSDavid du Colombier 		gettime(&x, 0, 0);
97780ee5cbfSDavid du Colombier 		hnputts(ntpout.xmitts, x);
97880ee5cbfSDavid du Colombier 		if(write(fd, &ntpout, NTPSIZE) < 0){
97980ee5cbfSDavid du Colombier 			alarm(0);
98080ee5cbfSDavid du Colombier 			continue;
98180ee5cbfSDavid du Colombier 		}
98280ee5cbfSDavid du Colombier 
9833f9c8393SDavid du Colombier 		n = read(fd, &ntpin, sizeof ntpin);
98480ee5cbfSDavid du Colombier 		alarm(0);
98580ee5cbfSDavid du Colombier 		gettime(&destts, 0, 0);
98680ee5cbfSDavid du Colombier 		if(n >= NTPSIZE){
98780ee5cbfSDavid du Colombier 			close(fd);
98880ee5cbfSDavid du Colombier 
9893f9c8393SDavid du Colombier 			/* we got one, use it */
99080ee5cbfSDavid du Colombier 			recvts = nhgetts(ntpin.recvts);
99180ee5cbfSDavid du Colombier 			origts = nhgetts(ntpin.origts);
99280ee5cbfSDavid du Colombier 			xmitts = nhgetts(ntpin.xmitts);
99380ee5cbfSDavid du Colombier 			dt = ((recvts - origts) + (xmitts - destts))/2;
99480ee5cbfSDavid du Colombier 
9953f9c8393SDavid du Colombier 			/* save results */
99680ee5cbfSDavid du Colombier 			ns->rtt = ((destts - origts) - (xmitts - recvts))/2;
99780ee5cbfSDavid du Colombier 			ns->dt = dt;
99880ee5cbfSDavid du Colombier 			ns->stratum = ntpin.stratum;
99980ee5cbfSDavid du Colombier 			ns->precision = ntpin.precision;
100080ee5cbfSDavid du Colombier 			ns->rootdelay = nhgetfp(ntpin.rootdelay);
100180ee5cbfSDavid du Colombier 			ns->rootdisp = nhgetfp(ntpin.rootdisp);
100280ee5cbfSDavid du Colombier 
100380ee5cbfSDavid du Colombier 			if(debug)
100480ee5cbfSDavid du Colombier 				fprint(2, "ntp %s stratum %d ntpdelay(%lld)\n",
100580ee5cbfSDavid du Colombier 					ns->name, ntpin.stratum, ns->rtt);
100680ee5cbfSDavid du Colombier 			return 0;
100780ee5cbfSDavid du Colombier 		}
100880ee5cbfSDavid du Colombier 
10093f9c8393SDavid du Colombier 		/* try again */
101080ee5cbfSDavid du Colombier 		sleep(250);
101180ee5cbfSDavid du Colombier 	}
101280ee5cbfSDavid du Colombier 	close(fd);
101380ee5cbfSDavid du Colombier 	return -1;
101480ee5cbfSDavid du Colombier }
101580ee5cbfSDavid du Colombier 
101680ee5cbfSDavid du Colombier static vlong
gpssample(void)10177c881178SDavid du Colombier gpssample(void)
10187c881178SDavid du Colombier {
10197c881178SDavid du Colombier 	vlong	l, g, d;
10207c881178SDavid du Colombier 	int	i, n;
10217c881178SDavid du Colombier 	char	*v[4], buf[128];
10227c881178SDavid du Colombier 
10237c881178SDavid du Colombier 	d = -1000000000000000000LL;
10247c881178SDavid du Colombier 	for(i = 0; i < 5; i++){
10257c881178SDavid du Colombier 		sleep(1100);
10267c881178SDavid du Colombier 		seek(gpsfil, 0, 0);
10277c881178SDavid du Colombier 		n = read(gpsfil, buf, sizeof buf - 1);
10287c881178SDavid du Colombier 		if (n <= 0)
10293f9c8393SDavid du Colombier 			return 0;
10307c881178SDavid du Colombier 		buf[n] = 0;
10317c881178SDavid du Colombier 		n = tokenize(buf, v, nelem(v));
10323f9c8393SDavid du Colombier 		if(n != 4 || strcmp(v[3], "A") != 0)
10333f9c8393SDavid du Colombier 			return 0;
10347c881178SDavid du Colombier 		g = atoll(v[1]);
10357c881178SDavid du Colombier 		l = atoll(v[2]);
10367c881178SDavid du Colombier 		if(g-l > d)
10377c881178SDavid du Colombier 			d = g-l;
10387c881178SDavid du Colombier 	}
10393f9c8393SDavid du Colombier 	return d;
10407c881178SDavid du Colombier }
10417c881178SDavid du Colombier 
10427c881178SDavid du Colombier static vlong
ntpsample(void)104380ee5cbfSDavid du Colombier ntpsample(void)
104480ee5cbfSDavid du Colombier {
104580ee5cbfSDavid du Colombier 	NTPserver *tns, *ns;
104680ee5cbfSDavid du Colombier 	vlong metric, x;
104780ee5cbfSDavid du Colombier 
104880ee5cbfSDavid du Colombier 	metric = 1000LL*SEC;
104980ee5cbfSDavid du Colombier 	ns = nil;
105080ee5cbfSDavid du Colombier 	for(tns = ntpservers; tns != nil; tns = tns->next){
105180ee5cbfSDavid du Colombier 		if(ntptimediff(tns) < 0)
105280ee5cbfSDavid du Colombier 			continue;
105367031067SDavid du Colombier 		x = vabs(tns->rootdisp) + (vabs(tns->rtt+tns->rootdelay)>>1);
105495fdf19cSDavid du Colombier 		if(debug)
105595fdf19cSDavid du Colombier 			fprint(2, "ntp %s rootdelay %lld rootdisp %lld metric %lld\n",
105695fdf19cSDavid du Colombier 				tns->name, tns->rootdelay, tns->rootdisp, x);
105780ee5cbfSDavid du Colombier 		if(x < metric){
105880ee5cbfSDavid du Colombier 			metric = x;
105980ee5cbfSDavid du Colombier 			ns = tns;
106080ee5cbfSDavid du Colombier 		}
106180ee5cbfSDavid du Colombier 	}
106280ee5cbfSDavid du Colombier 
106380ee5cbfSDavid du Colombier 	if(ns == nil)
10643f9c8393SDavid du Colombier 		return 0;
106580ee5cbfSDavid du Colombier 
10663f9c8393SDavid du Colombier 	/* save data for our server */
106780ee5cbfSDavid du Colombier 	rootdisp = ns->rootdisp;
106867031067SDavid du Colombier 	rootdelay = ns->rootdelay;
106980ee5cbfSDavid du Colombier 	mydelay = ns->rtt;
107067031067SDavid du Colombier 	mydisp = avgerr;
107180ee5cbfSDavid du Colombier 	if(ns->stratum == 0)
107280ee5cbfSDavid du Colombier 		stratum = 0;
107380ee5cbfSDavid du Colombier 	else
107480ee5cbfSDavid du Colombier 		stratum = ns->stratum + 1;
107580ee5cbfSDavid du Colombier 
1076ed250ae1SDavid du Colombier 	ε = abs(ns->rtt/2);
107780ee5cbfSDavid du Colombier 	return ns->dt;
107880ee5cbfSDavid du Colombier }
107980ee5cbfSDavid du Colombier 
10803f9c8393SDavid du Colombier /*
10813f9c8393SDavid du Colombier  * sample the utc file
10823f9c8393SDavid du Colombier  */
10835fab9909SDavid du Colombier static vlong
utcsample(void)10845fab9909SDavid du Colombier utcsample(void)
10855fab9909SDavid du Colombier {
10865fab9909SDavid du Colombier 	vlong	s;
10875fab9909SDavid du Colombier 	int	n;
10885fab9909SDavid du Colombier 	char	*v[2], buf[128];
10895fab9909SDavid du Colombier 
10905fab9909SDavid du Colombier 	s = 0;
10915fab9909SDavid du Colombier 	seek(utcfil, 0, 0);
10925fab9909SDavid du Colombier 	n = read(utcfil, buf, sizeof buf - 1);
10935fab9909SDavid du Colombier 	if (n <= 0)
10943f9c8393SDavid du Colombier 		return 0;
10955fab9909SDavid du Colombier 	buf[n] = 0;
10965fab9909SDavid du Colombier 	n = tokenize(buf, v, nelem(v));
10975fab9909SDavid du Colombier 	if (strcmp(v[0], "0") == 0)
10983f9c8393SDavid du Colombier 		return 0;
10995fab9909SDavid du Colombier 	if (n == 2) {
11005fab9909SDavid du Colombier 		gettime(&s, nil, nil);
11015fab9909SDavid du Colombier 		s -= atoll(v[1]);
11025fab9909SDavid du Colombier 	}
11031118d624SDavid du Colombier 	lastutc = atoll(v[0]) + s;
11043f9c8393SDavid du Colombier 	return lastutc;
11055fab9909SDavid du Colombier }
11065fab9909SDavid du Colombier 
11073f9c8393SDavid du Colombier /*
11083f9c8393SDavid du Colombier  *  sntp server
11093f9c8393SDavid du Colombier  */
111080ee5cbfSDavid du Colombier static int
openlisten(char * net)111180ee5cbfSDavid du Colombier openlisten(char *net)
111280ee5cbfSDavid du Colombier {
111380ee5cbfSDavid du Colombier 	int fd, cfd;
11143f9c8393SDavid du Colombier 	char data[128], devdir[40];
111580ee5cbfSDavid du Colombier 
111680ee5cbfSDavid du Colombier 	sprint(data, "%s/udp!*!ntp", net);
111780ee5cbfSDavid du Colombier 	cfd = announce(data, devdir);
111880ee5cbfSDavid du Colombier 	if(cfd < 0)
111980ee5cbfSDavid du Colombier 		sysfatal("can't announce");
112080ee5cbfSDavid du Colombier 	if(fprint(cfd, "headers") < 0)
112180ee5cbfSDavid du Colombier 		sysfatal("can't set header mode");
112280ee5cbfSDavid du Colombier 
112380ee5cbfSDavid du Colombier 	sprint(data, "%s/data", devdir);
112480ee5cbfSDavid du Colombier 	fd = open(data, ORDWR);
112580ee5cbfSDavid du Colombier 	if(fd < 0)
1126f27a9a5aSDavid du Colombier 		sysfatal("open %s: %r", data);
112780ee5cbfSDavid du Colombier 	return fd;
112880ee5cbfSDavid du Colombier }
112942dedc50SDavid du Colombier 
113080ee5cbfSDavid du Colombier static void
ntpserver(char * servenet)11319a747e4fSDavid du Colombier ntpserver(char *servenet)
113280ee5cbfSDavid du Colombier {
11333f9c8393SDavid du Colombier 	int fd, n, vers, mode;
113480ee5cbfSDavid du Colombier 	vlong recvts, x;
11353f9c8393SDavid du Colombier 	char buf[512];
11363f9c8393SDavid du Colombier 	NTPpkt *ntp;
113780ee5cbfSDavid du Colombier 
11389a747e4fSDavid du Colombier 	fd = openlisten(servenet);
113980ee5cbfSDavid du Colombier 
11405fab9909SDavid du Colombier 	if (Rootid == nil)
114180ee5cbfSDavid du Colombier 		switch(type){
114280ee5cbfSDavid du Colombier 		case Fs:
11435fab9909SDavid du Colombier 			Rootid = "WWV";
114480ee5cbfSDavid du Colombier 			break;
114580ee5cbfSDavid du Colombier 		case Rtc:
11465fab9909SDavid du Colombier 			Rootid = "LOCL";
11475fab9909SDavid du Colombier 			break;
11485fab9909SDavid du Colombier 		case Utc:
11495fab9909SDavid du Colombier 			Rootid = "UTC";
115080ee5cbfSDavid du Colombier 			break;
115142dedc50SDavid du Colombier 		case Gps:
115242dedc50SDavid du Colombier 			Rootid = "GPS";
115342dedc50SDavid du Colombier 			break;
115480ee5cbfSDavid du Colombier 		case Ntp:
115580ee5cbfSDavid du Colombier 			/* set by the ntp client */
115680ee5cbfSDavid du Colombier 			break;
115780ee5cbfSDavid du Colombier 		}
11585fab9909SDavid du Colombier 	if (Rootid != nil)
11595fab9909SDavid du Colombier 		memmove(rootid, Rootid, strlen(Rootid) > 4? 4: strlen(Rootid));
116080ee5cbfSDavid du Colombier 
116180ee5cbfSDavid du Colombier 	for(;;){
11623f9c8393SDavid du Colombier 		n = read(fd, buf, sizeof buf);
116380ee5cbfSDavid du Colombier 		gettime(&recvts, 0, 0);
116442dedc50SDavid du Colombier 		if(n <= 0) {
116542dedc50SDavid du Colombier 			/* don't croak on input error, but don't spin either */
116642dedc50SDavid du Colombier 			sleep(500);
116742dedc50SDavid du Colombier 			continue;
116842dedc50SDavid du Colombier 		}
1169f27a9a5aSDavid du Colombier 		if(n < Udphdrsize + NTPSIZE)
117080ee5cbfSDavid du Colombier 			continue;
117180ee5cbfSDavid du Colombier 
1172f27a9a5aSDavid du Colombier 		ntp = (NTPpkt*)(buf + Udphdrsize);
117380ee5cbfSDavid du Colombier 		mode = ntp->mode & 7;
117480ee5cbfSDavid du Colombier 		vers = (ntp->mode>>3) & 7;
117580ee5cbfSDavid du Colombier 		if(mode != 3)
117680ee5cbfSDavid du Colombier 			continue;
117780ee5cbfSDavid du Colombier 
117880ee5cbfSDavid du Colombier 		ntp->mode = (vers<<3)|4;
117980ee5cbfSDavid du Colombier 		ntp->stratum = stratum;
118067031067SDavid du Colombier 		ntp->precision = myprec;
118180ee5cbfSDavid du Colombier 		hnputfp(ntp->rootdelay, rootdelay + mydelay);
118280ee5cbfSDavid du Colombier 		hnputfp(ntp->rootdisp, rootdisp + mydisp);
11831118d624SDavid du Colombier 		hnputts(ntp->refts, lastutc);
118480ee5cbfSDavid du Colombier 		memmove(ntp->origts, ntp->xmitts, sizeof(ntp->origts));
118580ee5cbfSDavid du Colombier 		hnputts(ntp->recvts, recvts);
118680ee5cbfSDavid du Colombier 		memmove(ntp->rootid, rootid, sizeof(ntp->rootid));
118780ee5cbfSDavid du Colombier 		gettime(&x, 0, 0);
118880ee5cbfSDavid du Colombier 		hnputts(ntp->xmitts, x);
1189f27a9a5aSDavid du Colombier 		write(fd, buf, NTPSIZE + Udphdrsize);
119080ee5cbfSDavid du Colombier 	}
119180ee5cbfSDavid du Colombier }
119280ee5cbfSDavid du Colombier 
11933f9c8393SDavid du Colombier /*
11943f9c8393SDavid du Colombier  *  get the current time from the file system
11953f9c8393SDavid du Colombier  */
119680ee5cbfSDavid du Colombier static long
fstime(void)119780ee5cbfSDavid du Colombier fstime(void)
119880ee5cbfSDavid du Colombier {
11999a747e4fSDavid du Colombier 	Dir *d;
12009a747e4fSDavid du Colombier 	ulong t;
120180ee5cbfSDavid du Colombier 
12029a747e4fSDavid du Colombier 	d = dirstat("/n/boot");
12039a747e4fSDavid du Colombier 	if(d != nil){
12049a747e4fSDavid du Colombier 		t = d->atime;
12059a747e4fSDavid du Colombier 		free(d);
12069a747e4fSDavid du Colombier 	} else
12079a747e4fSDavid du Colombier 		t = 0;
12089a747e4fSDavid du Colombier 	return t;
120980ee5cbfSDavid du Colombier }
121080ee5cbfSDavid du Colombier 
12113f9c8393SDavid du Colombier /*
12123f9c8393SDavid du Colombier  *  get the current time from the real time clock
12133f9c8393SDavid du Colombier  */
121480ee5cbfSDavid du Colombier static long
rtctime(void)121580ee5cbfSDavid du Colombier rtctime(void)
121680ee5cbfSDavid du Colombier {
121780ee5cbfSDavid du Colombier 	char b[20];
121880ee5cbfSDavid du Colombier 	static int f = -1;
121980ee5cbfSDavid du Colombier 	int i, retries;
122080ee5cbfSDavid du Colombier 
122180ee5cbfSDavid du Colombier 	memset(b, 0, sizeof(b));
122280ee5cbfSDavid du Colombier 	for(retries = 0; retries < 100; retries++){
122380ee5cbfSDavid du Colombier 		if(f < 0)
122480ee5cbfSDavid du Colombier 			f = open("/dev/rtc", OREAD|OCEXEC);
122580ee5cbfSDavid du Colombier 		if(f < 0)
122680ee5cbfSDavid du Colombier 			break;
12273f9c8393SDavid du Colombier 		if(seek(f, 0, 0) < 0 || (i = read(f, b, sizeof b)) < 0){
122880ee5cbfSDavid du Colombier 			close(f);
122980ee5cbfSDavid du Colombier 			f = -1;
12303f9c8393SDavid du Colombier 		} else
123180ee5cbfSDavid du Colombier 			if(i != 0)
123280ee5cbfSDavid du Colombier 				break;
123380ee5cbfSDavid du Colombier 	}
123480ee5cbfSDavid du Colombier 	return strtoul(b, 0, 10)+gmtdelta;
123580ee5cbfSDavid du Colombier }
123680ee5cbfSDavid du Colombier 
123780ee5cbfSDavid du Colombier 
12383f9c8393SDavid du Colombier /*
12393f9c8393SDavid du Colombier  *  Sample a clock.  We wait for the clock to always
12403f9c8393SDavid du Colombier  *  be at the leading edge of a clock period.
12413f9c8393SDavid du Colombier  */
124280ee5cbfSDavid du Colombier static vlong
sample(long (* get)(void))124380ee5cbfSDavid du Colombier sample(long (*get)(void))
124480ee5cbfSDavid du Colombier {
124580ee5cbfSDavid du Colombier 	long this, last;
124680ee5cbfSDavid du Colombier 	vlong start, end;
124780ee5cbfSDavid du Colombier 
124880ee5cbfSDavid du Colombier 	/*
124980ee5cbfSDavid du Colombier 	 *  wait for the second to change
125080ee5cbfSDavid du Colombier 	 */
125180ee5cbfSDavid du Colombier 	last = (*get)();
125280ee5cbfSDavid du Colombier 	for(;;){
125380ee5cbfSDavid du Colombier 		gettime(&start, 0, 0);
125480ee5cbfSDavid du Colombier 		this = (*get)();
125580ee5cbfSDavid du Colombier 		gettime(&end, 0, 0);
125680ee5cbfSDavid du Colombier 		if(this != last)
125780ee5cbfSDavid du Colombier 			break;
125880ee5cbfSDavid du Colombier 		last = this;
125980ee5cbfSDavid du Colombier 	}
126080ee5cbfSDavid du Colombier 	return SEC*this - (end-start)/2;
126180ee5cbfSDavid du Colombier }
126280ee5cbfSDavid du Colombier 
12633f9c8393SDavid du Colombier /*
12643f9c8393SDavid du Colombier  * the name of the frequency file has the method and possibly the
12653f9c8393SDavid du Colombier  * server name encoded in it.
12663f9c8393SDavid du Colombier  */
126780ee5cbfSDavid du Colombier static int
openfreqfile(void)126880ee5cbfSDavid du Colombier openfreqfile(void)
126980ee5cbfSDavid du Colombier {
1270b9b1ded9SDavid du Colombier 	char *p;
127180ee5cbfSDavid du Colombier 	int fd;
127280ee5cbfSDavid du Colombier 
127380ee5cbfSDavid du Colombier 	if(sysid == nil)
127480ee5cbfSDavid du Colombier 		return -1;
127580ee5cbfSDavid du Colombier 
127680ee5cbfSDavid du Colombier 	switch(type){
127780ee5cbfSDavid du Colombier 	case Ntp:
1278b9b1ded9SDavid du Colombier 		p = smprint("%s/ts.%s.%d.%s", dir, sysid, type, timeserver);
127980ee5cbfSDavid du Colombier 		break;
128080ee5cbfSDavid du Colombier 	default:
1281b9b1ded9SDavid du Colombier 		p = smprint("%s/ts.%s.%d", dir, sysid, type);
128280ee5cbfSDavid du Colombier 		break;
128380ee5cbfSDavid du Colombier 	}
1284b9b1ded9SDavid du Colombier 	fd = open(p, ORDWR);
128580ee5cbfSDavid du Colombier 	if(fd < 0)
1286b9b1ded9SDavid du Colombier 		fd = create(p, ORDWR, 0666);
1287b9b1ded9SDavid du Colombier 	free(p);
128880ee5cbfSDavid du Colombier 	if(fd < 0)
128980ee5cbfSDavid du Colombier 		return -1;
129080ee5cbfSDavid du Colombier 	return fd;
129180ee5cbfSDavid du Colombier }
129280ee5cbfSDavid du Colombier 
12933f9c8393SDavid du Colombier /*
12943f9c8393SDavid du Colombier  *  the file contains the last known frequency and the
12953f9c8393SDavid du Colombier  *  number of seconds it was sampled over
12963f9c8393SDavid du Colombier  */
129780ee5cbfSDavid du Colombier static vlong
readfreqfile(int fd,vlong ohz,vlong minhz,vlong maxhz)129880ee5cbfSDavid du Colombier readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz)
129980ee5cbfSDavid du Colombier {
130080ee5cbfSDavid du Colombier 	int n;
130180ee5cbfSDavid du Colombier 	char buf[128];
130280ee5cbfSDavid du Colombier 	vlong hz;
130380ee5cbfSDavid du Colombier 
13043f9c8393SDavid du Colombier 	n = read(fd, buf, sizeof buf-1);
130580ee5cbfSDavid du Colombier 	if(n <= 0)
130680ee5cbfSDavid du Colombier 		return ohz;
130780ee5cbfSDavid du Colombier 	buf[n] = 0;
130880ee5cbfSDavid du Colombier 	hz = strtoll(buf, nil, 0);
130980ee5cbfSDavid du Colombier 
131080ee5cbfSDavid du Colombier 	if(hz > maxhz || hz < minhz)
131180ee5cbfSDavid du Colombier 		return ohz;
131280ee5cbfSDavid du Colombier 
131380ee5cbfSDavid du Colombier 	settime(-1, hz, 0, 0);
131480ee5cbfSDavid du Colombier 	return hz;
131580ee5cbfSDavid du Colombier }
131680ee5cbfSDavid du Colombier 
13173f9c8393SDavid du Colombier /*
13183f9c8393SDavid du Colombier  *  remember hz and averaging period
13193f9c8393SDavid du Colombier  */
132080ee5cbfSDavid du Colombier static void
writefreqfile(int fd,vlong hz,int secs,vlong diff)132180ee5cbfSDavid du Colombier writefreqfile(int fd, vlong hz, int secs, vlong diff)
132280ee5cbfSDavid du Colombier {
132380ee5cbfSDavid du Colombier 	long now;
132480ee5cbfSDavid du Colombier 	static long last;
132580ee5cbfSDavid du Colombier 
132680ee5cbfSDavid du Colombier 	if(fd < 0)
132780ee5cbfSDavid du Colombier 		return;
132880ee5cbfSDavid du Colombier 	now = time(0);
13299a747e4fSDavid du Colombier 	if(now - last < 10*60)
133080ee5cbfSDavid du Colombier 		return;
133180ee5cbfSDavid du Colombier 	last = now;
133280ee5cbfSDavid du Colombier 	if(seek(fd, 0, 0) < 0)
133380ee5cbfSDavid du Colombier 		return;
133480ee5cbfSDavid du Colombier 	fprint(fd, "%lld %d %d %lld\n", hz, secs, type, diff);
133580ee5cbfSDavid du Colombier }
133680ee5cbfSDavid du Colombier 
133780ee5cbfSDavid du Colombier static uvlong
vabs(vlong x)133880ee5cbfSDavid du Colombier vabs(vlong x)
133980ee5cbfSDavid du Colombier {
13403f9c8393SDavid du Colombier 	if(x < 0)
13413f9c8393SDavid du Colombier 		return -x;
134280ee5cbfSDavid du Colombier 	else
13433f9c8393SDavid du Colombier 		return x;
134480ee5cbfSDavid du Colombier }
134595fdf19cSDavid du Colombier 
134695fdf19cSDavid du Colombier static void
background(void)134795fdf19cSDavid du Colombier background(void)
134895fdf19cSDavid du Colombier {
134995fdf19cSDavid du Colombier 	static int inbackground;
135095fdf19cSDavid du Colombier 
135195fdf19cSDavid du Colombier 	if(inbackground)
135295fdf19cSDavid du Colombier 		return;
135395fdf19cSDavid du Colombier 
13543f9c8393SDavid du Colombier 	if(!debug)
135595fdf19cSDavid du Colombier 		switch(rfork(RFPROC|RFFDG|RFNAMEG|RFNOTEG|RFNOWAIT)){
135695fdf19cSDavid du Colombier 		case -1:
135795fdf19cSDavid du Colombier 			sysfatal("forking: %r");
135895fdf19cSDavid du Colombier 			break;
135995fdf19cSDavid du Colombier 		case 0:
136095fdf19cSDavid du Colombier 			break;
136195fdf19cSDavid du Colombier 		default:
136295fdf19cSDavid du Colombier 			exits(0);
136395fdf19cSDavid du Colombier 		}
136495fdf19cSDavid du Colombier 	inbackground = 1;
136595fdf19cSDavid du Colombier }
136667031067SDavid du Colombier 
136767031067SDavid du Colombier static int
getclockprecision(vlong hz)136867031067SDavid du Colombier getclockprecision(vlong hz)
136967031067SDavid du Colombier {
137067031067SDavid du Colombier 	int i;
137167031067SDavid du Colombier 
137267031067SDavid du Colombier 	i = 8;
137367031067SDavid du Colombier 	while(hz > 0){
137467031067SDavid du Colombier 		i--;
137567031067SDavid du Colombier 		hz >>= 1;
137667031067SDavid du Colombier 	}
137767031067SDavid du Colombier 	return i;
137867031067SDavid du Colombier }
1379