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*)ℴ
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*)ℴ
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