#include #include #include #include #include /* nanosecond times */ #define SEC 1000000000LL #define MIN (60LL*SEC) #define HOUR (60LL*MIN) #define DAY (24LL*HOUR) enum { Fs, Rtc, Ntp, Utc, Gps, HZAvgSecs= 3*60, /* target averaging period for the frequency in seconds */ MinSampleSecs= 60, /* minimum sampling time in seconds */ }; char *dir = "/tmp"; // directory sample files live in char *logfile = "timesync"; char *timeserver; char *Rootid; int utcfil; int gpsfil; int debug; int impotent; int logging; int type; int gmtdelta; // rtc+gmtdelta = gmt uvlong avgerr; // ntp server info int stratum = 14; vlong mydisp, rootdisp; vlong mydelay, rootdelay; vlong avgdelay; vlong lastutc; uchar rootid[4]; char *sysid; int myprec; // list of time samples typedef struct Sample Sample; struct Sample { Sample *next; uvlong ticks; vlong ltime; vlong stime; }; // ntp packet typedef struct NTPpkt NTPpkt; struct NTPpkt { uchar mode; uchar stratum; uchar poll; uchar precision; uchar rootdelay[4]; uchar rootdisp[4]; uchar rootid[4]; uchar refts[8]; uchar origts[8]; // departed client uchar recvts[8]; // arrived at server uchar xmitts[8]; // departed server uchar keyid[4]; uchar digest[16]; }; // ntp server typedef struct NTPserver NTPserver; struct NTPserver { NTPserver *next; char *name; uchar stratum; uchar precision; vlong rootdelay; vlong rootdisp; vlong rtt; vlong dt; }; NTPserver *ntpservers; enum { NTPSIZE= 48, // basic ntp packet NTPDIGESTSIZE= 20, // key and digest }; // error bound of last sample ulong ε; static void addntpserver(char *name); static int adjustperiod(vlong diff, vlong accuracy, int secs); static void background(void); static int caperror(vlong dhz, int tsecs, vlong taccuracy); static long fstime(void); static int gettime(vlong *nsec, uvlong *ticks, uvlong *hz); // returns time, ticks, hz static int getclockprecision(vlong); static vlong gpssample(void); static void hnputts(void *p, vlong nsec); static void hnputts(void *p, vlong nsec); static void inittime(void); static vlong nhgetts(void *p); static vlong nhgetts(void *p); static void ntpserver(char*); static vlong ntpsample(void); static int ntptimediff(NTPserver *ns); static int openfreqfile(void); static vlong readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz); static long rtctime(void); static vlong sample(long (*get)(void)); static void setpriority(void); static void setrootid(char *d); static void settime(vlong now, uvlong hz, vlong delta, int n); // set time, hz, delta, period static vlong utcsample(void); static uvlong vabs(vlong); static uvlong whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz, vlong dt, vlong ticks, vlong period); static void writefreqfile(int fd, vlong hz, int secs, vlong diff); // ((1970-1900)*365 + 17/*leap days*/)*24*60*60 #define EPOCHDIFF 2208988800UL void main(int argc, char **argv) { int i; int secs; // sampling period int tsecs; // temporary sampling period int t, fd; Sample *s, *x, *first, **l; vlong diff, accuracy, taccuracy; uvlong hz, minhz, maxhz, period, nhz; char *servenet[4]; int nservenet; char *a; Tm tl, tg; type = Fs; // by default, sync with the file system debug = 0; accuracy = 1000000LL; // default accuracy is 1 millisecond nservenet = 0; tsecs = secs = MinSampleSecs; timeserver = ""; ARGBEGIN{ case 'a': a = ARGF(); if(a == nil) sysfatal("bad accuracy specified"); accuracy = strtoll(a, 0, 0); // accuracy specified in ns if(accuracy <= 1LL) sysfatal("bad accuracy specified"); break; case 'f': type = Fs; stratum = 2; break; case 'r': type = Rtc; stratum = 0; break; case 'U': type = Utc; stratum = 1; break; case 'G': type = Gps; stratum = 1; break; case 'n': type = Ntp; break; case 'D': debug = 1; break; case 'd': dir = ARGF(); break; case 'L': // // Assume time source in local time rather than GMT. // Calculate difference so that rtctime can return GMT. // This is useful with the rtc on PC's that run Windows // since Windows keeps the local time in the rtc. // t = time(0); tl = *localtime(t); tg = *gmtime(t); // if the years are different, we're at most a day off, so just rewrite if(tl.year < tg.year){ tg.year--; tg.yday = tl.yday + 1; }else if(tl.year > tg.year){ tl.year--; tl.yday = tg.yday+1; } assert(tl.year == tg.year); tg.sec -= tl.sec; tg.min -= tl.min; tg.hour -= tl.hour; tg.yday -= tl.yday; gmtdelta = tg.sec+60*(tg.min+60*(tg.hour+tg.yday*24)); assert(abs(gmtdelta) <= 24*60*60); break; case 'i': impotent = 1; break; case 'I': Rootid = ARGF(); break; case 's': if(nservenet >= nelem(servenet)) sysfatal("too many networks to serve on"); a = ARGF(); if(a == nil) sysfatal("must specify network to serve on"); servenet[nservenet++] = a; break; case 'l': logging = 1; break; case 'S': a = ARGF(); if(a == nil) sysfatal("bad stratum specified"); stratum = strtoll(a, 0, 0); break; }ARGEND; fmtinstall('E', eipfmt); fmtinstall('I', eipfmt); fmtinstall('V', eipfmt); sysid = getenv("sysname"); // detach from the current namespace if(debug) rfork(RFNAMEG); switch(type){ case Utc: if(argc > 0) timeserver = argv[0]; else sysfatal("bad time source"); break; case Gps: if(argc > 0) timeserver = argv[0]; else timeserver = "/mnt/gps/time"; break; case Fs: if(argc > 0) timeserver = argv[0]; else timeserver = "/srv/boot"; break; case Ntp: if(argc > 0){ for(i = 0; i >1; for(;; background(),sleep(tsecs*(1000))){ s = mallocz(sizeof(*s), 1); diff = 0; // get times for this sample ε = ~0; switch(type){ case Fs: s->stime = sample(fstime); break; case Rtc: s->stime = sample(rtctime); break; case Utc: s->stime = utcsample(); if(s->stime == 0LL){ if(logging) syslog(0, logfile, "no sample"); free(s); if (secs > 60 * 15) tsecs = 60*15; continue; } break; case Ntp: diff = ntpsample(); if(diff == 0LL){ if(logging) syslog(0, logfile, "no sample"); free(s); if(secs > 60*15) tsecs = 60*15; continue; } break; case Gps: diff = gpssample(); if(diff == 0LL){ if(logging) syslog(0, logfile, "no sample"); free(s); if(secs > 60*15) tsecs = 60*15; continue; } } // use fastest method to read local clock and ticks gettime(&s->ltime, &s->ticks, 0); if(type == Ntp || type == Gps) s->stime = s->ltime + diff; // if the sample was bad, ignore it if(s->stime < 0){ free(s); continue; } // reset local time diff = s->stime - s->ltime; if(diff > 10*SEC || diff < -10*SEC){ // we're way off, just set the time secs = MinSampleSecs; settime(s->stime, 0, 0, 0); } else { // keep a running average of the error. avgerr = (avgerr>>1) + (vabs(diff)>>1); // the time to next sample depends on how good or // bad we're doing. tsecs = secs = adjustperiod(diff, accuracy, secs); // work off the fixed difference. This is done // by adding a ramp to the clock. Each 100th of a // second (or so) the kernel will add diff/(4*secs*100) // to the clock. we only do 1/4 of the difference per // period to dampen any measurement noise. // // any difference greater than ε we work off during the // sampling period. if(abs(diff) > ε){ if(diff > 0) settime(-1, 0, diff-((3*ε)/4), secs); else settime(-1, 0, diff+((3*ε)/4), secs); } else settime(-1, 0, diff, 4*secs); } if(debug) fprint(2, "δ %lld avgδ %lld f %lld\n", diff, avgerr, hz); // dump old samples (keep at least one) while(first != nil){ if(first->next == nil) break; if(s->stime - first->next->stime < DAY) break; x = first; first = first->next; free(x); } // The sampling error is limited by the total error. If // we make sure the sampling period is at least 16 million // times the average error, we should calculate a frequency // with on average a 1e-7 error. // // So that big hz changes don't blow our accuracy requirement, // we shorten the period to make sure that δhz*secs will be // greater than the accuracy limit. period = avgerr<<24; for(x = first; x != nil; x = x->next){ if(s->stime - x->stime < period) break; if(x->next == nil || s->stime - x->next->stime < period) break; } if(x != nil){ nhz = whatisthefrequencykenneth( hz, minhz, maxhz, s->stime - x->stime, s->ticks - x->ticks, period); tsecs = caperror(vabs(nhz-hz), tsecs, taccuracy); hz = nhz; writefreqfile(fd, hz, (s->stime - x->stime)/SEC, diff); } // add current sample to list. *l = s; l = &s->next; if(logging) syslog(0, logfile, "δ %lld avgδ %lld hz %lld", diff, avgerr, hz); } } // // adjust the sampling period with some histeresis // static int adjustperiod(vlong diff, vlong accuracy, int secs) { uvlong absdiff; absdiff = vabs(diff); if(absdiff < (accuracy>>1)) secs += 60; else if(absdiff > accuracy) secs >>= 1; else secs -= 60; if(secs < MinSampleSecs) secs = MinSampleSecs; return secs; } // // adjust the frequency // static uvlong whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz, vlong dt, vlong ticks, vlong period) { static mpint *mpdt; static mpint *mpticks; static mpint *mphz; static mpint *mpbillion; uvlong ohz = hz; // sanity check if(dt <= 0 || ticks <= 0) return hz; if(mphz == nil){ mphz = mpnew(0); mpbillion = uvtomp(SEC, nil); } // hz = (ticks*SEC)/dt mpdt = vtomp(dt, mpdt); mpticks = vtomp(ticks, mpticks); mpmul(mpticks, mpbillion, mpticks); mpdiv(mpticks, mpdt, mphz, nil); hz = mptoui(mphz); // sanity if(hz < minhz || hz > maxhz) return ohz; // damp the change if we're shorter than the target period if(period > dt) hz = (12ULL*ohz + 4ULL*hz)/16ULL; settime(-1, hz, 0, 0); return hz; } // We may be changing the frequency to match a bad measurement // or to match a condition no longer in effet. To make sure // that this doesn't blow our error budget over the next measurement // period, shorten the period to make sure that δhz*secs will be // less than the accuracy limit. Here taccuracy is accuracy converted // from nanoseconds to ticks. static int caperror(vlong dhz, int tsecs, vlong taccuracy) { if(dhz*tsecs <= taccuracy) return tsecs; if(debug) fprint(2, "δhz %lld tsecs %d tacc %lld\n", dhz, tsecs, taccuracy); tsecs = taccuracy/dhz; if(tsecs < MinSampleSecs) tsecs = MinSampleSecs; return tsecs; } // // kernel interface // enum { Ibintime, Insec, Itiming, }; int ifc; int bintimefd = -1; int timingfd = -1; int nsecfd = -1; int fastclockfd = -1; static void inittime(void) { int mode; if(impotent) mode = OREAD; else mode = ORDWR; // bind in clocks if(access("/dev/time", 0) < 0) bind("#c", "/dev", MAFTER); if(access("/dev/rtc", 0) < 0) bind("#r", "/dev", MAFTER); // figure out what interface we have ifc = Ibintime; bintimefd = open("/dev/bintime", mode); if(bintimefd >= 0) return; ifc = Insec; nsecfd = open("/dev/nsec", mode); if(nsecfd < 0) sysfatal("opening /dev/nsec"); fastclockfd = open("/dev/fastclock", mode); if(fastclockfd < 0) sysfatal("opening /dev/fastclock"); timingfd = open("/dev/timing", OREAD); if(timingfd < 0) return; ifc = Itiming; } // // convert binary numbers from/to kernel // static uvlong uvorder = 0x0001020304050607ULL; static uchar* be2vlong(vlong *to, uchar *f) { uchar *t, *o; int i; t = (uchar*)to; o = (uchar*)&uvorder; for(i = 0; i < sizeof(vlong); i++) t[o[i]] = f[i]; return f+sizeof(vlong); } static uchar* vlong2be(uchar *t, vlong from) { uchar *f, *o; int i; f = (uchar*)&from; o = (uchar*)&uvorder; for(i = 0; i < sizeof(vlong); i++) t[i] = f[o[i]]; return t+sizeof(vlong); } static long order = 0x00010203; static uchar* be2long(long *to, uchar *f) { uchar *t, *o; int i; t = (uchar*)to; o = (uchar*)ℴ for(i = 0; i < sizeof(long); i++) t[o[i]] = f[i]; return f+sizeof(long); } static uchar* long2be(uchar *t, long from) { uchar *f, *o; int i; f = (uchar*)&from; o = (uchar*)ℴ for(i = 0; i < sizeof(long); i++) t[i] = f[o[i]]; return t+sizeof(long); } // // read ticks and local time in nanoseconds // static int gettime(vlong *nsec, uvlong *ticks, uvlong *hz) { int i, n; uchar ub[3*8], *p; char b[2*24+1]; switch(ifc){ case Ibintime: n = sizeof(vlong); if(hz != nil) n = 3*sizeof(vlong); if(ticks != nil) n = 2*sizeof(vlong); i = read(bintimefd, ub, n); if(i != n) break; p = ub; if(nsec != nil) be2vlong(nsec, ub); p += sizeof(vlong); if(ticks != nil) be2vlong((vlong*)ticks, p); p += sizeof(vlong); if(hz != nil) be2vlong((vlong*)hz, p); return 0; case Itiming: n = sizeof(vlong); if(ticks != nil) n = 2*sizeof(vlong); i = read(timingfd, ub, n); if(i != n) break; p = ub; if(nsec != nil) be2vlong(nsec, ub); p += sizeof(vlong); if(ticks != nil) be2vlong((vlong*)ticks, p); if(hz != nil){ seek(fastclockfd, 0, 0); n = read(fastclockfd, b, sizeof(b)-1); if(n <= 0) break; b[n] = 0; *hz = strtoll(b+24, 0, 0); } return 0; case Insec: if(nsec != nil){ seek(nsecfd, 0, 0); n = read(nsecfd, b, sizeof(b)-1); if(n <= 0) break; b[n] = 0; *nsec = strtoll(b, 0, 0); } if(ticks != nil){ seek(fastclockfd, 0, 0); n = read(fastclockfd, b, sizeof(b)-1); if(n <= 0) break; b[n] = 0; *ticks = strtoll(b, 0, 0); } if(hz != nil){ seek(fastclockfd, 0, 0); n = read(fastclockfd, b, sizeof(b)-1); if(n <= 24) break; b[n] = 0; *hz = strtoll(b+24, 0, 0); } return 0; } return -1; } static void settime(vlong now, uvlong hz, vlong delta, int n) { uchar b[1+sizeof(vlong)+sizeof(long)], *p; if(debug) fprint(2, "settime(now=%lld, hz=%llud, delta=%lld, period=%d)\n", now, hz, delta, n); if(impotent) return; switch(ifc){ case Ibintime: if(now >= 0){ p = b; *p++ = 'n'; p = vlong2be(p, now); if(write(bintimefd, b, p-b) < 0) sysfatal("writing /dev/bintime: %r"); } if(delta != 0){ p = b; *p++ = 'd'; p = vlong2be(p, delta); p = long2be(p, n); if(write(bintimefd, b, p-b) < 0) sysfatal("writing /dev/bintime: %r"); } if(hz != 0){ p = b; *p++ = 'f'; p = vlong2be(p, hz); if(write(bintimefd, b, p-b) < 0) sysfatal("writing /dev/bintime: %r"); } break; case Itiming: case Insec: seek(nsecfd, 0, 0); if(now >= 0 || delta != 0){ if(fprint(nsecfd, "%lld %lld %d", now, delta, n) < 0) sysfatal("writing /dev/nsec: %r"); } if(hz > 0){ seek(fastclockfd, 0, 0); if(fprint(fastclockfd, "%lld", hz) < 0) sysfatal("writing /dev/fastclock: %r"); } } } // // set priority high and wire process to a processor // static void setpriority(void) { int fd; char buf[32]; sprint(buf, "/proc/%d/ctl", getpid()); fd = open(buf, ORDWR); if(fd < 0){ fprint(2, "can't set priority\n"); return; } if(fprint(fd, "pri 100") < 0) fprint(2, "can't set priority\n"); if(fprint(fd, "wired 2") < 0) fprint(2, "can't wire process\n"); close(fd); } // convert to ntp timestamps static void hnputts(void *p, vlong nsec) { uchar *a; ulong tsh; ulong tsl; a = p; // zero is a special case if(nsec == 0) return; tsh = (nsec/SEC); nsec -= tsh*SEC; tsl = (nsec<<32)/SEC; hnputl(a, tsh+EPOCHDIFF); hnputl(a+4, tsl); } // convert from ntp timestamps static vlong nhgetts(void *p) { uchar *a; ulong tsh, tsl; vlong nsec; a = p; tsh = nhgetl(a); tsl = nhgetl(a+4); nsec = tsl*SEC; nsec >>= 32; nsec += (tsh - EPOCHDIFF)*SEC; return nsec; } // convert to ntp 32 bit fixed point static void hnputfp(void *p, vlong nsec) { uchar *a; ulong fp; a = p; fp = nsec/(SEC/((vlong)(1<<16))); hnputl(a, fp); } // convert from ntp fixed point to nanosecs static vlong nhgetfp(void *p) { uchar *a; ulong fp; vlong nsec; a = p; fp = nhgetl(a); nsec = ((vlong)fp)*(SEC/((vlong)(1<<16))); return nsec; } // get network address of the server static void setrootid(char *d) { char buf[128]; int fd, n; char *p; snprint(buf, sizeof(buf), "%s/remote", d); fd = open(buf, OREAD); if(fd < 0) return; n = read(fd, buf, sizeof buf); close(fd); if(n <= 0) return; p = strchr(buf, '!'); if(p != nil) *p = 0; v4parseip(rootid, buf); } static void ding(void*, char *s) { if(strstr(s, "alarm") != nil) noted(NCONT); noted(NDFLT); } static void addntpserver(char *name) { NTPserver *ns, **l; ns = mallocz(sizeof(NTPserver), 1); if(ns == nil) sysfatal("addntpserver: %r"); timeserver = strdup(name); ns->name = name; for(l = &ntpservers; *l != nil; l = &(*l)->next) ; *l = ns; } // // sntp client, we keep calling if the delay seems // unusually high, i.e., 30% longer than avg. // static int ntptimediff(NTPserver *ns) { int fd, tries, n; NTPpkt ntpin, ntpout; vlong dt, recvts, origts, xmitts, destts, x; char dir[64]; fd = dial(netmkaddr(ns->name, "udp", "ntp"), 0, dir, 0); if(fd < 0){ syslog(0, logfile, "can't reach %s: %r", ns->name); return -1; } setrootid(dir); notify(ding); memset(&ntpout, 0, sizeof(ntpout)); ntpout.mode = 3 | (3 << 3); for(tries = 0; tries < 3; tries++){ alarm(2*1000); gettime(&x, 0, 0); hnputts(ntpout.xmitts, x); if(write(fd, &ntpout, NTPSIZE) < 0){ alarm(0); continue; } n = read(fd, &ntpin, sizeof(ntpin)); alarm(0); gettime(&destts, 0, 0); if(n >= NTPSIZE){ close(fd); // we got one, use it recvts = nhgetts(ntpin.recvts); origts = nhgetts(ntpin.origts); xmitts = nhgetts(ntpin.xmitts); dt = ((recvts - origts) + (xmitts - destts))/2; // save results ns->rtt = ((destts - origts) - (xmitts - recvts))/2; ns->dt = dt; ns->stratum = ntpin.stratum; ns->precision = ntpin.precision; ns->rootdelay = nhgetfp(ntpin.rootdelay); ns->rootdisp = nhgetfp(ntpin.rootdisp); if(debug) fprint(2, "ntp %s stratum %d ntpdelay(%lld)\n", ns->name, ntpin.stratum, ns->rtt); return 0; } // try again sleep(250); } close(fd); return -1; } static vlong gpssample(void) { vlong l, g, d; int i, n; char *v[4], buf[128]; d = -1000000000000000000LL; for(i = 0; i < 5; i++){ sleep(1100); seek(gpsfil, 0, 0); n = read(gpsfil, buf, sizeof buf - 1); if (n <= 0) return(0LL); buf[n] = 0; n = tokenize(buf, v, nelem(v)); if(n != 4) return(0LL); if(strcmp(v[3], "A") != 0) return(0LL); g = atoll(v[1]); l = atoll(v[2]); if(g-l > d) d = g-l; } return(d); } static vlong ntpsample(void) { NTPserver *tns, *ns; vlong metric, x; metric = 1000LL*SEC; ns = nil; for(tns = ntpservers; tns != nil; tns = tns->next){ if(ntptimediff(tns) < 0) continue; x = vabs(tns->rootdisp) + (vabs(tns->rtt+tns->rootdelay)>>1); if(debug) fprint(2, "ntp %s rootdelay %lld rootdisp %lld metric %lld\n", tns->name, tns->rootdelay, tns->rootdisp, x); if(x < metric){ metric = x; ns = tns; } } if(ns == nil) return 0LL; // save data for our server rootdisp = ns->rootdisp; rootdelay = ns->rootdelay; mydelay = ns->rtt; mydisp = avgerr; if(ns->stratum == 0) stratum = 0; else stratum = ns->stratum + 1; ε = abs(ns->rtt/2); return ns->dt; } // // sample the utc file // static vlong utcsample(void) { vlong s; int n; char *v[2], buf[128]; s = 0; seek(utcfil, 0, 0); n = read(utcfil, buf, sizeof buf - 1); if (n <= 0) return(0LL); buf[n] = 0; n = tokenize(buf, v, nelem(v)); if (strcmp(v[0], "0") == 0) return(0LL); if (n == 2) { gettime(&s, nil, nil); s -= atoll(v[1]); } lastutc = atoll(v[0]) + s; return(lastutc); } // // sntp server // static int openlisten(char *net) { int fd, cfd; char data[128]; char devdir[40]; sprint(data, "%s/udp!*!ntp", net); cfd = announce(data, devdir); if(cfd < 0) sysfatal("can't announce"); if(fprint(cfd, "headers") < 0) sysfatal("can't set header mode"); fprint(cfd, "oldheaders"); sprint(data, "%s/data", devdir); fd = open(data, ORDWR); if(fd < 0) sysfatal("open udp data"); return fd; } static void ntpserver(char *servenet) { int fd, n; NTPpkt *ntp; char buf[512]; int vers, mode; vlong recvts, x; fd = openlisten(servenet); if (Rootid == nil) switch(type){ case Fs: Rootid = "WWV"; break; case Rtc: Rootid = "LOCL"; break; case Utc: Rootid = "UTC"; break; case Ntp: /* set by the ntp client */ break; } if (Rootid != nil) memmove(rootid, Rootid, strlen(Rootid) > 4 ? 4 : strlen(Rootid)); for(;;){ n = read(fd, buf, sizeof(buf)); gettime(&recvts, 0, 0); if(n < 0) return; if(n < OUdphdrsize + NTPSIZE) continue; ntp = (NTPpkt*)(buf+OUdphdrsize); mode = ntp->mode & 7; vers = (ntp->mode>>3) & 7; if(mode != 3) continue; ntp->mode = (vers<<3)|4; ntp->stratum = stratum; ntp->precision = myprec; hnputfp(ntp->rootdelay, rootdelay + mydelay); hnputfp(ntp->rootdisp, rootdisp + mydisp); hnputts(ntp->refts, lastutc); memmove(ntp->origts, ntp->xmitts, sizeof(ntp->origts)); hnputts(ntp->recvts, recvts); memmove(ntp->rootid, rootid, sizeof(ntp->rootid)); gettime(&x, 0, 0); hnputts(ntp->xmitts, x); write(fd, buf, NTPSIZE+OUdphdrsize); } } // // get the current time from the file system // static long fstime(void) { Dir *d; ulong t; d = dirstat("/n/boot"); if(d != nil){ t = d->atime; free(d); } else t = 0; return t; } // // get the current time from the real time clock // static long rtctime(void) { char b[20]; static int f = -1; int i, retries; memset(b, 0, sizeof(b)); for(retries = 0; retries < 100; retries++){ if(f < 0) f = open("/dev/rtc", OREAD|OCEXEC); if(f < 0) break; if(seek(f, 0, 0) < 0 || (i = read(f, b, sizeof(b))) < 0){ close(f); f = -1; } else { if(i != 0) break; } } return strtoul(b, 0, 10)+gmtdelta; } // // Sample a clock. We wait for the clock to always // be at the leading edge of a clock period. // static vlong sample(long (*get)(void)) { long this, last; vlong start, end; /* * wait for the second to change */ last = (*get)(); for(;;){ gettime(&start, 0, 0); this = (*get)(); gettime(&end, 0, 0); if(this != last) break; last = this; } return SEC*this - (end-start)/2; } // // the name of the frequency file has the method and possibly the // server name encoded in it. // static int openfreqfile(void) { char *p; int fd; if(sysid == nil) return -1; switch(type){ case Ntp: p = smprint("%s/ts.%s.%d.%s", dir, sysid, type, timeserver); break; default: p = smprint("%s/ts.%s.%d", dir, sysid, type); break; } fd = open(p, ORDWR); if(fd < 0) fd = create(p, ORDWR, 0666); free(p); if(fd < 0) return -1; return fd; } // // the file contains the last known frequency and the // number of seconds it was sampled over // static vlong readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz) { int n; char buf[128]; vlong hz; n = read(fd, buf, sizeof(buf)-1); if(n <= 0) return ohz; buf[n] = 0; hz = strtoll(buf, nil, 0); if(hz > maxhz || hz < minhz) return ohz; settime(-1, hz, 0, 0); return hz; } // // remember hz and averaging period // static void writefreqfile(int fd, vlong hz, int secs, vlong diff) { long now; static long last; if(fd < 0) return; now = time(0); if(now - last < 10*60) return; last = now; if(seek(fd, 0, 0) < 0) return; fprint(fd, "%lld %d %d %lld\n", hz, secs, type, diff); } static uvlong vabs(vlong x) { if(x < 0LL) return (uvlong)-x; else return (uvlong)x; } static void background(void) { static int inbackground; if(inbackground) return; if(!debug) { switch(rfork(RFPROC|RFFDG|RFNAMEG|RFNOTEG|RFNOWAIT)){ case -1: sysfatal("forking: %r"); break; case 0: break; default: exits(0); } } inbackground = 1; } static int getclockprecision(vlong hz) { int i; i = 8; while(hz > 0){ i--; hz >>= 1; } return i; }