12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298 |
- #include <u.h>
- #include <libc.h>
- #include <auth.h>
- #include <ip.h>
- #include <mp.h>
- /* nanosecond times */
- #define SEC 1000000000LL
- #define MIN (60LL*SEC)
- #define HOUR (60LL*MIN)
- #define DAY (24LL*HOUR)
- enum {
- Fs,
- Rtc,
- Ntp,
- Utc,
- 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 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
- };
- 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 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;
- timeserver = ARGF();
- if (timeserver == nil)
- sysfatal("bad time source");
- 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 Fs:
- if(argc > 0)
- timeserver = argv[0];
- else
- timeserver = "/srv/boot";
- break;
- case Ntp:
- if(argc > 0){
- for(i = 0; i <argc; i++)
- addntpserver(argv[i]);
- } else {
- addntpserver("$ntp");
- }
- break;
- }
- setpriority();
- // figure out our time interface and initial frequency
- inittime();
- gettime(0, 0, &hz);
- minhz = hz - (hz>>2);
- maxhz = hz + (hz>>2);
- myprec = getclockprecision(hz);
- // convert the accuracy from nanoseconds to ticks
- taccuracy = hz*accuracy/SEC;
- //
- // bind in clocks
- //
- switch(type){
- case Fs:
- fd = open(timeserver, ORDWR);
- if(fd < 0)
- sysfatal("opening %s: %r\n", timeserver);
- if(amount(fd, "/n/boot", MREPL, "") < 0)
- sysfatal("mounting %s: %r\n", timeserver);
- close(fd);
- break;
- case Rtc:
- bind("#r", "/dev", MAFTER);
- if(access("/dev/rtc", AREAD) < 0)
- sysfatal("accessing /dev/rtc: %r\n");
- break;
- case Utc:
- fd = open(timeserver, OREAD);
- if(fd < 0)
- sysfatal("opening %s: %r\n", timeserver);
- utcfil = fd;
- break;
- }
- //
- // start a local ntp server(s)
- //
- for(i = 0; i < nservenet; i++){
- switch(rfork(RFPROC|RFFDG|RFMEM|RFNOWAIT)){
- case -1:
- sysfatal("forking: %r");
- break;
- case 0:
- ntpserver(servenet[i]);
- _exits(0);
- break;
- default:
- break;
- }
- }
- // get the last known frequency from the file
- fd = openfreqfile();
- hz = readfreqfile(fd, hz, minhz, maxhz);
- // this is the main loop. it gets a sample, adjusts the
- // clock and computes a sleep period until the next loop.
- // we balance frequency drift against the length of the
- // period to avoid blowing the accuracy limit.
- first = nil;
- l = &first;
- avgerr = accuracy>>1;
- for(;; background(),sleep(tsecs*(1000))){
- s = mallocz(sizeof(*s), 1);
- diff = 0;
- // get times for this sample
- 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;
- }
- // use fastest method to read local clock and ticks
- gettime(&s->ltime, &s->ticks, 0);
- if(type == Ntp)
- 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.
- 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
- 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;
- 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 buf[29];
- int fd;
- if(sysid == nil)
- return -1;
- switch(type){
- case Ntp:
- snprint(buf, sizeof buf, "%s/ts.%s.%d.%s", dir, sysid, type, timeserver);
- break;
- default:
- snprint(buf, sizeof buf, "%s/ts.%s.%d", dir, sysid, type);
- break;
- }
- fd = open(buf, ORDWR);
- if(fd < 0)
- fd = create(buf, ORDWR, 0666);
- 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;
- }
|