xref: /netbsd-src/external/bsd/ntp/dist/ntpd/ntp_util.c (revision 4d12bfcd155352508213ace5ccc59ce930ea2974)
1 /*	$NetBSD: ntp_util.c,v 1.3 2012/02/01 07:46:22 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 #ifndef VMS
362 	const char temp_ext[] = ".TEMP";
363 #else
364 	const char temp_ext[] = "-TEMP";
365 #endif
366 
367 	/*
368 	 * Expand environment strings under Windows NT, since the
369 	 * command interpreter doesn't do this, the program must.
370 	 */
371 #ifdef SYS_WINNT
372 	char newvalue[MAX_PATH], parameter[MAX_PATH];
373 
374 	if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
375  		switch(item) {
376 		    case STATS_FREQ_FILE:
377 			strcpy(parameter,"STATS_FREQ_FILE");
378 			break;
379 
380 		    case STATS_LEAP_FILE:
381 			strcpy(parameter,"STATS_LEAP_FILE");
382 			break;
383 
384 		    case STATS_STATSDIR:
385 			strcpy(parameter,"STATS_STATSDIR");
386 			break;
387 
388 		    case STATS_PID_FILE:
389 			strcpy(parameter,"STATS_PID_FILE");
390 			break;
391 
392 		    default:
393 			strcpy(parameter,"UNKNOWN");
394 			break;
395 		}
396 		value = invalue;
397 		msyslog(LOG_ERR,
398 		    "ExpandEnvironmentStrings(%s) failed: %m\n",
399 		    parameter);
400 	} else {
401 		value = newvalue;
402 	}
403 #else
404 	value = invalue;
405 #endif /* SYS_WINNT */
406 
407 	switch(item) {
408 
409 	/*
410 	 * Open and read frequency file.
411 	 */
412 	case STATS_FREQ_FILE:
413 		if (!value || (len = strlen(value)) == 0)
414 			break;
415 
416 		stats_drift_file = erealloc(stats_drift_file, len + 1);
417 		stats_temp_file = erealloc(stats_temp_file,
418 					   len + sizeof(".TEMP"));
419 
420 		memcpy(stats_drift_file, value, (unsigned)(len+1));
421 		memcpy(stats_temp_file, value, (unsigned)len);
422 		memcpy(stats_temp_file + len, temp_ext,
423 		       sizeof(temp_ext));
424 
425 		/*
426 		 * Open drift file and read frequency. If the file is
427 		 * missing or contains errors, tell the loop to reset.
428 		 */
429 		if ((fp = fopen(stats_drift_file, "r")) == NULL)
430 			break;
431 
432 		if (fscanf(fp, "%lf", &old_drift) != 1) {
433 			msyslog(LOG_ERR,
434 				"format error frequency file %s",
435 				stats_drift_file);
436 			fclose(fp);
437 			break;
438 
439 		}
440 		fclose(fp);
441 		old_drift /= 1e6;
442 		prev_drift_comp = old_drift;
443 		break;
444 
445 	/*
446 	 * Specify statistics directory.
447 	 */
448 	case STATS_STATSDIR:
449 
450 		/*
451 		 * HMS: the following test is insufficient:
452 		 * - value may be missing the DIR_SEP
453 		 * - we still need the filename after it
454 		 */
455 		if (strlen(value) >= sizeof(statsdir)) {
456 			msyslog(LOG_ERR,
457 			    "statsdir too long (>%d, sigh)",
458 			    (int)sizeof(statsdir) - 1);
459 		} else {
460 			l_fp now;
461 			int add_dir_sep;
462 			int value_l = strlen(value);
463 
464 			/* Add a DIR_SEP unless we already have one. */
465 			if (value_l == 0)
466 				add_dir_sep = 0;
467 			else
468 				add_dir_sep = (DIR_SEP !=
469 				    value[value_l - 1]);
470 
471 			if (add_dir_sep)
472 			    snprintf(statsdir, sizeof(statsdir),
473 				"%s%c", value, DIR_SEP);
474 			else
475 			    snprintf(statsdir, sizeof(statsdir),
476 				"%s", value);
477 
478 			get_systime(&now);
479 			if(peerstats.prefix == &statsdir[0] &&
480 			    peerstats.fp != NULL) {
481 				fclose(peerstats.fp);
482 				peerstats.fp = NULL;
483 				filegen_setup(&peerstats, now.l_ui);
484 			}
485 			if(loopstats.prefix == &statsdir[0] &&
486 			    loopstats.fp != NULL) {
487 				fclose(loopstats.fp);
488 				loopstats.fp = NULL;
489 				filegen_setup(&loopstats, now.l_ui);
490 			}
491 			if(clockstats.prefix == &statsdir[0] &&
492 			    clockstats.fp != NULL) {
493 				fclose(clockstats.fp);
494 				clockstats.fp = NULL;
495 				filegen_setup(&clockstats, now.l_ui);
496 			}
497 			if(rawstats.prefix == &statsdir[0] &&
498 			    rawstats.fp != NULL) {
499 				fclose(rawstats.fp);
500 				rawstats.fp = NULL;
501 				filegen_setup(&rawstats, now.l_ui);
502 			}
503 			if(sysstats.prefix == &statsdir[0] &&
504 			    sysstats.fp != NULL) {
505 				fclose(sysstats.fp);
506 				sysstats.fp = NULL;
507 				filegen_setup(&sysstats, now.l_ui);
508 			}
509 			if(protostats.prefix == &statsdir[0] &&
510 			    protostats.fp != NULL) {
511 				fclose(protostats.fp);
512 				protostats.fp = NULL;
513 				filegen_setup(&protostats, now.l_ui);
514 			}
515 #ifdef OPENSSL
516 			if(cryptostats.prefix == &statsdir[0] &&
517 			    cryptostats.fp != NULL) {
518 				fclose(cryptostats.fp);
519 				cryptostats.fp = NULL;
520 				filegen_setup(&cryptostats, now.l_ui);
521 			}
522 #endif /* OPENSSL */
523 #ifdef DEBUG_TIMING
524 			if(timingstats.prefix == &statsdir[0] &&
525 			    timingstats.fp != NULL) {
526 				fclose(timingstats.fp);
527 				timingstats.fp = NULL;
528 				filegen_setup(&timingstats, now.l_ui);
529 			}
530 #endif /* DEBUG_TIMING */
531 		}
532 		break;
533 
534 	/*
535 	 * Open pid file.
536 	 */
537 	case STATS_PID_FILE:
538 		if ((fp = fopen(value, "w")) == NULL) {
539 			msyslog(LOG_ERR, "pid file %s: %m",
540 			    value);
541 			break;
542 		}
543 		fprintf(fp, "%d", (int)getpid());
544 		fclose(fp);;
545 		break;
546 
547 	/*
548 	 * Read leapseconds file.
549 	 */
550 	case STATS_LEAP_FILE:
551 		if ((fp = fopen(value, "r")) == NULL) {
552 			msyslog(LOG_ERR, "leapseconds file %s: %m",
553 			    value);
554 			break;
555 		}
556 
557 		if (leap_file(fp) < 0) {
558 			msyslog(LOG_ERR,
559 			    "format error leapseconds file %s",
560 			    value);
561 		} else {
562 			strcpy(str1, fstostr(leap_sec));
563 			strcpy(str2, fstostr(leap_expire));
564 			snprintf(tbuf, sizeof(tbuf),
565 			    "%d leap %s expire %s", leap_tai, str1,
566 			    str2);
567 			report_event(EVNT_TAI, NULL, tbuf);
568 		}
569 		fclose(fp);
570 		break;
571 
572 	default:
573 		/* oh well */
574 		break;
575 	}
576 }
577 
578 
579 /*
580  * record_peer_stats - write peer statistics to file
581  *
582  * file format:
583  * day (MJD)
584  * time (s past UTC midnight)
585  * IP address
586  * status word (hex)
587  * offset
588  * delay
589  * dispersion
590  * jitter
591 */
592 void
593 record_peer_stats(
594 	sockaddr_u *addr,
595 	int	status,
596 	double	offset,		/* offset */
597 	double	delay,		/* delay */
598 	double	dispersion,	/* dispersion */
599 	double	jitter		/* jitter */
600 	)
601 {
602 	l_fp	now;
603 	u_long	day;
604 
605 	if (!stats_control)
606 		return;
607 
608 	get_systime(&now);
609 	filegen_setup(&peerstats, now.l_ui);
610 	day = now.l_ui / 86400 + MJD_1900;
611 	now.l_ui %= 86400;
612 	if (peerstats.fp != NULL) {
613 		fprintf(peerstats.fp,
614 		    "%lu %s %s %x %.9f %.9f %.9f %.9f\n", day,
615 		    ulfptoa(&now, 3), stoa(addr), status, offset,
616 		    delay, dispersion, jitter);
617 		fflush(peerstats.fp);
618 	}
619 }
620 
621 
622 /*
623  * record_loop_stats - write loop filter statistics to file
624  *
625  * file format:
626  * day (MJD)
627  * time (s past midnight)
628  * offset
629  * frequency (PPM)
630  * jitter
631  * wnder (PPM)
632  * time constant (log2)
633  */
634 void
635 record_loop_stats(
636 	double	offset,		/* offset */
637 	double	freq,		/* frequency (PPM) */
638 	double	jitter,		/* jitter */
639 	double	wander,		/* wander (PPM) */
640 	int spoll
641 	)
642 {
643 	l_fp	now;
644 	u_long	day;
645 
646 	if (!stats_control)
647 		return;
648 
649 	get_systime(&now);
650 	filegen_setup(&loopstats, now.l_ui);
651 	day = now.l_ui / 86400 + MJD_1900;
652 	now.l_ui %= 86400;
653 	if (loopstats.fp != NULL) {
654 		fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n",
655 		    day, ulfptoa(&now, 3), offset, freq * 1e6, jitter,
656 		    wander * 1e6, spoll);
657 		fflush(loopstats.fp);
658 	}
659 }
660 
661 
662 /*
663  * record_clock_stats - write clock statistics to file
664  *
665  * file format:
666  * day (MJD)
667  * time (s past midnight)
668  * IP address
669  * text message
670  */
671 void
672 record_clock_stats(
673 	sockaddr_u *addr,
674 	const char *text	/* timecode string */
675 	)
676 {
677 	l_fp	now;
678 	u_long	day;
679 
680 	if (!stats_control)
681 		return;
682 
683 	get_systime(&now);
684 	filegen_setup(&clockstats, now.l_ui);
685 	day = now.l_ui / 86400 + MJD_1900;
686 	now.l_ui %= 86400;
687 	if (clockstats.fp != NULL) {
688 		fprintf(clockstats.fp, "%lu %s %s %s\n", day,
689 		    ulfptoa(&now, 3), stoa(addr), text);
690 		fflush(clockstats.fp);
691 	}
692 }
693 
694 
695 /*
696  * record_raw_stats - write raw timestamps to file
697  *
698  * file format
699  * day (MJD)
700  * time (s past midnight)
701  * peer ip address
702  * IP address
703  * t1 t2 t3 t4 timestamps
704  */
705 void
706 record_raw_stats(
707 	sockaddr_u *srcadr,
708 	sockaddr_u *dstadr,
709 	l_fp	*t1,		/* originate timestamp */
710 	l_fp	*t2,		/* receive timestamp */
711 	l_fp	*t3,		/* transmit timestamp */
712 	l_fp	*t4		/* destination timestamp */
713 	)
714 {
715 	l_fp	now;
716 	u_long	day;
717 
718 	if (!stats_control)
719 		return;
720 
721 	get_systime(&now);
722 	filegen_setup(&rawstats, now.l_ui);
723 	day = now.l_ui / 86400 + MJD_1900;
724 	now.l_ui %= 86400;
725 	if (rawstats.fp != NULL) {
726 		fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s\n", day,
727 		    ulfptoa(&now, 3), stoa(srcadr), dstadr ?
728 		    stoa(dstadr) : "-",	ulfptoa(t1, 9), ulfptoa(t2, 9),
729 		    ulfptoa(t3, 9), ulfptoa(t4, 9));
730 		fflush(rawstats.fp);
731 	}
732 }
733 
734 
735 /*
736  * record_sys_stats - write system statistics to file
737  *
738  * file format
739  * day (MJD)
740  * time (s past midnight)
741  * time since reset
742  * packets recieved
743  * packets for this host
744  * current version
745  * old version
746  * access denied
747  * bad length or format
748  * bad authentication
749  * declined
750  * rate exceeded
751  * KoD sent
752  */
753 void
754 record_sys_stats(void)
755 {
756 	l_fp	now;
757 	u_long	day;
758 
759 	if (!stats_control)
760 		return;
761 
762 	get_systime(&now);
763 	filegen_setup(&sysstats, now.l_ui);
764 	day = now.l_ui / 86400 + MJD_1900;
765 	now.l_ui %= 86400;
766 	if (sysstats.fp != NULL) {
767 		fprintf(sysstats.fp,
768 		    "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
769 		    day, ulfptoa(&now, 3), current_time - sys_stattime,
770 		    sys_received, sys_processed, sys_newversion,
771 		    sys_oldversion, sys_restricted, sys_badlength,
772 		    sys_badauth, sys_declined, sys_limitrejected,
773 		    sys_kodsent);
774 		fflush(sysstats.fp);
775 		proto_clr_stats();
776 	}
777 }
778 
779 
780 /*
781  * record_proto_stats - write system statistics to file
782  *
783  * file format
784  * day (MJD)
785  * time (s past midnight)
786  * text message
787  */
788 void
789 record_proto_stats(
790 	char	*str		/* text string */
791 	)
792 {
793 	l_fp	now;
794 	u_long	day;
795 
796 	if (!stats_control)
797 		return;
798 
799 	get_systime(&now);
800 	filegen_setup(&protostats, now.l_ui);
801 	day = now.l_ui / 86400 + MJD_1900;
802 	now.l_ui %= 86400;
803 	if (protostats.fp != NULL) {
804 		fprintf(protostats.fp, "%lu %s %s\n", day,
805 		    ulfptoa(&now, 3), str);
806 		fflush(protostats.fp);
807 	}
808 }
809 
810 
811 #ifdef OPENSSL
812 /*
813  * record_crypto_stats - write crypto statistics to file
814  *
815  * file format:
816  * day (mjd)
817  * time (s past midnight)
818  * peer ip address
819  * text message
820  */
821 void
822 record_crypto_stats(
823 	sockaddr_u *addr,
824 	const char *text	/* text message */
825 	)
826 {
827 	l_fp	now;
828 	u_long	day;
829 
830 	if (!stats_control)
831 		return;
832 
833 	get_systime(&now);
834 	filegen_setup(&cryptostats, now.l_ui);
835 	day = now.l_ui / 86400 + MJD_1900;
836 	now.l_ui %= 86400;
837 	if (cryptostats.fp != NULL) {
838 		if (addr == NULL)
839 			fprintf(cryptostats.fp, "%lu %s 0.0.0.0 %s\n",
840 			    day, ulfptoa(&now, 3), text);
841 		else
842 			fprintf(cryptostats.fp, "%lu %s %s %s\n",
843 			    day, ulfptoa(&now, 3), stoa(addr), text);
844 		fflush(cryptostats.fp);
845 	}
846 }
847 #endif /* OPENSSL */
848 
849 
850 #ifdef DEBUG_TIMING
851 /*
852  * record_timing_stats - write timing statistics to file
853  *
854  * file format:
855  * day (mjd)
856  * time (s past midnight)
857  * text message
858  */
859 void
860 record_timing_stats(
861 	const char *text	/* text message */
862 	)
863 {
864 	static unsigned int flshcnt;
865 	l_fp	now;
866 	u_long	day;
867 
868 	if (!stats_control)
869 		return;
870 
871 	get_systime(&now);
872 	filegen_setup(&timingstats, now.l_ui);
873 	day = now.l_ui / 86400 + MJD_1900;
874 	now.l_ui %= 86400;
875 	if (timingstats.fp != NULL) {
876 		fprintf(timingstats.fp, "%lu %s %s\n", day, lfptoa(&now,
877 		    3), text);
878 		if (++flshcnt % 100 == 0)
879 			fflush(timingstats.fp);
880 	}
881 }
882 #endif
883 
884 
885 /*
886  * leap_file - read leapseconds file
887  *
888  * Read the ERTS leapsecond file in NIST text format and extract the
889  * NTP seconds of the latest leap and TAI offset after the leap.
890  */
891 static int
892 leap_file(
893 	FILE	*fp		/* file handle */
894 	)
895 {
896 	char	buf[NTP_MAXSTRLEN]; /* file line buffer */
897 	u_long	leap;		/* NTP time at leap */
898 	u_long	expire;		/* NTP time when file expires */
899 	int	offset;		/* TAI offset at leap (s) */
900 	int	i;
901 
902 	/*
903 	 * Read and parse the leapseconds file. Empty lines and comments
904 	 * are ignored. A line beginning with #@ contains the file
905 	 * expiration time in NTP seconds. Other lines begin with two
906 	 * integers followed by junk or comments. The first integer is
907 	 * the NTP seconds at the leap, the second is the TAI offset
908 	 * after the leap.
909  	 */
910 	offset = 0;
911 	leap = 0;
912 	expire = 0;
913 	i = 10;
914 	while (fgets(buf, NTP_MAXSTRLEN - 1, fp) != NULL) {
915 		if (strlen(buf) < 1)
916 			continue;
917 
918 		if (buf[0] == '#') {
919 			if (strlen(buf) < 3)
920 				continue;
921 
922 			/*
923 			 * Note the '@' flag was used only in the 2006
924 			 * table; previious to that the flag was '$'.
925 			 */
926 			if (buf[1] == '@' || buf[1] == '$') {
927 				if (sscanf(&buf[2], "%lu", &expire) !=
928 				    1)
929 					return (-1);
930 
931 				continue;
932 			}
933 		}
934 		if (sscanf(buf, "%lu %d", &leap, &offset) == 2) {
935 
936 			/*
937 			 * Valid offsets must increase by one for each
938 			 * leap.
939 			 */
940 			if (i++ != offset)
941 				return (-1);
942 		}
943 	}
944 
945 	/*
946 	 * There must be at least one leap.
947 	 */
948 	if (i == 10)
949 		return (-1);
950 
951 	leap_tai = offset;
952 	leap_sec = leap;
953 	leap_expire = expire;
954 	return (0);
955 }
956 
957 
958 /*
959  * leap_month - returns seconds until the end of the month.
960  */
961 u_long
962 leap_month(
963 	u_long	sec		/* current NTP second */
964 	)
965 {
966 	u_long	ltemp;
967 	u_long	*ptr;
968 	u_long	year[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
969 		    31};
970 	u_long	lyear[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30,
971 		    31};
972 
973 	/*
974 	 * Find current leap cycle.
975 	 */
976 	ltemp = sec;
977 	while (ltemp >= L_CENT)
978 		ltemp -= L_CENT;
979 	while (ltemp >= L_4YEAR)
980 		ltemp -= L_4YEAR;
981 
982 	/*
983 	 * We are within four years of the target. If in leap year, use
984 	 * leap year month table; otherwise, use year month table.
985 	 */
986 	if (ltemp < L_LYEAR) {
987 		ptr = lyear;
988 	} else {
989 		ptr = year;
990 		ltemp -= L_LYEAR;
991 		while (ltemp >= L_YEAR)
992 			ltemp -= L_YEAR;
993 	}
994 
995 	/*
996 	 * We are within one year of the target. Find the month of the
997 	 * leap.
998 	 */
999 	while (ltemp >= *ptr * L_DAY)
1000 		ltemp -= *ptr++ * L_DAY;
1001 
1002 	/*
1003 	 * The result is the number of seconds until the end of the
1004 	 * month when the leap is to occur.
1005 	 */
1006 	return (*ptr * L_DAY - ltemp - L_DAY);
1007 }
1008 
1009 
1010 /*
1011  * getauthkeys - read the authentication keys from the specified file
1012  */
1013 void
1014 getauthkeys(
1015 	const char *keyfile
1016 	)
1017 {
1018 	int len;
1019 
1020 	len = strlen(keyfile);
1021 	if (!len)
1022 		return;
1023 
1024 #ifndef SYS_WINNT
1025 	key_file_name = erealloc(key_file_name, len + 1);
1026 	memmove(key_file_name, keyfile, len + 1);
1027 #else
1028 	key_file_name = erealloc(key_file_name, _MAX_PATH);
1029 	if (len + 1 > _MAX_PATH)
1030 		return;
1031 	if (!ExpandEnvironmentStrings(keyfile, key_file_name,
1032 				      _MAX_PATH)) {
1033 		msyslog(LOG_ERR,
1034 			"ExpandEnvironmentStrings(KEY_FILE) failed: %m");
1035 		strncpy(key_file_name, keyfile, _MAX_PATH);
1036 	}
1037 #endif /* SYS_WINNT */
1038 
1039 	authreadkeys(key_file_name);
1040 }
1041 
1042 
1043 /*
1044  * rereadkeys - read the authentication key file over again.
1045  */
1046 void
1047 rereadkeys(void)
1048 {
1049 	if (NULL != key_file_name)
1050 		authreadkeys(key_file_name);
1051 }
1052 
1053 
1054 /*
1055  * sock_hash - hash a sockaddr_u structure
1056  */
1057 u_short
1058 sock_hash(
1059 	sockaddr_u *addr
1060 	)
1061 {
1062 	u_int hashVal;
1063 	u_int j;
1064 	size_t len;
1065 	u_char *pch;
1066 	hashVal = 0;
1067 	len = 0;
1068 
1069 	/*
1070 	 * We can't just hash the whole thing because there are hidden
1071 	 * fields in sockaddr_in6 that might be filled in by recvfrom(),
1072 	 * so just use the family, port and address.
1073 	 */
1074 	pch = (u_char *)&AF(addr);
1075 	hashVal = 37 * hashVal + *pch;
1076 	if (sizeof(AF(addr)) > 1) {
1077 		pch++;
1078 		hashVal = 37 * hashVal + *pch;
1079 	}
1080 	switch(AF(addr)) {
1081 	case AF_INET:
1082 		pch = (u_char *)&SOCK_ADDR4(addr);
1083 		len = sizeof(SOCK_ADDR4(addr));
1084 		break;
1085 
1086 	case AF_INET6:
1087 		pch = (u_char *)&SOCK_ADDR6(addr);
1088 		len = sizeof(SOCK_ADDR6(addr));
1089 		break;
1090 	}
1091 
1092 	for (j = 0; j < len ; j++)
1093 		hashVal = 37 * hashVal + pch[j];
1094 
1095 	hashVal = hashVal & NTP_HASH_MASK;
1096 
1097 	return (u_short)hashVal;
1098 }
1099 
1100 
1101 #if notyet
1102 /*
1103  * ntp_exit - document explicitly that ntpd has exited
1104  */
1105 void
1106 ntp_exit(int retval)
1107 {
1108 	msyslog(LOG_ERR, "EXITING with return code %d", retval);
1109 	exit(retval);
1110 }
1111 #endif
1112 
1113 /*
1114  * fstostr - prettyprint NTP seconds
1115  */
1116 char * fstostr(
1117 	time_t	ntp_stamp
1118 	)
1119 {
1120 	static char	str[20];
1121 	struct tm *	tm;
1122 	time_t		unix_stamp;
1123 
1124 	unix_stamp = ntp_stamp - JAN_1970;
1125 	tm = gmtime(&unix_stamp);
1126 	if (NULL != tm)
1127 		snprintf(str, sizeof(str),
1128 			 "%04d%02d%02d%02d%02d",
1129 			 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1130 			 tm->tm_hour, tm->tm_min);
1131 	else
1132 		strcpy(str, "gmtime() error");
1133 
1134 	return str;
1135 }
1136