1/*
2 * ntp_util.c - stuff I didn't have any other place for
3 */
4#ifdef HAVE_CONFIG_H
5# include <config.h>
6#endif
7
8#include "ntpd.h"
9#include "ntp_unixtime.h"
10#include "ntp_filegen.h"
11#include "ntp_if.h"
12#include "ntp_stdlib.h"
13#include "ntp_assert.h"
14#include "ntp_calendar.h"
15#include "ntp_leapsec.h"
16#include "lib_strbuf.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#ifdef HAVE_UNISTD_H
25# include <unistd.h>
26#endif
27#include <sys/stat.h>
28
29#ifdef HAVE_IEEEFP_H
30# include <ieeefp.h>
31#endif
32#ifdef HAVE_MATH_H
33# include <math.h>
34#endif
35
36#if defined(VMS)
37# include <descrip.h>
38#endif /* VMS */
39
40/*
41 * Defines used by the leapseconds stuff
42 */
43#define	MAX_TAI	100			/* max TAI offset (s) */
44#define	L_DAY	86400UL			/* seconds per day */
45#define	L_YEAR	(L_DAY * 365)		/* days per year */
46#define	L_LYEAR	(L_YEAR + L_DAY)	/* days per leap year */
47#define	L_4YEAR	(L_LYEAR + 3 * L_YEAR)	/* days per leap cycle */
48#define	L_CENT	(L_4YEAR * 25)		/* days per century */
49
50/*
51 * This contains odds and ends, including the hourly stats, various
52 * configuration items, leapseconds stuff, etc.
53 */
54/*
55 * File names
56 */
57static	char *key_file_name;		/* keys file name */
58static char	  *leapfile_name;		/* leapseconds file name */
59static struct stat leapfile_stat;	/* leapseconds file stat() buffer */
60static int /*BOOL*/have_leapfile = FALSE;
61static int /*BOOL*/chck_leaphash = TRUE;
62char	*stats_drift_file;		/* frequency file name */
63static	char *stats_temp_file;		/* temp frequency file name */
64static double wander_resid;		/* last frequency update */
65double	wander_threshold = 1e-7;	/* initial frequency threshold */
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
79char statsdir[MAXFILENAME] = NTP_VAR;
80static FILEGEN peerstats;
81static FILEGEN loopstats;
82static FILEGEN clockstats;
83static FILEGEN rawstats;
84static FILEGEN sysstats;
85static FILEGEN protostats;
86static FILEGEN cryptostats;
87static FILEGEN timingstats;
88
89/*
90 * This controls whether stats are written to the fileset. Provided
91 * so that ntpdc can turn off stats when the file system fills up.
92 */
93int stats_control;
94
95/*
96 * Last frequency written to file.
97 */
98static double prev_drift_comp;		/* last frequency update */
99
100/*
101 * Function prototypes
102 */
103static	void	record_sys_stats(void);
104	void	ntpd_time_stepped(void);
105static  void	check_leap_expiration(int, uint32_t, const time_t*);
106
107/*
108 * Prototypes
109 */
110#ifdef DEBUG
111void	uninit_util(void);
112#endif
113
114/*
115 * uninit_util - free memory allocated by init_util
116 */
117#ifdef DEBUG
118void
119uninit_util(void)
120{
121#if defined(_MSC_VER) && defined (_DEBUG)
122	_CrtCheckMemory();
123#endif
124	if (stats_drift_file) {
125		free(stats_drift_file);
126		free(stats_temp_file);
127		stats_drift_file = NULL;
128		stats_temp_file = NULL;
129	}
130	if (key_file_name) {
131		free(key_file_name);
132		key_file_name = NULL;
133	}
134	filegen_unregister("peerstats");
135	filegen_unregister("loopstats");
136	filegen_unregister("clockstats");
137	filegen_unregister("rawstats");
138	filegen_unregister("sysstats");
139	filegen_unregister("protostats");
140#ifdef AUTOKEY
141	filegen_unregister("cryptostats");
142#endif	/* AUTOKEY */
143#ifdef DEBUG_TIMING
144	filegen_unregister("timingstats");
145#endif	/* DEBUG_TIMING */
146
147#if defined(_MSC_VER) && defined (_DEBUG)
148	_CrtCheckMemory();
149#endif
150}
151#endif /* DEBUG */
152
153
154/*
155 * init_util - initialize the util module of ntpd
156 */
157void
158init_util(void)
159{
160	filegen_register(statsdir, "peerstats",	  &peerstats);
161	filegen_register(statsdir, "loopstats",	  &loopstats);
162	filegen_register(statsdir, "clockstats",  &clockstats);
163	filegen_register(statsdir, "rawstats",	  &rawstats);
164	filegen_register(statsdir, "sysstats",	  &sysstats);
165	filegen_register(statsdir, "protostats",  &protostats);
166	filegen_register(statsdir, "cryptostats", &cryptostats);
167	filegen_register(statsdir, "timingstats", &timingstats);
168	/*
169	 * register with libntp ntp_set_tod() to call us back
170	 * when time is stepped.
171	 */
172	step_callback = &ntpd_time_stepped;
173#ifdef DEBUG
174	atexit(&uninit_util);
175#endif /* DEBUG */
176}
177
178
179/*
180 * hourly_stats - print some interesting stats
181 */
182void
183write_stats(void)
184{
185	FILE	*fp;
186#ifdef DOSYNCTODR
187	struct timeval tv;
188#if !defined(VMS)
189	int	prio_set;
190#endif
191#ifdef HAVE_GETCLOCK
192	struct timespec ts;
193#endif
194	int	o_prio;
195
196	/*
197	 * Sometimes having a Sun can be a drag.
198	 *
199	 * The kernel variable dosynctodr controls whether the system's
200	 * soft clock is kept in sync with the battery clock. If it
201	 * is zero, then the soft clock is not synced, and the battery
202	 * clock is simply left to rot. That means that when the system
203	 * reboots, the battery clock (which has probably gone wacky)
204	 * sets the soft clock. That means ntpd starts off with a very
205	 * confused idea of what time it is. It then takes a large
206	 * amount of time to figure out just how wacky the battery clock
207	 * has made things drift, etc, etc. The solution is to make the
208	 * battery clock sync up to system time. The way to do THAT is
209	 * to simply set the time of day to the current time of day, but
210	 * as quickly as possible. This may, or may not be a sensible
211	 * thing to do.
212	 *
213	 * CAVEAT: settimeofday() steps the sun clock by about 800 us,
214	 *	   so setting DOSYNCTODR seems a bad idea in the
215	 *	   case of us resolution
216	 */
217
218#if !defined(VMS)
219	/*
220	 * (prr) getpriority returns -1 on error, but -1 is also a valid
221	 * return value (!), so instead we have to zero errno before the
222	 * call and check it for non-zero afterwards.
223	 */
224	errno = 0;
225	prio_set = 0;
226	o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */
227
228	/*
229	 * (prr) if getpriority succeeded, call setpriority to raise
230	 * scheduling priority as high as possible.  If that succeeds
231	 * as well, set the prio_set flag so we remember to reset
232	 * priority to its previous value below.  Note that on Solaris
233	 * 2.6 (and beyond?), both getpriority and setpriority will fail
234	 * with ESRCH, because sched_setscheduler (called from main) put
235	 * us in the real-time scheduling class which setpriority
236	 * doesn't know about. Being in the real-time class is better
237	 * than anything setpriority can do, anyhow, so this error is
238	 * silently ignored.
239	 */
240	if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
241		prio_set = 1;	/* overdrive */
242#endif /* VMS */
243#ifdef HAVE_GETCLOCK
244	(void) getclock(TIMEOFDAY, &ts);
245	tv.tv_sec = ts.tv_sec;
246	tv.tv_usec = ts.tv_nsec / 1000;
247#else /*  not HAVE_GETCLOCK */
248	GETTIMEOFDAY(&tv,(struct timezone *)NULL);
249#endif /* not HAVE_GETCLOCK */
250	if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0)
251		msyslog(LOG_ERR, "can't sync battery time: %m");
252#if !defined(VMS)
253	if (prio_set)
254		setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */
255#endif /* VMS */
256#endif /* DOSYNCTODR */
257	record_sys_stats();
258	if (stats_drift_file != 0) {
259
260		/*
261		 * When the frequency file is written, initialize the
262		 * prev_drift_comp and wander_resid. Thereafter,
263		 * reduce the wander_resid by half each hour. When
264		 * the difference between the prev_drift_comp and
265		 * drift_comp is less than the wander_resid, update
266		 * the frequncy file. This minimizes the file writes to
267		 * nonvolaile storage.
268		 */
269#ifdef DEBUG
270		if (debug)
271			printf("write_stats: frequency %.6lf thresh %.6lf, freq %.6lf\n",
272			    (prev_drift_comp - drift_comp) * 1e6, wander_resid *
273			    1e6, drift_comp * 1e6);
274#endif
275		if (fabs(prev_drift_comp - drift_comp) < wander_resid) {
276			wander_resid *= 0.5;
277			return;
278		}
279		prev_drift_comp = drift_comp;
280		wander_resid = wander_threshold;
281		if ((fp = fopen(stats_temp_file, "w")) == NULL) {
282			msyslog(LOG_ERR, "frequency file %s: %m",
283			    stats_temp_file);
284			return;
285		}
286		fprintf(fp, "%.3f\n", drift_comp * 1e6);
287		(void)fclose(fp);
288		/* atomic */
289#ifdef SYS_WINNT
290		if (_unlink(stats_drift_file)) /* rename semantics differ under NT */
291			msyslog(LOG_WARNING,
292				"Unable to remove prior drift file %s, %m",
293				stats_drift_file);
294#endif /* SYS_WINNT */
295
296#ifndef NO_RENAME
297		if (rename(stats_temp_file, stats_drift_file))
298			msyslog(LOG_WARNING,
299				"Unable to rename temp drift file %s to %s, %m",
300				stats_temp_file, stats_drift_file);
301#else
302		/* we have no rename NFS of ftp in use */
303		if ((fp = fopen(stats_drift_file, "w")) ==
304		    NULL) {
305			msyslog(LOG_ERR,
306			    "frequency file %s: %m",
307			    stats_drift_file);
308			return;
309		}
310#endif
311
312#if defined(VMS)
313		/* PURGE */
314		{
315			$DESCRIPTOR(oldvers,";-1");
316			struct dsc$descriptor driftdsc = {
317				strlen(stats_drift_file), 0, 0,
318				    stats_drift_file };
319			while(lib$delete_file(&oldvers,
320			    &driftdsc) & 1);
321		}
322#endif
323	}
324}
325
326
327/*
328 * stats_config - configure the stats operation
329 */
330void
331stats_config(
332	int item,
333	const char *invalue,	/* only one type so far */
334	int optflag
335	)
336{
337	FILE	*fp;
338	const char *value;
339	size_t	len;
340	double	old_drift;
341	l_fp	now;
342	time_t  ttnow;
343#ifndef VMS
344	const char temp_ext[] = ".TEMP";
345#else
346	const char temp_ext[] = "-TEMP";
347#endif
348
349	/*
350	 * Expand environment strings under Windows NT, since the
351	 * command interpreter doesn't do this, the program must.
352	 */
353#ifdef SYS_WINNT
354	char newvalue[MAX_PATH], parameter[MAX_PATH];
355
356	if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
357		switch (item) {
358		case STATS_FREQ_FILE:
359			strlcpy(parameter, "STATS_FREQ_FILE",
360				sizeof(parameter));
361			break;
362
363		case STATS_LEAP_FILE:
364			strlcpy(parameter, "STATS_LEAP_FILE",
365				sizeof(parameter));
366			break;
367
368		case STATS_STATSDIR:
369			strlcpy(parameter, "STATS_STATSDIR",
370				sizeof(parameter));
371			break;
372
373		case STATS_PID_FILE:
374			strlcpy(parameter, "STATS_PID_FILE",
375				sizeof(parameter));
376			break;
377
378		default:
379			strlcpy(parameter, "UNKNOWN",
380				sizeof(parameter));
381			break;
382		}
383		value = invalue;
384		msyslog(LOG_ERR,
385			"ExpandEnvironmentStrings(%s) failed: %m\n",
386			parameter);
387	} else {
388		value = newvalue;
389	}
390#else
391	value = invalue;
392#endif /* SYS_WINNT */
393
394	switch (item) {
395
396	/*
397	 * Open and read frequency file.
398	 */
399	case STATS_FREQ_FILE:
400		if (!value || (len = strlen(value)) == 0)
401			break;
402
403		stats_drift_file = erealloc(stats_drift_file, len + 1);
404		stats_temp_file = erealloc(stats_temp_file,
405		    len + sizeof(".TEMP"));
406		memcpy(stats_drift_file, value, (size_t)(len+1));
407		memcpy(stats_temp_file, value, (size_t)len);
408		memcpy(stats_temp_file + len, temp_ext, sizeof(temp_ext));
409
410		/*
411		 * Open drift file and read frequency. If the file is
412		 * missing or contains errors, tell the loop to reset.
413		 */
414		if ((fp = fopen(stats_drift_file, "r")) == NULL)
415			break;
416
417		if (fscanf(fp, "%lf", &old_drift) != 1) {
418			msyslog(LOG_ERR,
419				"format error frequency file %s",
420				stats_drift_file);
421			fclose(fp);
422			break;
423
424		}
425		fclose(fp);
426		loop_config(LOOP_FREQ, old_drift);
427		prev_drift_comp = drift_comp;
428		break;
429
430	/*
431	 * Specify statistics directory.
432	 */
433	case STATS_STATSDIR:
434
435		/* - 1 since value may be missing the DIR_SEP. */
436		if (strlen(value) >= sizeof(statsdir) - 1) {
437			msyslog(LOG_ERR,
438			    "statsdir too long (>%d, sigh)",
439			    (int)sizeof(statsdir) - 2);
440		} else {
441			int add_dir_sep;
442			size_t value_l;
443
444			/* Add a DIR_SEP unless we already have one. */
445			value_l = strlen(value);
446			if (0 == value_l)
447				add_dir_sep = FALSE;
448			else
449				add_dir_sep = (DIR_SEP !=
450				    value[value_l - 1]);
451
452			if (add_dir_sep)
453				snprintf(statsdir, sizeof(statsdir),
454				    "%s%c", value, DIR_SEP);
455			else
456				snprintf(statsdir, sizeof(statsdir),
457				    "%s", value);
458			filegen_statsdir();
459		}
460		break;
461
462	/*
463	 * Open pid file.
464	 */
465	case STATS_PID_FILE:
466		if ((fp = fopen(value, "w")) == NULL) {
467			msyslog(LOG_ERR, "pid file %s: %m",
468			    value);
469			break;
470		}
471		fprintf(fp, "%d", (int)getpid());
472		fclose(fp);
473		break;
474
475	/*
476	 * Read leapseconds file.
477	 *
478	 * Note: Currently a leap file without SHA1 signature is
479	 * accepted, but if there is a signature line, the signature
480	 * must be valid or the file is rejected.
481	 */
482	case STATS_LEAP_FILE:
483		if (!value || (len = strlen(value)) == 0)
484			break;
485
486		leapfile_name = erealloc(leapfile_name, len + 1);
487		memcpy(leapfile_name, value, len + 1);
488		chck_leaphash = optflag;
489
490		if (leapsec_load_file(
491			    leapfile_name, &leapfile_stat,
492			    TRUE, TRUE, chck_leaphash))
493		{
494			leap_signature_t lsig;
495
496			get_systime(&now);
497			time(&ttnow);
498			leapsec_getsig(&lsig);
499			mprintf_event(EVNT_TAI, NULL,
500				      "%d leap %s %s %s",
501				      lsig.taiof,
502				      fstostr(lsig.ttime),
503				      leapsec_expired(now.l_ui, NULL)
504					  ? "expired"
505					  : "expires",
506				      fstostr(lsig.etime));
507
508			have_leapfile = TRUE;
509
510			/* force an immediate daily expiration check of
511			 * the leap seconds table
512			 */
513			check_leap_expiration(TRUE, now.l_ui, &ttnow);
514		}
515		break;
516
517	default:
518		/* oh well */
519		break;
520	}
521}
522
523
524/*
525 * record_peer_stats - write peer statistics to file
526 *
527 * file format:
528 * day (MJD)
529 * time (s past UTC midnight)
530 * IP address
531 * status word (hex)
532 * offset
533 * delay
534 * dispersion
535 * jitter
536*/
537void
538record_peer_stats(
539	sockaddr_u *addr,
540	int	status,
541	double	offset,		/* offset */
542	double	delay,		/* delay */
543	double	dispersion,	/* dispersion */
544	double	jitter		/* jitter */
545	)
546{
547	l_fp	now;
548	u_long	day;
549
550	if (!stats_control)
551		return;
552
553	get_systime(&now);
554	filegen_setup(&peerstats, now.l_ui);
555	day = now.l_ui / 86400 + MJD_1900;
556	now.l_ui %= 86400;
557	if (peerstats.fp != NULL) {
558		fprintf(peerstats.fp,
559		    "%lu %s %s %x %.9f %.9f %.9f %.9f\n", day,
560		    ulfptoa(&now, 3), stoa(addr), status, offset,
561		    delay, dispersion, jitter);
562		fflush(peerstats.fp);
563	}
564}
565
566
567/*
568 * record_loop_stats - write loop filter statistics to file
569 *
570 * file format:
571 * day (MJD)
572 * time (s past midnight)
573 * offset
574 * frequency (PPM)
575 * jitter
576 * wnder (PPM)
577 * time constant (log2)
578 */
579void
580record_loop_stats(
581	double	offset,		/* offset */
582	double	freq,		/* frequency (PPM) */
583	double	jitter,		/* jitter */
584	double	wander,		/* wander (PPM) */
585	int spoll
586	)
587{
588	l_fp	now;
589	u_long	day;
590
591	if (!stats_control)
592		return;
593
594	get_systime(&now);
595	filegen_setup(&loopstats, now.l_ui);
596	day = now.l_ui / 86400 + MJD_1900;
597	now.l_ui %= 86400;
598	if (loopstats.fp != NULL) {
599		fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n",
600		    day, ulfptoa(&now, 3), offset, freq * 1e6, jitter,
601		    wander * 1e6, spoll);
602		fflush(loopstats.fp);
603	}
604}
605
606
607/*
608 * record_clock_stats - write clock statistics to file
609 *
610 * file format:
611 * day (MJD)
612 * time (s past midnight)
613 * IP address
614 * text message
615 */
616void
617record_clock_stats(
618	sockaddr_u *addr,
619	const char *text	/* timecode string */
620	)
621{
622	l_fp	now;
623	u_long	day;
624
625	if (!stats_control)
626		return;
627
628	get_systime(&now);
629	filegen_setup(&clockstats, now.l_ui);
630	day = now.l_ui / 86400 + MJD_1900;
631	now.l_ui %= 86400;
632	if (clockstats.fp != NULL) {
633		fprintf(clockstats.fp, "%lu %s %s %s\n", day,
634		    ulfptoa(&now, 3), stoa(addr), text);
635		fflush(clockstats.fp);
636	}
637}
638
639
640/*
641 * mprintf_clock_stats - write clock statistics to file with
642 *			msnprintf-style formatting.
643 */
644int
645mprintf_clock_stats(
646	sockaddr_u *addr,
647	const char *fmt,
648	...
649	)
650{
651	va_list	ap;
652	int	rc;
653	char	msg[512];
654
655	va_start(ap, fmt);
656	rc = mvsnprintf(msg, sizeof(msg), fmt, ap);
657	va_end(ap);
658	if (stats_control)
659		record_clock_stats(addr, msg);
660
661	return rc;
662}
663
664/*
665 * record_raw_stats - write raw timestamps to file
666 *
667 * file format
668 * day (MJD)
669 * time (s past midnight)
670 * peer ip address
671 * IP address
672 * t1 t2 t3 t4 timestamps
673 * leap, version, mode, stratum, ppoll, precision, root delay, root dispersion, REFID
674 * length and hex dump of any EFs and any legacy MAC.
675 */
676void
677record_raw_stats(
678	sockaddr_u *srcadr,
679	sockaddr_u *dstadr,
680	l_fp	*t1,		/* originate timestamp */
681	l_fp	*t2,		/* receive timestamp */
682	l_fp	*t3,		/* transmit timestamp */
683	l_fp	*t4,		/* destination timestamp */
684	int	leap,
685	int	version,
686	int	mode,
687	int	stratum,
688	int	ppoll,
689	int	precision,
690	double	root_delay,	/* seconds */
691	double	root_dispersion,/* seconds */
692	u_int32	refid,
693	int	len,
694	u_char	*extra
695	)
696{
697	l_fp	now;
698	u_long	day;
699
700	if (!stats_control)
701		return;
702
703	get_systime(&now);
704	filegen_setup(&rawstats, now.l_ui);
705	day = now.l_ui / 86400 + MJD_1900;
706	now.l_ui %= 86400;
707	if (rawstats.fp != NULL) {
708		fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s %d %d %d %d %d %d %.6f %.6f %s",
709		    day, ulfptoa(&now, 3),
710		    srcadr ? stoa(srcadr) : "-",
711		    dstadr ? stoa(dstadr) : "-",
712		    ulfptoa(t1, 9), ulfptoa(t2, 9),
713		    ulfptoa(t3, 9), ulfptoa(t4, 9),
714		    leap, version, mode, stratum, ppoll, precision,
715		    root_delay, root_dispersion, refid_str(refid, stratum));
716		if (len > 0) {
717			int i;
718
719			fprintf(rawstats.fp, " %d: ", len);
720			for (i = 0; i < len; ++i) {
721				fprintf(rawstats.fp, "%02x", extra[i]);
722			}
723		}
724		fprintf(rawstats.fp, "\n");
725		fflush(rawstats.fp);
726	}
727}
728
729
730/*
731 * record_sys_stats - write system statistics to file
732 *
733 * file format
734 * day (MJD)
735 * time (s past midnight)
736 * time since reset
737 * packets recieved
738 * packets for this host
739 * current version
740 * old version
741 * access denied
742 * bad length or format
743 * bad authentication
744 * declined
745 * rate exceeded
746 * KoD sent
747 */
748void
749record_sys_stats(void)
750{
751	l_fp	now;
752	u_long	day;
753
754	if (!stats_control)
755		return;
756
757	get_systime(&now);
758	filegen_setup(&sysstats, now.l_ui);
759	day = now.l_ui / 86400 + MJD_1900;
760	now.l_ui %= 86400;
761	if (sysstats.fp != NULL) {
762		fprintf(sysstats.fp,
763		    "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
764		    day, ulfptoa(&now, 3), current_time - sys_stattime,
765		    sys_received, sys_processed, sys_newversion,
766		    sys_oldversion, sys_restricted, sys_badlength,
767		    sys_badauth, sys_declined, sys_limitrejected,
768		    sys_kodsent);
769		fflush(sysstats.fp);
770		proto_clr_stats();
771	}
772}
773
774
775/*
776 * record_proto_stats - write system statistics to file
777 *
778 * file format
779 * day (MJD)
780 * time (s past midnight)
781 * text message
782 */
783void
784record_proto_stats(
785	char	*str		/* text string */
786	)
787{
788	l_fp	now;
789	u_long	day;
790
791	if (!stats_control)
792		return;
793
794	get_systime(&now);
795	filegen_setup(&protostats, now.l_ui);
796	day = now.l_ui / 86400 + MJD_1900;
797	now.l_ui %= 86400;
798	if (protostats.fp != NULL) {
799		fprintf(protostats.fp, "%lu %s %s\n", day,
800		    ulfptoa(&now, 3), str);
801		fflush(protostats.fp);
802	}
803}
804
805
806#ifdef AUTOKEY
807/*
808 * record_crypto_stats - write crypto statistics to file
809 *
810 * file format:
811 * day (mjd)
812 * time (s past midnight)
813 * peer ip address
814 * text message
815 */
816void
817record_crypto_stats(
818	sockaddr_u *addr,
819	const char *text	/* text message */
820	)
821{
822	l_fp	now;
823	u_long	day;
824
825	if (!stats_control)
826		return;
827
828	get_systime(&now);
829	filegen_setup(&cryptostats, now.l_ui);
830	day = now.l_ui / 86400 + MJD_1900;
831	now.l_ui %= 86400;
832	if (cryptostats.fp != NULL) {
833		if (addr == NULL)
834			fprintf(cryptostats.fp, "%lu %s 0.0.0.0 %s\n",
835			    day, ulfptoa(&now, 3), text);
836		else
837			fprintf(cryptostats.fp, "%lu %s %s %s\n",
838			    day, ulfptoa(&now, 3), stoa(addr), text);
839		fflush(cryptostats.fp);
840	}
841}
842#endif	/* AUTOKEY */
843
844
845#ifdef DEBUG_TIMING
846/*
847 * record_timing_stats - write timing statistics to file
848 *
849 * file format:
850 * day (mjd)
851 * time (s past midnight)
852 * text message
853 */
854void
855record_timing_stats(
856	const char *text	/* text message */
857	)
858{
859	static unsigned int flshcnt;
860	l_fp	now;
861	u_long	day;
862
863	if (!stats_control)
864		return;
865
866	get_systime(&now);
867	filegen_setup(&timingstats, now.l_ui);
868	day = now.l_ui / 86400 + MJD_1900;
869	now.l_ui %= 86400;
870	if (timingstats.fp != NULL) {
871		fprintf(timingstats.fp, "%lu %s %s\n", day, lfptoa(&now,
872		    3), text);
873		if (++flshcnt % 100 == 0)
874			fflush(timingstats.fp);
875	}
876}
877#endif
878
879
880/*
881 * check_leap_file - See if the leapseconds file has been updated.
882 *
883 * Returns: n/a
884 *
885 * Note: This loads a new leapfile on the fly. Currently a leap file
886 * without SHA1 signature is accepted, but if there is a signature line,
887 * the signature must be valid or the file is rejected.
888 */
889void
890check_leap_file(
891	int           is_daily_check,
892	uint32_t      ntptime       ,
893	const time_t *systime
894	)
895{
896	/* just do nothing if there is no leap file */
897	if ( ! (leapfile_name && *leapfile_name))
898		return;
899
900	/* try to load leapfile, force it if no leapfile loaded yet */
901	if (leapsec_load_file(
902		    leapfile_name, &leapfile_stat,
903		    !have_leapfile, is_daily_check, chck_leaphash))
904		have_leapfile = TRUE;
905	else if (!have_leapfile)
906		return;
907
908	check_leap_expiration(is_daily_check, ntptime, systime);
909}
910
911/*
912 * check expiration of a loaded leap table
913 */
914static void
915check_leap_expiration(
916	int           is_daily_check,
917	uint32_t      ntptime       ,
918	const time_t *systime
919	)
920{
921	static const char * const logPrefix = "leapsecond file";
922	int  rc;
923
924	/* test the expiration of the leap data and log with proper
925	 * level and frequency (once/hour or once/day, depending on the
926	 * state.
927	 */
928	rc = leapsec_daystolive(ntptime, systime);
929	if (rc == 0) {
930		msyslog(LOG_WARNING,
931			"%s ('%s'): will expire in less than one day",
932			logPrefix, leapfile_name);
933	} else if (is_daily_check && rc < 28) {
934		if (rc < 0)
935			msyslog(LOG_ERR,
936				"%s ('%s'): expired %d day%s ago",
937				logPrefix, leapfile_name, -rc, (rc == -1 ? "" : "s"));
938		else
939			msyslog(LOG_WARNING,
940				"%s ('%s'): will expire in less than %d days",
941				logPrefix, leapfile_name, 1+rc);
942	}
943}
944
945
946/*
947 * getauthkeys - read the authentication keys from the specified file
948 */
949void
950getauthkeys(
951	const char *keyfile
952	)
953{
954	size_t len;
955
956	len = strlen(keyfile);
957	if (!len)
958		return;
959
960#ifndef SYS_WINNT
961	key_file_name = erealloc(key_file_name, len + 1);
962	memcpy(key_file_name, keyfile, len + 1);
963#else
964	key_file_name = erealloc(key_file_name, _MAX_PATH);
965	if (len + 1 > _MAX_PATH)
966		return;
967	if (!ExpandEnvironmentStrings(keyfile, key_file_name,
968				      _MAX_PATH)) {
969		msyslog(LOG_ERR,
970			"ExpandEnvironmentStrings(KEY_FILE) failed: %m");
971		strlcpy(key_file_name, keyfile, _MAX_PATH);
972	}
973	key_file_name = erealloc(key_file_name,
974				 1 + strlen(key_file_name));
975#endif /* SYS_WINNT */
976
977	authreadkeys(key_file_name);
978}
979
980
981/*
982 * rereadkeys - read the authentication key file over again.
983 */
984void
985rereadkeys(void)
986{
987	if (NULL != key_file_name)
988		authreadkeys(key_file_name);
989}
990
991
992#if notyet
993/*
994 * ntp_exit - document explicitly that ntpd has exited
995 */
996void
997ntp_exit(int retval)
998{
999	msyslog(LOG_ERR, "EXITING with return code %d", retval);
1000	exit(retval);
1001}
1002#endif
1003
1004/*
1005 * fstostr - prettyprint NTP seconds
1006 */
1007char * fstostr(
1008	time_t	ntp_stamp
1009	)
1010{
1011	char *		buf;
1012	struct calendar tm;
1013
1014	LIB_GETBUF(buf);
1015	if (ntpcal_ntp_to_date(&tm, (u_int32)ntp_stamp, NULL) < 0)
1016		snprintf(buf, LIB_BUFLENGTH, "ntpcal_ntp_to_date: %ld: range error",
1017			 (long)ntp_stamp);
1018	else
1019		snprintf(buf, LIB_BUFLENGTH, "%04d%02d%02d%02d%02d",
1020			 tm.year, tm.month, tm.monthday,
1021			 tm.hour, tm.minute);
1022	return buf;
1023}
1024
1025
1026/*
1027 * ntpd_time_stepped is called back by step_systime(), allowing ntpd
1028 * to do any one-time processing necessitated by the step.
1029 */
1030void
1031ntpd_time_stepped(void)
1032{
1033	u_int saved_mon_enabled;
1034
1035	/*
1036	 * flush the monitor MRU list which contains l_fp timestamps
1037	 * which should not be compared across the step.
1038	 */
1039	if (MON_OFF != mon_enabled) {
1040		saved_mon_enabled = mon_enabled;
1041		mon_stop(MON_OFF);
1042		mon_start(saved_mon_enabled);
1043	}
1044
1045	/* inform interpolating Windows code to allow time to go back */
1046#ifdef SYS_WINNT
1047	win_time_stepped();
1048#endif
1049}
1050