123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377 |
- #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,
- Gps,
- HZAvgSecs= 3*60, /* target averaging period for 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
- static void
- usage(void)
- {
- fprint(2, "usage: %s [-a accuracy][-d dir][-I rootid][-s net]"
- "[-S stratum][-DfGilLnrU] timesource ...\n", argv0);
- exits("usage");
- }
- void
- main(int argc, char **argv)
- {
- int i, t, fd, nservenet;
- int secs; /* sampling period */
- int tsecs; /* temporary sampling period */
- uvlong hz, minhz, maxhz, period, nhz;
- vlong diff, accuracy, taccuracy;
- char *servenet[4];
- Sample *s, *x, *first, **l;
- 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':
- accuracy = strtoll(EARGF(usage()), 0, 0); /* specified in ns */
- if(accuracy <= 1)
- sysfatal("bad accuracy specified");
- break;
- case 'd':
- dir = EARGF(usage());
- break;
- case 'D':
- debug = 1;
- break;
- case 'f':
- type = Fs;
- stratum = 2;
- break;
- case 'G':
- type = Gps;
- stratum = 1;
- break;
- case 'i':
- impotent = 1;
- break;
- case 'I':
- Rootid = EARGF(usage());
- break;
- case 'l':
- logging = 1;
- 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 'n':
- type = Ntp;
- break;
- case 'r':
- type = Rtc;
- stratum = 0;
- break;
- case 'U':
- type = Utc;
- stratum = 1;
- break;
- case 's':
- if(nservenet >= nelem(servenet))
- sysfatal("too many networks to serve on");
- servenet[nservenet++] = EARGF(usage());
- break;
- case 'S':
- stratum = strtoll(EARGF(usage()), 0, 0);
- break;
- default:
- usage();
- }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 < 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/10;
- maxhz = hz*10;
- 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", timeserver);
- if(amount(fd, "/n/boot", MREPL, "") < 0)
- sysfatal("mounting %s: %r", timeserver);
- close(fd);
- break;
- case Rtc:
- bind("#r", "/dev", MAFTER);
- if(access("/dev/rtc", AREAD) < 0)
- sysfatal("accessing /dev/rtc: %r");
- break;
- case Utc:
- fd = open(timeserver, OREAD);
- if(fd < 0)
- sysfatal("opening %s: %r", timeserver);
- utcfil = fd;
- break;
- case Gps:
- fd = open(timeserver, OREAD);
- if(fd < 0)
- sysfatal("opening %s: %r", timeserver);
- gpsfil = 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");
- case 0:
- ntpserver(servenet[i]);
- _exits(0);
- }
- /* 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 */
- ε = ~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 ||
- 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)
- {
- uvlong ohz = hz;
- static mpint *mpdt, *mpticks, *mphz, *mpbillion;
- /* 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 effect. 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, 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];
- static int whined;
- notify(ding);
- alarm(30*1000); /* don't wait forever if ns->name is unreachable */
- fd = dial(netmkaddr(ns->name, "udp", "ntp"), 0, dir, 0);
- if(fd < 0){
- if (!whined++)
- syslog(0, logfile, "can't reach %s: %r", ns->name);
- return -1;
- }
- setrootid(dir);
- 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 0;
- buf[n] = 0;
- n = tokenize(buf, v, nelem(v));
- if(n != 4 || strcmp(v[3], "A") != 0)
- return 0;
- 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 0;
- /* 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 0;
- buf[n] = 0;
- n = tokenize(buf, v, nelem(v));
- if (strcmp(v[0], "0") == 0)
- return 0;
- 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], 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");
- sprint(data, "%s/data", devdir);
- fd = open(data, ORDWR);
- if(fd < 0)
- sysfatal("open %s: %r", data);
- return fd;
- }
- static void
- ntpserver(char *servenet)
- {
- int fd, n, vers, mode;
- vlong recvts, x;
- char buf[512];
- NTPpkt *ntp;
- fd = openlisten(servenet);
- if (Rootid == nil)
- switch(type){
- case Fs:
- Rootid = "WWV";
- break;
- case Rtc:
- Rootid = "LOCL";
- break;
- case Utc:
- Rootid = "UTC";
- break;
- case Gps:
- Rootid = "GPS";
- 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) {
- /* don't croak on input error, but don't spin either */
- sleep(500);
- continue;
- }
- if(n < Udphdrsize + NTPSIZE)
- continue;
- ntp = (NTPpkt*)(buf + Udphdrsize);
- 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 + Udphdrsize);
- }
- }
- /*
- * 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 < 0)
- return -x;
- else
- return 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;
- }
|