Change the default for ntpd back to -s, the bug which triggered this
[dragonfly.git] / contrib / ntp / ntpd / ntp_util.c
1 /*
2  * ntp_util.c - stuff I didn't have any other place for
3  */
4
5 #ifdef HAVE_CONFIG_H
6 # include <config.h>
7 #endif
8
9 #include "ntpd.h"
10 #include "ntp_io.h"
11 #include "ntp_unixtime.h"
12 #include "ntp_filegen.h"
13 #include "ntp_if.h"
14 #include "ntp_stdlib.h"
15
16 #include <stdio.h>
17 #include <ctype.h>
18 #include <sys/types.h>
19 #ifdef HAVE_SYS_IOCTL_H
20 # include <sys/ioctl.h>
21 #endif
22
23 #ifdef HAVE_IEEEFP_H
24 # include <ieeefp.h>
25 #endif
26 #ifdef HAVE_MATH_H
27 # include <math.h>
28 #endif
29
30 #ifdef  DOSYNCTODR
31 #if !defined(VMS)
32 #include <sys/resource.h>
33 #endif /* VMS */
34 #endif
35
36 #if defined(VMS)
37 #include <descrip.h>
38 #endif /* VMS */
39
40 /*
41  * This contains odds and ends.  Right now the only thing you'll find
42  * in here is the hourly stats printer and some code to support rereading
43  * the keys file, but I may eventually put other things in here such as
44  * code to do something with the leap bits.
45  */
46
47 /*
48  * Name of the keys file
49  */
50 static  char *key_file_name;
51
52 /*
53  * The name of the drift_comp file and the temporary.
54  */
55 static  char *stats_drift_file;
56 static  char *stats_temp_file;
57
58 /*
59  * Statistics file stuff
60  */
61 #ifndef NTP_VAR
62 #ifndef SYS_WINNT
63 #define NTP_VAR "/var/NTP/"             /* NOTE the trailing '/' */
64 #else
65 #define NTP_VAR "c:\\var\\ntp\\"                /* NOTE the trailing '\\' */
66 #endif /* SYS_WINNT */
67 #endif
68
69 #ifndef MAXPATHLEN
70 #define MAXPATHLEN 256
71 #endif
72
73 static  char statsdir[MAXPATHLEN] = NTP_VAR;
74
75 static FILEGEN peerstats;
76 static FILEGEN loopstats;
77 static FILEGEN clockstats;
78 static FILEGEN rawstats;
79
80 /*
81  * This controls whether stats are written to the fileset. Provided
82  * so that ntpdc can turn off stats when the file system fills up. 
83  */
84 int stats_control;
85
86 /*
87  * init_util - initialize the utilities
88  */
89 void
90 init_util(void)
91 {
92         stats_drift_file = 0;
93         stats_temp_file = 0;
94         key_file_name = 0;
95
96 #define PEERNAME "peerstats"
97 #define LOOPNAME "loopstats"
98 #define CLOCKNAME "clockstats"
99 #define RAWNAME "rawstats"
100         peerstats.fp       = NULL;
101         peerstats.prefix   = &statsdir[0];
102         peerstats.basename = (char*)emalloc(strlen(PEERNAME)+1);
103         strcpy(peerstats.basename, PEERNAME);
104         peerstats.id       = 0;
105         peerstats.type     = FILEGEN_DAY;
106         peerstats.flag     = FGEN_FLAG_LINK; /* not yet enabled !!*/
107         filegen_register("peerstats", &peerstats);
108         
109         loopstats.fp       = NULL;
110         loopstats.prefix   = &statsdir[0];
111         loopstats.basename = (char*)emalloc(strlen(LOOPNAME)+1);
112         strcpy(loopstats.basename, LOOPNAME);
113         loopstats.id       = 0;
114         loopstats.type     = FILEGEN_DAY;
115         loopstats.flag     = FGEN_FLAG_LINK; /* not yet enabled !!*/
116         filegen_register("loopstats", &loopstats);
117
118         clockstats.fp      = NULL;
119         clockstats.prefix  = &statsdir[0];
120         clockstats.basename = (char*)emalloc(strlen(CLOCKNAME)+1);
121         strcpy(clockstats.basename, CLOCKNAME);
122         clockstats.id      = 0;
123         clockstats.type    = FILEGEN_DAY;
124         clockstats.flag    = FGEN_FLAG_LINK; /* not yet enabled !!*/
125         filegen_register("clockstats", &clockstats);
126
127         rawstats.fp      = NULL;
128         rawstats.prefix  = &statsdir[0];
129         rawstats.basename = (char*)emalloc(strlen(RAWNAME)+1);
130         strcpy(rawstats.basename, RAWNAME);
131         rawstats.id      = 0;
132         rawstats.type    = FILEGEN_DAY;
133         rawstats.flag    = FGEN_FLAG_LINK; /* not yet enabled !!*/
134         filegen_register("rawstats", &rawstats);
135
136 #undef PEERNAME
137 #undef LOOPNAME
138 #undef CLOCKNAME
139 #undef RAWNAME
140
141 }
142
143
144 /*
145  * hourly_stats - print some interesting stats
146  */
147 void
148 hourly_stats(void)
149 {
150         FILE *fp;
151
152 #ifdef DOSYNCTODR
153         struct timeval tv;
154 #if !defined(VMS)
155         int prio_set;
156 #endif
157 #ifdef HAVE_GETCLOCK
158         struct timespec ts;
159 #endif
160         int o_prio;
161
162         /*
163          * Sometimes having a Sun can be a drag.
164          *
165          * The kernel variable dosynctodr controls whether the system's
166          * soft clock is kept in sync with the battery clock. If it
167          * is zero, then the soft clock is not synced, and the battery
168          * clock is simply left to rot. That means that when the system
169          * reboots, the battery clock (which has probably gone wacky)
170          * sets the soft clock. That means ntpd starts off with a very
171          * confused idea of what time it is. It then takes a large
172          * amount of time to figure out just how wacky the battery clock
173          * has made things drift, etc, etc. The solution is to make the
174          * battery clock sync up to system time. The way to do THAT is
175          * to simply set the time of day to the current time of day, but
176          * as quickly as possible. This may, or may not be a sensible
177          * thing to do.
178          *
179          * CAVEAT: settimeofday() steps the sun clock by about 800 us,
180          *         so setting DOSYNCTODR seems a bad idea in the
181          *         case of us resolution
182          */
183
184 #if !defined(VMS)
185         /* (prr) getpriority returns -1 on error, but -1 is also a valid
186          * return value (!), so instead we have to zero errno before the call
187          * and check it for non-zero afterwards.
188          */
189
190         errno = 0;
191         prio_set = 0;
192         o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */
193
194         /* (prr) if getpriority succeeded, call setpriority to raise
195          * scheduling priority as high as possible.  If that succeeds
196          * as well, set the prio_set flag so we remember to reset
197          * priority to its previous value below.  Note that on Solaris 2.6
198          * (and beyond?), both getpriority and setpriority will fail with
199          * ESRCH, because sched_setscheduler (called from main) put us in
200          * the real-time scheduling class which setpriority doesn't know about.
201          * Being in the real-time class is better than anything setpriority
202          * can do, anyhow, so this error is silently ignored.
203          */
204
205         if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
206             prio_set = 1;       /* overdrive */
207 #endif /* VMS */
208 #ifdef HAVE_GETCLOCK
209         (void) getclock(TIMEOFDAY, &ts);
210         tv.tv_sec = ts.tv_sec;
211         tv.tv_usec = ts.tv_nsec / 1000;
212 #else /*  not HAVE_GETCLOCK */
213         GETTIMEOFDAY(&tv,(struct timezone *)NULL);
214 #endif /* not HAVE_GETCLOCK */
215         if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0)
216         {
217                 msyslog(LOG_ERR, "can't sync battery time: %m");
218         }
219 #if !defined(VMS)
220         if (prio_set)
221             setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */
222 #endif /* VMS */
223 #endif /* DOSYNCTODR */
224
225         NLOG(NLOG_SYSSTATIST)
226                 msyslog(LOG_INFO,
227                     "offset %.6f sec freq %.3f ppm error %.6f poll %d",
228                     last_offset, drift_comp * 1e6, sys_jitter, sys_poll);
229
230         
231         if (stats_drift_file != 0) {
232                 if ((fp = fopen(stats_temp_file, "w")) == NULL) {
233                         msyslog(LOG_ERR, "can't open %s: %m",
234                             stats_temp_file);
235                         return;
236                 }
237                 fprintf(fp, "%.3f\n", drift_comp * 1e6);
238                 (void)fclose(fp);
239                 /* atomic */
240 #ifdef SYS_WINNT
241                 (void) unlink(stats_drift_file); /* rename semantics differ under NT */
242 #endif /* SYS_WINNT */
243
244 #ifndef NO_RENAME
245                 (void) rename(stats_temp_file, stats_drift_file);
246 #else
247         /* we have no rename NFS of ftp in use*/
248                 if ((fp = fopen(stats_drift_file, "w")) == NULL) {
249                         msyslog(LOG_ERR, "can't open %s: %m",
250                             stats_drift_file);
251                         return;
252                 }
253
254 #endif
255
256 #if defined(VMS)
257                 /* PURGE */
258                 {
259                         $DESCRIPTOR(oldvers,";-1");
260                         struct dsc$descriptor driftdsc = {
261                                 strlen(stats_drift_file),0,0,stats_drift_file };
262
263                         while(lib$delete_file(&oldvers,&driftdsc) & 1) ;
264                 }
265 #endif
266         }
267 }
268
269
270 /*
271  * stats_config - configure the stats operation
272  */
273 void
274 stats_config(
275         int item,
276         char *invalue   /* only one type so far */
277         )
278 {
279         FILE *fp;
280         char *value;
281         double old_drift;
282         int len;
283
284         /* Expand environment strings under Windows NT, since the command
285          * interpreter doesn't do this, the program must.
286          */
287 #ifdef SYS_WINNT
288         char newvalue[MAX_PATH], parameter[MAX_PATH];
289
290         if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH))
291         {
292                 switch(item) {
293                     case STATS_FREQ_FILE:
294                         strcpy(parameter,"STATS_FREQ_FILE");
295                         break;
296                     case STATS_STATSDIR:
297                         strcpy(parameter,"STATS_STATSDIR");
298                         break;
299                     case STATS_PID_FILE:
300                         strcpy(parameter,"STATS_PID_FILE");
301                         break;
302                     default:
303                         strcpy(parameter,"UNKNOWN");
304                         break;
305                 }
306                 value = invalue;
307
308                 msyslog(LOG_ERR,
309                     "ExpandEnvironmentStrings(%s) failed: %m\n", parameter);
310         }
311         else 
312                 value = newvalue;
313 #else    
314         value = invalue;
315 #endif /* SYS_WINNT */
316
317         
318         
319         switch(item) {
320             case STATS_FREQ_FILE:
321                 if (stats_drift_file != 0) {
322                         (void) free(stats_drift_file);
323                         (void) free(stats_temp_file);
324                         stats_drift_file = 0;
325                         stats_temp_file = 0;
326                 }
327
328                 if (value == 0 || (len = strlen(value)) == 0)
329                     break;
330
331                 stats_drift_file = (char*)emalloc((u_int)(len + 1));
332 #if !defined(VMS)
333                 stats_temp_file = (char*)emalloc((u_int)(len + sizeof(".TEMP")));
334 #else
335                 stats_temp_file = (char*)emalloc((u_int)(len + sizeof("-TEMP")));
336 #endif /* VMS */
337                 memmove(stats_drift_file, value, (unsigned)(len+1));
338                 memmove(stats_temp_file, value, (unsigned)len);
339 #if !defined(VMS)
340                 memmove(stats_temp_file + len, ".TEMP", sizeof(".TEMP"));
341 #else
342                 memmove(stats_temp_file + len, "-TEMP", sizeof("-TEMP"));
343 #endif /* VMS */
344
345                 /*
346                  * Open drift file and read frequency
347                  */
348                 if ((fp = fopen(stats_drift_file, "r")) == NULL) {
349                         break;
350                 }
351                 if (fscanf(fp, "%lf", &old_drift) != 1) {
352                         msyslog(LOG_ERR, "Un-parsable frequency in %s", 
353                             stats_drift_file);
354                         (void) fclose(fp);
355                         break;
356                 }
357                 (void) fclose(fp);
358                 if (
359 #ifdef HAVE_FINITE
360                         !finite(old_drift)
361 #else  /* not HAVE_FINITE */
362 # ifdef HAVE_ISFINITE
363                         !isfinite(old_drift)
364 # else  /* not HAVE_ISFINITE */
365                         0
366 # endif /* not HAVE_ISFINITE */
367 #endif /* not HAVE_FINITE */
368                     || (fabs(old_drift) > (NTP_MAXFREQ * 1e6))) {
369                         msyslog(LOG_ERR, "invalid frequency (%f) in %s", 
370                             old_drift, stats_drift_file);
371                         old_drift = 0.0;
372                 }
373                 msyslog(LOG_INFO, "frequency initialized %.3f from %s",
374                     old_drift, stats_drift_file);
375                 loop_config(LOOP_DRIFTCOMP, old_drift / 1e6);
376                 break;
377         
378             case STATS_STATSDIR:
379                 if (strlen(value) >= sizeof(statsdir)) {
380                         msyslog(LOG_ERR,
381                             "value for statsdir too long (>%d, sigh)",
382                             (int)sizeof(statsdir)-1);
383                 } else {
384                         l_fp now;
385
386                         get_systime(&now);
387                         strcpy(statsdir,value);
388                         if(peerstats.prefix == &statsdir[0] &&
389                             peerstats.fp != NULL) {
390                                 fclose(peerstats.fp);
391                                 peerstats.fp = NULL;
392                                 filegen_setup(&peerstats, now.l_ui);
393                         }
394                         if(loopstats.prefix == &statsdir[0] &&
395                             loopstats.fp != NULL) {
396                                 fclose(loopstats.fp);
397                                 loopstats.fp = NULL;
398                                 filegen_setup(&loopstats, now.l_ui);
399                         }
400                         if(clockstats.prefix == &statsdir[0] &&
401                             clockstats.fp != NULL) {
402                                 fclose(clockstats.fp);
403                                 clockstats.fp = NULL;
404                                 filegen_setup(&clockstats, now.l_ui);
405                         }
406                         if(rawstats.prefix == &statsdir[0] &&
407                             rawstats.fp != NULL) {
408                                 fclose(rawstats.fp);
409                                 rawstats.fp = NULL;
410                                 filegen_setup(&rawstats, now.l_ui);
411                         }
412                 }
413                 break;
414
415             case STATS_PID_FILE:
416                 if ((fp = fopen(value, "w")) == NULL) {
417                         msyslog(LOG_ERR, "Can't open %s: %m", value);
418                         break;
419                 }
420                 fprintf(fp, "%d", (int) getpid());
421                 fclose(fp);;
422                 break;
423
424             default:
425                 /* oh well */
426                 break;
427         }
428 }
429
430 /*
431  * record_peer_stats - write peer statistics to file
432  *
433  * file format:
434  * day (mjd)
435  * time (s past UTC midnight)
436  * peer (ip address)
437  * peer status word (hex)
438  * peer offset (s)
439  * peer delay (s)
440  * peer error bound (s)
441  * peer error (s)
442 */
443 void
444 record_peer_stats(
445         struct sockaddr_in *addr,
446         int status,
447         double offset,
448         double delay,
449         double dispersion,
450         double skew
451         )
452 {
453         struct timeval tv;
454 #ifdef HAVE_GETCLOCK
455         struct timespec ts;
456 #endif
457         u_long day, sec, msec;
458
459         if (!stats_control)
460                 return;
461 #ifdef HAVE_GETCLOCK
462         (void) getclock(TIMEOFDAY, &ts);
463         tv.tv_sec = ts.tv_sec;
464         tv.tv_usec = ts.tv_nsec / 1000;
465 #else /*  not HAVE_GETCLOCK */
466         GETTIMEOFDAY(&tv, (struct timezone *)NULL);
467 #endif /* not HAVE_GETCLOCK */
468         day = tv.tv_sec / 86400 + MJD_1970;
469         sec = tv.tv_sec % 86400;
470         msec = tv.tv_usec / 1000;
471
472         filegen_setup(&peerstats, (u_long)(tv.tv_sec + JAN_1970));
473         if (peerstats.fp != NULL) {
474                 fprintf(peerstats.fp,
475                     "%lu %lu.%03lu %s %x %.9f %.9f %.9f %.9f\n",
476                     day, sec, msec, ntoa(addr), status, offset,
477                     delay, dispersion, skew);
478                 fflush(peerstats.fp);
479         }
480 }
481 /*
482  * record_loop_stats - write loop filter statistics to file
483  *
484  * file format:
485  * day (mjd)
486  * time (s past midnight)
487  * offset (s)
488  * frequency (approx ppm)
489  * time constant (log base 2)
490  */
491 void
492 record_loop_stats(
493         double offset,
494         double freq,
495         double jitter,
496         double stability,
497         int poll
498         )
499 {
500         struct timeval tv;
501 #ifdef HAVE_GETCLOCK
502         struct timespec ts;
503 #endif
504         u_long day, sec, msec;
505
506         if (!stats_control)
507                 return;
508 #ifdef HAVE_GETCLOCK
509         (void) getclock(TIMEOFDAY, &ts);
510         tv.tv_sec = ts.tv_sec;
511         tv.tv_usec = ts.tv_nsec / 1000;
512 #else /*  not HAVE_GETCLOCK */
513         GETTIMEOFDAY(&tv, (struct timezone *)NULL);
514 #endif /* not HAVE_GETCLOCK */
515         day = tv.tv_sec / 86400 + MJD_1970;
516         sec = tv.tv_sec % 86400;
517         msec = tv.tv_usec / 1000;
518
519         filegen_setup(&loopstats, (u_long)(tv.tv_sec + JAN_1970));
520         if (loopstats.fp != NULL) {
521                 fprintf(loopstats.fp, "%lu %lu.%03lu %.9f %.6f %.9f %.6f %d\n",
522                     day, sec, msec, offset, freq * 1e6, jitter,
523                     stability * 1e6, poll);
524                 fflush(loopstats.fp);
525         }
526 }
527
528 /*
529  * record_clock_stats - write clock statistics to file
530  *
531  * file format:
532  * day (mjd)
533  * time (s past midnight)
534  * peer (ip address)
535  * text message
536  */
537 void
538 record_clock_stats(
539         struct sockaddr_in *addr,
540         const char *text
541         )
542 {
543         struct timeval tv;
544 #ifdef HAVE_GETCLOCK
545         struct timespec ts;
546 #endif
547         u_long day, sec, msec;
548
549         if (!stats_control)
550                 return;
551 #ifdef HAVE_GETCLOCK
552         (void) getclock(TIMEOFDAY, &ts);
553         tv.tv_sec = ts.tv_sec;
554         tv.tv_usec = ts.tv_nsec / 1000;
555 #else /*  not HAVE_GETCLOCK */
556         GETTIMEOFDAY(&tv, (struct timezone *)NULL);
557 #endif /* not HAVE_GETCLOCK */
558         day = tv.tv_sec / 86400 + MJD_1970;
559         sec = tv.tv_sec % 86400;
560         msec = tv.tv_usec / 1000;
561
562         filegen_setup(&clockstats, (u_long)(tv.tv_sec + JAN_1970));
563         if (clockstats.fp != NULL) {
564                 fprintf(clockstats.fp, "%lu %lu.%03lu %s %s\n",
565                     day, sec, msec, ntoa(addr), text);
566                 fflush(clockstats.fp);
567         }
568 }
569
570 /*
571  * record_raw_stats - write raw timestamps to file
572  *
573  *
574  * file format
575  * time (s past midnight)
576  * peer ip address
577  * local ip address
578  * t1 t2 t3 t4 timestamps
579  */
580 void
581 record_raw_stats(
582         struct sockaddr_in *srcadr,
583         struct sockaddr_in *dstadr,
584         l_fp *t1,
585         l_fp *t2,
586         l_fp *t3,
587         l_fp *t4
588         )
589 {
590         struct timeval tv;
591 #ifdef HAVE_GETCLOCK
592         struct timespec ts;
593 #endif
594         u_long day, sec, msec;
595
596         if (!stats_control)
597                 return;
598 #ifdef HAVE_GETCLOCK
599         (void) getclock(TIMEOFDAY, &ts);
600         tv.tv_sec = ts.tv_sec;
601         tv.tv_usec = ts.tv_nsec / 1000;
602 #else /*  not HAVE_GETCLOCK */
603         GETTIMEOFDAY(&tv, (struct timezone *)NULL);
604 #endif /* not HAVE_GETCLOCK */
605         day = tv.tv_sec / 86400 + MJD_1970;
606         sec = tv.tv_sec % 86400;
607         msec = tv.tv_usec / 1000;
608
609         filegen_setup(&rawstats, (u_long)(tv.tv_sec + JAN_1970));
610         if (rawstats.fp != NULL) {
611                 fprintf(rawstats.fp, "%lu %lu.%03lu %s %s %s %s %s %s\n",
612                     day, sec, msec, ntoa(srcadr), ntoa(dstadr),
613                     ulfptoa(t1, 9), ulfptoa(t2, 9), ulfptoa(t3, 9),
614                     ulfptoa(t4, 9));
615                 fflush(rawstats.fp);
616         }
617 }
618
619 /*
620  * getauthkeys - read the authentication keys from the specified file
621  */
622 void
623 getauthkeys(
624         char *keyfile
625         )
626 {
627         int len;
628
629         len = strlen(keyfile);
630         if (len == 0)
631                 return;
632         
633         if (key_file_name != 0) {
634                 if (len > (int)strlen(key_file_name)) {
635                         (void) free(key_file_name);
636                         key_file_name = 0;
637                 }
638         }
639
640         if (key_file_name == 0) {
641 #ifndef SYS_WINNT
642                 key_file_name = (char*)emalloc((u_int) (len + 1));
643 #else
644                 key_file_name = (char*)emalloc((u_int)  (MAXPATHLEN));
645 #endif
646         }
647 #ifndef SYS_WINNT
648         memmove(key_file_name, keyfile, (unsigned)(len+1));
649 #else
650         if (!ExpandEnvironmentStrings(keyfile, key_file_name, MAXPATHLEN)) 
651         {
652                 msyslog(LOG_ERR,
653                     "ExpandEnvironmentStrings(KEY_FILE) failed: %m\n");
654         }
655 #endif /* SYS_WINNT */
656
657         authreadkeys(key_file_name);
658 }
659
660
661 /*
662  * rereadkeys - read the authentication key file over again.
663  */
664 void
665 rereadkeys(void)
666 {
667         if (key_file_name != 0)
668             authreadkeys(key_file_name);
669 }