4 * ntp_util.c - stuff I didn't have any other place for
12 #include "ntp_unixtime.h"
13 #include "ntp_filegen.h"
15 #include "ntp_stdlib.h"
16 #include "ntp_assert.h"
20 #include <sys/types.h>
21 #ifdef HAVE_SYS_IOCTL_H
22 # include <sys/ioctl.h>
34 # include <sys/resource.h>
43 * Defines used by the leapseconds stuff
45 #define MAX_TAI 100 /* max TAI offset (s) */
46 #define L_DAY 86400UL /* seconds per day */
47 #define L_YEAR (L_DAY * 365) /* days per year */
48 #define L_LYEAR (L_YEAR + L_DAY) /* days per leap year */
49 #define L_4YEAR (L_LYEAR + 3 * L_YEAR) /* days per leap cycle */
50 #define L_CENT (L_4YEAR * 25) /* days per century */
53 * This contains odds and ends, including the hourly stats, various
54 * configuration items, leapseconds stuff, etc.
59 static char *key_file_name
; /* keys file name */
60 char *leapseconds_file_name
; /* leapseconds file name */
61 char *stats_drift_file
; /* frequency file name */
62 static char *stats_temp_file
; /* temp frequency file name */
63 double wander_resid
; /* wander threshold */
64 double wander_threshold
= 1e-7; /* initial wander threshold */
65 int drift_file_sw
; /* clock update switch */
68 * Statistics file stuff
72 # define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */
74 # define NTP_VAR "c:\\var\\ntp\\" /* NOTE the trailing '\\' */
75 # endif /* SYS_WINNT */
79 # define MAXPATHLEN 256
83 static FILEGEN timingstats
;
86 static FILEGEN cryptostats
;
89 static char statsdir
[MAXPATHLEN
] = NTP_VAR
;
90 static FILEGEN peerstats
;
91 static FILEGEN loopstats
;
92 static FILEGEN clockstats
;
93 static FILEGEN rawstats
;
94 static FILEGEN sysstats
;
95 static FILEGEN protostats
;
98 * This controls whether stats are written to the fileset. Provided
99 * so that ntpdc can turn off stats when the file system fills up.
104 * Initial frequency offset later passed to the loopfilter.
106 double old_drift
= 1e9
; /* current frequency */
107 static double prev_drift_comp
; /* last frequency update */
112 static int leap_file(FILE *);
113 static void record_sys_stats(void);
119 void uninit_util(void);
124 * uninit_util - free memory allocated by init_util
130 #if defined(_MSC_VER) && defined (_DEBUG)
133 if (stats_drift_file
) {
134 free(stats_drift_file
);
135 free(stats_temp_file
);
136 stats_drift_file
= NULL
;
137 stats_temp_file
= NULL
;
141 key_file_name
= NULL
;
143 filegen_unregister("peerstats");
144 filegen_unregister("loopstats");
145 filegen_unregister("clockstats");
146 filegen_unregister("rawstats");
147 filegen_unregister("sysstats");
148 filegen_unregister("protostats");
150 filegen_unregister("cryptostats");
153 filegen_unregister("timingstats");
154 #endif /* DEBUG_TIMING */
156 #if defined(_MSC_VER) && defined (_DEBUG)
164 * init_util - initialize the utilities (ntpd included)
169 stats_drift_file
= NULL
;
170 stats_temp_file
= NULL
;
171 key_file_name
= NULL
;
172 filegen_register(statsdir
, "peerstats", &peerstats
);
173 filegen_register(statsdir
, "loopstats", &loopstats
);
174 filegen_register(statsdir
, "clockstats", &clockstats
);
175 filegen_register(statsdir
, "rawstats", &rawstats
);
176 filegen_register(statsdir
, "sysstats", &sysstats
);
177 filegen_register(statsdir
, "protostats", &protostats
);
179 filegen_register(statsdir
, "cryptostats", &cryptostats
);
182 filegen_register(statsdir
, "timingstats", &timingstats
);
183 #endif /* DEBUG_TIMING */
191 * hourly_stats - print some interesting stats
209 * Sometimes having a Sun can be a drag.
211 * The kernel variable dosynctodr controls whether the system's
212 * soft clock is kept in sync with the battery clock. If it
213 * is zero, then the soft clock is not synced, and the battery
214 * clock is simply left to rot. That means that when the system
215 * reboots, the battery clock (which has probably gone wacky)
216 * sets the soft clock. That means ntpd starts off with a very
217 * confused idea of what time it is. It then takes a large
218 * amount of time to figure out just how wacky the battery clock
219 * has made things drift, etc, etc. The solution is to make the
220 * battery clock sync up to system time. The way to do THAT is
221 * to simply set the time of day to the current time of day, but
222 * as quickly as possible. This may, or may not be a sensible
225 * CAVEAT: settimeofday() steps the sun clock by about 800 us,
226 * so setting DOSYNCTODR seems a bad idea in the
227 * case of us resolution
232 * (prr) getpriority returns -1 on error, but -1 is also a valid
233 * return value (!), so instead we have to zero errno before the
234 * call and check it for non-zero afterwards.
238 o_prio
= getpriority(PRIO_PROCESS
,0); /* Save setting */
241 * (prr) if getpriority succeeded, call setpriority to raise
242 * scheduling priority as high as possible. If that succeeds
243 * as well, set the prio_set flag so we remember to reset
244 * priority to its previous value below. Note that on Solaris
245 * 2.6 (and beyond?), both getpriority and setpriority will fail
246 * with ESRCH, because sched_setscheduler (called from main) put
247 * us in the real-time scheduling class which setpriority
248 * doesn't know about. Being in the real-time class is better
249 * than anything setpriority can do, anyhow, so this error is
252 if ((errno
== 0) && (setpriority(PRIO_PROCESS
,0,-20) == 0))
253 prio_set
= 1; /* overdrive */
256 (void) getclock(TIMEOFDAY
, &ts
);
257 tv
.tv_sec
= ts
.tv_sec
;
258 tv
.tv_usec
= ts
.tv_nsec
/ 1000;
259 #else /* not HAVE_GETCLOCK */
260 GETTIMEOFDAY(&tv
,(struct timezone
*)NULL
);
261 #endif /* not HAVE_GETCLOCK */
262 if (ntp_set_tod(&tv
,(struct timezone
*)NULL
) != 0)
263 msyslog(LOG_ERR
, "can't sync battery time: %m");
266 setpriority(PRIO_PROCESS
, 0, o_prio
); /* downshift */
268 #endif /* DOSYNCTODR */
270 ftemp
= fabs(prev_drift_comp
- drift_comp
);
271 prev_drift_comp
= drift_comp
;
272 if (ftemp
> clock_phi
)
275 if (stats_drift_file
!= 0 && drift_file_sw
) {
278 * When the frequency file is written, initialize the
279 * wander threshold to a configured initial value.
280 * Thereafter reduce it by a factor of 0.5. When it
281 * drops below the frequency wander, write the frequency
282 * file. This adapts to the prevailing wander yet
283 * minimizes the file writes.
285 drift_file_sw
= FALSE
;
289 printf("write_stats: wander %.6lf thresh %.6lf, freq %.6lf\n",
290 clock_stability
* 1e6
, wander_resid
* 1e6
,
293 if (sys_leap
!= LEAP_NOTINSYNC
&& clock_stability
>
295 wander_resid
= wander_threshold
;
296 if ((fp
= fopen(stats_temp_file
, "w")) == NULL
)
299 "frequency file %s: %m",
303 fprintf(fp
, "%.3f\n", drift_comp
* 1e6
);
307 if (_unlink(stats_drift_file
)) /* rename semantics differ under NT */
309 "Unable to remove prior drift file %s, %m",
311 #endif /* SYS_WINNT */
314 if (rename(stats_temp_file
, stats_drift_file
))
316 "Unable to rename temp drift file %s to %s, %m",
317 stats_temp_file
, stats_drift_file
);
319 /* we have no rename NFS of ftp in use */
320 if ((fp
= fopen(stats_drift_file
, "w")) ==
323 "frequency file %s: %m",
332 $
DESCRIPTOR(oldvers
,";-1");
333 struct dsc$descriptor driftdsc
= {
334 strlen(stats_drift_file
), 0, 0,
336 while(lib$
delete_file(&oldvers
,
341 /* XXX: Log a message at INFO level */
348 * stats_config - configure the stats operation
353 const char *invalue
/* only one type so far */
360 char str1
[20], str2
[20];
363 * Expand environment strings under Windows NT, since the
364 * command interpreter doesn't do this, the program must.
367 char newvalue
[MAX_PATH
], parameter
[MAX_PATH
];
369 if (!ExpandEnvironmentStrings(invalue
, newvalue
, MAX_PATH
)) {
371 case STATS_FREQ_FILE
:
372 strcpy(parameter
,"STATS_FREQ_FILE");
375 case STATS_LEAP_FILE
:
376 strcpy(parameter
,"STATS_LEAP_FILE");
380 strcpy(parameter
,"STATS_STATSDIR");
384 strcpy(parameter
,"STATS_PID_FILE");
388 strcpy(parameter
,"UNKNOWN");
393 "ExpandEnvironmentStrings(%s) failed: %m\n",
400 #endif /* SYS_WINNT */
405 * Open and read frequency file.
407 case STATS_FREQ_FILE
:
408 if (!value
|| (len
= strlen(value
)) == 0)
411 stats_drift_file
= erealloc(stats_drift_file
, len
+ 1);
412 stats_temp_file
= erealloc(stats_temp_file
,
413 len
+ sizeof(".TEMP"));
415 memmove(stats_drift_file
, value
, (unsigned)(len
+1));
416 memmove(stats_temp_file
, value
, (unsigned)len
);
417 memmove(stats_temp_file
+ len
,
419 ".TEMP", sizeof(".TEMP"));
421 "-TEMP", sizeof("-TEMP"));
425 * Open drift file and read frequency. If the file is
426 * missing or contains errors, tell the loop to reset.
428 if ((fp
= fopen(stats_drift_file
, "r")) == NULL
)
431 if (fscanf(fp
, "%lf", &old_drift
) != 1) {
433 "format error frequency file %s",
441 prev_drift_comp
= old_drift
;
445 * Specify statistics directory.
450 * HMS: the following test is insufficient:
451 * - value may be missing the DIR_SEP
452 * - we still need the filename after it
454 if (strlen(value
) >= sizeof(statsdir
)) {
456 "statsdir too long (>%d, sigh)",
457 (int)sizeof(statsdir
) - 1);
461 int value_l
= strlen(value
);
463 /* Add a DIR_SEP unless we already have one. */
467 add_dir_sep
= (DIR_SEP
!=
471 snprintf(statsdir
, sizeof(statsdir
),
472 "%s%c", value
, DIR_SEP
);
474 snprintf(statsdir
, sizeof(statsdir
),
478 if(peerstats
.prefix
== &statsdir
[0] &&
479 peerstats
.fp
!= NULL
) {
480 fclose(peerstats
.fp
);
482 filegen_setup(&peerstats
, now
.l_ui
);
484 if(loopstats
.prefix
== &statsdir
[0] &&
485 loopstats
.fp
!= NULL
) {
486 fclose(loopstats
.fp
);
488 filegen_setup(&loopstats
, now
.l_ui
);
490 if(clockstats
.prefix
== &statsdir
[0] &&
491 clockstats
.fp
!= NULL
) {
492 fclose(clockstats
.fp
);
493 clockstats
.fp
= NULL
;
494 filegen_setup(&clockstats
, now
.l_ui
);
496 if(rawstats
.prefix
== &statsdir
[0] &&
497 rawstats
.fp
!= NULL
) {
500 filegen_setup(&rawstats
, now
.l_ui
);
502 if(sysstats
.prefix
== &statsdir
[0] &&
503 sysstats
.fp
!= NULL
) {
506 filegen_setup(&sysstats
, now
.l_ui
);
508 if(protostats
.prefix
== &statsdir
[0] &&
509 protostats
.fp
!= NULL
) {
510 fclose(protostats
.fp
);
511 protostats
.fp
= NULL
;
512 filegen_setup(&protostats
, now
.l_ui
);
515 if(cryptostats
.prefix
== &statsdir
[0] &&
516 cryptostats
.fp
!= NULL
) {
517 fclose(cryptostats
.fp
);
518 cryptostats
.fp
= NULL
;
519 filegen_setup(&cryptostats
, now
.l_ui
);
523 if(timingstats
.prefix
== &statsdir
[0] &&
524 timingstats
.fp
!= NULL
) {
525 fclose(timingstats
.fp
);
526 timingstats
.fp
= NULL
;
527 filegen_setup(&timingstats
, now
.l_ui
);
529 #endif /* DEBUG_TIMING */
537 if ((fp
= fopen(value
, "w")) == NULL
) {
538 msyslog(LOG_ERR
, "pid file %s: %m",
542 fprintf(fp
, "%d", (int)getpid());
547 * Read leapseconds file.
549 case STATS_LEAP_FILE
:
550 if ((fp
= fopen(value
, "r")) == NULL
) {
551 msyslog(LOG_ERR
, "leapseconds file %s: %m",
556 if (leap_file(fp
) < 0) {
558 "format error leapseconds file %s",
561 strcpy(str1
, fstostr(leap_sec
));
562 strcpy(str2
, fstostr(leap_expire
));
563 snprintf(tbuf
, sizeof(tbuf
),
564 "%d leap %s expire %s", leap_tai
, str1
,
566 report_event(EVNT_TAI
, NULL
, tbuf
);
579 * record_peer_stats - write peer statistics to file
583 * time (s past UTC midnight)
595 double offset
, /* offset */
596 double delay
, /* delay */
597 double dispersion
, /* dispersion */
598 double jitter
/* jitter */
608 filegen_setup(&peerstats
, now
.l_ui
);
609 day
= now
.l_ui
/ 86400 + MJD_1900
;
611 if (peerstats
.fp
!= NULL
) {
612 fprintf(peerstats
.fp
,
613 "%lu %s %s %x %.9f %.9f %.9f %.9f\n", day
,
614 ulfptoa(&now
, 3), stoa(addr
), status
, offset
,
615 delay
, dispersion
, jitter
);
616 fflush(peerstats
.fp
);
622 * record_loop_stats - write loop filter statistics to file
626 * time (s past midnight)
631 * time constant (log2)
635 double offset
, /* offset */
636 double freq
, /* frequency (PPM) */
637 double jitter
, /* jitter */
638 double wander
, /* wander (PPM) */
649 filegen_setup(&loopstats
, now
.l_ui
);
650 day
= now
.l_ui
/ 86400 + MJD_1900
;
652 if (loopstats
.fp
!= NULL
) {
653 fprintf(loopstats
.fp
, "%lu %s %.9f %.3f %.9f %.6f %d\n",
654 day
, ulfptoa(&now
, 3), offset
, freq
* 1e6
, jitter
,
655 wander
* 1e6
, spoll
);
656 fflush(loopstats
.fp
);
662 * record_clock_stats - write clock statistics to file
666 * time (s past midnight)
673 const char *text
/* timecode string */
683 filegen_setup(&clockstats
, now
.l_ui
);
684 day
= now
.l_ui
/ 86400 + MJD_1900
;
686 if (clockstats
.fp
!= NULL
) {
687 fprintf(clockstats
.fp
, "%lu %s %s %s\n", day
,
688 ulfptoa(&now
, 3), stoa(addr
), text
);
689 fflush(clockstats
.fp
);
695 * record_raw_stats - write raw timestamps to file
699 * time (s past midnight)
702 * t1 t2 t3 t4 timestamps
708 l_fp
*t1
, /* originate timestamp */
709 l_fp
*t2
, /* receive timestamp */
710 l_fp
*t3
, /* transmit timestamp */
711 l_fp
*t4
/* destination timestamp */
721 filegen_setup(&rawstats
, now
.l_ui
);
722 day
= now
.l_ui
/ 86400 + MJD_1900
;
724 if (rawstats
.fp
!= NULL
) {
725 fprintf(rawstats
.fp
, "%lu %s %s %s %s %s %s %s\n", day
,
726 ulfptoa(&now
, 3), stoa(srcadr
), dstadr
?
727 stoa(dstadr
) : "-", ulfptoa(t1
, 9), ulfptoa(t2
, 9),
728 ulfptoa(t3
, 9), ulfptoa(t4
, 9));
735 * record_sys_stats - write system statistics to file
739 * time (s past midnight)
742 * packets for this host
746 * bad length or format
753 record_sys_stats(void)
762 filegen_setup(&sysstats
, now
.l_ui
);
763 day
= now
.l_ui
/ 86400 + MJD_1900
;
765 if (sysstats
.fp
!= NULL
) {
767 "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
768 day
, ulfptoa(&now
, 3), current_time
- sys_stattime
,
769 sys_received
, sys_processed
, sys_newversion
,
770 sys_oldversion
, sys_restricted
, sys_badlength
,
771 sys_badauth
, sys_declined
, sys_limitrejected
,
780 * record_proto_stats - write system statistics to file
784 * time (s past midnight)
789 char *str
/* text string */
799 filegen_setup(&protostats
, now
.l_ui
);
800 day
= now
.l_ui
/ 86400 + MJD_1900
;
802 if (protostats
.fp
!= NULL
) {
803 fprintf(protostats
.fp
, "%lu %s %s\n", day
,
804 ulfptoa(&now
, 3), str
);
805 fflush(protostats
.fp
);
812 * record_crypto_stats - write crypto statistics to file
816 * time (s past midnight)
823 const char *text
/* text message */
833 filegen_setup(&cryptostats
, now
.l_ui
);
834 day
= now
.l_ui
/ 86400 + MJD_1900
;
836 if (cryptostats
.fp
!= NULL
) {
838 fprintf(cryptostats
.fp
, "%lu %s 0.0.0.0 %s\n",
839 day
, ulfptoa(&now
, 3), text
);
841 fprintf(cryptostats
.fp
, "%lu %s %s %s\n",
842 day
, ulfptoa(&now
, 3), stoa(addr
), text
);
843 fflush(cryptostats
.fp
);
851 * record_timing_stats - write timing statistics to file
855 * time (s past midnight)
860 const char *text
/* text message */
863 static unsigned int flshcnt
;
871 filegen_setup(&timingstats
, now
.l_ui
);
872 day
= now
.l_ui
/ 86400 + MJD_1900
;
874 if (timingstats
.fp
!= NULL
) {
875 fprintf(timingstats
.fp
, "%lu %s %s\n", day
, lfptoa(&now
,
877 if (++flshcnt
% 100 == 0)
878 fflush(timingstats
.fp
);
885 * leap_file - read leapseconds file
887 * Read the ERTS leapsecond file in NIST text format and extract the
888 * NTP seconds of the latest leap and TAI offset after the leap.
892 FILE *fp
/* file handle */
895 char buf
[NTP_MAXSTRLEN
]; /* file line buffer */
896 u_long leap
; /* NTP time at leap */
897 u_long expire
; /* NTP time when file expires */
898 int offset
; /* TAI offset at leap (s) */
902 * Read and parse the leapseconds file. Empty lines and comments
903 * are ignored. A line beginning with #@ contains the file
904 * expiration time in NTP seconds. Other lines begin with two
905 * integers followed by junk or comments. The first integer is
906 * the NTP seconds at the leap, the second is the TAI offset
913 while (fgets(buf
, NTP_MAXSTRLEN
- 1, fp
) != NULL
) {
922 * Note the '@' flag was used only in the 2006
923 * table; previious to that the flag was '$'.
925 if (buf
[1] == '@' || buf
[1] == '$') {
926 if (sscanf(&buf
[2], "%lu", &expire
) !=
933 if (sscanf(buf
, "%lu %d", &leap
, &offset
) == 2) {
936 * Valid offsets must increase by one for each
945 * There must be at least one leap.
952 leap_expire
= expire
;
958 * leap_month - returns seconds until the end of the month.
962 u_long sec
/* current NTP second */
967 u_long year
[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
969 u_long lyear
[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30,
973 * Find current leap cycle.
976 while (ltemp
>= L_CENT
)
978 while (ltemp
>= L_4YEAR
)
982 * We are within four years of the target. If in leap year, use
983 * leap year month table; otherwise, use year month table.
985 if (ltemp
< L_LYEAR
) {
990 while (ltemp
>= L_YEAR
)
995 * We are within one year of the target. Find the month of the
998 while (ltemp
>= *ptr
* L_DAY
)
999 ltemp
-= *ptr
++ * L_DAY
;
1002 * The result is the number of seconds until the end of the
1003 * month when the leap is to occur.
1005 return (*ptr
* L_DAY
- ltemp
- L_DAY
);
1010 * getauthkeys - read the authentication keys from the specified file
1019 len
= strlen(keyfile
);
1024 key_file_name
= erealloc(key_file_name
, len
+ 1);
1025 memmove(key_file_name
, keyfile
, len
+ 1);
1027 key_file_name
= erealloc(key_file_name
, _MAX_PATH
);
1028 if (len
+ 1 > _MAX_PATH
)
1030 if (!ExpandEnvironmentStrings(keyfile
, key_file_name
,
1033 "ExpandEnvironmentStrings(KEY_FILE) failed: %m");
1034 strncpy(key_file_name
, keyfile
, _MAX_PATH
);
1036 #endif /* SYS_WINNT */
1038 authreadkeys(key_file_name
);
1043 * rereadkeys - read the authentication key file over again.
1048 if (NULL
!= key_file_name
)
1049 authreadkeys(key_file_name
);
1054 * sock_hash - hash a sockaddr_u structure
1069 * We can't just hash the whole thing because there are hidden
1070 * fields in sockaddr_in6 that might be filled in by recvfrom(),
1071 * so just use the family, port and address.
1073 pch
= (u_char
*)&AF(addr
);
1074 hashVal
= 37 * hashVal
+ *pch
;
1075 if (sizeof(AF(addr
)) > 1) {
1077 hashVal
= 37 * hashVal
+ *pch
;
1081 pch
= (u_char
*)&SOCK_ADDR4(addr
);
1082 len
= sizeof(SOCK_ADDR4(addr
));
1086 pch
= (u_char
*)&SOCK_ADDR6(addr
);
1087 len
= sizeof(SOCK_ADDR6(addr
));
1091 for (j
= 0; j
< len
; j
++)
1092 hashVal
= 37 * hashVal
+ pch
[j
];
1094 hashVal
= hashVal
& NTP_HASH_MASK
;
1096 return (u_short
)hashVal
;
1102 * ntp_exit - document explicitly that ntpd has exited
1105 ntp_exit(int retval
)
1107 msyslog(LOG_ERR
, "EXITING with return code %d", retval
);
1113 * fstostr - prettyprint NTP seconds
1119 static char str
[20];
1123 unix_stamp
= ntp_stamp
- JAN_1970
;
1124 tm
= gmtime(&unix_stamp
);
1126 snprintf(str
, sizeof(str
),
1127 "%04d%02d%02d%02d%02d",
1128 tm
->tm_year
+ 1900, tm
->tm_mon
+ 1, tm
->tm_mday
,
1129 tm
->tm_hour
, tm
->tm_min
);
1131 strcpy(str
, "gmtime() error");