xref: /plan9/sys/src/cmd/aux/timesync.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1*7dd7cddfSDavid du Colombier #include <u.h>
2*7dd7cddfSDavid du Colombier #include <libc.h>
3*7dd7cddfSDavid du Colombier #include <ip.h>
4*7dd7cddfSDavid du Colombier 
5*7dd7cddfSDavid du Colombier 
6*7dd7cddfSDavid du Colombier #define SEC 1000000000LL
7*7dd7cddfSDavid du Colombier #define MAXSECS 5*60
8*7dd7cddfSDavid du Colombier double gain = .25;
9*7dd7cddfSDavid du Colombier 
10*7dd7cddfSDavid du Colombier enum {
11*7dd7cddfSDavid du Colombier 	Fs,
12*7dd7cddfSDavid du Colombier 	Rtc,
13*7dd7cddfSDavid du Colombier 	Ntp,
14*7dd7cddfSDavid du Colombier };
15*7dd7cddfSDavid du Colombier 
16*7dd7cddfSDavid du Colombier 
17*7dd7cddfSDavid du Colombier char *dir = "/tmp";	// directory sample files live in
18*7dd7cddfSDavid du Colombier char *logfile = "timesync";
19*7dd7cddfSDavid du Colombier char *timeserver;
20*7dd7cddfSDavid du Colombier int debug;
21*7dd7cddfSDavid du Colombier int impotent;
22*7dd7cddfSDavid du Colombier int logging;
23*7dd7cddfSDavid du Colombier int type;
24*7dd7cddfSDavid du Colombier int gmtdelta;	// rtc+gmtdelta = gmt
25*7dd7cddfSDavid du Colombier 
26*7dd7cddfSDavid du Colombier // ntp server info
27*7dd7cddfSDavid du Colombier int stratum = 14;
28*7dd7cddfSDavid du Colombier int mydisp, rootdisp;
29*7dd7cddfSDavid du Colombier int mydelay, rootdelay;
30*7dd7cddfSDavid du Colombier vlong avgdelay;
31*7dd7cddfSDavid du Colombier uchar rootid[4];
32*7dd7cddfSDavid du Colombier char *sysid;
33*7dd7cddfSDavid du Colombier 
34*7dd7cddfSDavid du Colombier // list of time samples
35*7dd7cddfSDavid du Colombier typedef struct Sample Sample;
36*7dd7cddfSDavid du Colombier struct Sample
37*7dd7cddfSDavid du Colombier {
38*7dd7cddfSDavid du Colombier 	Sample	*next;
39*7dd7cddfSDavid du Colombier 	uvlong	ticks;
40*7dd7cddfSDavid du Colombier 	vlong	ltime;
41*7dd7cddfSDavid du Colombier 	vlong	stime;
42*7dd7cddfSDavid du Colombier };
43*7dd7cddfSDavid du Colombier 
44*7dd7cddfSDavid du Colombier // ntp packet
45*7dd7cddfSDavid du Colombier typedef struct NTPpkt NTPpkt;
46*7dd7cddfSDavid du Colombier struct NTPpkt
47*7dd7cddfSDavid du Colombier {
48*7dd7cddfSDavid du Colombier 	uchar	mode;
49*7dd7cddfSDavid du Colombier 	uchar	stratum;
50*7dd7cddfSDavid du Colombier 	uchar	poll;
51*7dd7cddfSDavid du Colombier 	uchar	precision;
52*7dd7cddfSDavid du Colombier 	uchar	rootdelay[4];
53*7dd7cddfSDavid du Colombier 	uchar	rootdisp[4];
54*7dd7cddfSDavid du Colombier 	uchar	rootid[4];
55*7dd7cddfSDavid du Colombier 	uchar	refts[8];
56*7dd7cddfSDavid du Colombier 	uchar	origts[8];		// departed client
57*7dd7cddfSDavid du Colombier 	uchar	recvts[8];		// arrived at server
58*7dd7cddfSDavid du Colombier 	uchar	xmitts[8];		// departed server
59*7dd7cddfSDavid du Colombier 	uchar	keyid[4];
60*7dd7cddfSDavid du Colombier 	uchar	digest[16];
61*7dd7cddfSDavid du Colombier };
62*7dd7cddfSDavid du Colombier 
63*7dd7cddfSDavid du Colombier enum
64*7dd7cddfSDavid du Colombier {
65*7dd7cddfSDavid du Colombier 	NTPSIZE= 	48,		// basic ntp packet
66*7dd7cddfSDavid du Colombier 	NTPDIGESTSIZE=	20,		// key and digest
67*7dd7cddfSDavid du Colombier };
68*7dd7cddfSDavid du Colombier 
69*7dd7cddfSDavid du Colombier static void	inittime(void);
70*7dd7cddfSDavid du Colombier static int	gettime(vlong*, uvlong*, uvlong*);	// returns time, ticks, hz
71*7dd7cddfSDavid du Colombier static void	settime(vlong, uvlong, vlong, int);	// set time, hz, delta, period
72*7dd7cddfSDavid du Colombier static void	setpriority(void);
73*7dd7cddfSDavid du Colombier 
74*7dd7cddfSDavid du Colombier // ((1970-1900)*365 + 17/*leap days*/)*24*60*60
75*7dd7cddfSDavid du Colombier #define EPOCHDIFF 2208988800UL
76*7dd7cddfSDavid du Colombier 
77*7dd7cddfSDavid du Colombier // convert to ntp timestamps
78*7dd7cddfSDavid du Colombier void
79*7dd7cddfSDavid du Colombier hnputts(void *p, vlong nsec)
80*7dd7cddfSDavid du Colombier {
81*7dd7cddfSDavid du Colombier 	uchar *a;
82*7dd7cddfSDavid du Colombier 	ulong tsh;
83*7dd7cddfSDavid du Colombier 	ulong tsl;
84*7dd7cddfSDavid du Colombier 
85*7dd7cddfSDavid du Colombier 	a = p;
86*7dd7cddfSDavid du Colombier 
87*7dd7cddfSDavid du Colombier 	// zero is a special case
88*7dd7cddfSDavid du Colombier 	if(nsec == 0)
89*7dd7cddfSDavid du Colombier 		return;
90*7dd7cddfSDavid du Colombier 
91*7dd7cddfSDavid du Colombier 	tsh = (nsec/SEC);
92*7dd7cddfSDavid du Colombier 	nsec -= tsh*SEC;
93*7dd7cddfSDavid du Colombier 	tsl = (nsec<<32)/SEC;
94*7dd7cddfSDavid du Colombier 	hnputl(a, tsh+EPOCHDIFF);
95*7dd7cddfSDavid du Colombier 	hnputl(a+4, tsl);
96*7dd7cddfSDavid du Colombier }
97*7dd7cddfSDavid du Colombier 
98*7dd7cddfSDavid du Colombier // convert from ntp timestamps
99*7dd7cddfSDavid du Colombier vlong
100*7dd7cddfSDavid du Colombier nhgetts(void *p)
101*7dd7cddfSDavid du Colombier {
102*7dd7cddfSDavid du Colombier 	uchar *a;
103*7dd7cddfSDavid du Colombier 	ulong tsh, tsl;
104*7dd7cddfSDavid du Colombier 	vlong nsec;
105*7dd7cddfSDavid du Colombier 
106*7dd7cddfSDavid du Colombier 	a = p;
107*7dd7cddfSDavid du Colombier 	tsh = nhgetl(a);
108*7dd7cddfSDavid du Colombier 	tsl = nhgetl(a+4);
109*7dd7cddfSDavid du Colombier 	nsec = tsl*SEC;
110*7dd7cddfSDavid du Colombier 	nsec >>= 32;
111*7dd7cddfSDavid du Colombier 	nsec += (tsh - EPOCHDIFF)*SEC;
112*7dd7cddfSDavid du Colombier 	return nsec;
113*7dd7cddfSDavid du Colombier }
114*7dd7cddfSDavid du Colombier 
115*7dd7cddfSDavid du Colombier // get network address of the server
116*7dd7cddfSDavid du Colombier void
117*7dd7cddfSDavid du Colombier setrootid(char *d)
118*7dd7cddfSDavid du Colombier {
119*7dd7cddfSDavid du Colombier 	char buf[128];
120*7dd7cddfSDavid du Colombier 	int fd, n;
121*7dd7cddfSDavid du Colombier 	char *p;
122*7dd7cddfSDavid du Colombier 
123*7dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/remote", d);
124*7dd7cddfSDavid du Colombier 	fd = open(buf, OREAD);
125*7dd7cddfSDavid du Colombier 	if(fd < 0)
126*7dd7cddfSDavid du Colombier 		return;
127*7dd7cddfSDavid du Colombier 	n = read(fd, buf, sizeof buf);
128*7dd7cddfSDavid du Colombier 	close(fd);
129*7dd7cddfSDavid du Colombier 	if(n <= 0)
130*7dd7cddfSDavid du Colombier 		return;
131*7dd7cddfSDavid du Colombier 	p = strchr(buf, '!');
132*7dd7cddfSDavid du Colombier 	if(p != nil)
133*7dd7cddfSDavid du Colombier 		*p = 0;
134*7dd7cddfSDavid du Colombier 	v4parseip(rootid, buf);
135*7dd7cddfSDavid du Colombier }
136*7dd7cddfSDavid du Colombier 
137*7dd7cddfSDavid du Colombier void
138*7dd7cddfSDavid du Colombier ding(void*, char *s)
139*7dd7cddfSDavid du Colombier {
140*7dd7cddfSDavid du Colombier 	if(strstr(s, "alarm") != nil)
141*7dd7cddfSDavid du Colombier 		noted(NCONT);
142*7dd7cddfSDavid du Colombier 	noted(NDFLT);
143*7dd7cddfSDavid du Colombier }
144*7dd7cddfSDavid du Colombier 
145*7dd7cddfSDavid du Colombier //
146*7dd7cddfSDavid du Colombier //  sntp client, we keep calling if the delay seemed
147*7dd7cddfSDavid du Colombier //  unusually high, i.e., 30% longer than avg.
148*7dd7cddfSDavid du Colombier //
149*7dd7cddfSDavid du Colombier vlong
150*7dd7cddfSDavid du Colombier ntptimediff(char *server)
151*7dd7cddfSDavid du Colombier {
152*7dd7cddfSDavid du Colombier 	int fd, tries, n;
153*7dd7cddfSDavid du Colombier 	NTPpkt ntpin, ntpout;
154*7dd7cddfSDavid du Colombier 	vlong dt, recvts, origts, xmitts, destts, delay, x;
155*7dd7cddfSDavid du Colombier 	char dir[64];
156*7dd7cddfSDavid du Colombier 
157*7dd7cddfSDavid du Colombier 	fd = dial(netmkaddr(server, "udp", "ntp"), 0, 0, 0);
158*7dd7cddfSDavid du Colombier 	if(fd < 0){
159*7dd7cddfSDavid du Colombier 		syslog(0, logfile, "can't reach %s: %r", server);
160*7dd7cddfSDavid du Colombier 		return 0LL;
161*7dd7cddfSDavid du Colombier 	}
162*7dd7cddfSDavid du Colombier 	setrootid(dir);
163*7dd7cddfSDavid du Colombier 	notify(ding);
164*7dd7cddfSDavid du Colombier 
165*7dd7cddfSDavid du Colombier 	memset(&ntpout, 0, sizeof(ntpout));
166*7dd7cddfSDavid du Colombier 	ntpout.mode = 3 | (3 << 3);
167*7dd7cddfSDavid du Colombier 
168*7dd7cddfSDavid du Colombier 	for(tries = 0; tries < 100; tries++){
169*7dd7cddfSDavid du Colombier 		alarm(2*1000);
170*7dd7cddfSDavid du Colombier 
171*7dd7cddfSDavid du Colombier 		gettime(&x, 0, 0);
172*7dd7cddfSDavid du Colombier 		hnputts(ntpout.xmitts, x);
173*7dd7cddfSDavid du Colombier 		if(write(fd, &ntpout, NTPSIZE) < 0){
174*7dd7cddfSDavid du Colombier 			alarm(0);
175*7dd7cddfSDavid du Colombier 			continue;
176*7dd7cddfSDavid du Colombier 		}
177*7dd7cddfSDavid du Colombier 
178*7dd7cddfSDavid du Colombier 		n = read(fd, &ntpin, sizeof(ntpin));
179*7dd7cddfSDavid du Colombier 		alarm(0);
180*7dd7cddfSDavid du Colombier 		gettime(&destts, 0, 0);
181*7dd7cddfSDavid du Colombier 		if(n >= NTPSIZE){
182*7dd7cddfSDavid du Colombier 			recvts = nhgetts(ntpin.recvts);
183*7dd7cddfSDavid du Colombier 			origts = nhgetts(ntpin.origts);
184*7dd7cddfSDavid du Colombier 			xmitts = nhgetts(ntpin.xmitts);
185*7dd7cddfSDavid du Colombier 			stratum = ntpin.stratum;
186*7dd7cddfSDavid du Colombier 			dt = ((recvts - origts) + (xmitts - destts))/2;
187*7dd7cddfSDavid du Colombier 			delay = ((destts - origts) - (xmitts - recvts))/2;
188*7dd7cddfSDavid du Colombier 			mydelay = (avgdelay<<16)/SEC;
189*7dd7cddfSDavid du Colombier 			rootdelay = nhgetl(ntpin.rootdelay);
190*7dd7cddfSDavid du Colombier 			mydisp = (dt<<16)/SEC;
191*7dd7cddfSDavid du Colombier 			rootdisp = nhgetl(ntpin.rootdisp);
192*7dd7cddfSDavid du Colombier 			if(100*delay < avgdelay*130){
193*7dd7cddfSDavid du Colombier 				avgdelay = 7*(avgdelay>>3) + (delay>>3);
194*7dd7cddfSDavid du Colombier 				if(debug)
195*7dd7cddfSDavid du Colombier 					fprint(2, "ntpdelay(%lld)\n", delay);
196*7dd7cddfSDavid du Colombier 				close(fd);
197*7dd7cddfSDavid du Colombier 				return dt;
198*7dd7cddfSDavid du Colombier 			}
199*7dd7cddfSDavid du Colombier 			avgdelay = 7*(avgdelay>>3) + (delay>>3);
200*7dd7cddfSDavid du Colombier 		}
201*7dd7cddfSDavid du Colombier 
202*7dd7cddfSDavid du Colombier 		// try again
203*7dd7cddfSDavid du Colombier 		sleep(1000);
204*7dd7cddfSDavid du Colombier 	}
205*7dd7cddfSDavid du Colombier 	close(fd);
206*7dd7cddfSDavid du Colombier 	return 0LL;
207*7dd7cddfSDavid du Colombier }
208*7dd7cddfSDavid du Colombier 
209*7dd7cddfSDavid du Colombier //
210*7dd7cddfSDavid du Colombier //  sntp server
211*7dd7cddfSDavid du Colombier //
212*7dd7cddfSDavid du Colombier void
213*7dd7cddfSDavid du Colombier ntpserver(void)
214*7dd7cddfSDavid du Colombier {
215*7dd7cddfSDavid du Colombier 	int fd, cfd, n;
216*7dd7cddfSDavid du Colombier 	NTPpkt *ntp;
217*7dd7cddfSDavid du Colombier 	char buf[512];
218*7dd7cddfSDavid du Colombier 	int vers, mode;
219*7dd7cddfSDavid du Colombier 	vlong recvts, x;
220*7dd7cddfSDavid du Colombier 
221*7dd7cddfSDavid du Colombier 	fd = dial("udp!0!ntp", "123", 0, &cfd);
222*7dd7cddfSDavid du Colombier 	if(fd < 0)
223*7dd7cddfSDavid du Colombier 		return;
224*7dd7cddfSDavid du Colombier 	if(fprint(cfd, "headers") < 0)
225*7dd7cddfSDavid du Colombier 		return;
226*7dd7cddfSDavid du Colombier 	close(cfd);
227*7dd7cddfSDavid du Colombier 
228*7dd7cddfSDavid du Colombier 	switch(type){
229*7dd7cddfSDavid du Colombier 	case Fs:
230*7dd7cddfSDavid du Colombier 		memmove(rootid, "WWV", 3);
231*7dd7cddfSDavid du Colombier 		break;
232*7dd7cddfSDavid du Colombier 	case Rtc:
233*7dd7cddfSDavid du Colombier 		memmove(rootid, "LOCL", 3);
234*7dd7cddfSDavid du Colombier 		break;
235*7dd7cddfSDavid du Colombier 	case Ntp:
236*7dd7cddfSDavid du Colombier 		/* set by the ntp client */
237*7dd7cddfSDavid du Colombier 		break;
238*7dd7cddfSDavid du Colombier 	}
239*7dd7cddfSDavid du Colombier 
240*7dd7cddfSDavid du Colombier 	for(;;){
241*7dd7cddfSDavid du Colombier 		n = read(fd, buf, sizeof(buf));
242*7dd7cddfSDavid du Colombier 		gettime(&recvts, 0, 0);
243*7dd7cddfSDavid du Colombier 		if(n < 0)
244*7dd7cddfSDavid du Colombier 			return;
245*7dd7cddfSDavid du Colombier 		if(n < Udphdrsize + NTPSIZE)
246*7dd7cddfSDavid du Colombier 			continue;
247*7dd7cddfSDavid du Colombier 
248*7dd7cddfSDavid du Colombier 		ntp = (NTPpkt*)(buf+Udphdrsize);
249*7dd7cddfSDavid du Colombier 		mode = ntp->mode & 7;
250*7dd7cddfSDavid du Colombier 		vers = (ntp->mode>>3) & 7;
251*7dd7cddfSDavid du Colombier 		if(mode != 3)
252*7dd7cddfSDavid du Colombier 			continue;
253*7dd7cddfSDavid du Colombier 
254*7dd7cddfSDavid du Colombier 		ntp->mode = (vers<<3)|4;
255*7dd7cddfSDavid du Colombier 		ntp->stratum = stratum + 1;
256*7dd7cddfSDavid du Colombier 		hnputl(ntp->rootdelay, rootdelay + mydelay);
257*7dd7cddfSDavid du Colombier 		hnputl(ntp->rootdisp, rootdelay + mydisp);
258*7dd7cddfSDavid du Colombier 		memmove(ntp->origts, ntp->xmitts, sizeof(ntp->origts));
259*7dd7cddfSDavid du Colombier 		hnputts(ntp->recvts, recvts);
260*7dd7cddfSDavid du Colombier 		memmove(ntp->rootid, rootid, sizeof(ntp->rootid));
261*7dd7cddfSDavid du Colombier 		gettime(&x, 0, 0);
262*7dd7cddfSDavid du Colombier 		hnputts(ntp->xmitts, x);
263*7dd7cddfSDavid du Colombier 		write(fd, buf, NTPSIZE+Udphdrsize);
264*7dd7cddfSDavid du Colombier 	}
265*7dd7cddfSDavid du Colombier }
266*7dd7cddfSDavid du Colombier 
267*7dd7cddfSDavid du Colombier long
268*7dd7cddfSDavid du Colombier fstime(void)
269*7dd7cddfSDavid du Colombier {
270*7dd7cddfSDavid du Colombier 	Dir d;
271*7dd7cddfSDavid du Colombier 
272*7dd7cddfSDavid du Colombier 	if(dirstat("/n/boot", &d) < 0)
273*7dd7cddfSDavid du Colombier 		sysfatal("stating /n/boot: %r");
274*7dd7cddfSDavid du Colombier 	return d.atime;
275*7dd7cddfSDavid du Colombier }
276*7dd7cddfSDavid du Colombier 
277*7dd7cddfSDavid du Colombier long
278*7dd7cddfSDavid du Colombier rtctime(void)
279*7dd7cddfSDavid du Colombier {
280*7dd7cddfSDavid du Colombier 	char b[20];
281*7dd7cddfSDavid du Colombier 	static int f = -1;
282*7dd7cddfSDavid du Colombier 	int i, retries;
283*7dd7cddfSDavid du Colombier 
284*7dd7cddfSDavid du Colombier 	memset(b, 0, sizeof(b));
285*7dd7cddfSDavid du Colombier 	for(retries = 0; retries < 100; retries++){
286*7dd7cddfSDavid du Colombier 		if(f < 0)
287*7dd7cddfSDavid du Colombier 			f = open("/dev/rtc", OREAD|OCEXEC);
288*7dd7cddfSDavid du Colombier 		if(f < 0)
289*7dd7cddfSDavid du Colombier 			break;
290*7dd7cddfSDavid du Colombier 		if(seek(f, 0, 0) < 0 || (i = read(f, b, sizeof(b))) < 0){
291*7dd7cddfSDavid du Colombier 			close(f);
292*7dd7cddfSDavid du Colombier 			f = -1;
293*7dd7cddfSDavid du Colombier 		} else {
294*7dd7cddfSDavid du Colombier 			if(i != 0)
295*7dd7cddfSDavid du Colombier 				break;
296*7dd7cddfSDavid du Colombier 		}
297*7dd7cddfSDavid du Colombier 	}
298*7dd7cddfSDavid du Colombier 	return strtoul(b, 0, 10)+gmtdelta;
299*7dd7cddfSDavid du Colombier }
300*7dd7cddfSDavid du Colombier 
301*7dd7cddfSDavid du Colombier vlong
302*7dd7cddfSDavid du Colombier sample(long (*get)(void))
303*7dd7cddfSDavid du Colombier {
304*7dd7cddfSDavid du Colombier 	long this, last;
305*7dd7cddfSDavid du Colombier 	vlong start, end;
306*7dd7cddfSDavid du Colombier 
307*7dd7cddfSDavid du Colombier 	/*
308*7dd7cddfSDavid du Colombier 	 *  wait for the second to change
309*7dd7cddfSDavid du Colombier 	 */
310*7dd7cddfSDavid du Colombier 	last = (*get)();
311*7dd7cddfSDavid du Colombier 	for(;;){
312*7dd7cddfSDavid du Colombier 		gettime(&start, 0, 0);
313*7dd7cddfSDavid du Colombier 		this = (*get)();
314*7dd7cddfSDavid du Colombier 		gettime(&end, 0, 0);
315*7dd7cddfSDavid du Colombier 		if(this != last)
316*7dd7cddfSDavid du Colombier 			break;
317*7dd7cddfSDavid du Colombier 		last = this;
318*7dd7cddfSDavid du Colombier 	}
319*7dd7cddfSDavid du Colombier 	return SEC*this - (end-start)/2;
320*7dd7cddfSDavid du Colombier }
321*7dd7cddfSDavid du Colombier 
322*7dd7cddfSDavid du Colombier void
323*7dd7cddfSDavid du Colombier getsysid(void)
324*7dd7cddfSDavid du Colombier {
325*7dd7cddfSDavid du Colombier 	sysid = getenv("sysname");
326*7dd7cddfSDavid du Colombier 	if(sysid != nil)
327*7dd7cddfSDavid du Colombier 		return;
328*7dd7cddfSDavid du Colombier }
329*7dd7cddfSDavid du Colombier 
330*7dd7cddfSDavid du Colombier int
331*7dd7cddfSDavid du Colombier openfreqfile(void)
332*7dd7cddfSDavid du Colombier {
333*7dd7cddfSDavid du Colombier 	char buf[64];
334*7dd7cddfSDavid du Colombier 	int fd;
335*7dd7cddfSDavid du Colombier 
336*7dd7cddfSDavid du Colombier 	if(sysid == nil)
337*7dd7cddfSDavid du Colombier 		return -1;
338*7dd7cddfSDavid du Colombier 
339*7dd7cddfSDavid du Colombier 	switch(type){
340*7dd7cddfSDavid du Colombier 	case Ntp:
341*7dd7cddfSDavid du Colombier 		snprint(buf, sizeof buf, "%s/ts.%s.%d.%s", dir, sysid, type, timeserver);
342*7dd7cddfSDavid du Colombier 		break;
343*7dd7cddfSDavid du Colombier 	default:
344*7dd7cddfSDavid du Colombier 		snprint(buf, sizeof buf, "%s/ts.%s.%d", dir, sysid, type);
345*7dd7cddfSDavid du Colombier 		break;
346*7dd7cddfSDavid du Colombier 	}
347*7dd7cddfSDavid du Colombier 	fd = open(buf, ORDWR);
348*7dd7cddfSDavid du Colombier 	if(fd < 0)
349*7dd7cddfSDavid du Colombier 		fd = create(buf, ORDWR, 0666);
350*7dd7cddfSDavid du Colombier 	if(fd < 0)
351*7dd7cddfSDavid du Colombier 		return -1;
352*7dd7cddfSDavid du Colombier 	return fd;
353*7dd7cddfSDavid du Colombier }
354*7dd7cddfSDavid du Colombier 
355*7dd7cddfSDavid du Colombier //
356*7dd7cddfSDavid du Colombier //  the file contains the last known frequency and the
357*7dd7cddfSDavid du Colombier //  number of seconds it was sampled over
358*7dd7cddfSDavid du Colombier //
359*7dd7cddfSDavid du Colombier vlong
360*7dd7cddfSDavid du Colombier readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz)
361*7dd7cddfSDavid du Colombier {
362*7dd7cddfSDavid du Colombier 	int n;
363*7dd7cddfSDavid du Colombier 	char buf[128];
364*7dd7cddfSDavid du Colombier 	vlong hz;
365*7dd7cddfSDavid du Colombier 
366*7dd7cddfSDavid du Colombier 	n = read(fd, buf, sizeof(buf)-1);
367*7dd7cddfSDavid du Colombier 	if(n <= 0)
368*7dd7cddfSDavid du Colombier 		return ohz;
369*7dd7cddfSDavid du Colombier 	buf[n] = 0;
370*7dd7cddfSDavid du Colombier 	hz = strtoll(buf, nil, 0);
371*7dd7cddfSDavid du Colombier 
372*7dd7cddfSDavid du Colombier 	if(hz > maxhz || hz < minhz)
373*7dd7cddfSDavid du Colombier 		return ohz;
374*7dd7cddfSDavid du Colombier 	return hz;
375*7dd7cddfSDavid du Colombier }
376*7dd7cddfSDavid du Colombier 
377*7dd7cddfSDavid du Colombier //
378*7dd7cddfSDavid du Colombier //  remember hz and averaging period
379*7dd7cddfSDavid du Colombier //
380*7dd7cddfSDavid du Colombier void
381*7dd7cddfSDavid du Colombier writefreqfile(int fd, vlong hz, int secs, vlong diff)
382*7dd7cddfSDavid du Colombier {
383*7dd7cddfSDavid du Colombier 	if(fd < 0)
384*7dd7cddfSDavid du Colombier 		return;
385*7dd7cddfSDavid du Colombier 	if(seek(fd, 0, 0) < 0)
386*7dd7cddfSDavid du Colombier 		return;
387*7dd7cddfSDavid du Colombier 	fprint(fd, "%lld %d %d %lld\n", hz, secs, type, diff);
388*7dd7cddfSDavid du Colombier }
389*7dd7cddfSDavid du Colombier 
390*7dd7cddfSDavid du Colombier void
391*7dd7cddfSDavid du Colombier main(int argc, char **argv)
392*7dd7cddfSDavid du Colombier {
393*7dd7cddfSDavid du Colombier 	int diffsecs, secs, minsecs, avgdiff;
394*7dd7cddfSDavid du Colombier 	int t, fd;
395*7dd7cddfSDavid du Colombier 	Sample *s, *x, *first, **l;
396*7dd7cddfSDavid du Colombier 	vlong diff, absdiff, accuracy;
397*7dd7cddfSDavid du Colombier 	uvlong hz;
398*7dd7cddfSDavid du Colombier 	double dT, dt, minhz, maxhz;
399*7dd7cddfSDavid du Colombier 	char *a;
400*7dd7cddfSDavid du Colombier 	Tm tl, tg;
401*7dd7cddfSDavid du Colombier 
402*7dd7cddfSDavid du Colombier 	type = Fs;
403*7dd7cddfSDavid du Colombier 	debug = 0;
404*7dd7cddfSDavid du Colombier 	minsecs = 60;		// most frequent resync
405*7dd7cddfSDavid du Colombier 	accuracy = 1000000LL;	// default accuracy is 1 millisecond
406*7dd7cddfSDavid du Colombier 	avgdiff = 1;
407*7dd7cddfSDavid du Colombier 	diffsecs = 0;
408*7dd7cddfSDavid du Colombier 
409*7dd7cddfSDavid du Colombier 	ARGBEGIN{
410*7dd7cddfSDavid du Colombier 	case 'a':
411*7dd7cddfSDavid du Colombier 		a = ARGF();
412*7dd7cddfSDavid du Colombier 		if(a == nil)
413*7dd7cddfSDavid du Colombier 			sysfatal("bad accuracy specified");
414*7dd7cddfSDavid du Colombier 		accuracy = strtoll(a, 0, 0);	// accuracy specified in ns
415*7dd7cddfSDavid du Colombier 		if(accuracy <= 1LL)
416*7dd7cddfSDavid du Colombier 			sysfatal("bad accuracy specified");
417*7dd7cddfSDavid du Colombier 		break;
418*7dd7cddfSDavid du Colombier 	case 'f':
419*7dd7cddfSDavid du Colombier 		type = Fs;
420*7dd7cddfSDavid du Colombier 		stratum = 2;
421*7dd7cddfSDavid du Colombier 		break;
422*7dd7cddfSDavid du Colombier 	case 'r':
423*7dd7cddfSDavid du Colombier 		type = Rtc;
424*7dd7cddfSDavid du Colombier 		stratum = 1;
425*7dd7cddfSDavid du Colombier 		break;
426*7dd7cddfSDavid du Colombier 	case 'n':
427*7dd7cddfSDavid du Colombier 		type = Ntp;
428*7dd7cddfSDavid du Colombier 		break;
429*7dd7cddfSDavid du Colombier 	case 'D':
430*7dd7cddfSDavid du Colombier 		debug = 1;
431*7dd7cddfSDavid du Colombier 		break;
432*7dd7cddfSDavid du Colombier 	case 'd':
433*7dd7cddfSDavid du Colombier 		dir = ARGF();
434*7dd7cddfSDavid du Colombier 		break;
435*7dd7cddfSDavid du Colombier 	case 'L':
436*7dd7cddfSDavid du Colombier 		//
437*7dd7cddfSDavid du Colombier 		// Assume time source in local time rather than GMT.
438*7dd7cddfSDavid du Colombier 		// Calculate difference so that rtctime can return GMT.
439*7dd7cddfSDavid du Colombier 		// This is useful with the rtc on PC's that run Windows
440*7dd7cddfSDavid du Colombier 		// since Windows keeps the local time in the rtc.
441*7dd7cddfSDavid du Colombier 		//
442*7dd7cddfSDavid du Colombier 		t = time(0);
443*7dd7cddfSDavid du Colombier 		tl = *localtime(t);
444*7dd7cddfSDavid du Colombier 		tg = *gmtime(t);
445*7dd7cddfSDavid du Colombier 
446*7dd7cddfSDavid du Colombier 		// if the years are different, we're at most a day off, so just rewrite
447*7dd7cddfSDavid du Colombier 		if(tl.year < tg.year){
448*7dd7cddfSDavid du Colombier 			tg.year--;
449*7dd7cddfSDavid du Colombier 			tg.yday = tl.yday + 1;
450*7dd7cddfSDavid du Colombier 		}else if(tl.year > tg.year){
451*7dd7cddfSDavid du Colombier 			tl.year--;
452*7dd7cddfSDavid du Colombier 			tl.yday = tg.yday+1;
453*7dd7cddfSDavid du Colombier 		}
454*7dd7cddfSDavid du Colombier 		assert(tl.year == tg.year);
455*7dd7cddfSDavid du Colombier 
456*7dd7cddfSDavid du Colombier 		tg.sec -= tl.sec;
457*7dd7cddfSDavid du Colombier 		tg.min -= tl.min;
458*7dd7cddfSDavid du Colombier 		tg.hour -= tl.hour;
459*7dd7cddfSDavid du Colombier 		tg.yday -= tl.yday;
460*7dd7cddfSDavid du Colombier 		gmtdelta = tg.sec+60*(tg.min+60*(tg.hour+tg.yday*24));
461*7dd7cddfSDavid du Colombier 
462*7dd7cddfSDavid du Colombier 		assert(abs(gmtdelta) <= 24*60*60);
463*7dd7cddfSDavid du Colombier 		break;
464*7dd7cddfSDavid du Colombier 	case 'i':
465*7dd7cddfSDavid du Colombier 		impotent = 1;
466*7dd7cddfSDavid du Colombier 		break;
467*7dd7cddfSDavid du Colombier 	case 'l':
468*7dd7cddfSDavid du Colombier 		logging = 1;
469*7dd7cddfSDavid du Colombier 		break;
470*7dd7cddfSDavid du Colombier 	}ARGEND;
471*7dd7cddfSDavid du Colombier 
472*7dd7cddfSDavid du Colombier 	fmtinstall('E', eipconv);
473*7dd7cddfSDavid du Colombier 	fmtinstall('I', eipconv);
474*7dd7cddfSDavid du Colombier 	fmtinstall('V', eipconv);
475*7dd7cddfSDavid du Colombier 	getsysid();
476*7dd7cddfSDavid du Colombier 
477*7dd7cddfSDavid du Colombier 	// initial sampling period
478*7dd7cddfSDavid du Colombier 	secs = 60;
479*7dd7cddfSDavid du Colombier 
480*7dd7cddfSDavid du Colombier 	if(argc > 0)
481*7dd7cddfSDavid du Colombier 		timeserver = argv[0];
482*7dd7cddfSDavid du Colombier 	else
483*7dd7cddfSDavid du Colombier 		switch(type){
484*7dd7cddfSDavid du Colombier 		case Fs:
485*7dd7cddfSDavid du Colombier 			timeserver = "/srv/boot";
486*7dd7cddfSDavid du Colombier 			break;
487*7dd7cddfSDavid du Colombier 		case Ntp:
488*7dd7cddfSDavid du Colombier 			timeserver = "$ntp";
489*7dd7cddfSDavid du Colombier 			break;
490*7dd7cddfSDavid du Colombier 		}
491*7dd7cddfSDavid du Colombier 
492*7dd7cddfSDavid du Colombier 	setpriority();
493*7dd7cddfSDavid du Colombier 
494*7dd7cddfSDavid du Colombier 	//
495*7dd7cddfSDavid du Colombier 	//  detach from the current namespace
496*7dd7cddfSDavid du Colombier 	//
497*7dd7cddfSDavid du Colombier 	if(debug)
498*7dd7cddfSDavid du Colombier 		rfork(RFNAMEG);
499*7dd7cddfSDavid du Colombier 	else {
500*7dd7cddfSDavid du Colombier 		switch(rfork(RFPROC|RFFDG|RFNAMEG|RFNOTEG|RFNOWAIT)){
501*7dd7cddfSDavid du Colombier 		case -1:
502*7dd7cddfSDavid du Colombier 			sysfatal("forking: %r");
503*7dd7cddfSDavid du Colombier 			break;
504*7dd7cddfSDavid du Colombier 		case 0:
505*7dd7cddfSDavid du Colombier 			break;
506*7dd7cddfSDavid du Colombier 		default:
507*7dd7cddfSDavid du Colombier 			exits(0);
508*7dd7cddfSDavid du Colombier 		}
509*7dd7cddfSDavid du Colombier 	}
510*7dd7cddfSDavid du Colombier 
511*7dd7cddfSDavid du Colombier 	// figure out our time interface
512*7dd7cddfSDavid du Colombier 	inittime();
513*7dd7cddfSDavid du Colombier 	gettime(0, 0, &hz);
514*7dd7cddfSDavid du Colombier 
515*7dd7cddfSDavid du Colombier 	// some sanity limits
516*7dd7cddfSDavid du Colombier 	minhz = hz * 0.8;
517*7dd7cddfSDavid du Colombier 	maxhz = hz * 1.2;
518*7dd7cddfSDavid du Colombier 
519*7dd7cddfSDavid du Colombier 	//
520*7dd7cddfSDavid du Colombier 	//  bind in clocks
521*7dd7cddfSDavid du Colombier 	//
522*7dd7cddfSDavid du Colombier 	switch(type){
523*7dd7cddfSDavid du Colombier 	case Fs:
524*7dd7cddfSDavid du Colombier 		fd = open(timeserver, ORDWR);
525*7dd7cddfSDavid du Colombier 		if(fd < 0)
526*7dd7cddfSDavid du Colombier 			sysfatal("opening %s: %r\n", timeserver);
527*7dd7cddfSDavid du Colombier 		if(mount(fd, "/n/boot", MREPL, "") < 0)
528*7dd7cddfSDavid du Colombier 			sysfatal("mounting %s: %r\n", timeserver);
529*7dd7cddfSDavid du Colombier 		close(fd);
530*7dd7cddfSDavid du Colombier 		break;
531*7dd7cddfSDavid du Colombier 	case Rtc:
532*7dd7cddfSDavid du Colombier 		bind("#r", "/dev", MAFTER);
533*7dd7cddfSDavid du Colombier 		if(access("/dev/rtc", AREAD) < 0)
534*7dd7cddfSDavid du Colombier 			sysfatal("accessing /dev/rtc: %r\n");
535*7dd7cddfSDavid du Colombier 		break;
536*7dd7cddfSDavid du Colombier 	}
537*7dd7cddfSDavid du Colombier 
538*7dd7cddfSDavid du Colombier 	//
539*7dd7cddfSDavid du Colombier 	//  start an ntp server
540*7dd7cddfSDavid du Colombier 	//
541*7dd7cddfSDavid du Colombier 	if(!impotent){
542*7dd7cddfSDavid du Colombier 		switch(rfork(RFPROC|RFFDG|RFMEM|RFNOWAIT)){
543*7dd7cddfSDavid du Colombier 		case -1:
544*7dd7cddfSDavid du Colombier 			sysfatal("forking: %r");
545*7dd7cddfSDavid du Colombier 			break;
546*7dd7cddfSDavid du Colombier 		case 0:
547*7dd7cddfSDavid du Colombier 			ntpserver();
548*7dd7cddfSDavid du Colombier 			_exits(0);
549*7dd7cddfSDavid du Colombier 			break;
550*7dd7cddfSDavid du Colombier 		default:
551*7dd7cddfSDavid du Colombier 			break;
552*7dd7cddfSDavid du Colombier 		}
553*7dd7cddfSDavid du Colombier 	}
554*7dd7cddfSDavid du Colombier 
555*7dd7cddfSDavid du Colombier 	fd = openfreqfile();
556*7dd7cddfSDavid du Colombier 
557*7dd7cddfSDavid du Colombier 	// get the last known frequency from the file
558*7dd7cddfSDavid du Colombier 	hz = readfreqfile(fd, hz, minhz, maxhz);
559*7dd7cddfSDavid du Colombier 
560*7dd7cddfSDavid du Colombier 	first = nil;
561*7dd7cddfSDavid du Colombier 	l = &first;
562*7dd7cddfSDavid du Colombier 	for(;; sleep(secs*(1000))){
563*7dd7cddfSDavid du Colombier 		s = mallocz(sizeof(*s), 1);
564*7dd7cddfSDavid du Colombier 		diff = 0;
565*7dd7cddfSDavid du Colombier 
566*7dd7cddfSDavid du Colombier 		// get times for this sample
567*7dd7cddfSDavid du Colombier 		switch(type){
568*7dd7cddfSDavid du Colombier 		case Fs:
569*7dd7cddfSDavid du Colombier 			s->stime = sample(fstime);
570*7dd7cddfSDavid du Colombier 			break;
571*7dd7cddfSDavid du Colombier 		case Rtc:
572*7dd7cddfSDavid du Colombier 			s->stime = sample(rtctime);
573*7dd7cddfSDavid du Colombier 			break;
574*7dd7cddfSDavid du Colombier 		case Ntp:
575*7dd7cddfSDavid du Colombier 			diff = ntptimediff(timeserver);
576*7dd7cddfSDavid du Colombier 			break;
577*7dd7cddfSDavid du Colombier 		}
578*7dd7cddfSDavid du Colombier 
579*7dd7cddfSDavid du Colombier 		// use fastest method to read local clock and ticks
580*7dd7cddfSDavid du Colombier 		gettime(&s->ltime, &s->ticks, 0);
581*7dd7cddfSDavid du Colombier 		if(type == Ntp)
582*7dd7cddfSDavid du Colombier 			s->stime = s->ltime + diff;
583*7dd7cddfSDavid du Colombier 		if(s->stime < 0)
584*7dd7cddfSDavid du Colombier 			continue;
585*7dd7cddfSDavid du Colombier 
586*7dd7cddfSDavid du Colombier 		// forget any old samples
587*7dd7cddfSDavid du Colombier 		while(first != nil){
588*7dd7cddfSDavid du Colombier 			if(first->next == nil)
589*7dd7cddfSDavid du Colombier 				break;
590*7dd7cddfSDavid du Colombier 			if(s->stime - first->stime <= (5LL*MAXSECS*SEC)/4LL)
591*7dd7cddfSDavid du Colombier 				break;
592*7dd7cddfSDavid du Colombier 			x = first->next;
593*7dd7cddfSDavid du Colombier 			free(first);
594*7dd7cddfSDavid du Colombier 			first = x;
595*7dd7cddfSDavid du Colombier 		}
596*7dd7cddfSDavid du Colombier 
597*7dd7cddfSDavid du Colombier 		diff = s->stime - s->ltime;
598*7dd7cddfSDavid du Colombier 		if(debug)
599*7dd7cddfSDavid du Colombier 			fprint(2, "diff = %lld\n", diff);
600*7dd7cddfSDavid du Colombier 		if(diff > 10*SEC || diff < -10*SEC){
601*7dd7cddfSDavid du Colombier 			// we're way off, set the time
602*7dd7cddfSDavid du Colombier 			// and forget all previous samples
603*7dd7cddfSDavid du Colombier 			settime(s->stime, 0, 0, 0);
604*7dd7cddfSDavid du Colombier 			while(first != nil){
605*7dd7cddfSDavid du Colombier 				x = first;
606*7dd7cddfSDavid du Colombier 				first = x->next;
607*7dd7cddfSDavid du Colombier 				free(x);
608*7dd7cddfSDavid du Colombier 			}
609*7dd7cddfSDavid du Colombier 			l = &first;
610*7dd7cddfSDavid du Colombier 		} else {
611*7dd7cddfSDavid du Colombier 			// adjust period to fit error
612*7dd7cddfSDavid du Colombier 			absdiff = diff;
613*7dd7cddfSDavid du Colombier 			if(diff < 0)
614*7dd7cddfSDavid du Colombier 				absdiff = -diff;
615*7dd7cddfSDavid du Colombier 			if(secs < minsecs || absdiff < (accuracy>>1))
616*7dd7cddfSDavid du Colombier 				secs += 30;
617*7dd7cddfSDavid du Colombier 			else if(absdiff > accuracy && secs > minsecs)
618*7dd7cddfSDavid du Colombier 				secs >>= 1;
619*7dd7cddfSDavid du Colombier 
620*7dd7cddfSDavid du Colombier 			// work off difference
621*7dd7cddfSDavid du Colombier 			avgdiff = (avgdiff>>1) + (diff>>1);
622*7dd7cddfSDavid du Colombier 			if(avgdiff == 0)
623*7dd7cddfSDavid du Colombier 				avgdiff = 1;
624*7dd7cddfSDavid du Colombier 			diffsecs = 2*(diff*secs)/avgdiff;
625*7dd7cddfSDavid du Colombier 			if(diffsecs < 0)
626*7dd7cddfSDavid du Colombier 				diffsecs = -diffsecs;
627*7dd7cddfSDavid du Colombier 			else if(diffsecs == 0)
628*7dd7cddfSDavid du Colombier 				diffsecs = 1;
629*7dd7cddfSDavid du Colombier 			else if(diffsecs > 4*secs)
630*7dd7cddfSDavid du Colombier 				diffsecs = 4*secs;
631*7dd7cddfSDavid du Colombier 			settime(-1, 0, diff, diffsecs);
632*7dd7cddfSDavid du Colombier 		}
633*7dd7cddfSDavid du Colombier 
634*7dd7cddfSDavid du Colombier 		// lots of checking to avoid floating pt exceptions
635*7dd7cddfSDavid du Colombier 		if(first != nil)
636*7dd7cddfSDavid du Colombier 		if(s->ticks > first->ticks)
637*7dd7cddfSDavid du Colombier 		if(s->stime > first->stime){
638*7dd7cddfSDavid du Colombier 			dT = s->ticks - first->ticks;
639*7dd7cddfSDavid du Colombier 			dt = s->stime - first->stime;
640*7dd7cddfSDavid du Colombier 			dT = dT/dt;
641*7dd7cddfSDavid du Colombier 			dT *= SEC;
642*7dd7cddfSDavid du Colombier 
643*7dd7cddfSDavid du Colombier 			// use result only if it looks sane
644*7dd7cddfSDavid du Colombier 			if(dT <= maxhz && dT > minhz){
645*7dd7cddfSDavid du Colombier 				hz = (1-gain)*hz + gain*dT;
646*7dd7cddfSDavid du Colombier 				settime(-1, hz, 0, 0);
647*7dd7cddfSDavid du Colombier 				writefreqfile(fd, hz, (s->stime - first->stime)/SEC, diff);
648*7dd7cddfSDavid du Colombier 			}
649*7dd7cddfSDavid du Colombier 		}
650*7dd7cddfSDavid du Colombier 
651*7dd7cddfSDavid du Colombier 		if(logging)
652*7dd7cddfSDavid du Colombier 			syslog(0, logfile, "%lld %lld %lld %lld %d %s %lld %d",
653*7dd7cddfSDavid du Colombier 				s->ltime, s->stime,
654*7dd7cddfSDavid du Colombier 				s->ticks, hz,
655*7dd7cddfSDavid du Colombier 				type, type == Ntp ? timeserver : "",
656*7dd7cddfSDavid du Colombier 				avgdelay, diffsecs);
657*7dd7cddfSDavid du Colombier 
658*7dd7cddfSDavid du Colombier 		*l = s;
659*7dd7cddfSDavid du Colombier 		l = &s->next;
660*7dd7cddfSDavid du Colombier 	}
661*7dd7cddfSDavid du Colombier }
662*7dd7cddfSDavid du Colombier 
663*7dd7cddfSDavid du Colombier 
664*7dd7cddfSDavid du Colombier //
665*7dd7cddfSDavid du Colombier //  kernel interface
666*7dd7cddfSDavid du Colombier //
667*7dd7cddfSDavid du Colombier enum
668*7dd7cddfSDavid du Colombier {
669*7dd7cddfSDavid du Colombier 	Ibintime,
670*7dd7cddfSDavid du Colombier 	Insec,
671*7dd7cddfSDavid du Colombier 	Itiming,
672*7dd7cddfSDavid du Colombier };
673*7dd7cddfSDavid du Colombier int ifc;
674*7dd7cddfSDavid du Colombier int bintimefd = -1;
675*7dd7cddfSDavid du Colombier int timingfd = -1;
676*7dd7cddfSDavid du Colombier int nsecfd = -1;
677*7dd7cddfSDavid du Colombier int fastclockfd = -1;
678*7dd7cddfSDavid du Colombier 
679*7dd7cddfSDavid du Colombier static void
680*7dd7cddfSDavid du Colombier inittime(void)
681*7dd7cddfSDavid du Colombier {
682*7dd7cddfSDavid du Colombier 	int mode;
683*7dd7cddfSDavid du Colombier 
684*7dd7cddfSDavid du Colombier 	if(impotent)
685*7dd7cddfSDavid du Colombier 		mode = OREAD;
686*7dd7cddfSDavid du Colombier 	else
687*7dd7cddfSDavid du Colombier 		mode = ORDWR;
688*7dd7cddfSDavid du Colombier 
689*7dd7cddfSDavid du Colombier 	// bind in clocks
690*7dd7cddfSDavid du Colombier 	if(access("/dev/time", 0) < 0)
691*7dd7cddfSDavid du Colombier 		bind("#c", "/dev", MAFTER);
692*7dd7cddfSDavid du Colombier 	if(access("/dev/rtc", 0) < 0)
693*7dd7cddfSDavid du Colombier 		bind("#r", "/dev", MAFTER);
694*7dd7cddfSDavid du Colombier 
695*7dd7cddfSDavid du Colombier 	// figure out what interface we have
696*7dd7cddfSDavid du Colombier 	ifc = Ibintime;
697*7dd7cddfSDavid du Colombier 	bintimefd = open("/dev/bintime", mode);
698*7dd7cddfSDavid du Colombier 	if(bintimefd >= 0)
699*7dd7cddfSDavid du Colombier 		return;
700*7dd7cddfSDavid du Colombier 	ifc = Insec;
701*7dd7cddfSDavid du Colombier 	nsecfd = open("/dev/nsec", mode);
702*7dd7cddfSDavid du Colombier 	if(nsecfd < 0)
703*7dd7cddfSDavid du Colombier 		sysfatal("opening /dev/nsec");
704*7dd7cddfSDavid du Colombier 	fastclockfd = open("/dev/fastclock", mode);
705*7dd7cddfSDavid du Colombier 	if(fastclockfd < 0)
706*7dd7cddfSDavid du Colombier 		sysfatal("opening /dev/fastclock");
707*7dd7cddfSDavid du Colombier 	timingfd = open("/dev/timing", OREAD);
708*7dd7cddfSDavid du Colombier 	if(timingfd < 0)
709*7dd7cddfSDavid du Colombier 		return;
710*7dd7cddfSDavid du Colombier 	ifc = Itiming;
711*7dd7cddfSDavid du Colombier }
712*7dd7cddfSDavid du Colombier 
713*7dd7cddfSDavid du Colombier //
714*7dd7cddfSDavid du Colombier //  convert binary numbers from/to kernel
715*7dd7cddfSDavid du Colombier //
716*7dd7cddfSDavid du Colombier static uvlong uvorder = 0x0001020304050607ULL;
717*7dd7cddfSDavid du Colombier 
718*7dd7cddfSDavid du Colombier static uchar*
719*7dd7cddfSDavid du Colombier be2vlong(vlong *to, uchar *f)
720*7dd7cddfSDavid du Colombier {
721*7dd7cddfSDavid du Colombier 	uchar *t, *o;
722*7dd7cddfSDavid du Colombier 	int i;
723*7dd7cddfSDavid du Colombier 
724*7dd7cddfSDavid du Colombier 	t = (uchar*)to;
725*7dd7cddfSDavid du Colombier 	o = (uchar*)&uvorder;
726*7dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(vlong); i++)
727*7dd7cddfSDavid du Colombier 		t[o[i]] = f[i];
728*7dd7cddfSDavid du Colombier 	return f+sizeof(vlong);
729*7dd7cddfSDavid du Colombier }
730*7dd7cddfSDavid du Colombier 
731*7dd7cddfSDavid du Colombier static uchar*
732*7dd7cddfSDavid du Colombier vlong2be(uchar *t, vlong from)
733*7dd7cddfSDavid du Colombier {
734*7dd7cddfSDavid du Colombier 	uchar *f, *o;
735*7dd7cddfSDavid du Colombier 	int i;
736*7dd7cddfSDavid du Colombier 
737*7dd7cddfSDavid du Colombier 	f = (uchar*)&from;
738*7dd7cddfSDavid du Colombier 	o = (uchar*)&uvorder;
739*7dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(vlong); i++)
740*7dd7cddfSDavid du Colombier 		t[i] = f[o[i]];
741*7dd7cddfSDavid du Colombier 	return t+sizeof(vlong);
742*7dd7cddfSDavid du Colombier }
743*7dd7cddfSDavid du Colombier 
744*7dd7cddfSDavid du Colombier static long order = 0x00010203;
745*7dd7cddfSDavid du Colombier 
746*7dd7cddfSDavid du Colombier static uchar*
747*7dd7cddfSDavid du Colombier be2long(long *to, uchar *f)
748*7dd7cddfSDavid du Colombier {
749*7dd7cddfSDavid du Colombier 	uchar *t, *o;
750*7dd7cddfSDavid du Colombier 	int i;
751*7dd7cddfSDavid du Colombier 
752*7dd7cddfSDavid du Colombier 	t = (uchar*)to;
753*7dd7cddfSDavid du Colombier 	o = (uchar*)&order;
754*7dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(long); i++)
755*7dd7cddfSDavid du Colombier 		t[o[i]] = f[i];
756*7dd7cddfSDavid du Colombier 	return f+sizeof(long);
757*7dd7cddfSDavid du Colombier }
758*7dd7cddfSDavid du Colombier 
759*7dd7cddfSDavid du Colombier static uchar*
760*7dd7cddfSDavid du Colombier long2be(uchar *t, long from)
761*7dd7cddfSDavid du Colombier {
762*7dd7cddfSDavid du Colombier 	uchar *f, *o;
763*7dd7cddfSDavid du Colombier 	int i;
764*7dd7cddfSDavid du Colombier 
765*7dd7cddfSDavid du Colombier 	f = (uchar*)&from;
766*7dd7cddfSDavid du Colombier 	o = (uchar*)&order;
767*7dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(long); i++)
768*7dd7cddfSDavid du Colombier 		t[i] = f[o[i]];
769*7dd7cddfSDavid du Colombier 	return t+sizeof(long);
770*7dd7cddfSDavid du Colombier }
771*7dd7cddfSDavid du Colombier 
772*7dd7cddfSDavid du Colombier //
773*7dd7cddfSDavid du Colombier // read ticks and local time in nanoseconds
774*7dd7cddfSDavid du Colombier //
775*7dd7cddfSDavid du Colombier static int
776*7dd7cddfSDavid du Colombier gettime(vlong *nsec, uvlong *ticks, uvlong *hz)
777*7dd7cddfSDavid du Colombier {
778*7dd7cddfSDavid du Colombier 	int i, n;
779*7dd7cddfSDavid du Colombier 	uchar ub[3*8], *p;
780*7dd7cddfSDavid du Colombier 	char b[2*24+1];
781*7dd7cddfSDavid du Colombier 
782*7dd7cddfSDavid du Colombier 	switch(ifc){
783*7dd7cddfSDavid du Colombier 	case Ibintime:
784*7dd7cddfSDavid du Colombier 		n = sizeof(vlong);
785*7dd7cddfSDavid du Colombier 		if(hz != nil)
786*7dd7cddfSDavid du Colombier 			n = 3*sizeof(vlong);
787*7dd7cddfSDavid du Colombier 		if(ticks != nil)
788*7dd7cddfSDavid du Colombier 			n = 2*sizeof(vlong);
789*7dd7cddfSDavid du Colombier 		i = read(bintimefd, ub, n);
790*7dd7cddfSDavid du Colombier 		if(i != n)
791*7dd7cddfSDavid du Colombier 			break;
792*7dd7cddfSDavid du Colombier 		p = ub;
793*7dd7cddfSDavid du Colombier 		if(nsec != nil)
794*7dd7cddfSDavid du Colombier 			be2vlong(nsec, ub);
795*7dd7cddfSDavid du Colombier 		p += sizeof(vlong);
796*7dd7cddfSDavid du Colombier 		if(ticks != nil)
797*7dd7cddfSDavid du Colombier 			be2vlong((vlong*)ticks, p);
798*7dd7cddfSDavid du Colombier 		p += sizeof(vlong);
799*7dd7cddfSDavid du Colombier 		if(hz != nil)
800*7dd7cddfSDavid du Colombier 			be2vlong((vlong*)hz, p);
801*7dd7cddfSDavid du Colombier 		return 0;
802*7dd7cddfSDavid du Colombier 	case Itiming:
803*7dd7cddfSDavid du Colombier 		n = sizeof(vlong);
804*7dd7cddfSDavid du Colombier 		if(ticks != nil)
805*7dd7cddfSDavid du Colombier 			n = 2*sizeof(vlong);
806*7dd7cddfSDavid du Colombier 		i = read(timingfd, ub, n);
807*7dd7cddfSDavid du Colombier 		if(i != n)
808*7dd7cddfSDavid du Colombier 			break;
809*7dd7cddfSDavid du Colombier 		p = ub;
810*7dd7cddfSDavid du Colombier 		if(nsec != nil)
811*7dd7cddfSDavid du Colombier 			be2vlong(nsec, ub);
812*7dd7cddfSDavid du Colombier 		p += sizeof(vlong);
813*7dd7cddfSDavid du Colombier 		if(ticks != nil)
814*7dd7cddfSDavid du Colombier 			be2vlong((vlong*)ticks, p);
815*7dd7cddfSDavid du Colombier 		if(hz != nil){
816*7dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
817*7dd7cddfSDavid du Colombier 			n = read(fastclockfd, b, sizeof(b)-1);
818*7dd7cddfSDavid du Colombier 			if(n <= 0)
819*7dd7cddfSDavid du Colombier 				break;
820*7dd7cddfSDavid du Colombier 			b[n] = 0;
821*7dd7cddfSDavid du Colombier 			*hz = strtoll(b+24, 0, 0);
822*7dd7cddfSDavid du Colombier 		}
823*7dd7cddfSDavid du Colombier 		return 0;
824*7dd7cddfSDavid du Colombier 	case Insec:
825*7dd7cddfSDavid du Colombier 		if(nsec != nil){
826*7dd7cddfSDavid du Colombier 			seek(nsecfd, 0, 0);
827*7dd7cddfSDavid du Colombier 			n = read(nsecfd, b, sizeof(b)-1);
828*7dd7cddfSDavid du Colombier 			if(n <= 0)
829*7dd7cddfSDavid du Colombier 				break;
830*7dd7cddfSDavid du Colombier 			b[n] = 0;
831*7dd7cddfSDavid du Colombier 			*nsec = strtoll(b, 0, 0);
832*7dd7cddfSDavid du Colombier 		}
833*7dd7cddfSDavid du Colombier 		if(ticks != nil){
834*7dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
835*7dd7cddfSDavid du Colombier 			n = read(fastclockfd, b, sizeof(b)-1);
836*7dd7cddfSDavid du Colombier 			if(n <= 0)
837*7dd7cddfSDavid du Colombier 				break;
838*7dd7cddfSDavid du Colombier 			b[n] = 0;
839*7dd7cddfSDavid du Colombier 			*ticks = strtoll(b, 0, 0);
840*7dd7cddfSDavid du Colombier 		}
841*7dd7cddfSDavid du Colombier 		if(hz != nil){
842*7dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
843*7dd7cddfSDavid du Colombier 			n = read(fastclockfd, b, sizeof(b)-1);
844*7dd7cddfSDavid du Colombier 			if(n <= 24)
845*7dd7cddfSDavid du Colombier 				break;
846*7dd7cddfSDavid du Colombier 			b[n] = 0;
847*7dd7cddfSDavid du Colombier 			*hz = strtoll(b+24, 0, 0);
848*7dd7cddfSDavid du Colombier 		}
849*7dd7cddfSDavid du Colombier 		return 0;
850*7dd7cddfSDavid du Colombier 	}
851*7dd7cddfSDavid du Colombier 	return -1;
852*7dd7cddfSDavid du Colombier }
853*7dd7cddfSDavid du Colombier 
854*7dd7cddfSDavid du Colombier static void
855*7dd7cddfSDavid du Colombier settime(vlong now, uvlong hz, vlong delta, int n)
856*7dd7cddfSDavid du Colombier {
857*7dd7cddfSDavid du Colombier 	uchar b[1+sizeof(vlong)+sizeof(long)], *p;
858*7dd7cddfSDavid du Colombier 
859*7dd7cddfSDavid du Colombier 	if(debug)
860*7dd7cddfSDavid du Colombier 		fprint(2, "settime(now=%lld, hz=%llud, delta=%lld, period=%d)\n", now, hz, delta, n);
861*7dd7cddfSDavid du Colombier 	if(impotent)
862*7dd7cddfSDavid du Colombier 		return;
863*7dd7cddfSDavid du Colombier 	switch(ifc){
864*7dd7cddfSDavid du Colombier 	case Ibintime:
865*7dd7cddfSDavid du Colombier 		if(now >= 0){
866*7dd7cddfSDavid du Colombier 			p = b;
867*7dd7cddfSDavid du Colombier 			*p++ = 'n';
868*7dd7cddfSDavid du Colombier 			p = vlong2be(p, now);
869*7dd7cddfSDavid du Colombier 			if(write(bintimefd, b, p-b) < 0)
870*7dd7cddfSDavid du Colombier 				sysfatal("writing /dev/bintime: %r");
871*7dd7cddfSDavid du Colombier 		}
872*7dd7cddfSDavid du Colombier 		if(delta != 0){
873*7dd7cddfSDavid du Colombier 			p = b;
874*7dd7cddfSDavid du Colombier 			*p++ = 'd';
875*7dd7cddfSDavid du Colombier 			p = vlong2be(p, delta);
876*7dd7cddfSDavid du Colombier 			p = long2be(p, n);
877*7dd7cddfSDavid du Colombier 			if(write(bintimefd, b, p-b) < 0)
878*7dd7cddfSDavid du Colombier 				sysfatal("writing /dev/bintime: %r");
879*7dd7cddfSDavid du Colombier 		}
880*7dd7cddfSDavid du Colombier 		if(hz != 0){
881*7dd7cddfSDavid du Colombier 			p = b;
882*7dd7cddfSDavid du Colombier 			*p++ = 'f';
883*7dd7cddfSDavid du Colombier 			p = vlong2be(p, hz);
884*7dd7cddfSDavid du Colombier 			if(write(bintimefd, b, p-b) < 0)
885*7dd7cddfSDavid du Colombier 				sysfatal("writing /dev/bintime: %r");
886*7dd7cddfSDavid du Colombier 		}
887*7dd7cddfSDavid du Colombier 		break;
888*7dd7cddfSDavid du Colombier 	case Itiming:
889*7dd7cddfSDavid du Colombier 	case Insec:
890*7dd7cddfSDavid du Colombier 		seek(nsecfd, 0, 0);
891*7dd7cddfSDavid du Colombier 		if(now >= 0 || delta != 0){
892*7dd7cddfSDavid du Colombier 			if(fprint(nsecfd, "%lld %lld %d", now, delta, n) < 0)
893*7dd7cddfSDavid du Colombier 				sysfatal("writing /dev/nsec: %r");
894*7dd7cddfSDavid du Colombier 		}
895*7dd7cddfSDavid du Colombier 		if(hz > 0){
896*7dd7cddfSDavid du Colombier 			seek(fastclockfd, 0, 0);
897*7dd7cddfSDavid du Colombier 			if(fprint(fastclockfd, "%lld", hz) < 0)
898*7dd7cddfSDavid du Colombier 				sysfatal("writing /dev/fastclock: %r");
899*7dd7cddfSDavid du Colombier 		}
900*7dd7cddfSDavid du Colombier 	}
901*7dd7cddfSDavid du Colombier }
902*7dd7cddfSDavid du Colombier 
903*7dd7cddfSDavid du Colombier //
904*7dd7cddfSDavid du Colombier //  set priority high
905*7dd7cddfSDavid du Colombier //
906*7dd7cddfSDavid du Colombier static void
907*7dd7cddfSDavid du Colombier setpriority(void)
908*7dd7cddfSDavid du Colombier {
909*7dd7cddfSDavid du Colombier 	int fd;
910*7dd7cddfSDavid du Colombier 	char buf[32];
911*7dd7cddfSDavid du Colombier 
912*7dd7cddfSDavid du Colombier 	sprint(buf, "/proc/%d/ctl", getpid());
913*7dd7cddfSDavid du Colombier 	fd = open(buf, ORDWR);
914*7dd7cddfSDavid du Colombier 	if(fd < 0){
915*7dd7cddfSDavid du Colombier 		fprint(2, "can't set priority\n");
916*7dd7cddfSDavid du Colombier 		return;
917*7dd7cddfSDavid du Colombier 	}
918*7dd7cddfSDavid du Colombier 	if(fprint(fd, "pri 100") < 0)
919*7dd7cddfSDavid du Colombier 		fprint(2, "can't set priority\n");
920*7dd7cddfSDavid du Colombier 	close(fd);
921*7dd7cddfSDavid du Colombier }
922