]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/timesync.c
abaco: cleanup, handle image/x-icon, don't use backspace as a hotkey, and remove...
[plan9front.git] / sys / src / cmd / aux / timesync.c
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <ip.h>
5 #include <mp.h>
6
7 /* nanosecond times */
8 #define SEC 1000000000LL
9 #define MIN (60LL*SEC)
10 #define HOUR (60LL*MIN)
11 #define DAY (24LL*HOUR)
12
13 enum {
14         Fs,
15         Rtc,
16         Ntp,
17         Utc,
18         Gps,
19
20         HZAvgSecs= 3*60,  /* target averaging period for frequency in seconds */
21         MinSampleSecs= 60,      /* minimum sampling time in seconds */
22 };
23
24
25 char *dir = "/tmp";     /* directory sample files live in */
26 char *logfile = "timesync";
27 char *timeserver;
28 char *Rootid;
29 int utcfil;
30 int gpsfil;
31 int debug;
32 int impotent;
33 int logging;
34 int type;
35 int gmtdelta;           /* rtc+gmtdelta = gmt */
36 uvlong avgerr;
37
38 /* ntp server info */
39 int stratum = 14;
40 vlong mydisp, rootdisp;
41 vlong mydelay, rootdelay;
42 vlong avgdelay;
43 vlong lastutc;
44 uchar rootid[4];
45 char *sysid;
46 int myprec;
47
48 /* list of time samples */
49 typedef struct Sample Sample;
50 struct Sample
51 {
52         Sample  *next;
53         uvlong  ticks;
54         vlong   ltime;
55         vlong   stime;
56 };
57
58 /* ntp packet */
59 typedef struct NTPpkt NTPpkt;
60 struct NTPpkt
61 {
62         uchar   mode;
63         uchar   stratum;
64         uchar   poll;
65         uchar   precision;
66         uchar   rootdelay[4];
67         uchar   rootdisp[4];
68         uchar   rootid[4];
69         uchar   refts[8];
70         uchar   origts[8];      /* departed client */
71         uchar   recvts[8];      /* arrived at server */
72         uchar   xmitts[8];      /* departed server */
73         uchar   keyid[4];
74         uchar   digest[16];
75 };
76
77 /* ntp server */
78 typedef struct NTPserver NTPserver;
79 struct NTPserver
80 {
81         NTPserver *next;
82         char    *name;
83         uchar   stratum;
84         uchar   precision;
85         vlong   rootdelay;
86         vlong   rootdisp;
87         vlong   rtt;
88         vlong   dt;
89 };
90
91 NTPserver *ntpservers;
92
93 enum
94 {
95         NTPSIZE=        48,     /* basic ntp packet */
96         NTPDIGESTSIZE=  20,     /* key and digest */
97 };
98
99 /* error bound of last sample */
100 ulong   ε;
101
102 static void     addntpserver(char *name);
103 static int      adjustperiod(vlong diff, vlong accuracy, int secs);
104 static void     background(void);
105 static int      caperror(vlong dhz, int tsecs, vlong taccuracy);
106 static long     fstime(void);
107 static int      gettime(vlong *nsec, uvlong *ticks, uvlong *hz); /* returns time, ticks, hz */
108 static int      getclockprecision(vlong);
109 static vlong    gpssample(void);
110 static void     hnputts(void *p, vlong nsec);
111 static void     hnputts(void *p, vlong nsec);
112 static void     inittime(void);
113 static vlong    nhgetts(void *p);
114 static vlong    nhgetts(void *p);
115 static void     ntpserver(char*);
116 static vlong    ntpsample(void);
117 static int      ntptimediff(NTPserver *ns);
118 static int      openfreqfile(void);
119 static vlong    readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz);
120 static long     rtctime(void);
121 static void     setrtctime(long);
122 static vlong    sample(long (*get)(void));
123 static void     setpriority(void);
124 static void     setrootid(char *d);
125 static void     settime(vlong now, uvlong hz, vlong delta, int n); /* set time, hz, delta, period */
126 static vlong    utcsample(void);
127 static uvlong   vabs(vlong);
128 static uvlong   whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz,
129                         vlong dt, vlong ticks, vlong period);
130 static void     writefreqfile(int fd, vlong hz, int secs, vlong diff);
131
132 // ((1970-1900)*365 + 17 /*leap days*/)*24*60*60
133 #define EPOCHDIFF 2208988800UL
134
135 static void
136 usage(void)
137 {
138         fprint(2, "usage: %s [-a accuracy][-d dir][-I rootid][-s net]"
139                 "[-S stratum][-DfGilLnrU] timesource ...\n", argv0);
140         exits("usage");
141 }
142
143 void
144 main(int argc, char **argv)
145 {
146         int i, t, fd, nservenet;
147         int secs;               /* sampling period */
148         int tsecs;              /* temporary sampling period */
149         int syncrtc;
150         uvlong hz, minhz, maxhz, period, nhz;
151         vlong diff, accuracy, taccuracy;
152         char *servenet[4];
153         Sample *s, *x, *first, **l;
154         Tm tl, tg;
155
156         type = Fs;              /* by default, sync with the file system */
157         debug = 0;
158         syncrtc = 1;
159         accuracy = 1000000LL;   /* default accuracy is 1 millisecond */
160         nservenet = 0;
161         tsecs = secs = MinSampleSecs;
162         timeserver = "";
163
164         ARGBEGIN{
165         case 'a':
166                 accuracy = strtoll(EARGF(usage()), 0, 0); /* specified in ns */
167                 if(accuracy <= 1)
168                         sysfatal("bad accuracy specified");
169                 break;
170         case 'd':
171                 dir = EARGF(usage());
172                 break;
173         case 'D':
174                 debug = 1;
175                 break;
176         case 'f':
177                 type = Fs;
178                 stratum = 2;
179                 break;
180         case 'G':
181                 type = Gps;
182                 stratum = 1;
183                 break;
184         case 'i':
185                 impotent = 1;
186                 break;
187         case 'I':
188                 Rootid = EARGF(usage());
189                 break;
190         case 'l':
191                 logging = 1;
192                 break;
193         case 'L':
194                 /*
195                  * Assume time source in local time rather than GMT.
196                  * Calculate difference so that rtctime can return GMT.
197                  * This is useful with the rtc on PC's that run Windows
198                  * since Windows keeps the local time in the rtc.
199                  */
200                 t = time(0);
201                 tl = *localtime(t);
202                 tg = *gmtime(t);
203
204                 /*
205                  * if the years are different, we're at most a day off,
206                  * so just rewrite
207                  */
208                 if(tl.year < tg.year){
209                         tg.year--;
210                         tg.yday = tl.yday + 1;
211                 }else if(tl.year > tg.year){
212                         tl.year--;
213                         tl.yday = tg.yday+1;
214                 }
215                 assert(tl.year == tg.year);
216
217                 tg.sec -= tl.sec;
218                 tg.min -= tl.min;
219                 tg.hour -= tl.hour;
220                 tg.yday -= tl.yday;
221                 gmtdelta = tg.sec+60*(tg.min+60*(tg.hour+tg.yday*24));
222
223                 assert(abs(gmtdelta) <= 24*60*60);
224                 break;
225         case 'n':
226                 type = Ntp;
227                 break;
228         case 'r':
229                 type = Rtc;
230                 stratum = 0;
231                 syncrtc = 0;
232                 break;
233         case 'U':
234                 type = Utc;
235                 stratum = 1;
236                 break;
237         case 's':
238                 if(nservenet >= nelem(servenet))
239                         sysfatal("too many networks to serve on");
240                 servenet[nservenet++] = EARGF(usage());
241                 break;
242         case 'S':
243                 stratum = strtoll(EARGF(usage()), 0, 0);
244                 break;
245         default:
246                 usage();
247         }ARGEND;
248
249         fmtinstall('E', eipfmt);
250         fmtinstall('I', eipfmt);
251         fmtinstall('V', eipfmt);
252         sysid = getenv("sysname");
253
254         /* detach from the current namespace */
255         if(debug)
256                 rfork(RFNAMEG);
257
258         switch(type){
259         case Utc:
260                 if(argc > 0)
261                         timeserver = argv[0];
262                 else
263                         sysfatal("bad time source");
264                 break;
265         case Gps:
266                 if(argc > 0)
267                         timeserver = argv[0];
268                 else
269                         timeserver = "/mnt/gps/time";
270                 break;
271         case Fs:
272                 if(argc > 0)
273                         timeserver = argv[0];
274                 else
275                         timeserver = "/srv/boot";
276                 break;
277         case Ntp:
278                 if(argc > 0)
279                         for(i = 0; i < argc; i++)
280                                 addntpserver(argv[i]);
281                 else
282                         addntpserver("$ntp");
283                 break;
284         }
285
286         setpriority();
287
288         /* figure out our time interface and initial frequency */
289         inittime();
290         gettime(0, 0, &hz);
291         minhz = hz / 2;
292         maxhz = hz * 2;
293         myprec = getclockprecision(hz);
294
295         /* convert the accuracy from nanoseconds to ticks */
296         taccuracy = hz*accuracy/SEC;
297
298         /*
299          * bind in clocks
300          */
301         switch(type){
302         case Fs:
303                 fd = open(timeserver, ORDWR);
304                 if(fd < 0)
305                         sysfatal("opening %s: %r", timeserver);
306                 if(amount(fd, "/n/boot", MREPL, "") < 0)
307                         sysfatal("mounting %s: %r", timeserver);
308                 close(fd);
309                 break;
310         case Rtc:
311                 bind("#r", "/dev", MAFTER);
312                 if(access("/dev/rtc", AREAD) < 0)
313                         sysfatal("accessing /dev/rtc: %r");
314                 break;
315         case Utc:
316                 fd = open(timeserver, OREAD);
317                 if(fd < 0)
318                         sysfatal("opening %s: %r", timeserver);
319                 utcfil = fd;
320                 break;
321         case Gps:
322                 fd = open(timeserver, OREAD);
323                 if(fd < 0)
324                         sysfatal("opening %s: %r", timeserver);
325                 gpsfil = fd;
326                 break;
327         }
328
329         /*
330          * start a local ntp server(s)
331          */
332         for(i = 0; i < nservenet; i++)
333                 switch(rfork(RFPROC|RFFDG|RFMEM|RFNOWAIT)){
334                 case -1:
335                         sysfatal("forking: %r");
336                 case 0:
337                         ntpserver(servenet[i]);
338                         _exits(0);
339                 }
340
341         /* get the last known frequency from the file */
342         fd = openfreqfile();
343         hz = readfreqfile(fd, hz, minhz, maxhz);
344
345         /*
346          * this is the main loop.  it gets a sample, adjusts the
347          * clock and computes a sleep period until the next loop.
348          * we balance frequency drift against the length of the
349          * period to avoid blowing the accuracy limit.
350          */
351         first = nil;
352         l = &first;
353         avgerr = accuracy >> 1;
354         for(;; background(), sleep(tsecs*1000)){
355                 s = mallocz(sizeof *s, 1);
356                 diff = 0;
357
358                 /* get times for this sample */
359                 ε = ~0;
360                 switch(type){
361                 case Fs:
362                         s->stime = sample(fstime);
363                         break;
364                 case Rtc:
365                         s->stime = sample(rtctime);
366                         break;
367                 case Utc:
368                         s->stime = utcsample();
369                         if(s->stime == 0LL){
370                                 if(logging)
371                                         syslog(0, logfile, "no sample");
372                                 free(s);
373                                 if (secs > 60 * 15)
374                                         tsecs = 60*15;
375                                 continue;
376                         }
377                         break;
378                 case Ntp:
379                         diff = ntpsample();
380                         if(diff == 0LL){
381                                 if(logging)
382                                         syslog(0, logfile, "no sample");
383                                 free(s);
384                                 if(secs > 60*15)
385                                         tsecs = 60*15;
386                                 continue;
387                         }
388                         break;
389                 case Gps:
390                         diff = gpssample();
391                         if(diff == 0LL){
392                                 if(logging)
393                                         syslog(0, logfile, "no sample");
394                                 free(s);
395                                 if(secs > 60*15)
396                                         tsecs = 60*15;
397                                 continue;
398                         }
399                 }
400
401                 /* use fastest method to read local clock and ticks */
402                 gettime(&s->ltime, &s->ticks, 0);
403                 if(type == Ntp || type == Gps)
404                         s->stime = s->ltime + diff;
405
406                 /* if the sample was bad, ignore it */
407                 if(s->stime < 0){
408                         free(s);
409                         continue;
410                 }
411
412                 /* reset local time */
413                 diff = s->stime - s->ltime;
414                 if(diff > 10*SEC || diff < -10*SEC){
415                         /* we're way off, just set the time */
416                         secs = MinSampleSecs;
417                         settime(s->stime, 0, 0, 0);
418                 } else {
419                         /* keep a running average of the error. */
420                         avgerr = (avgerr>>1) + (vabs(diff)>>1);
421
422                         /*
423                          * the time to next sample depends on how good or
424                          * bad we're doing.
425                          */
426                         tsecs = secs = adjustperiod(diff, accuracy, secs);
427
428                         /*
429                          * work off the fixed difference.  This is done
430                          * by adding a ramp to the clock.  Each 100th of a
431                          * second (or so) the kernel will add diff/(4*secs*100)
432                          * to the clock.  we only do 1/4 of the difference per
433                          * period to dampen any measurement noise.
434                          *
435                          * any difference greater than ε we work off during the
436                          * sampling period.
437                          */
438                         if(abs(diff) > ε)
439                                 if(diff > 0)
440                                         settime(-1, 0, diff-((3*ε)/4), secs);
441                                 else
442                                         settime(-1, 0, diff+((3*ε)/4), secs);
443                         else
444                                 settime(-1, 0, diff, 4*secs);
445
446                 }
447
448                 if(syncrtc)
449                         setrtctime(s->stime / SEC);
450
451                 if(debug)
452                         fprint(2, "δ %lld avgδ %lld f %lld\n", diff, avgerr, hz);
453
454                 /* dump old samples (keep at least one) */
455                 while(first != nil){
456                         if(first->next == nil)
457                                 break;
458                         if(s->stime - first->next->stime < DAY)
459                                 break;
460                         x = first;
461                         first = first->next;
462                         free(x);
463                 }
464
465                 /*
466                  * The sampling error is limited by the total error.  If
467                  * we make sure the sampling period is at least 16 million
468                  * times the average error, we should calculate a frequency
469                  * with on average a 1e-7 error.
470                  *
471                  * So that big hz changes don't blow our accuracy requirement,
472                  * we shorten the period to make sure that δhz*secs will be
473                  * greater than the accuracy limit.
474                  */
475                 period = avgerr << 24;
476                 for(x = first; x != nil; x = x->next)
477                         if(s->stime - x->stime < period ||
478                            x->next == nil || s->stime - x->next->stime < period)
479                                 break;
480                 if(x != nil){
481                         nhz = whatisthefrequencykenneth(
482                                 hz, minhz, maxhz,
483                                 s->stime - x->stime,
484                                 s->ticks - x->ticks,
485                                 period);
486                         tsecs = caperror(vabs(nhz-hz), tsecs, taccuracy);
487                         hz = nhz;
488                         writefreqfile(fd, hz, (s->stime - x->stime)/SEC, diff);
489                 }
490
491                 /* add current sample to list. */
492                 *l = s;
493                 l = &s->next;
494
495                 if(logging)
496                         syslog(0, logfile, "δ %lld avgδ %lld hz %lld",
497                                 diff, avgerr, hz);
498         }
499 }
500
501 /*
502  * adjust the sampling period with some histeresis
503  */
504 static int
505 adjustperiod(vlong diff, vlong accuracy, int secs)
506 {
507         uvlong absdiff;
508
509         absdiff = vabs(diff);
510
511         if(absdiff < (accuracy>>1))
512                 secs += 60;
513         else if(absdiff > accuracy)
514                 secs >>= 1;
515         else
516                 secs -= 60;
517         if(secs < MinSampleSecs)
518                 secs = MinSampleSecs;
519         return secs;
520 }
521
522 /*
523  * adjust the frequency
524  */
525 static uvlong
526 whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz, vlong dt,
527         vlong ticks, vlong period)
528 {
529         uvlong ohz = hz;
530         static mpint *mpdt, *mpticks, *mphz, *mpbillion;
531
532         /* sanity check */
533         if(dt <= 0 || ticks <= 0)
534                 return hz;
535
536         if(mphz == nil){
537                 mphz = mpnew(0);
538                 mpbillion = uvtomp(SEC, nil);
539         }
540
541         /* hz = (ticks*SEC)/dt */
542         mpdt = vtomp(dt, mpdt);
543         mpticks = vtomp(ticks, mpticks);
544         mpmul(mpticks, mpbillion, mpticks);
545         mpdiv(mpticks, mpdt, mphz, nil);
546         hz = mptoui(mphz);
547
548         /* sanity */
549         if(hz < minhz || hz > maxhz)
550                 return ohz;
551
552         /* damp the change if we're shorter than the target period */
553         if(period > dt)
554                 hz = (12ULL*ohz + 4ULL*hz)/16ULL;
555
556         settime(-1, hz, 0, 0);
557         return hz;
558 }
559
560 /*
561  * We may be changing the frequency to match a bad measurement
562  * or to match a condition no longer in effect.  To make sure
563  * that this doesn't blow our error budget over the next measurement
564  * period, shorten the period to make sure that δhz*secs will be
565  * less than the accuracy limit.  Here taccuracy is accuracy converted
566  * from nanoseconds to ticks.
567  */
568 static int
569 caperror(vlong dhz, int tsecs, vlong taccuracy)
570 {
571         if(dhz*tsecs <= taccuracy)
572                 return tsecs;
573
574         if(debug)
575                 fprint(2, "δhz %lld tsecs %d tacc %lld\n", dhz, tsecs, taccuracy);
576
577         tsecs = taccuracy/dhz;
578         if(tsecs < MinSampleSecs)
579                 tsecs = MinSampleSecs;
580         return tsecs;
581 }
582
583 /*
584  *  kernel interface
585  */
586 enum
587 {
588         Ibintime,
589         Insec,
590         Itiming,
591 };
592 int ifc;
593 int bintimefd = -1;
594 int timingfd = -1;
595 int nsecfd = -1;
596 int fastclockfd = -1;
597
598 static void
599 inittime(void)
600 {
601         int mode;
602
603         if(impotent)
604                 mode = OREAD;
605         else
606                 mode = ORDWR;
607
608         /* bind in clocks */
609         if(access("/dev/time", 0) < 0)
610                 bind("#c", "/dev", MAFTER);
611         if(access("/dev/rtc", 0) < 0)
612                 bind("#r", "/dev", MAFTER);
613
614         /* figure out what interface we have */
615         ifc = Ibintime;
616         bintimefd = open("/dev/bintime", mode);
617         if(bintimefd >= 0)
618                 return;
619         ifc = Insec;
620         nsecfd = open("/dev/nsec", mode);
621         if(nsecfd < 0)
622                 sysfatal("opening /dev/nsec");
623         fastclockfd = open("/dev/fastclock", mode);
624         if(fastclockfd < 0)
625                 sysfatal("opening /dev/fastclock");
626         timingfd = open("/dev/timing", OREAD);
627         if(timingfd < 0)
628                 return;
629         ifc = Itiming;
630 }
631
632 /*
633  *  convert binary numbers from/to kernel
634  */
635 static uvlong uvorder = 0x0001020304050607ULL;
636
637 static uchar*
638 be2vlong(vlong *to, uchar *f)
639 {
640         uchar *t, *o;
641         int i;
642
643         t = (uchar*)to;
644         o = (uchar*)&uvorder;
645         for(i = 0; i < sizeof(vlong); i++)
646                 t[o[i]] = f[i];
647         return f+sizeof(vlong);
648 }
649
650 static uchar*
651 vlong2be(uchar *t, vlong from)
652 {
653         uchar *f, *o;
654         int i;
655
656         f = (uchar*)&from;
657         o = (uchar*)&uvorder;
658         for(i = 0; i < sizeof(vlong); i++)
659                 t[i] = f[o[i]];
660         return t+sizeof(vlong);
661 }
662
663 static long order = 0x00010203;
664
665 static uchar*
666 be2long(long *to, uchar *f)
667 {
668         uchar *t, *o;
669         int i;
670
671         t = (uchar*)to;
672         o = (uchar*)&order;
673         for(i = 0; i < sizeof(long); i++)
674                 t[o[i]] = f[i];
675         return f+sizeof(long);
676 }
677
678 static uchar*
679 long2be(uchar *t, long from)
680 {
681         uchar *f, *o;
682         int i;
683
684         f = (uchar*)&from;
685         o = (uchar*)&order;
686         for(i = 0; i < sizeof(long); i++)
687                 t[i] = f[o[i]];
688         return t+sizeof(long);
689 }
690
691 /*
692  * read ticks and local time in nanoseconds
693  */
694 static int
695 gettime(vlong *nsec, uvlong *ticks, uvlong *hz)
696 {
697         int i, n;
698         uchar ub[3*8], *p;
699         char b[2*24+1];
700
701         switch(ifc){
702         case Ibintime:
703                 n = sizeof(vlong);
704                 if(hz != nil)
705                         n = 3*sizeof(vlong);
706                 if(ticks != nil)
707                         n = 2*sizeof(vlong);
708                 i = read(bintimefd, ub, n);
709                 if(i != n)
710                         break;
711                 p = ub;
712                 if(nsec != nil)
713                         be2vlong(nsec, ub);
714                 p += sizeof(vlong);
715                 if(ticks != nil)
716                         be2vlong((vlong*)ticks, p);
717                 p += sizeof(vlong);
718                 if(hz != nil)
719                         be2vlong((vlong*)hz, p);
720                 return 0;
721         case Itiming:
722                 n = sizeof(vlong);
723                 if(ticks != nil)
724                         n = 2*sizeof(vlong);
725                 i = read(timingfd, ub, n);
726                 if(i != n)
727                         break;
728                 p = ub;
729                 if(nsec != nil)
730                         be2vlong(nsec, ub);
731                 p += sizeof(vlong);
732                 if(ticks != nil)
733                         be2vlong((vlong*)ticks, p);
734                 if(hz != nil){
735                         seek(fastclockfd, 0, 0);
736                         n = read(fastclockfd, b, sizeof(b)-1);
737                         if(n <= 0)
738                                 break;
739                         b[n] = 0;
740                         *hz = strtoll(b+24, 0, 0);
741                 }
742                 return 0;
743         case Insec:
744                 if(nsec != nil){
745                         seek(nsecfd, 0, 0);
746                         n = read(nsecfd, b, sizeof(b)-1);
747                         if(n <= 0)
748                                 break;
749                         b[n] = 0;
750                         *nsec = strtoll(b, 0, 0);
751                 }
752                 if(ticks != nil){
753                         seek(fastclockfd, 0, 0);
754                         n = read(fastclockfd, b, sizeof(b)-1);
755                         if(n <= 0)
756                                 break;
757                         b[n] = 0;
758                         *ticks = strtoll(b, 0, 0);
759                 }
760                 if(hz != nil){
761                         seek(fastclockfd, 0, 0);
762                         n = read(fastclockfd, b, sizeof(b)-1);
763                         if(n <= 24)
764                                 break;
765                         b[n] = 0;
766                         *hz = strtoll(b+24, 0, 0);
767                 }
768                 return 0;
769         }
770         return -1;
771 }
772
773 static void
774 settime(vlong now, uvlong hz, vlong delta, int n)
775 {
776         uchar b[1+sizeof(vlong)+sizeof(long)], *p;
777
778         if(debug)
779                 fprint(2, "settime(now=%lld, hz=%llud, delta=%lld, period=%d)\n",
780                         now, hz, delta, n);
781         if(impotent)
782                 return;
783         switch(ifc){
784         case Ibintime:
785                 if(now >= 0){
786                         p = b;
787                         *p++ = 'n';
788                         p = vlong2be(p, now);
789                         if(write(bintimefd, b, p-b) < 0)
790                                 sysfatal("writing /dev/bintime: %r");
791                 }
792                 if(delta != 0){
793                         p = b;
794                         *p++ = 'd';
795                         p = vlong2be(p, delta);
796                         p = long2be(p, n);
797                         if(write(bintimefd, b, p-b) < 0)
798                                 sysfatal("writing /dev/bintime: %r");
799                 }
800                 if(hz != 0){
801                         p = b;
802                         *p++ = 'f';
803                         p = vlong2be(p, hz);
804                         if(write(bintimefd, b, p-b) < 0)
805                                 sysfatal("writing /dev/bintime: %r");
806                 }
807                 break;
808         case Itiming:
809         case Insec:
810                 seek(nsecfd, 0, 0);
811                 if(now >= 0 || delta != 0){
812                         if(fprint(nsecfd, "%lld %lld %d", now, delta, n) < 0)
813                                 sysfatal("writing /dev/nsec: %r");
814                 }
815                 if(hz > 0){
816                         seek(fastclockfd, 0, 0);
817                         if(fprint(fastclockfd, "%lld", hz) < 0)
818                                 sysfatal("writing /dev/fastclock: %r");
819                 }
820         }
821 }
822
823 /*
824  *  set priority high and wire process to a processor
825  */
826 static void
827 setpriority(void)
828 {
829         int fd;
830         char buf[32];
831
832         sprint(buf, "/proc/%d/ctl", getpid());
833         fd = open(buf, ORDWR);
834         if(fd < 0){
835                 fprint(2, "can't set priority\n");
836                 return;
837         }
838         if(fprint(fd, "pri 100") < 0)
839                 fprint(2, "can't set priority\n");
840         if(fprint(fd, "wired 2") < 0)
841                 fprint(2, "can't wire process\n");
842         close(fd);
843 }
844
845 /* convert to ntp timestamps */
846 static void
847 hnputts(void *p, vlong nsec)
848 {
849         uchar *a;
850         ulong tsh, tsl;
851
852         a = p;
853
854         /* zero is a special case */
855         if(nsec == 0)
856                 return;
857
858         tsh = nsec/SEC;
859         nsec -= tsh*SEC;
860         tsl = (nsec<<32)/SEC;
861         hnputl(a, tsh+EPOCHDIFF);
862         hnputl(a+4, tsl);
863 }
864
865 /* convert from ntp timestamps */
866 static vlong
867 nhgetts(void *p)
868 {
869         uchar *a;
870         ulong tsh, tsl;
871         vlong nsec;
872
873         a = p;
874         tsh = nhgetl(a);
875         tsl = nhgetl(a+4);
876         nsec = tsl*SEC;
877         nsec >>= 32;
878         nsec += (tsh - EPOCHDIFF)*SEC;
879         return nsec;
880 }
881
882 /* convert to ntp 32 bit fixed point */
883 static void
884 hnputfp(void *p, vlong nsec)
885 {
886         uchar *a;
887         ulong fp;
888
889         a = p;
890
891         fp = nsec/(SEC/((vlong)(1<<16)));
892         hnputl(a, fp);
893 }
894
895 /* convert from ntp fixed point to nanosecs */
896 static vlong
897 nhgetfp(void *p)
898 {
899         uchar *a;
900         ulong fp;
901         vlong nsec;
902
903         a = p;
904         fp = nhgetl(a);
905         nsec = ((vlong)fp)*(SEC/((vlong)(1<<16)));
906         return nsec;
907 }
908
909 /* get network address of the server */
910 static void
911 setrootid(char *d)
912 {
913         char buf[128];
914         int fd, n;
915         char *p;
916
917         snprint(buf, sizeof buf, "%s/remote", d);
918         fd = open(buf, OREAD);
919         if(fd < 0)
920                 return;
921         n = read(fd, buf, sizeof buf);
922         close(fd);
923         if(n <= 0)
924                 return;
925         p = strchr(buf, '!');
926         if(p != nil)
927                 *p = 0;
928         v4parseip(rootid, buf);
929 }
930
931 static void
932 ding(void*, char *s)
933 {
934         if(strstr(s, "alarm") != nil)
935                 noted(NCONT);
936         noted(NDFLT);
937 }
938
939 static void
940 addntpserver(char *name)
941 {
942         NTPserver *ns, **l;
943
944         ns = mallocz(sizeof(NTPserver), 1);
945         if(ns == nil)
946                 sysfatal("addntpserver: %r");
947         timeserver = strdup(name);
948         ns->name = name;
949         for(l = &ntpservers; *l != nil; l = &(*l)->next)
950                 ;
951         *l = ns;
952 }
953
954 /*
955  *  sntp client, we keep calling if the delay seems
956  *  unusually high, i.e., 30% longer than avg.
957  */
958 static int
959 ntptimediff(NTPserver *ns)
960 {
961         int fd, tries, n;
962         NTPpkt ntpin, ntpout;
963         vlong dt, recvts, origts, xmitts, destts, x;
964         char dir[64];
965         static int whined;
966
967         notify(ding);
968         alarm(30*1000); /* don't wait forever if ns->name is unreachable */
969         fd = dial(netmkaddr(ns->name, "udp", "ntp"), 0, dir, 0);
970         if(fd < 0){
971                 if (!whined++)
972                         syslog(0, logfile, "can't reach %s: %r", ns->name);
973                 return -1;
974         }
975         setrootid(dir);
976
977         memset(&ntpout, 0, sizeof(ntpout));
978         ntpout.mode = 3 | (3 << 3);
979
980         for(tries = 0; tries < 3; tries++){
981                 alarm(2*1000);
982
983                 gettime(&x, 0, 0);
984                 hnputts(ntpout.xmitts, x);
985                 if(write(fd, &ntpout, NTPSIZE) < 0){
986                         alarm(0);
987                         continue;
988                 }
989
990                 n = read(fd, &ntpin, sizeof ntpin);
991                 alarm(0);
992                 gettime(&destts, 0, 0);
993                 if(n >= NTPSIZE){
994                         close(fd);
995
996                         /* we got one, use it */
997                         recvts = nhgetts(ntpin.recvts);
998                         origts = nhgetts(ntpin.origts);
999                         xmitts = nhgetts(ntpin.xmitts);
1000                         dt = ((recvts - origts) + (xmitts - destts))/2;
1001
1002                         /* save results */
1003                         ns->rtt = ((destts - origts) - (xmitts - recvts))/2;
1004                         ns->dt = dt;
1005                         ns->stratum = ntpin.stratum;
1006                         ns->precision = ntpin.precision;
1007                         ns->rootdelay = nhgetfp(ntpin.rootdelay);
1008                         ns->rootdisp = nhgetfp(ntpin.rootdisp);
1009
1010                         if(debug)
1011                                 fprint(2, "ntp %s stratum %d ntpdelay(%lld)\n",
1012                                         ns->name, ntpin.stratum, ns->rtt);
1013                         return 0;
1014                 }
1015
1016                 /* try again */
1017                 sleep(250);
1018         }
1019         close(fd);
1020         return -1;
1021 }
1022
1023 static vlong
1024 gpssample(void)
1025 {
1026         vlong   l, g, d;
1027         int     i, n;
1028         char    *v[4], buf[128];
1029
1030         d = -1000000000000000000LL;
1031         for(i = 0; i < 5; i++){
1032                 sleep(1100);
1033                 seek(gpsfil, 0, 0);
1034                 n = read(gpsfil, buf, sizeof buf - 1);
1035                 if (n <= 0)
1036                         return 0;
1037                 buf[n] = 0;
1038                 n = tokenize(buf, v, nelem(v));
1039                 if(n != 4 || strcmp(v[3], "A") != 0)
1040                         return 0;
1041                 g = atoll(v[1]);
1042                 l = atoll(v[2]);
1043                 if(g-l > d)
1044                         d = g-l;
1045         }
1046         return d;
1047 }
1048
1049 static vlong
1050 ntpsample(void)
1051 {
1052         NTPserver *tns, *ns;
1053         vlong metric, x;
1054
1055         metric = 1000LL*SEC;
1056         ns = nil;
1057         for(tns = ntpservers; tns != nil; tns = tns->next){
1058                 if(ntptimediff(tns) < 0)
1059                         continue;
1060                 x = vabs(tns->rootdisp) + (vabs(tns->rtt+tns->rootdelay)>>1);
1061                 if(debug)
1062                         fprint(2, "ntp %s rootdelay %lld rootdisp %lld metric %lld\n",
1063                                 tns->name, tns->rootdelay, tns->rootdisp, x);
1064                 if(x < metric){
1065                         metric = x;
1066                         ns = tns;
1067                 }
1068         }
1069
1070         if(ns == nil)
1071                 return 0;
1072
1073         /* save data for our server */
1074         rootdisp = ns->rootdisp;
1075         rootdelay = ns->rootdelay;
1076         mydelay = ns->rtt;
1077         mydisp = avgerr;
1078         if(ns->stratum == 0)
1079                 stratum = 0;
1080         else
1081                 stratum = ns->stratum + 1;
1082
1083         ε = abs(ns->rtt/2);
1084         return ns->dt;
1085 }
1086
1087 /*
1088  * sample the utc file
1089  */
1090 static vlong
1091 utcsample(void)
1092 {
1093         vlong   s;
1094         int     n;
1095         char    *v[2], buf[128];
1096
1097         s = 0;
1098         seek(utcfil, 0, 0);
1099         n = read(utcfil, buf, sizeof buf - 1);
1100         if (n <= 0)
1101                 return 0;
1102         buf[n] = 0;
1103         n = tokenize(buf, v, nelem(v));
1104         if (strcmp(v[0], "0") == 0)
1105                 return 0;
1106         if (n == 2) {
1107                 gettime(&s, nil, nil);
1108                 s -= atoll(v[1]);
1109         }
1110         lastutc = atoll(v[0]) + s;
1111         return lastutc;
1112 }
1113
1114 /*
1115  *  sntp server
1116  */
1117 static int
1118 openlisten(char *net)
1119 {
1120         int fd, cfd;
1121         char data[128], devdir[40];
1122
1123         sprint(data, "%s/udp!*!ntp", net);
1124         cfd = announce(data, devdir);
1125         if(cfd < 0)
1126                 sysfatal("can't announce");
1127         if(fprint(cfd, "headers") < 0)
1128                 sysfatal("can't set header mode");
1129
1130         sprint(data, "%s/data", devdir);
1131         fd = open(data, ORDWR);
1132         if(fd < 0)
1133                 sysfatal("open %s: %r", data);
1134         return fd;
1135 }
1136
1137 static void
1138 ntpserver(char *servenet)
1139 {
1140         int fd, n, vers, mode;
1141         vlong recvts, x;
1142         char buf[512];
1143         NTPpkt *ntp;
1144
1145         fd = openlisten(servenet);
1146
1147         if (Rootid == nil)
1148                 switch(type){
1149                 case Fs:
1150                         Rootid = "WWV";
1151                         break;
1152                 case Rtc:
1153                         Rootid = "LOCL";
1154                         break;
1155                 case Utc:
1156                         Rootid = "UTC";
1157                         break;
1158                 case Gps:
1159                         Rootid = "GPS";
1160                         break;
1161                 case Ntp:
1162                         /* set by the ntp client */
1163                         break;
1164                 }
1165         if (Rootid != nil)
1166                 memmove(rootid, Rootid, strlen(Rootid) > 4? 4: strlen(Rootid));
1167
1168         for(;;){
1169                 n = read(fd, buf, sizeof buf);
1170                 gettime(&recvts, 0, 0);
1171                 if(n <= 0) {
1172                         /* don't croak on input error, but don't spin either */
1173                         sleep(500);
1174                         continue;
1175                 }
1176                 if(n < Udphdrsize + NTPSIZE)
1177                         continue;
1178
1179                 ntp = (NTPpkt*)(buf + Udphdrsize);
1180                 mode = ntp->mode & 7;
1181                 vers = (ntp->mode>>3) & 7;
1182                 if(mode != 3)
1183                         continue;
1184
1185                 ntp->mode = (vers<<3)|4;
1186                 ntp->stratum = stratum;
1187                 ntp->precision = myprec;
1188                 hnputfp(ntp->rootdelay, rootdelay + mydelay);
1189                 hnputfp(ntp->rootdisp, rootdisp + mydisp);
1190                 hnputts(ntp->refts, lastutc);
1191                 memmove(ntp->origts, ntp->xmitts, sizeof(ntp->origts));
1192                 hnputts(ntp->recvts, recvts);
1193                 memmove(ntp->rootid, rootid, sizeof(ntp->rootid));
1194                 gettime(&x, 0, 0);
1195                 hnputts(ntp->xmitts, x);
1196                 write(fd, buf, NTPSIZE + Udphdrsize);
1197         }
1198 }
1199
1200 /*
1201  *  get the current time from the file system
1202  */
1203 static long
1204 fstime(void)
1205 {
1206         Dir *d;
1207         ulong t;
1208
1209         d = dirstat("/n/boot");
1210         if(d != nil){
1211                 t = d->atime;
1212                 free(d);
1213         } else
1214                 t = 0;
1215         return t;
1216 }
1217
1218 /*
1219  *  get the current time from the real time clock
1220  */
1221 static long
1222 rtctime(void)
1223 {
1224         char b[20];
1225         static int f = -1;
1226         int i, retries;
1227
1228         memset(b, 0, sizeof(b));
1229         for(retries = 0; retries < 100; retries++){
1230                 if(f < 0)
1231                         f = open("/dev/rtc", OREAD|OCEXEC);
1232                 if(f < 0)
1233                         break;
1234                 if(seek(f, 0, 0) < 0 || (i = read(f, b, sizeof b)) < 0){
1235                         close(f);
1236                         f = -1;
1237                 } else
1238                         if(i != 0)
1239                                 break;
1240         }
1241         return strtoul(b, 0, 10)+gmtdelta;
1242 }
1243
1244 static void
1245 setrtctime(long t)
1246 {
1247         static int f = -1;
1248
1249         if(f < 0)
1250                 f = open("/dev/rtc", OWRITE|OCEXEC);
1251         if(f < 0)
1252                 return;
1253         if(seek(f, 0, 0) < 0 || fprint(f, "%ld", t-gmtdelta) < 0){
1254                 close(f);
1255                 f = -1;
1256         }
1257 }
1258
1259
1260 /*
1261  *  Sample a clock.  We wait for the clock to always
1262  *  be at the leading edge of a clock period.
1263  */
1264 static vlong
1265 sample(long (*get)(void))
1266 {
1267         long this, last;
1268         vlong start, end;
1269
1270         /*
1271          *  wait for the second to change
1272          */
1273         last = (*get)();
1274         for(;;){
1275                 gettime(&start, 0, 0);
1276                 sleep(5);
1277                 this = (*get)();
1278                 gettime(&end, 0, 0);
1279                 if(this != last)
1280                         break;
1281                 last = this;
1282         }
1283         return SEC*this - (end-start)/2;
1284 }
1285
1286 /*
1287  * the name of the frequency file has the method and possibly the
1288  * server name encoded in it.
1289  */
1290 static int
1291 openfreqfile(void)
1292 {
1293         char *p;
1294         int fd;
1295
1296         if(sysid == nil)
1297                 return -1;
1298
1299         switch(type){
1300         case Ntp:
1301                 p = smprint("%s/ts.%s.%d.%s", dir, sysid, type, timeserver);
1302                 break;
1303         default:
1304                 p = smprint("%s/ts.%s.%d", dir, sysid, type);
1305                 break;
1306         }
1307         fd = open(p, ORDWR);
1308         if(fd < 0)
1309                 fd = create(p, ORDWR, 0666);
1310         free(p);
1311         if(fd < 0)
1312                 return -1;
1313         return fd;
1314 }
1315
1316 /*
1317  *  the file contains the last known frequency and the
1318  *  number of seconds it was sampled over
1319  */
1320 static vlong
1321 readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz)
1322 {
1323         int n;
1324         char buf[128];
1325         vlong hz;
1326
1327         n = read(fd, buf, sizeof buf-1);
1328         if(n <= 0)
1329                 return ohz;
1330         buf[n] = 0;
1331         hz = strtoll(buf, nil, 0);
1332
1333         if(hz > maxhz || hz < minhz)
1334                 return ohz;
1335
1336         settime(-1, hz, 0, 0);
1337         return hz;
1338 }
1339
1340 /*
1341  *  remember hz and averaging period
1342  */
1343 static void
1344 writefreqfile(int fd, vlong hz, int secs, vlong diff)
1345 {
1346         long now;
1347         static long last;
1348
1349         if(fd < 0)
1350                 return;
1351         now = time(0);
1352         if(now - last < 10*60)
1353                 return;
1354         last = now;
1355         if(seek(fd, 0, 0) < 0)
1356                 return;
1357         fprint(fd, "%lld %d %d %lld\n", hz, secs, type, diff);
1358 }
1359
1360 static uvlong
1361 vabs(vlong x)
1362 {
1363         if(x < 0)
1364                 return -x;
1365         else
1366                 return x;
1367 }
1368
1369 static void
1370 background(void)
1371 {
1372         static int inbackground;
1373
1374         if(inbackground)
1375                 return;
1376
1377         if(!debug) 
1378                 switch(rfork(RFPROC|RFFDG|RFNAMEG|RFNOTEG|RFNOWAIT)){
1379                 case -1:
1380                         sysfatal("forking: %r");
1381                         break;
1382                 case 0:
1383                         break;
1384                 default:
1385                         exits(0);
1386                 }
1387         inbackground = 1;
1388 }
1389
1390 static int
1391 getclockprecision(vlong hz)
1392 {
1393         int i;
1394
1395         i = 8;
1396         while(hz > 0){
1397                 i--;
1398                 hz >>= 1;
1399         }
1400         return i;
1401 }