2018-01-06 01:08:25 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
/* Portions of this file are Copyright (C) 2015-2018 Giacomo Tesio <giacomo@tesio.it>
|
|
|
|
* See /doc/license/gpl-2.0.txt for details about the licensing.
|
|
|
|
*/
|
|
|
|
/* Portions of this file are Copyright (C) 9front's team.
|
|
|
|
* See /doc/license/9front-mit for details about the licensing.
|
2021-12-06 17:39:58 +01:00
|
|
|
* See http://git.9front.org/plan9front/plan9front/HEAD/info.html for a list of authors.
|
2018-01-06 01:08:25 +01:00
|
|
|
*/
|
2016-11-25 17:18:40 +01:00
|
|
|
#include <u.h>
|
2017-04-19 23:33:14 +02:00
|
|
|
#include <lib9.h>
|
2017-10-20 00:55:05 +02:00
|
|
|
#include <envvars.h>
|
2016-11-25 17:18:40 +01:00
|
|
|
#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 */
|
|
|
|
uint64_t avgerr;
|
|
|
|
|
|
|
|
/* ntp server info */
|
|
|
|
int stratum = 14;
|
|
|
|
int64_t mydisp, rootdisp;
|
|
|
|
int64_t mydelay, rootdelay;
|
|
|
|
int64_t avgdelay;
|
|
|
|
int64_t lastutc;
|
|
|
|
uint8_t rootid[4];
|
|
|
|
char *sysid;
|
|
|
|
int myprec;
|
|
|
|
|
|
|
|
/* list of time samples */
|
|
|
|
typedef struct Sample Sample;
|
|
|
|
struct Sample
|
|
|
|
{
|
|
|
|
Sample *next;
|
|
|
|
uint64_t ticks;
|
|
|
|
int64_t ltime;
|
|
|
|
int64_t stime;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* ntp packet */
|
|
|
|
typedef struct NTPpkt NTPpkt;
|
|
|
|
struct NTPpkt
|
|
|
|
{
|
|
|
|
uint8_t mode;
|
|
|
|
uint8_t stratum;
|
|
|
|
uint8_t poll;
|
|
|
|
uint8_t precision;
|
|
|
|
uint8_t rootdelay[4];
|
|
|
|
uint8_t rootdisp[4];
|
|
|
|
uint8_t rootid[4];
|
|
|
|
uint8_t refts[8];
|
|
|
|
uint8_t origts[8]; /* departed client */
|
|
|
|
uint8_t recvts[8]; /* arrived at server */
|
|
|
|
uint8_t xmitts[8]; /* departed server */
|
|
|
|
uint8_t keyid[4];
|
|
|
|
uint8_t digest[16];
|
|
|
|
};
|
|
|
|
|
|
|
|
/* ntp server */
|
|
|
|
typedef struct NTPserver NTPserver;
|
|
|
|
struct NTPserver
|
|
|
|
{
|
|
|
|
NTPserver *next;
|
|
|
|
char *name;
|
|
|
|
uint8_t stratum;
|
|
|
|
uint8_t precision;
|
|
|
|
int64_t rootdelay;
|
|
|
|
int64_t rootdisp;
|
|
|
|
int64_t rtt;
|
|
|
|
int64_t dt;
|
|
|
|
};
|
|
|
|
|
|
|
|
NTPserver *ntpservers;
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
NTPSIZE= 48, /* basic ntp packet */
|
|
|
|
NTPDIGESTSIZE= 20, /* key and digest */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* error bound of last sample */
|
|
|
|
uint32_t epsilon;
|
|
|
|
|
|
|
|
static void addntpserver(char *name);
|
|
|
|
static int adjustperiod(int64_t diff, int64_t accuracy, int secs);
|
|
|
|
static void background(void);
|
|
|
|
static int caperror(int64_t dhz, int tsecs, int64_t taccuracy);
|
|
|
|
static int32_t fstime(void);
|
|
|
|
static int gettime(int64_t *nsec, uint64_t *ticks, uint64_t *hz); /* returns time, ticks, hz */
|
|
|
|
static int getclockprecision(int64_t);
|
|
|
|
static int64_t gpssample(void);
|
|
|
|
static void hnputts(void *p, int64_t nsec);
|
|
|
|
static void hnputts(void *p, int64_t nsec);
|
|
|
|
static void inittime(void);
|
|
|
|
static int64_t nhgetts(void *p);
|
|
|
|
static int64_t nhgetts(void *p);
|
|
|
|
static void ntpserver(char*);
|
|
|
|
static int64_t ntpsample(void);
|
|
|
|
static int ntptimediff(NTPserver *ns);
|
|
|
|
static int openfreqfile(void);
|
|
|
|
static int64_t readfreqfile(int fd, int64_t ohz, int64_t minhz, int64_t maxhz);
|
|
|
|
static int32_t rtctime(void);
|
|
|
|
static void setrtctime(int32_t);
|
|
|
|
static int64_t sample(int32_t (*get)(void));
|
|
|
|
static void setpriority(void);
|
|
|
|
static void setrootid(char *d);
|
|
|
|
static void settime(int64_t now, uint64_t hz, int64_t delta, int n); /* set time, hz, delta, period */
|
|
|
|
static int64_t utcsample(void);
|
|
|
|
static uint64_t vabs(int64_t);
|
|
|
|
static uint64_t whatisthefrequencykenneth(uint64_t hz, uint64_t minhz, uint64_t maxhz,
|
|
|
|
int64_t dt, int64_t ticks, int64_t period);
|
|
|
|
static void writefreqfile(int fd, int64_t hz, int secs, int64_t 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 */
|
|
|
|
int syncrtc;
|
|
|
|
uint64_t hz, minhz, maxhz, period, nhz;
|
|
|
|
int64_t 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;
|
|
|
|
syncrtc = 1;
|
|
|
|
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;
|
|
|
|
syncrtc = 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);
|
2017-10-20 00:55:05 +02:00
|
|
|
sysid = getenv(ENV_SYSNAME);
|
2016-11-25 17:18:40 +01:00
|
|
|
|
|
|
|
/* detach from the current namespace */
|
|
|
|
if(debug)
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_rfork(RFNAMEG);
|
2016-11-25 17:18:40 +01:00
|
|
|
|
|
|
|
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 / 2;
|
|
|
|
maxhz = hz * 2;
|
|
|
|
myprec = getclockprecision(hz);
|
|
|
|
|
|
|
|
/* convert the accuracy from nanoseconds to ticks */
|
|
|
|
taccuracy = hz*accuracy/SEC;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* bind in clocks
|
|
|
|
*/
|
|
|
|
switch(type){
|
|
|
|
case Fs:
|
2019-11-26 02:25:23 +01:00
|
|
|
fd = sys_open(timeserver, ORDWR);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(fd < 0)
|
|
|
|
sysfatal("opening %s: %r", timeserver);
|
|
|
|
if(amount(fd, "/n/boot", MREPL, "") < 0)
|
|
|
|
sysfatal("mounting %s: %r", timeserver);
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
break;
|
|
|
|
case Rtc:
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_bind("#r", "/dev", MAFTER);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(access("/dev/rtc", AREAD) < 0)
|
|
|
|
sysfatal("accessing /dev/rtc: %r");
|
|
|
|
break;
|
|
|
|
case Utc:
|
2019-11-26 02:25:23 +01:00
|
|
|
fd = sys_open(timeserver, OREAD);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(fd < 0)
|
|
|
|
sysfatal("opening %s: %r", timeserver);
|
|
|
|
utcfil = fd;
|
|
|
|
break;
|
|
|
|
case Gps:
|
2019-11-26 02:25:23 +01:00
|
|
|
fd = sys_open(timeserver, OREAD);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(fd < 0)
|
|
|
|
sysfatal("opening %s: %r", timeserver);
|
|
|
|
gpsfil = fd;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* start a local ntp server(s)
|
|
|
|
*/
|
|
|
|
for(i = 0; i < nservenet; i++)
|
2019-11-26 02:25:23 +01:00
|
|
|
switch(sys_rfork(RFPROC|RFFDG|RFMEM|RFNOWAIT)){
|
2016-11-25 17:18:40 +01:00
|
|
|
case -1:
|
|
|
|
sysfatal("forking: %r");
|
|
|
|
case 0:
|
|
|
|
ntpserver(servenet[i]);
|
2019-11-26 02:25:23 +01:00
|
|
|
sys__exits(0);
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
epsilon = ~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 epsilon we work off during the
|
|
|
|
* sampling period.
|
|
|
|
*/
|
|
|
|
if(abs(diff) > epsilon)
|
|
|
|
if(diff > 0)
|
|
|
|
settime(-1, 0, diff-((3*epsilon)/4), secs);
|
|
|
|
else
|
|
|
|
settime(-1, 0, diff+((3*epsilon)/4), secs);
|
|
|
|
else
|
|
|
|
settime(-1, 0, diff, 4*secs);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(syncrtc)
|
|
|
|
setrtctime(s->stime / SEC);
|
|
|
|
|
|
|
|
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(int64_t diff, int64_t accuracy, int secs)
|
|
|
|
{
|
|
|
|
uint64_t 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 uint64_t
|
|
|
|
whatisthefrequencykenneth(uint64_t hz, uint64_t minhz, uint64_t maxhz, int64_t dt,
|
|
|
|
int64_t ticks, int64_t period)
|
|
|
|
{
|
|
|
|
uint64_t 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(int64_t dhz, int tsecs, int64_t 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 */
|
2016-12-01 00:09:42 +01:00
|
|
|
if(access("/dev/time", AEXIST) < 0)
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_bind("#c", "/dev", MAFTER);
|
2016-12-01 00:09:42 +01:00
|
|
|
if(access("/dev/rtc", AEXIST) < 0)
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_bind("#r", "/dev", MAFTER);
|
2016-11-25 17:18:40 +01:00
|
|
|
|
|
|
|
/* figure out what interface we have */
|
|
|
|
ifc = Ibintime;
|
2019-11-26 02:25:23 +01:00
|
|
|
bintimefd = sys_open("/dev/bintime", mode);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(bintimefd >= 0)
|
|
|
|
return;
|
|
|
|
ifc = Insec;
|
2019-11-26 02:25:23 +01:00
|
|
|
nsecfd = sys_open("/dev/nsec", mode);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(nsecfd < 0)
|
|
|
|
sysfatal("opening /dev/nsec");
|
2019-11-26 02:25:23 +01:00
|
|
|
fastclockfd = sys_open("/dev/fastclock", mode);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(fastclockfd < 0)
|
|
|
|
sysfatal("opening /dev/fastclock");
|
2019-11-26 02:25:23 +01:00
|
|
|
timingfd = sys_open("/dev/timing", OREAD);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(timingfd < 0)
|
|
|
|
return;
|
|
|
|
ifc = Itiming;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* convert binary numbers from/to kernel
|
|
|
|
*/
|
|
|
|
static uint64_t uvorder = 0x0001020304050607ULL;
|
|
|
|
|
|
|
|
static uint8_t*
|
|
|
|
be2vlong(int64_t *to, uint8_t *f)
|
|
|
|
{
|
|
|
|
uint8_t *t, *o;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
t = (uint8_t*)to;
|
|
|
|
o = (uint8_t*)&uvorder;
|
|
|
|
for(i = 0; i < sizeof(int64_t); i++)
|
|
|
|
t[o[i]] = f[i];
|
|
|
|
return f+sizeof(int64_t);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t*
|
|
|
|
vlong2be(uint8_t *t, int64_t from)
|
|
|
|
{
|
|
|
|
uint8_t *f, *o;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
f = (uint8_t*)&from;
|
|
|
|
o = (uint8_t*)&uvorder;
|
|
|
|
for(i = 0; i < sizeof(int64_t); i++)
|
|
|
|
t[i] = f[o[i]];
|
|
|
|
return t+sizeof(int64_t);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t order = 0x00010203;
|
|
|
|
/*
|
|
|
|
static uint8_t*
|
|
|
|
be2long(int32_t *to, uint8_t *f)
|
|
|
|
{
|
|
|
|
uint8_t *t, *o;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
t = (uint8_t*)to;
|
|
|
|
o = (uint8_t*)ℴ
|
|
|
|
for(i = 0; i < sizeof(int32_t); i++)
|
|
|
|
t[o[i]] = f[i];
|
|
|
|
return f+sizeof(int32_t);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
static uint8_t*
|
|
|
|
long2be(uint8_t *t, int32_t from)
|
|
|
|
{
|
|
|
|
uint8_t *f, *o;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
f = (uint8_t*)&from;
|
|
|
|
o = (uint8_t*)ℴ
|
|
|
|
for(i = 0; i < sizeof(int32_t); i++)
|
|
|
|
t[i] = f[o[i]];
|
|
|
|
return t+sizeof(int32_t);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* read ticks and local time in nanoseconds
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
gettime(int64_t *nsec, uint64_t *ticks, uint64_t *hz)
|
|
|
|
{
|
|
|
|
int i, n;
|
|
|
|
uint8_t ub[3*8], *p;
|
|
|
|
char b[2*24+1];
|
|
|
|
|
|
|
|
switch(ifc){
|
|
|
|
case Ibintime:
|
|
|
|
n = sizeof(int64_t);
|
|
|
|
if(hz != nil)
|
|
|
|
n = 3*sizeof(int64_t);
|
|
|
|
if(ticks != nil)
|
|
|
|
n = 2*sizeof(int64_t);
|
2019-11-26 02:25:23 +01:00
|
|
|
i = jehanne_read(bintimefd, ub, n);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(i != n)
|
|
|
|
break;
|
|
|
|
p = ub;
|
|
|
|
if(nsec != nil)
|
|
|
|
be2vlong(nsec, ub);
|
|
|
|
p += sizeof(int64_t);
|
|
|
|
if(ticks != nil)
|
|
|
|
be2vlong((int64_t*)ticks, p);
|
|
|
|
p += sizeof(int64_t);
|
|
|
|
if(hz != nil)
|
|
|
|
be2vlong((int64_t*)hz, p);
|
|
|
|
return 0;
|
|
|
|
case Itiming:
|
|
|
|
n = sizeof(int64_t);
|
|
|
|
if(ticks != nil)
|
|
|
|
n = 2*sizeof(int64_t);
|
2019-11-26 02:25:23 +01:00
|
|
|
i = jehanne_read(timingfd, ub, n);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(i != n)
|
|
|
|
break;
|
|
|
|
p = ub;
|
|
|
|
if(nsec != nil)
|
|
|
|
be2vlong(nsec, ub);
|
|
|
|
p += sizeof(int64_t);
|
|
|
|
if(ticks != nil)
|
|
|
|
be2vlong((int64_t*)ticks, p);
|
|
|
|
if(hz != nil){
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_seek(fastclockfd, 0, 0);
|
|
|
|
n = jehanne_read(fastclockfd, b, sizeof(b)-1);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(n <= 0)
|
|
|
|
break;
|
|
|
|
b[n] = 0;
|
|
|
|
*hz = strtoll(b+24, 0, 0);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
case Insec:
|
|
|
|
if(nsec != nil){
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_seek(nsecfd, 0, 0);
|
|
|
|
n = jehanne_read(nsecfd, b, sizeof(b)-1);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(n <= 0)
|
|
|
|
break;
|
|
|
|
b[n] = 0;
|
|
|
|
*nsec = strtoll(b, 0, 0);
|
|
|
|
}
|
|
|
|
if(ticks != nil){
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_seek(fastclockfd, 0, 0);
|
|
|
|
n = jehanne_read(fastclockfd, b, sizeof(b)-1);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(n <= 0)
|
|
|
|
break;
|
|
|
|
b[n] = 0;
|
|
|
|
*ticks = strtoll(b, 0, 0);
|
|
|
|
}
|
|
|
|
if(hz != nil){
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_seek(fastclockfd, 0, 0);
|
|
|
|
n = jehanne_read(fastclockfd, b, sizeof(b)-1);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(n <= 24)
|
|
|
|
break;
|
|
|
|
b[n] = 0;
|
|
|
|
*hz = strtoll(b+24, 0, 0);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
settime(int64_t now, uint64_t hz, int64_t delta, int n)
|
|
|
|
{
|
|
|
|
uint8_t b[1+sizeof(int64_t)+sizeof(int32_t)], *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);
|
2019-11-26 02:25:23 +01:00
|
|
|
if(jehanne_write(bintimefd, b, p-b) < 0)
|
2016-11-25 17:18:40 +01:00
|
|
|
sysfatal("writing /dev/bintime: %r");
|
|
|
|
}
|
|
|
|
if(delta != 0){
|
|
|
|
p = b;
|
|
|
|
*p++ = 'd';
|
|
|
|
p = vlong2be(p, delta);
|
|
|
|
p = long2be(p, n);
|
2019-11-26 02:25:23 +01:00
|
|
|
if(jehanne_write(bintimefd, b, p-b) < 0)
|
2016-11-25 17:18:40 +01:00
|
|
|
sysfatal("writing /dev/bintime: %r");
|
|
|
|
}
|
|
|
|
if(hz != 0){
|
|
|
|
p = b;
|
|
|
|
*p++ = 'f';
|
|
|
|
p = vlong2be(p, hz);
|
2019-11-26 02:25:23 +01:00
|
|
|
if(jehanne_write(bintimefd, b, p-b) < 0)
|
2016-11-25 17:18:40 +01:00
|
|
|
sysfatal("writing /dev/bintime: %r");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Itiming:
|
|
|
|
case Insec:
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_seek(nsecfd, 0, 0);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(now >= 0 || delta != 0){
|
|
|
|
if(fprint(nsecfd, "%lld %lld %d", now, delta, n) < 0)
|
|
|
|
sysfatal("writing /dev/nsec: %r");
|
|
|
|
}
|
|
|
|
if(hz > 0){
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_seek(fastclockfd, 0, 0);
|
2016-11-25 17:18:40 +01:00
|
|
|
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());
|
2019-11-26 02:25:23 +01:00
|
|
|
fd = sys_open(buf, ORDWR);
|
2016-11-25 17:18:40 +01:00
|
|
|
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");
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* convert to ntp timestamps */
|
|
|
|
static void
|
|
|
|
hnputts(void *p, int64_t nsec)
|
|
|
|
{
|
|
|
|
uint8_t *a;
|
|
|
|
uint32_t 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 int64_t
|
|
|
|
nhgetts(void *p)
|
|
|
|
{
|
|
|
|
uint8_t *a;
|
|
|
|
uint32_t tsh, tsl;
|
|
|
|
int64_t 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, int64_t nsec)
|
|
|
|
{
|
|
|
|
uint8_t *a;
|
|
|
|
uint32_t fp;
|
|
|
|
|
|
|
|
a = p;
|
|
|
|
|
|
|
|
fp = nsec/(SEC/((int64_t)(1<<16)));
|
|
|
|
hnputl(a, fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* convert from ntp fixed point to nanosecs */
|
|
|
|
static int64_t
|
|
|
|
nhgetfp(void *p)
|
|
|
|
{
|
|
|
|
uint8_t *a;
|
|
|
|
uint32_t fp;
|
|
|
|
int64_t nsec;
|
|
|
|
|
|
|
|
a = p;
|
|
|
|
fp = nhgetl(a);
|
|
|
|
nsec = ((int64_t)fp)*(SEC/((int64_t)(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);
|
2019-11-26 02:25:23 +01:00
|
|
|
fd = sys_open(buf, OREAD);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(fd < 0)
|
|
|
|
return;
|
2019-11-26 02:25:23 +01:00
|
|
|
n = jehanne_read(fd, buf, sizeof buf);
|
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
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)
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_noted(NCONT);
|
|
|
|
sys_noted(NDFLT);
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
int64_t dt, recvts, origts, xmitts, destts, x;
|
|
|
|
char dir[64];
|
|
|
|
static int whined;
|
|
|
|
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_notify(ding);
|
|
|
|
sys_alarm(30*1000); /* don't wait forever if ns->name is unreachable */
|
2016-11-25 17:18:40 +01:00
|
|
|
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++){
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_alarm(2*1000);
|
2016-11-25 17:18:40 +01:00
|
|
|
|
|
|
|
gettime(&x, 0, 0);
|
|
|
|
hnputts(ntpout.xmitts, x);
|
2019-11-26 02:25:23 +01:00
|
|
|
if(jehanne_write(fd, &ntpout, NTPSIZE) < 0){
|
|
|
|
sys_alarm(0);
|
2016-11-25 17:18:40 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-11-26 02:25:23 +01:00
|
|
|
n = jehanne_read(fd, &ntpin, sizeof ntpin);
|
|
|
|
sys_alarm(0);
|
2016-11-25 17:18:40 +01:00
|
|
|
gettime(&destts, 0, 0);
|
|
|
|
if(n >= NTPSIZE){
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_close(fd);
|
2016-11-25 17:18:40 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int64_t
|
|
|
|
gpssample(void)
|
|
|
|
{
|
|
|
|
int64_t l, g, d;
|
|
|
|
int i, n;
|
|
|
|
char *v[4], buf[128];
|
|
|
|
|
|
|
|
d = -1000000000000000000LL;
|
|
|
|
for(i = 0; i < 5; i++){
|
|
|
|
sleep(1100);
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_seek(gpsfil, 0, 0);
|
|
|
|
n = jehanne_read(gpsfil, buf, sizeof buf - 1);
|
2016-11-25 17:18:40 +01:00
|
|
|
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 int64_t
|
|
|
|
ntpsample(void)
|
|
|
|
{
|
|
|
|
NTPserver *tns, *ns;
|
|
|
|
int64_t 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;
|
|
|
|
|
|
|
|
epsilon = abs(ns->rtt/2);
|
|
|
|
return ns->dt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sample the utc file
|
|
|
|
*/
|
|
|
|
static int64_t
|
|
|
|
utcsample(void)
|
|
|
|
{
|
|
|
|
int64_t s;
|
|
|
|
int n;
|
|
|
|
char *v[2], buf[128];
|
|
|
|
|
|
|
|
s = 0;
|
2019-11-26 02:25:23 +01:00
|
|
|
sys_seek(utcfil, 0, 0);
|
|
|
|
n = jehanne_read(utcfil, buf, sizeof buf - 1);
|
2016-11-25 17:18:40 +01:00
|
|
|
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);
|
2019-11-26 02:25:23 +01:00
|
|
|
fd = sys_open(data, ORDWR);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(fd < 0)
|
|
|
|
sysfatal("open %s: %r", data);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ntpserver(char *servenet)
|
|
|
|
{
|
|
|
|
int fd, n, vers, mode;
|
|
|
|
int64_t 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(;;){
|
2019-11-26 02:25:23 +01:00
|
|
|
n = jehanne_read(fd, buf, sizeof buf);
|
2016-11-25 17:18:40 +01:00
|
|
|
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);
|
2019-11-26 02:25:23 +01:00
|
|
|
jehanne_write(fd, buf, NTPSIZE + Udphdrsize);
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get the current time from the file system
|
|
|
|
*/
|
|
|
|
static int32_t
|
|
|
|
fstime(void)
|
|
|
|
{
|
|
|
|
Dir *d;
|
|
|
|
uint32_t 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 int32_t
|
|
|
|
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)
|
2019-11-26 02:25:23 +01:00
|
|
|
f = sys_open("/dev/rtc", OREAD|OCEXEC);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(f < 0)
|
|
|
|
break;
|
2019-11-26 02:25:23 +01:00
|
|
|
if(sys_seek(f, 0, 0) < 0 || (i = jehanne_read(f, b, sizeof b)) < 0){
|
|
|
|
sys_close(f);
|
2016-11-25 17:18:40 +01:00
|
|
|
f = -1;
|
|
|
|
} else
|
|
|
|
if(i != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return strtoul(b, 0, 10)+gmtdelta;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
setrtctime(int32_t t)
|
|
|
|
{
|
|
|
|
static int f = -1;
|
|
|
|
|
|
|
|
if(f < 0)
|
2019-11-26 02:25:23 +01:00
|
|
|
f = sys_open("/dev/rtc", OWRITE|OCEXEC);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(f < 0)
|
|
|
|
return;
|
2019-11-26 02:25:23 +01:00
|
|
|
if(sys_seek(f, 0, 0) < 0 || fprint(f, "%ld", t-gmtdelta) < 0){
|
|
|
|
sys_close(f);
|
2016-11-25 17:18:40 +01:00
|
|
|
f = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sample a clock. We wait for the clock to always
|
|
|
|
* be at the leading edge of a clock period.
|
|
|
|
*/
|
|
|
|
static int64_t
|
|
|
|
sample(int32_t (*get)(void))
|
|
|
|
{
|
|
|
|
int32_t this, last;
|
|
|
|
int64_t start, end;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* wait for the second to change
|
|
|
|
*/
|
|
|
|
last = (*get)();
|
|
|
|
for(;;){
|
|
|
|
gettime(&start, 0, 0);
|
|
|
|
sleep(5);
|
|
|
|
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;
|
|
|
|
}
|
2019-11-26 02:25:23 +01:00
|
|
|
fd = sys_open(p, ORDWR);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(fd < 0)
|
2016-12-24 21:25:20 +01:00
|
|
|
fd = ocreate(p, ORDWR, 0666);
|
2016-11-25 17:18:40 +01:00
|
|
|
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 int64_t
|
|
|
|
readfreqfile(int fd, int64_t ohz, int64_t minhz, int64_t maxhz)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
char buf[128];
|
|
|
|
int64_t hz;
|
|
|
|
|
2019-11-26 02:25:23 +01:00
|
|
|
n = jehanne_read(fd, buf, sizeof buf-1);
|
2016-11-25 17:18:40 +01:00
|
|
|
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, int64_t hz, int secs, int64_t diff)
|
|
|
|
{
|
|
|
|
int32_t now;
|
|
|
|
static int32_t last;
|
|
|
|
|
|
|
|
if(fd < 0)
|
|
|
|
return;
|
|
|
|
now = time(0);
|
|
|
|
if(now - last < 10*60)
|
|
|
|
return;
|
|
|
|
last = now;
|
2019-11-26 02:25:23 +01:00
|
|
|
if(sys_seek(fd, 0, 0) < 0)
|
2016-11-25 17:18:40 +01:00
|
|
|
return;
|
|
|
|
fprint(fd, "%lld %d %d %lld\n", hz, secs, type, diff);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t
|
|
|
|
vabs(int64_t x)
|
|
|
|
{
|
|
|
|
if(x < 0)
|
|
|
|
return -x;
|
|
|
|
else
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
background(void)
|
|
|
|
{
|
|
|
|
static int inbackground;
|
|
|
|
|
|
|
|
if(inbackground)
|
|
|
|
return;
|
|
|
|
|
2016-12-01 00:09:42 +01:00
|
|
|
if(!debug)
|
2019-11-26 02:25:23 +01:00
|
|
|
switch(sys_rfork(RFPROC|RFFDG|RFNAMEG|RFNOTEG|RFNOWAIT)){
|
2016-11-25 17:18:40 +01:00
|
|
|
case -1:
|
|
|
|
sysfatal("forking: %r");
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
exits(0);
|
|
|
|
}
|
|
|
|
inbackground = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
getclockprecision(int64_t hz)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = 8;
|
|
|
|
while(hz > 0){
|
|
|
|
i--;
|
|
|
|
hz >>= 1;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|