1 /* $NetBSD: ntp_util.c,v 1.6 2006/06/18 21:35:57 kardel Exp $ */
4 * ntp_util.c - stuff I didn't have any other place for
13 #include "ntp_unixtime.h"
14 #include "ntp_filegen.h"
16 #include "ntp_stdlib.h"
20 #include <sys/types.h>
21 #ifdef HAVE_SYS_IOCTL_H
22 # include <sys/ioctl.h>
34 # include <sys/resource.h>
43 * This contains odds and ends. Right now the only thing you'll find
44 * in here is the hourly stats printer and some code to support
45 * rereading the keys file, but I may eventually put other things in
46 * here such as code to do something with the leap bits.
49 * Name of the keys file
51 static char *key_file_name
;
54 * The name of the drift_comp file and the temporary.
56 static char *stats_drift_file
;
57 static char *stats_temp_file
;
58 int stats_write_period
= 3600; /* # of seconds between writes. */
59 double stats_write_tolerance
= 0;
60 static double prev_drift_comp
= 99999.;
63 * Statistics file stuff
67 # define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */
69 # define NTP_VAR "c:\\var\\ntp\\" /* NOTE the trailing '\\' */
70 # endif /* SYS_WINNT */
74 # define MAXPATHLEN 256
77 static char statsdir
[MAXPATHLEN
] = NTP_VAR
;
79 static FILEGEN peerstats
;
80 static FILEGEN loopstats
;
81 static FILEGEN clockstats
;
82 static FILEGEN rawstats
;
83 static FILEGEN sysstats
;
85 static FILEGEN timingstats
;
88 static FILEGEN cryptostats
;
92 * This controls whether stats are written to the fileset. Provided
93 * so that ntpdc can turn off stats when the file system fills up.
98 * Initial frequency offset later passed to the loopfilter.
103 * init_util - initialize the utilities
108 stats_drift_file
= 0;
112 filegen_register(&statsdir
[0], "peerstats", &peerstats
);
114 filegen_register(&statsdir
[0], "loopstats", &loopstats
);
116 filegen_register(&statsdir
[0], "clockstats", &clockstats
);
118 filegen_register(&statsdir
[0], "rawstats", &rawstats
);
120 filegen_register(&statsdir
[0], "sysstats", &sysstats
);
123 filegen_register(&statsdir
[0], "cryptostats", &cryptostats
);
127 filegen_register(&statsdir
[0], "timingstats", &timingstats
);
133 * hourly_stats - print some interesting stats
151 * Sometimes having a Sun can be a drag.
153 * The kernel variable dosynctodr controls whether the system's
154 * soft clock is kept in sync with the battery clock. If it
155 * is zero, then the soft clock is not synced, and the battery
156 * clock is simply left to rot. That means that when the system
157 * reboots, the battery clock (which has probably gone wacky)
158 * sets the soft clock. That means ntpd starts off with a very
159 * confused idea of what time it is. It then takes a large
160 * amount of time to figure out just how wacky the battery clock
161 * has made things drift, etc, etc. The solution is to make the
162 * battery clock sync up to system time. The way to do THAT is
163 * to simply set the time of day to the current time of day, but
164 * as quickly as possible. This may, or may not be a sensible
167 * CAVEAT: settimeofday() steps the sun clock by about 800 us,
168 * so setting DOSYNCTODR seems a bad idea in the
169 * case of us resolution
173 /* (prr) getpriority returns -1 on error, but -1 is also a valid
174 * return value (!), so instead we have to zero errno before the
175 * call and check it for non-zero afterwards.
179 o_prio
= getpriority(PRIO_PROCESS
,0); /* Save setting */
182 * (prr) if getpriority succeeded, call setpriority to raise
183 * scheduling priority as high as possible. If that succeeds
184 * as well, set the prio_set flag so we remember to reset
185 * priority to its previous value below. Note that on Solaris
186 * 2.6 (and beyond?), both getpriority and setpriority will fail
187 * with ESRCH, because sched_setscheduler (called from main) put
188 * us in the real-time scheduling class which setpriority
189 * doesn't know about. Being in the real-time class is better
190 * than anything setpriority can do, anyhow, so this error is
193 if ((errno
== 0) && (setpriority(PRIO_PROCESS
,0,-20) == 0))
194 prio_set
= 1; /* overdrive */
197 (void) getclock(TIMEOFDAY
, &ts
);
198 tv
.tv_sec
= ts
.tv_sec
;
199 tv
.tv_usec
= ts
.tv_nsec
/ 1000;
200 #else /* not HAVE_GETCLOCK */
201 GETTIMEOFDAY(&tv
,(struct timezone
*)NULL
);
202 #endif /* not HAVE_GETCLOCK */
203 if (ntp_set_tod(&tv
,(struct timezone
*)NULL
) != 0) {
204 msyslog(LOG_ERR
, "can't sync battery time: %m");
208 setpriority(PRIO_PROCESS
, 0, o_prio
); /* downshift */
210 #endif /* DOSYNCTODR */
212 NLOG(NLOG_SYSSTATIST
)
214 "offset %.6f sec freq %.3f ppm error %.6f poll %d",
215 last_offset
, drift_comp
* 1e6
, sys_jitter
,
220 if ((u_long
)(fabs(prev_drift_comp
- drift_comp
) * 1e9
) <=
221 (u_long
)(fabs(stats_write_tolerance
* drift_comp
) * 1e9
)) {
224 prev_drift_comp
= drift_comp
;
225 if (stats_drift_file
!= 0) {
226 if ((fp
= fopen(stats_temp_file
, "w")) == NULL
) {
227 msyslog(LOG_ERR
, "can't open %s: %m",
231 fprintf(fp
, "%.3f\n", drift_comp
* 1e6
);
235 (void) _unlink(stats_drift_file
); /* rename semantics differ under NT */
236 #endif /* SYS_WINNT */
239 (void) rename(stats_temp_file
, stats_drift_file
);
241 /* we have no rename NFS of ftp in use */
242 if ((fp
= fopen(stats_drift_file
, "w")) == NULL
) {
243 msyslog(LOG_ERR
, "can't open %s: %m",
253 $
DESCRIPTOR(oldvers
,";-1");
254 struct dsc$descriptor driftdsc
= {
255 strlen(stats_drift_file
),0,0,stats_drift_file
};
257 while(lib$
delete_file(&oldvers
,&driftdsc
) & 1) ;
265 * stats_config - configure the stats operation
270 const char *invalue
/* only one type so far */
278 * Expand environment strings under Windows NT, since the
279 * command interpreter doesn't do this, the program must.
282 char newvalue
[MAX_PATH
], parameter
[MAX_PATH
];
284 if (!ExpandEnvironmentStrings(invalue
, newvalue
, MAX_PATH
)) {
286 case STATS_FREQ_FILE
:
287 strcpy(parameter
,"STATS_FREQ_FILE");
290 strcpy(parameter
,"STATS_STATSDIR");
293 strcpy(parameter
,"STATS_PID_FILE");
296 strcpy(parameter
,"UNKNOWN");
302 "ExpandEnvironmentStrings(%s) failed: %m\n", parameter
);
308 #endif /* SYS_WINNT */
311 case STATS_FREQ_FILE
:
312 if (stats_drift_file
!= 0) {
313 (void) free(stats_drift_file
);
314 (void) free(stats_temp_file
);
315 stats_drift_file
= 0;
319 if (value
== 0 || (len
= strlen(value
)) == 0)
322 stats_drift_file
= (char*)emalloc((u_int
)(len
+ 1));
324 stats_temp_file
= (char*)emalloc((u_int
)(len
+
327 stats_temp_file
= (char*)emalloc((u_int
)(len
+
330 memmove(stats_drift_file
, value
, (unsigned)(len
+1));
331 memmove(stats_temp_file
, value
, (unsigned)len
);
333 memmove(stats_temp_file
+ len
, ".TEMP",
336 memmove(stats_temp_file
+ len
, "-TEMP",
341 * Open drift file and read frequency. If the file is
342 * missing or contains errors, tell the loop to reset.
344 if ((fp
= fopen(stats_drift_file
, "r")) == NULL
) {
348 if (fscanf(fp
, "%lf", &old_drift
) != 1) {
349 msyslog(LOG_ERR
, "Frequency format error in %s",
356 prev_drift_comp
= old_drift
/ 1e6
;
358 "frequency initialized %.3f PPM from %s",
359 old_drift
, stats_drift_file
);
363 if (strlen(value
) >= sizeof(statsdir
)) {
365 "value for statsdir too long (>%d, sigh)",
366 (int)sizeof(statsdir
)-1);
371 strcpy(statsdir
,value
);
372 if(peerstats
.prefix
== &statsdir
[0] &&
373 peerstats
.fp
!= NULL
) {
374 fclose(peerstats
.fp
);
376 filegen_setup(&peerstats
, now
.l_ui
);
378 if(loopstats
.prefix
== &statsdir
[0] &&
379 loopstats
.fp
!= NULL
) {
380 fclose(loopstats
.fp
);
382 filegen_setup(&loopstats
, now
.l_ui
);
384 if(clockstats
.prefix
== &statsdir
[0] &&
385 clockstats
.fp
!= NULL
) {
386 fclose(clockstats
.fp
);
387 clockstats
.fp
= NULL
;
388 filegen_setup(&clockstats
, now
.l_ui
);
390 if(rawstats
.prefix
== &statsdir
[0] &&
391 rawstats
.fp
!= NULL
) {
394 filegen_setup(&rawstats
, now
.l_ui
);
396 if(sysstats
.prefix
== &statsdir
[0] &&
397 sysstats
.fp
!= NULL
) {
400 filegen_setup(&sysstats
, now
.l_ui
);
403 if(cryptostats
.prefix
== &statsdir
[0] &&
404 cryptostats
.fp
!= NULL
) {
405 fclose(cryptostats
.fp
);
406 cryptostats
.fp
= NULL
;
407 filegen_setup(&cryptostats
, now
.l_ui
);
414 if ((fp
= fopen(value
, "w")) == NULL
) {
415 msyslog(LOG_ERR
, "Can't open %s: %m", value
);
418 fprintf(fp
, "%d", (int) getpid());
429 * record_peer_stats - write peer statistics to file
433 * time (s past UTC midnight)
435 * peer status word (hex)
438 * peer error bound (s)
443 struct sockaddr_storage
*addr
,
458 filegen_setup(&peerstats
, now
.l_ui
);
459 day
= now
.l_ui
/ 86400 + MJD_1900
;
461 if (peerstats
.fp
!= NULL
) {
462 fprintf(peerstats
.fp
,
463 "%lu %s %s %x %.9f %.9f %.9f %.9f\n",
464 day
, ulfptoa(&now
, 3), stoa(addr
), status
, offset
,
465 delay
, dispersion
, skew
);
466 fflush(peerstats
.fp
);
471 * record_loop_stats - write loop filter statistics to file
475 * time (s past midnight)
477 * frequency (approx ppm)
478 * time constant (log base 2)
496 filegen_setup(&loopstats
, now
.l_ui
);
497 day
= now
.l_ui
/ 86400 + MJD_1900
;
499 if (loopstats
.fp
!= NULL
) {
500 fprintf(loopstats
.fp
, "%lu %s %.9f %.3f %.9f %.6f %d\n",
501 day
, ulfptoa(&now
, 3), offset
, freq
* 1e6
, jitter
,
502 stability
* 1e6
, spoll
);
503 fflush(loopstats
.fp
);
508 * record_clock_stats - write clock statistics to file
512 * time (s past midnight)
518 struct sockaddr_storage
*addr
,
529 filegen_setup(&clockstats
, now
.l_ui
);
530 day
= now
.l_ui
/ 86400 + MJD_1900
;
532 if (clockstats
.fp
!= NULL
) {
533 fprintf(clockstats
.fp
, "%lu %s %s %s\n",
534 day
, ulfptoa(&now
, 3), stoa(addr
), text
);
535 fflush(clockstats
.fp
);
540 * record_raw_stats - write raw timestamps to file
544 * time (s past midnight)
547 * t1 t2 t3 t4 timestamps
551 struct sockaddr_storage
*srcadr
,
552 struct sockaddr_storage
*dstadr
,
566 filegen_setup(&rawstats
, now
.l_ui
);
567 day
= now
.l_ui
/ 86400 + MJD_1900
;
569 if (rawstats
.fp
!= NULL
) {
570 fprintf(rawstats
.fp
, "%lu %s %s %s %s %s %s %s\n",
571 day
, ulfptoa(&now
, 3), stoa(srcadr
), dstadr
? stoa(dstadr
) : "-",
572 ulfptoa(t1
, 9), ulfptoa(t2
, 9), ulfptoa(t3
, 9),
580 * record_sys_stats - write system statistics to file
583 * time (s past midnight)
584 * time since startup (hr)
591 * bad length or format
596 record_sys_stats(void)
605 filegen_setup(&sysstats
, now
.l_ui
);
606 day
= now
.l_ui
/ 86400 + MJD_1900
;
608 if (sysstats
.fp
!= NULL
) {
610 "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
611 day
, ulfptoa(&now
, 3), sys_stattime
/ 3600,
612 sys_received
, sys_processed
, sys_newversionpkt
,
613 sys_oldversionpkt
, sys_unknownversion
,
614 sys_restricted
, sys_badlength
, sys_badauth
,
624 * record_crypto_stats - write crypto statistics to file
628 * time (s past midnight)
634 struct sockaddr_storage
*addr
,
645 filegen_setup(&cryptostats
, now
.l_ui
);
646 day
= now
.l_ui
/ 86400 + MJD_1900
;
648 if (cryptostats
.fp
!= NULL
) {
650 fprintf(cryptostats
.fp
, "%lu %s %s\n",
651 day
, ulfptoa(&now
, 3), text
);
653 fprintf(cryptostats
.fp
, "%lu %s %s %s\n",
654 day
, ulfptoa(&now
, 3), stoa(addr
), text
);
655 fflush(cryptostats
.fp
);
662 * record_crypto_stats - write crypto statistics to file
666 * time (s past midnight)
674 static unsigned int flshcnt
;
682 filegen_setup(&timingstats
, now
.l_ui
);
683 day
= now
.l_ui
/ 86400 + MJD_1900
;
685 if (timingstats
.fp
!= NULL
) {
686 fprintf(timingstats
.fp
, "%lu %s %s\n",
687 day
, lfptoa(&now
, 3), text
);
688 if (++flshcnt
% 100 == 0)
689 fflush(timingstats
.fp
);
694 * getauthkeys - read the authentication keys from the specified file
703 len
= strlen(keyfile
);
707 if (key_file_name
!= 0) {
708 if (len
> (int)strlen(key_file_name
)) {
709 (void) free(key_file_name
);
714 if (key_file_name
== 0) {
716 key_file_name
= (char*)emalloc((u_int
) (len
+ 1));
718 key_file_name
= (char*)emalloc((u_int
) (MAXPATHLEN
));
722 memmove(key_file_name
, keyfile
, (unsigned)(len
+1));
724 if (!ExpandEnvironmentStrings(keyfile
, key_file_name
, MAXPATHLEN
))
727 "ExpandEnvironmentStrings(KEY_FILE) failed: %m\n");
729 #endif /* SYS_WINNT */
731 authreadkeys(key_file_name
);
736 * rereadkeys - read the authentication key file over again.
741 if (key_file_name
!= 0)
742 authreadkeys(key_file_name
);
746 * sock_hash - hash an sockaddr_storage structure
750 struct sockaddr_storage
*addr
761 * We can't just hash the whole thing because there are hidden
762 * fields in sockaddr_in6 that might be filled in by recvfrom(),
763 * so just use the family, port and address.
765 ch
= (char *)&addr
->ss_family
;
766 hashVal
= 37 * hashVal
+ (int)*ch
;
767 if (sizeof(addr
->ss_family
) > 1) {
769 hashVal
= 37 * hashVal
+ (int)*ch
;
771 switch(addr
->ss_family
) {
773 ch
= (char *)&((struct sockaddr_in
*)addr
)->sin_addr
;
774 len
= sizeof(struct in_addr
);
777 ch
= (char *)&((struct sockaddr_in6
*)addr
)->sin6_addr
;
778 len
= sizeof(struct in6_addr
);
782 for (i
= 0; i
< len
; i
++)
783 hashVal
= 37 * hashVal
+ (int)*(ch
+ i
);
785 hashVal
= hashVal
% 128; /* % MON_HASH_SIZE hardcoded */
795 * ntp_exit - document explicitly that ntpd has exited
800 msyslog(LOG_ERR
, "EXITING with return code %d", retval
);