xref: /netbsd-src/external/bsd/ntp/dist/ntpd/ntp_util.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: ntp_util.c,v 1.1.1.1 2009/12/13 16:55:42 kardel Exp $	*/
2 
3 /*
4  * ntp_util.c - stuff I didn't have any other place for
5  */
6 #ifdef HAVE_CONFIG_H
7 # include <config.h>
8 #endif
9 
10 #include "ntpd.h"
11 #include "ntp_io.h"
12 #include "ntp_unixtime.h"
13 #include "ntp_filegen.h"
14 #include "ntp_if.h"
15 #include "ntp_stdlib.h"
16 #include "ntp_assert.h"
17 
18 #include <stdio.h>
19 #include <ctype.h>
20 #include <sys/types.h>
21 #ifdef HAVE_SYS_IOCTL_H
22 # include <sys/ioctl.h>
23 #endif
24 
25 #ifdef HAVE_IEEEFP_H
26 # include <ieeefp.h>
27 #endif
28 #ifdef HAVE_MATH_H
29 # include <math.h>
30 #endif
31 
32 #ifdef  DOSYNCTODR
33 # if !defined(VMS)
34 #  include <sys/resource.h>
35 # endif /* VMS */
36 #endif
37 
38 #if defined(VMS)
39 # include <descrip.h>
40 #endif /* VMS */
41 
42 /*
43  * Defines used by the leapseconds stuff
44  */
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 */
51 
52 /*
53  * This contains odds and ends, including the hourly stats, various
54  * configuration items, leapseconds stuff, etc.
55  */
56 /*
57  * File names
58  */
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 */
66 
67 /*
68  * Statistics file stuff
69  */
70 #ifndef NTP_VAR
71 # ifndef SYS_WINNT
72 #  define NTP_VAR "/var/NTP/"		/* NOTE the trailing '/' */
73 # else
74 #  define NTP_VAR "c:\\var\\ntp\\"	/* NOTE the trailing '\\' */
75 # endif /* SYS_WINNT */
76 #endif
77 
78 #ifndef MAXPATHLEN
79 # define MAXPATHLEN 256
80 #endif
81 
82 #ifdef DEBUG_TIMING
83 static FILEGEN timingstats;
84 #endif
85 #ifdef OPENSSL
86 static FILEGEN cryptostats;
87 #endif /* OPENSSL */
88 
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;
96 
97 /*
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.
100  */
101 int stats_control;
102 
103 /*
104  * Initial frequency offset later passed to the loopfilter.
105  */
106 double	old_drift = 1e9;		/* current frequency */
107 static double prev_drift_comp;		/* last frequency update */
108 
109 /*
110  * Static prototypes
111  */
112 static int leap_file(FILE *);
113 static void record_sys_stats(void);
114 
115 /*
116  * Prototypes
117  */
118 #ifdef DEBUG
119 void	uninit_util(void);
120 #endif
121 
122 
123 /*
124  * uninit_util - free memory allocated by init_util
125  */
126 #ifdef DEBUG
127 void
128 uninit_util(void)
129 {
130 #if defined(_MSC_VER) && defined (_DEBUG)
131 	_CrtCheckMemory();
132 #endif
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;
138 	}
139 	if (key_file_name) {
140 		free(key_file_name);
141 		key_file_name = NULL;
142 	}
143 	filegen_unregister("peerstats");
144 	filegen_unregister("loopstats");
145 	filegen_unregister("clockstats");
146 	filegen_unregister("rawstats");
147 	filegen_unregister("sysstats");
148 	filegen_unregister("protostats");
149 #ifdef OPENSSL
150 	filegen_unregister("cryptostats");
151 #endif /* OPENSSL */
152 #ifdef DEBUG_TIMING
153 	filegen_unregister("timingstats");
154 #endif /* DEBUG_TIMING */
155 
156 #if defined(_MSC_VER) && defined (_DEBUG)
157 	_CrtCheckMemory();
158 #endif
159 }
160 #endif /* DEBUG */
161 
162 
163 /*
164  * init_util - initialize the utilities (ntpd included)
165  */
166 void
167 init_util(void)
168 {
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);
178 #ifdef OPENSSL
179 	filegen_register(statsdir, "cryptostats", &cryptostats);
180 #endif /* OPENSSL */
181 #ifdef DEBUG_TIMING
182 	filegen_register(statsdir, "timingstats", &timingstats);
183 #endif /* DEBUG_TIMING */
184 #ifdef DEBUG
185 	atexit(uninit_util);
186 #endif /* DEBUG */
187 }
188 
189 
190 /*
191  * hourly_stats - print some interesting stats
192  */
193 void
194 write_stats(void)
195 {
196 	FILE	*fp;
197 	double	ftemp;
198 #ifdef DOSYNCTODR
199 	struct timeval tv;
200 #if !defined(VMS)
201 	int	prio_set;
202 #endif
203 #ifdef HAVE_GETCLOCK
204         struct timespec ts;
205 #endif
206 	int	o_prio;
207 
208 	/*
209 	 * Sometimes having a Sun can be a drag.
210 	 *
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
223 	 * thing to do.
224 	 *
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
228 	 */
229 
230 #if !defined(VMS)
231 	/*
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.
235 	 */
236 	errno = 0;
237 	prio_set = 0;
238 	o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */
239 
240 	/*
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
250 	 * silently ignored.
251 	 */
252 	if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
253 		prio_set = 1;	/* overdrive */
254 #endif /* VMS */
255 #ifdef HAVE_GETCLOCK
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");
264 #if !defined(VMS)
265 	if (prio_set)
266 		setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */
267 #endif /* VMS */
268 #endif /* DOSYNCTODR */
269 	record_sys_stats();
270 	ftemp = fabs(prev_drift_comp - drift_comp);
271 	prev_drift_comp = drift_comp;
272 	if (ftemp > clock_phi)
273 		return;
274 
275 	if (stats_drift_file != 0 && drift_file_sw) {
276 
277 		/*
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.
284 		 */
285 		drift_file_sw = FALSE;
286 		wander_resid *= 0.5;
287 #ifdef DEBUG
288 		if (debug)
289 			printf("write_stats: wander %.6lf thresh %.6lf, freq %.6lf\n",
290 			    clock_stability * 1e6, wander_resid * 1e6,
291 			    drift_comp * 1e6);
292 #endif
293  		if (sys_leap != LEAP_NOTINSYNC && clock_stability >
294 		    wander_resid) {
295 			wander_resid = wander_threshold;
296 			if ((fp = fopen(stats_temp_file, "w")) == NULL)
297 			    {
298 				msyslog(LOG_ERR,
299 				    "frequency file %s: %m",
300 				    stats_temp_file);
301 				return;
302 			}
303 			fprintf(fp, "%.3f\n", drift_comp * 1e6);
304 			(void)fclose(fp);
305 			/* atomic */
306 #ifdef SYS_WINNT
307 			if (_unlink(stats_drift_file)) /* rename semantics differ under NT */
308 				msyslog(LOG_WARNING,
309 					"Unable to remove prior drift file %s, %m",
310 					stats_drift_file);
311 #endif /* SYS_WINNT */
312 
313 #ifndef NO_RENAME
314 			if (rename(stats_temp_file, stats_drift_file))
315 				msyslog(LOG_WARNING,
316 					"Unable to rename temp drift file %s to %s, %m",
317 					stats_temp_file, stats_drift_file);
318 #else
319 			/* we have no rename NFS of ftp in use */
320 			if ((fp = fopen(stats_drift_file, "w")) ==
321 			    NULL) {
322 				msyslog(LOG_ERR,
323 				    "frequency file %s: %m",
324 				    stats_drift_file);
325 				return;
326 			}
327 #endif
328 
329 #if defined(VMS)
330 			/* PURGE */
331 			{
332 				$DESCRIPTOR(oldvers,";-1");
333 				struct dsc$descriptor driftdsc = {
334 					strlen(stats_drift_file), 0, 0,
335 					    stats_drift_file };
336 				while(lib$delete_file(&oldvers,
337 				    &driftdsc) & 1);
338 			}
339 #endif
340 		} else {
341 			/* XXX: Log a message at INFO level */
342 		}
343 	}
344 }
345 
346 
347 /*
348  * stats_config - configure the stats operation
349  */
350 void
351 stats_config(
352 	int item,
353 	const char *invalue	/* only one type so far */
354 	)
355 {
356 	FILE	*fp;
357 	const char *value;
358 	int	len;
359 	char	tbuf[80];
360 	char	str1[20], str2[20];
361 
362 	/*
363 	 * Expand environment strings under Windows NT, since the
364 	 * command interpreter doesn't do this, the program must.
365 	 */
366 #ifdef SYS_WINNT
367 	char newvalue[MAX_PATH], parameter[MAX_PATH];
368 
369 	if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
370  		switch(item) {
371 		    case STATS_FREQ_FILE:
372 			strcpy(parameter,"STATS_FREQ_FILE");
373 			break;
374 
375 		    case STATS_LEAP_FILE:
376 			strcpy(parameter,"STATS_LEAP_FILE");
377 			break;
378 
379 		    case STATS_STATSDIR:
380 			strcpy(parameter,"STATS_STATSDIR");
381 			break;
382 
383 		    case STATS_PID_FILE:
384 			strcpy(parameter,"STATS_PID_FILE");
385 			break;
386 
387 		    default:
388 			strcpy(parameter,"UNKNOWN");
389 			break;
390 		}
391 		value = invalue;
392 		msyslog(LOG_ERR,
393 		    "ExpandEnvironmentStrings(%s) failed: %m\n",
394 		    parameter);
395 	} else {
396 		value = newvalue;
397 	}
398 #else
399 	value = invalue;
400 #endif /* SYS_WINNT */
401 
402 	switch(item) {
403 
404 	/*
405 	 * Open and read frequency file.
406 	 */
407 	case STATS_FREQ_FILE:
408 		if (!value || (len = strlen(value)) == 0)
409 			break;
410 
411 		stats_drift_file = erealloc(stats_drift_file, len + 1);
412 		stats_temp_file = erealloc(stats_temp_file,
413 					   len + sizeof(".TEMP"));
414 
415 		memmove(stats_drift_file, value, (unsigned)(len+1));
416 		memmove(stats_temp_file, value, (unsigned)len);
417 		memmove(stats_temp_file + len,
418 #if !defined(VMS)
419 			".TEMP", sizeof(".TEMP"));
420 #else
421 			"-TEMP", sizeof("-TEMP"));
422 #endif /* VMS */
423 
424 		/*
425 		 * Open drift file and read frequency. If the file is
426 		 * missing or contains errors, tell the loop to reset.
427 		 */
428 		if ((fp = fopen(stats_drift_file, "r")) == NULL)
429 			break;
430 
431 		if (fscanf(fp, "%lf", &old_drift) != 1) {
432 			msyslog(LOG_ERR,
433 				"format error frequency file %s",
434 				stats_drift_file);
435 			fclose(fp);
436 			break;
437 
438 		}
439 		fclose(fp);
440 		old_drift /= 1e6;
441 		prev_drift_comp = old_drift;
442 		break;
443 
444 	/*
445 	 * Specify statistics directory.
446 	 */
447 	case STATS_STATSDIR:
448 
449 		/*
450 		 * HMS: the following test is insufficient:
451 		 * - value may be missing the DIR_SEP
452 		 * - we still need the filename after it
453 		 */
454 		if (strlen(value) >= sizeof(statsdir)) {
455 			msyslog(LOG_ERR,
456 			    "statsdir too long (>%d, sigh)",
457 			    (int)sizeof(statsdir) - 1);
458 		} else {
459 			l_fp now;
460 			int add_dir_sep;
461 			int value_l = strlen(value);
462 
463 			/* Add a DIR_SEP unless we already have one. */
464 			if (value_l == 0)
465 				add_dir_sep = 0;
466 			else
467 				add_dir_sep = (DIR_SEP !=
468 				    value[value_l - 1]);
469 
470 			if (add_dir_sep)
471 			    snprintf(statsdir, sizeof(statsdir),
472 				"%s%c", value, DIR_SEP);
473 			else
474 			    snprintf(statsdir, sizeof(statsdir),
475 				"%s", value);
476 
477 			get_systime(&now);
478 			if(peerstats.prefix == &statsdir[0] &&
479 			    peerstats.fp != NULL) {
480 				fclose(peerstats.fp);
481 				peerstats.fp = NULL;
482 				filegen_setup(&peerstats, now.l_ui);
483 			}
484 			if(loopstats.prefix == &statsdir[0] &&
485 			    loopstats.fp != NULL) {
486 				fclose(loopstats.fp);
487 				loopstats.fp = NULL;
488 				filegen_setup(&loopstats, now.l_ui);
489 			}
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);
495 			}
496 			if(rawstats.prefix == &statsdir[0] &&
497 			    rawstats.fp != NULL) {
498 				fclose(rawstats.fp);
499 				rawstats.fp = NULL;
500 				filegen_setup(&rawstats, now.l_ui);
501 			}
502 			if(sysstats.prefix == &statsdir[0] &&
503 			    sysstats.fp != NULL) {
504 				fclose(sysstats.fp);
505 				sysstats.fp = NULL;
506 				filegen_setup(&sysstats, now.l_ui);
507 			}
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);
513 			}
514 #ifdef OPENSSL
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);
520 			}
521 #endif /* OPENSSL */
522 #ifdef DEBUG_TIMING
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);
528 			}
529 #endif /* DEBUG_TIMING */
530 		}
531 		break;
532 
533 	/*
534 	 * Open pid file.
535 	 */
536 	case STATS_PID_FILE:
537 		if ((fp = fopen(value, "w")) == NULL) {
538 			msyslog(LOG_ERR, "pid file %s: %m",
539 			    value);
540 			break;
541 		}
542 		fprintf(fp, "%d", (int)getpid());
543 		fclose(fp);;
544 		break;
545 
546 	/*
547 	 * Read leapseconds file.
548 	 */
549 	case STATS_LEAP_FILE:
550 		if ((fp = fopen(value, "r")) == NULL) {
551 			msyslog(LOG_ERR, "leapseconds file %s: %m",
552 			    value);
553 			break;
554 		}
555 
556 		if (leap_file(fp) < 0) {
557 			msyslog(LOG_ERR,
558 			    "format error leapseconds file %s",
559 			    value);
560 		} else {
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,
565 			    str2);
566 			report_event(EVNT_TAI, NULL, tbuf);
567 		}
568 		fclose(fp);
569 		break;
570 
571 	default:
572 		/* oh well */
573 		break;
574 	}
575 }
576 
577 
578 /*
579  * record_peer_stats - write peer statistics to file
580  *
581  * file format:
582  * day (MJD)
583  * time (s past UTC midnight)
584  * IP address
585  * status word (hex)
586  * offset
587  * delay
588  * dispersion
589  * jitter
590 */
591 void
592 record_peer_stats(
593 	sockaddr_u *addr,
594 	int	status,
595 	double	offset,		/* offset */
596 	double	delay,		/* delay */
597 	double	dispersion,	/* dispersion */
598 	double	jitter		/* jitter */
599 	)
600 {
601 	l_fp	now;
602 	u_long	day;
603 
604 	if (!stats_control)
605 		return;
606 
607 	get_systime(&now);
608 	filegen_setup(&peerstats, now.l_ui);
609 	day = now.l_ui / 86400 + MJD_1900;
610 	now.l_ui %= 86400;
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);
617 	}
618 }
619 
620 
621 /*
622  * record_loop_stats - write loop filter statistics to file
623  *
624  * file format:
625  * day (MJD)
626  * time (s past midnight)
627  * offset
628  * frequency (PPM)
629  * jitter
630  * wnder (PPM)
631  * time constant (log2)
632  */
633 void
634 record_loop_stats(
635 	double	offset,		/* offset */
636 	double	freq,		/* frequency (PPM) */
637 	double	jitter,		/* jitter */
638 	double	wander,		/* wander (PPM) */
639 	int spoll
640 	)
641 {
642 	l_fp	now;
643 	u_long	day;
644 
645 	if (!stats_control)
646 		return;
647 
648 	get_systime(&now);
649 	filegen_setup(&loopstats, now.l_ui);
650 	day = now.l_ui / 86400 + MJD_1900;
651 	now.l_ui %= 86400;
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);
657 	}
658 }
659 
660 
661 /*
662  * record_clock_stats - write clock statistics to file
663  *
664  * file format:
665  * day (MJD)
666  * time (s past midnight)
667  * IP address
668  * text message
669  */
670 void
671 record_clock_stats(
672 	sockaddr_u *addr,
673 	const char *text	/* timecode string */
674 	)
675 {
676 	l_fp	now;
677 	u_long	day;
678 
679 	if (!stats_control)
680 		return;
681 
682 	get_systime(&now);
683 	filegen_setup(&clockstats, now.l_ui);
684 	day = now.l_ui / 86400 + MJD_1900;
685 	now.l_ui %= 86400;
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);
690 	}
691 }
692 
693 
694 /*
695  * record_raw_stats - write raw timestamps to file
696  *
697  * file format
698  * day (MJD)
699  * time (s past midnight)
700  * peer ip address
701  * IP address
702  * t1 t2 t3 t4 timestamps
703  */
704 void
705 record_raw_stats(
706 	sockaddr_u *srcadr,
707 	sockaddr_u *dstadr,
708 	l_fp	*t1,		/* originate timestamp */
709 	l_fp	*t2,		/* receive timestamp */
710 	l_fp	*t3,		/* transmit timestamp */
711 	l_fp	*t4		/* destination timestamp */
712 	)
713 {
714 	l_fp	now;
715 	u_long	day;
716 
717 	if (!stats_control)
718 		return;
719 
720 	get_systime(&now);
721 	filegen_setup(&rawstats, now.l_ui);
722 	day = now.l_ui / 86400 + MJD_1900;
723 	now.l_ui %= 86400;
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));
729 		fflush(rawstats.fp);
730 	}
731 }
732 
733 
734 /*
735  * record_sys_stats - write system statistics to file
736  *
737  * file format
738  * day (MJD)
739  * time (s past midnight)
740  * time since reset
741  * packets recieved
742  * packets for this host
743  * current version
744  * old version
745  * access denied
746  * bad length or format
747  * bad authentication
748  * declined
749  * rate exceeded
750  * KoD sent
751  */
752 void
753 record_sys_stats(void)
754 {
755 	l_fp	now;
756 	u_long	day;
757 
758 	if (!stats_control)
759 		return;
760 
761 	get_systime(&now);
762 	filegen_setup(&sysstats, now.l_ui);
763 	day = now.l_ui / 86400 + MJD_1900;
764 	now.l_ui %= 86400;
765 	if (sysstats.fp != NULL) {
766                 fprintf(sysstats.fp,
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,
772 		    sys_kodsent);
773 		fflush(sysstats.fp);
774 		proto_clr_stats();
775 	}
776 }
777 
778 
779 /*
780  * record_proto_stats - write system statistics to file
781  *
782  * file format
783  * day (MJD)
784  * time (s past midnight)
785  * text message
786  */
787 void
788 record_proto_stats(
789 	char	*str		/* text string */
790 	)
791 {
792 	l_fp	now;
793 	u_long	day;
794 
795 	if (!stats_control)
796 		return;
797 
798 	get_systime(&now);
799 	filegen_setup(&protostats, now.l_ui);
800 	day = now.l_ui / 86400 + MJD_1900;
801 	now.l_ui %= 86400;
802 	if (protostats.fp != NULL) {
803 		fprintf(protostats.fp, "%lu %s %s\n", day,
804 		    ulfptoa(&now, 3), str);
805 		fflush(protostats.fp);
806 	}
807 }
808 
809 
810 #ifdef OPENSSL
811 /*
812  * record_crypto_stats - write crypto statistics to file
813  *
814  * file format:
815  * day (mjd)
816  * time (s past midnight)
817  * peer ip address
818  * text message
819  */
820 void
821 record_crypto_stats(
822 	sockaddr_u *addr,
823 	const char *text	/* text message */
824 	)
825 {
826 	l_fp	now;
827 	u_long	day;
828 
829 	if (!stats_control)
830 		return;
831 
832 	get_systime(&now);
833 	filegen_setup(&cryptostats, now.l_ui);
834 	day = now.l_ui / 86400 + MJD_1900;
835 	now.l_ui %= 86400;
836 	if (cryptostats.fp != NULL) {
837 		if (addr == NULL)
838 			fprintf(cryptostats.fp, "%lu %s 0.0.0.0 %s\n",
839 			    day, ulfptoa(&now, 3), text);
840 		else
841 			fprintf(cryptostats.fp, "%lu %s %s %s\n",
842 			    day, ulfptoa(&now, 3), stoa(addr), text);
843 		fflush(cryptostats.fp);
844 	}
845 }
846 #endif /* OPENSSL */
847 
848 
849 #ifdef DEBUG_TIMING
850 /*
851  * record_timing_stats - write timing statistics to file
852  *
853  * file format:
854  * day (mjd)
855  * time (s past midnight)
856  * text message
857  */
858 void
859 record_timing_stats(
860 	const char *text	/* text message */
861 	)
862 {
863 	static unsigned int flshcnt;
864 	l_fp	now;
865 	u_long	day;
866 
867 	if (!stats_control)
868 		return;
869 
870 	get_systime(&now);
871 	filegen_setup(&timingstats, now.l_ui);
872 	day = now.l_ui / 86400 + MJD_1900;
873 	now.l_ui %= 86400;
874 	if (timingstats.fp != NULL) {
875 		fprintf(timingstats.fp, "%lu %s %s\n", day, lfptoa(&now,
876 		    3), text);
877 		if (++flshcnt % 100 == 0)
878 			fflush(timingstats.fp);
879 	}
880 }
881 #endif
882 
883 
884 /*
885  * leap_file - read leapseconds file
886  *
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.
889  */
890 static int
891 leap_file(
892 	FILE	*fp		/* file handle */
893 	)
894 {
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) */
899 	int	i;
900 
901 	/*
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
907 	 * after the leap.
908  	 */
909 	offset = 0;
910 	leap = 0;
911 	expire = 0;
912 	i = 10;
913 	while (fgets(buf, NTP_MAXSTRLEN - 1, fp) != NULL) {
914 		if (strlen(buf) < 1)
915 			continue;
916 
917 		if (buf[0] == '#') {
918 			if (strlen(buf) < 3)
919 				continue;
920 
921 			/*
922 			 * Note the '@' flag was used only in the 2006
923 			 * table; previious to that the flag was '$'.
924 			 */
925 			if (buf[1] == '@' || buf[1] == '$') {
926 				if (sscanf(&buf[2], "%lu", &expire) !=
927 				    1)
928 					return (-1);
929 
930 				continue;
931 			}
932 		}
933 		if (sscanf(buf, "%lu %d", &leap, &offset) == 2) {
934 
935 			/*
936 			 * Valid offsets must increase by one for each
937 			 * leap.
938 			 */
939 			if (i++ != offset)
940 				return (-1);
941 		}
942 	}
943 
944 	/*
945 	 * There must be at least one leap.
946 	 */
947 	if (i == 10)
948 		return (-1);
949 
950 	leap_tai = offset;
951 	leap_sec = leap;
952 	leap_expire = expire;
953 	return (0);
954 }
955 
956 
957 /*
958  * leap_month - returns seconds until the end of the month.
959  */
960 u_long
961 leap_month(
962 	u_long	sec		/* current NTP second */
963 	)
964 {
965 	u_long	ltemp;
966 	u_long	*ptr;
967 	u_long	year[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
968 		    31};
969 	u_long	lyear[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30,
970 		    31};
971 
972 	/*
973 	 * Find current leap cycle.
974 	 */
975 	ltemp = sec;
976 	while (ltemp >= L_CENT)
977 		ltemp -= L_CENT;
978 	while (ltemp >= L_4YEAR)
979 		ltemp -= L_4YEAR;
980 
981 	/*
982 	 * We are within four years of the target. If in leap year, use
983 	 * leap year month table; otherwise, use year month table.
984 	 */
985 	if (ltemp < L_LYEAR) {
986 		ptr = lyear;
987 	} else {
988 		ptr = year;
989 		ltemp -= L_LYEAR;
990 		while (ltemp >= L_YEAR)
991 			ltemp -= L_YEAR;
992 	}
993 
994 	/*
995 	 * We are within one year of the target. Find the month of the
996 	 * leap.
997 	 */
998 	while (ltemp >= *ptr * L_DAY)
999 		ltemp -= *ptr++ * L_DAY;
1000 
1001 	/*
1002 	 * The result is the number of seconds until the end of the
1003 	 * month when the leap is to occur.
1004 	 */
1005 	return (*ptr * L_DAY - ltemp - L_DAY);
1006 }
1007 
1008 
1009 /*
1010  * getauthkeys - read the authentication keys from the specified file
1011  */
1012 void
1013 getauthkeys(
1014 	const char *keyfile
1015 	)
1016 {
1017 	int len;
1018 
1019 	len = strlen(keyfile);
1020 	if (!len)
1021 		return;
1022 
1023 #ifndef SYS_WINNT
1024 	key_file_name = erealloc(key_file_name, len + 1);
1025 	memmove(key_file_name, keyfile, len + 1);
1026 #else
1027 	key_file_name = erealloc(key_file_name, _MAX_PATH);
1028 	if (len + 1 > _MAX_PATH)
1029 		return;
1030 	if (!ExpandEnvironmentStrings(keyfile, key_file_name,
1031 				      _MAX_PATH)) {
1032 		msyslog(LOG_ERR,
1033 			"ExpandEnvironmentStrings(KEY_FILE) failed: %m");
1034 		strncpy(key_file_name, keyfile, _MAX_PATH);
1035 	}
1036 #endif /* SYS_WINNT */
1037 
1038 	authreadkeys(key_file_name);
1039 }
1040 
1041 
1042 /*
1043  * rereadkeys - read the authentication key file over again.
1044  */
1045 void
1046 rereadkeys(void)
1047 {
1048 	if (NULL != key_file_name)
1049 		authreadkeys(key_file_name);
1050 }
1051 
1052 
1053 /*
1054  * sock_hash - hash a sockaddr_u structure
1055  */
1056 u_short
1057 sock_hash(
1058 	sockaddr_u *addr
1059 	)
1060 {
1061 	u_int hashVal;
1062 	u_int j;
1063 	size_t len;
1064 	u_char *pch;
1065 	hashVal = 0;
1066 	len = 0;
1067 
1068 	/*
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.
1072 	 */
1073 	pch = (u_char *)&AF(addr);
1074 	hashVal = 37 * hashVal + *pch;
1075 	if (sizeof(AF(addr)) > 1) {
1076 		pch++;
1077 		hashVal = 37 * hashVal + *pch;
1078 	}
1079 	switch(AF(addr)) {
1080 	case AF_INET:
1081 		pch = (u_char *)&SOCK_ADDR4(addr);
1082 		len = sizeof(SOCK_ADDR4(addr));
1083 		break;
1084 
1085 	case AF_INET6:
1086 		pch = (u_char *)&SOCK_ADDR6(addr);
1087 		len = sizeof(SOCK_ADDR6(addr));
1088 		break;
1089 	}
1090 
1091 	for (j = 0; j < len ; j++)
1092 		hashVal = 37 * hashVal + pch[j];
1093 
1094 	hashVal = hashVal & NTP_HASH_MASK;
1095 
1096 	return (u_short)hashVal;
1097 }
1098 
1099 
1100 #if notyet
1101 /*
1102  * ntp_exit - document explicitly that ntpd has exited
1103  */
1104 void
1105 ntp_exit(int retval)
1106 {
1107 	msyslog(LOG_ERR, "EXITING with return code %d", retval);
1108 	exit(retval);
1109 }
1110 #endif
1111 
1112 /*
1113  * fstostr - prettyprint NTP seconds
1114  */
1115 char * fstostr(
1116 	time_t	ntp_stamp
1117 	)
1118 {
1119 	static char	str[20];
1120 	struct tm *	tm;
1121 	time_t		unix_stamp;
1122 
1123 	unix_stamp = ntp_stamp - JAN_1970;
1124 	tm = gmtime(&unix_stamp);
1125 	if (NULL != tm)
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);
1130 	else
1131 		strcpy(str, "gmtime() error");
1132 
1133 	return str;
1134 }
1135