/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #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 voidusage(void){ fprint(2, "usage: %s [-a accuracy][-d dir][-I rootid][-s net]" "[-S stratum][-DfGilLnrU] timesource ...\n", argv0); exits("usage");} voidmain(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); }}Lines 1–500 of 1387. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #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); } }