154359Sroberto/*
254359Sroberto * ntp_util.c - stuff I didn't have any other place for
354359Sroberto */
454359Sroberto#ifdef HAVE_CONFIG_H
582498Sroberto# include <config.h>
654359Sroberto#endif
754359Sroberto
854359Sroberto#include "ntpd.h"
954359Sroberto#include "ntp_unixtime.h"
1054359Sroberto#include "ntp_filegen.h"
1154359Sroberto#include "ntp_if.h"
1254359Sroberto#include "ntp_stdlib.h"
13285612Sdelphij#include "ntp_assert.h"
14285612Sdelphij#include "ntp_calendar.h"
15285612Sdelphij#include "ntp_leapsec.h"
16285612Sdelphij#include "lib_strbuf.h"
1754359Sroberto
1882498Sroberto#include <stdio.h>
1982498Sroberto#include <ctype.h>
2082498Sroberto#include <sys/types.h>
2182498Sroberto#ifdef HAVE_SYS_IOCTL_H
2282498Sroberto# include <sys/ioctl.h>
2382498Sroberto#endif
24285612Sdelphij#ifdef HAVE_UNISTD_H
25285612Sdelphij# include <unistd.h>
26285612Sdelphij#endif
27285612Sdelphij#include <sys/stat.h>
2882498Sroberto
2982498Sroberto#ifdef HAVE_IEEEFP_H
3082498Sroberto# include <ieeefp.h>
3182498Sroberto#endif
3282498Sroberto#ifdef HAVE_MATH_H
3382498Sroberto# include <math.h>
3482498Sroberto#endif
3582498Sroberto
3654359Sroberto#if defined(VMS)
37182007Sroberto# include <descrip.h>
3854359Sroberto#endif /* VMS */
3954359Sroberto
4054359Sroberto/*
41285612Sdelphij * Defines used by the leapseconds stuff
4254359Sroberto */
43285612Sdelphij#define	MAX_TAI	100			/* max TAI offset (s) */
44285612Sdelphij#define	L_DAY	86400UL			/* seconds per day */
45285612Sdelphij#define	L_YEAR	(L_DAY * 365)		/* days per year */
46285612Sdelphij#define	L_LYEAR	(L_YEAR + L_DAY)	/* days per leap year */
47285612Sdelphij#define	L_4YEAR	(L_LYEAR + 3 * L_YEAR)	/* days per leap cycle */
48285612Sdelphij#define	L_CENT	(L_4YEAR * 25)		/* days per century */
49285612Sdelphij
5054359Sroberto/*
51285612Sdelphij * This contains odds and ends, including the hourly stats, various
52285612Sdelphij * configuration items, leapseconds stuff, etc.
5354359Sroberto */
5454359Sroberto/*
55285612Sdelphij * File names
5654359Sroberto */
57285612Sdelphijstatic	char *key_file_name;		/* keys file name */
58285612Sdelphijstatic char	  *leapfile_name;		/* leapseconds file name */
59285612Sdelphijstatic struct stat leapfile_stat;	/* leapseconds file stat() buffer */
60285612Sdelphijstatic int /*BOOL*/have_leapfile = FALSE;
61285612Sdelphijchar	*stats_drift_file;		/* frequency file name */
62285612Sdelphijstatic	char *stats_temp_file;		/* temp frequency file name */
63285612Sdelphijstatic double wander_resid;		/* last frequency update */
64285612Sdelphijdouble	wander_threshold = 1e-7;	/* initial frequency threshold */
6554359Sroberto
6654359Sroberto/*
6754359Sroberto * Statistics file stuff
6854359Sroberto */
6954359Sroberto#ifndef NTP_VAR
70182007Sroberto# ifndef SYS_WINNT
71182007Sroberto#  define NTP_VAR "/var/NTP/"		/* NOTE the trailing '/' */
72182007Sroberto# else
73285612Sdelphij#  define NTP_VAR "c:\\var\\ntp\\"	/* NOTE the trailing '\\' */
74182007Sroberto# endif /* SYS_WINNT */
7554359Sroberto#endif
7654359Sroberto
7754359Sroberto
78285612Sdelphijchar statsdir[MAXFILENAME] = NTP_VAR;
7954359Srobertostatic FILEGEN peerstats;
8054359Srobertostatic FILEGEN loopstats;
8154359Srobertostatic FILEGEN clockstats;
8254359Srobertostatic FILEGEN rawstats;
83132451Srobertostatic FILEGEN sysstats;
84285612Sdelphijstatic FILEGEN protostats;
85285612Sdelphijstatic FILEGEN cryptostats;
86182007Srobertostatic FILEGEN timingstats;
8754359Sroberto
8854359Sroberto/*
8954359Sroberto * This controls whether stats are written to the fileset. Provided
9054359Sroberto * so that ntpdc can turn off stats when the file system fills up.
9154359Sroberto */
9254359Srobertoint stats_control;
9354359Sroberto
9454359Sroberto/*
95285612Sdelphij * Last frequency written to file.
96182007Sroberto */
97285612Sdelphijstatic double prev_drift_comp;		/* last frequency update */
98182007Sroberto
99182007Sroberto/*
100285612Sdelphij * Function prototypes
10154359Sroberto */
102285612Sdelphijstatic	void	record_sys_stats(void);
103285612Sdelphij	void	ntpd_time_stepped(void);
104285612Sdelphijstatic  void	check_leap_expiration(int, uint32_t, const time_t*);
105285612Sdelphij
106285612Sdelphij/*
107285612Sdelphij * Prototypes
108285612Sdelphij */
109285612Sdelphij#ifdef DEBUG
110285612Sdelphijvoid	uninit_util(void);
111285612Sdelphij#endif
112285612Sdelphij
113285612Sdelphij/*
114285612Sdelphij * uninit_util - free memory allocated by init_util
115285612Sdelphij */
116285612Sdelphij#ifdef DEBUG
11754359Srobertovoid
118285612Sdelphijuninit_util(void)
11954359Sroberto{
120285612Sdelphij#if defined(_MSC_VER) && defined (_DEBUG)
121285612Sdelphij	_CrtCheckMemory();
122285612Sdelphij#endif
123285612Sdelphij	if (stats_drift_file) {
124285612Sdelphij		free(stats_drift_file);
125285612Sdelphij		free(stats_temp_file);
126285612Sdelphij		stats_drift_file = NULL;
127285612Sdelphij		stats_temp_file = NULL;
128285612Sdelphij	}
129285612Sdelphij	if (key_file_name) {
130285612Sdelphij		free(key_file_name);
131285612Sdelphij		key_file_name = NULL;
132285612Sdelphij	}
133285612Sdelphij	filegen_unregister("peerstats");
134285612Sdelphij	filegen_unregister("loopstats");
135285612Sdelphij	filegen_unregister("clockstats");
136285612Sdelphij	filegen_unregister("rawstats");
137285612Sdelphij	filegen_unregister("sysstats");
138285612Sdelphij	filegen_unregister("protostats");
139285612Sdelphij#ifdef AUTOKEY
140285612Sdelphij	filegen_unregister("cryptostats");
141285612Sdelphij#endif	/* AUTOKEY */
142285612Sdelphij#ifdef DEBUG_TIMING
143285612Sdelphij	filegen_unregister("timingstats");
144285612Sdelphij#endif	/* DEBUG_TIMING */
14554359Sroberto
146285612Sdelphij#if defined(_MSC_VER) && defined (_DEBUG)
147285612Sdelphij	_CrtCheckMemory();
148285612Sdelphij#endif
149285612Sdelphij}
150285612Sdelphij#endif /* DEBUG */
151132451Sroberto
15254359Sroberto
153285612Sdelphij/*
154285612Sdelphij * init_util - initialize the util module of ntpd
155285612Sdelphij */
156285612Sdelphijvoid
157285612Sdelphijinit_util(void)
158285612Sdelphij{
159285612Sdelphij	filegen_register(statsdir, "peerstats",	  &peerstats);
160285612Sdelphij	filegen_register(statsdir, "loopstats",	  &loopstats);
161285612Sdelphij	filegen_register(statsdir, "clockstats",  &clockstats);
162285612Sdelphij	filegen_register(statsdir, "rawstats",	  &rawstats);
163285612Sdelphij	filegen_register(statsdir, "sysstats",	  &sysstats);
164285612Sdelphij	filegen_register(statsdir, "protostats",  &protostats);
165285612Sdelphij	filegen_register(statsdir, "cryptostats", &cryptostats);
166285612Sdelphij	filegen_register(statsdir, "timingstats", &timingstats);
167285612Sdelphij	/*
168285612Sdelphij	 * register with libntp ntp_set_tod() to call us back
169285612Sdelphij	 * when time is stepped.
170285612Sdelphij	 */
171285612Sdelphij	step_callback = &ntpd_time_stepped;
172285612Sdelphij#ifdef DEBUG
173285612Sdelphij	atexit(&uninit_util);
174285612Sdelphij#endif /* DEBUG */
17554359Sroberto}
17654359Sroberto
17754359Sroberto
17854359Sroberto/*
17954359Sroberto * hourly_stats - print some interesting stats
18054359Sroberto */
18154359Srobertovoid
182182007Srobertowrite_stats(void)
18354359Sroberto{
184285612Sdelphij	FILE	*fp;
18554359Sroberto#ifdef DOSYNCTODR
18654359Sroberto	struct timeval tv;
18754359Sroberto#if !defined(VMS)
188285612Sdelphij	int	prio_set;
18954359Sroberto#endif
19054359Sroberto#ifdef HAVE_GETCLOCK
191285612Sdelphij	struct timespec ts;
19254359Sroberto#endif
193285612Sdelphij	int	o_prio;
19454359Sroberto
19554359Sroberto	/*
19654359Sroberto	 * Sometimes having a Sun can be a drag.
19754359Sroberto	 *
19854359Sroberto	 * The kernel variable dosynctodr controls whether the system's
19954359Sroberto	 * soft clock is kept in sync with the battery clock. If it
20054359Sroberto	 * is zero, then the soft clock is not synced, and the battery
20154359Sroberto	 * clock is simply left to rot. That means that when the system
20254359Sroberto	 * reboots, the battery clock (which has probably gone wacky)
20354359Sroberto	 * sets the soft clock. That means ntpd starts off with a very
20454359Sroberto	 * confused idea of what time it is. It then takes a large
20554359Sroberto	 * amount of time to figure out just how wacky the battery clock
20654359Sroberto	 * has made things drift, etc, etc. The solution is to make the
20754359Sroberto	 * battery clock sync up to system time. The way to do THAT is
20854359Sroberto	 * to simply set the time of day to the current time of day, but
20954359Sroberto	 * as quickly as possible. This may, or may not be a sensible
21054359Sroberto	 * thing to do.
21154359Sroberto	 *
21254359Sroberto	 * CAVEAT: settimeofday() steps the sun clock by about 800 us,
213285612Sdelphij	 *	   so setting DOSYNCTODR seems a bad idea in the
214285612Sdelphij	 *	   case of us resolution
21554359Sroberto	 */
21654359Sroberto
21754359Sroberto#if !defined(VMS)
218285612Sdelphij	/*
219285612Sdelphij	 * (prr) getpriority returns -1 on error, but -1 is also a valid
220132451Sroberto	 * return value (!), so instead we have to zero errno before the
221132451Sroberto	 * call and check it for non-zero afterwards.
22254359Sroberto	 */
22354359Sroberto	errno = 0;
22454359Sroberto	prio_set = 0;
22554359Sroberto	o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */
22654359Sroberto
227132451Sroberto	/*
228132451Sroberto	 * (prr) if getpriority succeeded, call setpriority to raise
22954359Sroberto	 * scheduling priority as high as possible.  If that succeeds
23054359Sroberto	 * as well, set the prio_set flag so we remember to reset
231132451Sroberto	 * priority to its previous value below.  Note that on Solaris
232132451Sroberto	 * 2.6 (and beyond?), both getpriority and setpriority will fail
233132451Sroberto	 * with ESRCH, because sched_setscheduler (called from main) put
234132451Sroberto	 * us in the real-time scheduling class which setpriority
235132451Sroberto	 * doesn't know about. Being in the real-time class is better
236132451Sroberto	 * than anything setpriority can do, anyhow, so this error is
237132451Sroberto	 * silently ignored.
23854359Sroberto	 */
23954359Sroberto	if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
240132451Sroberto		prio_set = 1;	/* overdrive */
24154359Sroberto#endif /* VMS */
24254359Sroberto#ifdef HAVE_GETCLOCK
243285612Sdelphij	(void) getclock(TIMEOFDAY, &ts);
244285612Sdelphij	tv.tv_sec = ts.tv_sec;
245285612Sdelphij	tv.tv_usec = ts.tv_nsec / 1000;
24654359Sroberto#else /*  not HAVE_GETCLOCK */
24754359Sroberto	GETTIMEOFDAY(&tv,(struct timezone *)NULL);
24854359Sroberto#endif /* not HAVE_GETCLOCK */
249285612Sdelphij	if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0)
25054359Sroberto		msyslog(LOG_ERR, "can't sync battery time: %m");
25154359Sroberto#if !defined(VMS)
25254359Sroberto	if (prio_set)
253132451Sroberto		setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */
25454359Sroberto#endif /* VMS */
25554359Sroberto#endif /* DOSYNCTODR */
256132451Sroberto	record_sys_stats();
25754359Sroberto	if (stats_drift_file != 0) {
258285612Sdelphij
259285612Sdelphij		/*
260285612Sdelphij		 * When the frequency file is written, initialize the
261285612Sdelphij		 * prev_drift_comp and wander_resid. Thereafter,
262285612Sdelphij		 * reduce the wander_resid by half each hour. When
263285612Sdelphij		 * the difference between the prev_drift_comp and
264285612Sdelphij		 * drift_comp is less than the wander_resid, update
265285612Sdelphij		 * the frequncy file. This minimizes the file writes to
266285612Sdelphij		 * nonvolaile storage.
267285612Sdelphij		 */
268285612Sdelphij#ifdef DEBUG
269285612Sdelphij		if (debug)
270285612Sdelphij			printf("write_stats: frequency %.6lf thresh %.6lf, freq %.6lf\n",
271285612Sdelphij			    (prev_drift_comp - drift_comp) * 1e6, wander_resid *
272285612Sdelphij			    1e6, drift_comp * 1e6);
273285612Sdelphij#endif
274285612Sdelphij		if (fabs(prev_drift_comp - drift_comp) < wander_resid) {
275285612Sdelphij			wander_resid *= 0.5;
276285612Sdelphij			return;
277285612Sdelphij		}
278285612Sdelphij		prev_drift_comp = drift_comp;
279285612Sdelphij		wander_resid = wander_threshold;
28054359Sroberto		if ((fp = fopen(stats_temp_file, "w")) == NULL) {
281285612Sdelphij			msyslog(LOG_ERR, "frequency file %s: %m",
28254359Sroberto			    stats_temp_file);
28354359Sroberto			return;
28454359Sroberto		}
28554359Sroberto		fprintf(fp, "%.3f\n", drift_comp * 1e6);
28654359Sroberto		(void)fclose(fp);
28754359Sroberto		/* atomic */
28854359Sroberto#ifdef SYS_WINNT
289285612Sdelphij		if (_unlink(stats_drift_file)) /* rename semantics differ under NT */
290285612Sdelphij			msyslog(LOG_WARNING,
291285612Sdelphij				"Unable to remove prior drift file %s, %m",
292285612Sdelphij				stats_drift_file);
29354359Sroberto#endif /* SYS_WINNT */
29454359Sroberto
29554359Sroberto#ifndef NO_RENAME
296285612Sdelphij		if (rename(stats_temp_file, stats_drift_file))
297285612Sdelphij			msyslog(LOG_WARNING,
298285612Sdelphij				"Unable to rename temp drift file %s to %s, %m",
299285612Sdelphij				stats_temp_file, stats_drift_file);
30054359Sroberto#else
301182007Sroberto		/* we have no rename NFS of ftp in use */
302285612Sdelphij		if ((fp = fopen(stats_drift_file, "w")) ==
303285612Sdelphij		    NULL) {
304285612Sdelphij			msyslog(LOG_ERR,
305285612Sdelphij			    "frequency file %s: %m",
30654359Sroberto			    stats_drift_file);
30754359Sroberto			return;
30854359Sroberto		}
30954359Sroberto#endif
31054359Sroberto
31154359Sroberto#if defined(VMS)
31254359Sroberto		/* PURGE */
31354359Sroberto		{
31454359Sroberto			$DESCRIPTOR(oldvers,";-1");
31554359Sroberto			struct dsc$descriptor driftdsc = {
316285612Sdelphij				strlen(stats_drift_file), 0, 0,
317285612Sdelphij				    stats_drift_file };
318285612Sdelphij			while(lib$delete_file(&oldvers,
319285612Sdelphij			    &driftdsc) & 1);
32054359Sroberto		}
32154359Sroberto#endif
32254359Sroberto	}
32354359Sroberto}
32454359Sroberto
32554359Sroberto
32654359Sroberto/*
32754359Sroberto * stats_config - configure the stats operation
32854359Sroberto */
32954359Srobertovoid
33054359Srobertostats_config(
33154359Sroberto	int item,
332182007Sroberto	const char *invalue	/* only one type so far */
33354359Sroberto	)
33454359Sroberto{
335285612Sdelphij	FILE	*fp;
336182007Sroberto	const char *value;
337293650Sglebius	size_t	len;
338285612Sdelphij	double	old_drift;
339285612Sdelphij	l_fp	now;
340285612Sdelphij	time_t  ttnow;
341285612Sdelphij#ifndef VMS
342285612Sdelphij	const char temp_ext[] = ".TEMP";
343285612Sdelphij#else
344285612Sdelphij	const char temp_ext[] = "-TEMP";
345285612Sdelphij#endif
34654359Sroberto
347132451Sroberto	/*
348132451Sroberto	 * Expand environment strings under Windows NT, since the
349132451Sroberto	 * command interpreter doesn't do this, the program must.
35054359Sroberto	 */
35154359Sroberto#ifdef SYS_WINNT
35254359Sroberto	char newvalue[MAX_PATH], parameter[MAX_PATH];
35354359Sroberto
354132451Sroberto	if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
355285612Sdelphij		switch (item) {
356285612Sdelphij		case STATS_FREQ_FILE:
357285612Sdelphij			strlcpy(parameter, "STATS_FREQ_FILE",
358285612Sdelphij				sizeof(parameter));
35954359Sroberto			break;
360285612Sdelphij
361285612Sdelphij		case STATS_LEAP_FILE:
362285612Sdelphij			strlcpy(parameter, "STATS_LEAP_FILE",
363285612Sdelphij				sizeof(parameter));
36454359Sroberto			break;
365285612Sdelphij
366285612Sdelphij		case STATS_STATSDIR:
367285612Sdelphij			strlcpy(parameter, "STATS_STATSDIR",
368285612Sdelphij				sizeof(parameter));
36954359Sroberto			break;
370285612Sdelphij
371285612Sdelphij		case STATS_PID_FILE:
372285612Sdelphij			strlcpy(parameter, "STATS_PID_FILE",
373285612Sdelphij				sizeof(parameter));
37454359Sroberto			break;
375285612Sdelphij
376285612Sdelphij		default:
377285612Sdelphij			strlcpy(parameter, "UNKNOWN",
378285612Sdelphij				sizeof(parameter));
379285612Sdelphij			break;
38054359Sroberto		}
38154359Sroberto		value = invalue;
38254359Sroberto		msyslog(LOG_ERR,
383285612Sdelphij			"ExpandEnvironmentStrings(%s) failed: %m\n",
384285612Sdelphij			parameter);
385132451Sroberto	} else {
386132451Sroberto		value = newvalue;
38754359Sroberto	}
388285612Sdelphij#else
38954359Sroberto	value = invalue;
39054359Sroberto#endif /* SYS_WINNT */
39154359Sroberto
392285612Sdelphij	switch (item) {
39354359Sroberto
394285612Sdelphij	/*
395285612Sdelphij	 * Open and read frequency file.
396285612Sdelphij	 */
397285612Sdelphij	case STATS_FREQ_FILE:
398285612Sdelphij		if (!value || (len = strlen(value)) == 0)
399285612Sdelphij			break;
40054359Sroberto
401285612Sdelphij		stats_drift_file = erealloc(stats_drift_file, len + 1);
402285612Sdelphij		stats_temp_file = erealloc(stats_temp_file,
403285612Sdelphij		    len + sizeof(".TEMP"));
404285612Sdelphij		memcpy(stats_drift_file, value, (size_t)(len+1));
405285612Sdelphij		memcpy(stats_temp_file, value, (size_t)len);
406285612Sdelphij		memcpy(stats_temp_file + len, temp_ext, sizeof(temp_ext));
40754359Sroberto
40854359Sroberto		/*
409132451Sroberto		 * Open drift file and read frequency. If the file is
410132451Sroberto		 * missing or contains errors, tell the loop to reset.
41154359Sroberto		 */
412285612Sdelphij		if ((fp = fopen(stats_drift_file, "r")) == NULL)
41354359Sroberto			break;
414285612Sdelphij
41554359Sroberto		if (fscanf(fp, "%lf", &old_drift) != 1) {
416285612Sdelphij			msyslog(LOG_ERR,
417285612Sdelphij				"format error frequency file %s",
418285612Sdelphij				stats_drift_file);
419132451Sroberto			fclose(fp);
42054359Sroberto			break;
421285612Sdelphij
42254359Sroberto		}
423132451Sroberto		fclose(fp);
424285612Sdelphij		loop_config(LOOP_FREQ, old_drift);
425285612Sdelphij		prev_drift_comp = drift_comp;
42654359Sroberto		break;
427285612Sdelphij
428285612Sdelphij	/*
429285612Sdelphij	 * Specify statistics directory.
430285612Sdelphij	 */
431285612Sdelphij	case STATS_STATSDIR:
432285612Sdelphij
433285612Sdelphij		/* - 1 since value may be missing the DIR_SEP. */
434285612Sdelphij		if (strlen(value) >= sizeof(statsdir) - 1) {
43554359Sroberto			msyslog(LOG_ERR,
436285612Sdelphij			    "statsdir too long (>%d, sigh)",
437285612Sdelphij			    (int)sizeof(statsdir) - 2);
43854359Sroberto		} else {
439285612Sdelphij			int add_dir_sep;
440293650Sglebius			size_t value_l;
44154359Sroberto
442285612Sdelphij			/* Add a DIR_SEP unless we already have one. */
443285612Sdelphij			value_l = strlen(value);
444285612Sdelphij			if (0 == value_l)
445285612Sdelphij				add_dir_sep = FALSE;
446285612Sdelphij			else
447285612Sdelphij				add_dir_sep = (DIR_SEP !=
448285612Sdelphij				    value[value_l - 1]);
449285612Sdelphij
450285612Sdelphij			if (add_dir_sep)
451285612Sdelphij				snprintf(statsdir, sizeof(statsdir),
452285612Sdelphij				    "%s%c", value, DIR_SEP);
453285612Sdelphij			else
454285612Sdelphij				snprintf(statsdir, sizeof(statsdir),
455285612Sdelphij				    "%s", value);
456285612Sdelphij			filegen_statsdir();
45754359Sroberto		}
45854359Sroberto		break;
45954359Sroberto
460285612Sdelphij	/*
461285612Sdelphij	 * Open pid file.
462285612Sdelphij	 */
463285612Sdelphij	case STATS_PID_FILE:
46454359Sroberto		if ((fp = fopen(value, "w")) == NULL) {
465285612Sdelphij			msyslog(LOG_ERR, "pid file %s: %m",
466285612Sdelphij			    value);
46754359Sroberto			break;
46854359Sroberto		}
469285612Sdelphij		fprintf(fp, "%d", (int)getpid());
470285612Sdelphij		fclose(fp);
47154359Sroberto		break;
47254359Sroberto
473285612Sdelphij	/*
474285612Sdelphij	 * Read leapseconds file.
475285612Sdelphij	 *
476285612Sdelphij	 * Note: Currently a leap file without SHA1 signature is
477285612Sdelphij	 * accepted, but if there is a signature line, the signature
478285612Sdelphij	 * must be valid or the file is rejected.
479285612Sdelphij	 */
480285612Sdelphij	case STATS_LEAP_FILE:
481285612Sdelphij		if (!value || (len = strlen(value)) == 0)
482285612Sdelphij			break;
483285612Sdelphij
484285612Sdelphij		leapfile_name = erealloc(leapfile_name, len + 1);
485285612Sdelphij		memcpy(leapfile_name, value, len + 1);
486285612Sdelphij
487285612Sdelphij		if (leapsec_load_file(
488285612Sdelphij			    leapfile_name, &leapfile_stat, TRUE, TRUE))
489285612Sdelphij		{
490285612Sdelphij			leap_signature_t lsig;
491285612Sdelphij
492285612Sdelphij			get_systime(&now);
493285612Sdelphij			time(&ttnow);
494285612Sdelphij			leapsec_getsig(&lsig);
495285612Sdelphij			mprintf_event(EVNT_TAI, NULL,
496285612Sdelphij				      "%d leap %s %s %s",
497285612Sdelphij				      lsig.taiof,
498285612Sdelphij				      fstostr(lsig.ttime),
499285612Sdelphij				      leapsec_expired(now.l_ui, NULL)
500285612Sdelphij					  ? "expired"
501285612Sdelphij					  : "expires",
502285612Sdelphij				      fstostr(lsig.etime));
503285612Sdelphij
504285612Sdelphij			have_leapfile = TRUE;
505285612Sdelphij
506285612Sdelphij			/* force an immediate daily expiration check of
507285612Sdelphij			 * the leap seconds table
508285612Sdelphij			 */
509285612Sdelphij			check_leap_expiration(TRUE, now.l_ui, &ttnow);
510285612Sdelphij		}
511285612Sdelphij		break;
512285612Sdelphij
513285612Sdelphij	default:
51454359Sroberto		/* oh well */
51554359Sroberto		break;
51654359Sroberto	}
51754359Sroberto}
51854359Sroberto
519285612Sdelphij
52054359Sroberto/*
52154359Sroberto * record_peer_stats - write peer statistics to file
52254359Sroberto *
52354359Sroberto * file format:
524285612Sdelphij * day (MJD)
52554359Sroberto * time (s past UTC midnight)
526285612Sdelphij * IP address
527285612Sdelphij * status word (hex)
528285612Sdelphij * offset
529285612Sdelphij * delay
530285612Sdelphij * dispersion
531285612Sdelphij * jitter
53254359Sroberto*/
53354359Srobertovoid
53454359Srobertorecord_peer_stats(
535285612Sdelphij	sockaddr_u *addr,
536132451Sroberto	int	status,
537285612Sdelphij	double	offset,		/* offset */
538285612Sdelphij	double	delay,		/* delay */
539285612Sdelphij	double	dispersion,	/* dispersion */
540285612Sdelphij	double	jitter		/* jitter */
54154359Sroberto	)
54254359Sroberto{
543132451Sroberto	l_fp	now;
544132451Sroberto	u_long	day;
54554359Sroberto
54654359Sroberto	if (!stats_control)
54754359Sroberto		return;
54854359Sroberto
549132451Sroberto	get_systime(&now);
550132451Sroberto	filegen_setup(&peerstats, now.l_ui);
551132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
552132451Sroberto	now.l_ui %= 86400;
55354359Sroberto	if (peerstats.fp != NULL) {
55454359Sroberto		fprintf(peerstats.fp,
555285612Sdelphij		    "%lu %s %s %x %.9f %.9f %.9f %.9f\n", day,
556285612Sdelphij		    ulfptoa(&now, 3), stoa(addr), status, offset,
557285612Sdelphij		    delay, dispersion, jitter);
55854359Sroberto		fflush(peerstats.fp);
55954359Sroberto	}
56054359Sroberto}
561182007Sroberto
562285612Sdelphij
56354359Sroberto/*
56454359Sroberto * record_loop_stats - write loop filter statistics to file
56554359Sroberto *
56654359Sroberto * file format:
567285612Sdelphij * day (MJD)
56854359Sroberto * time (s past midnight)
569285612Sdelphij * offset
570285612Sdelphij * frequency (PPM)
571285612Sdelphij * jitter
572285612Sdelphij * wnder (PPM)
573285612Sdelphij * time constant (log2)
57454359Sroberto */
57554359Srobertovoid
57682498Srobertorecord_loop_stats(
577285612Sdelphij	double	offset,		/* offset */
578285612Sdelphij	double	freq,		/* frequency (PPM) */
579285612Sdelphij	double	jitter,		/* jitter */
580285612Sdelphij	double	wander,		/* wander (PPM) */
581132451Sroberto	int spoll
58282498Sroberto	)
58354359Sroberto{
584132451Sroberto	l_fp	now;
585132451Sroberto	u_long	day;
58654359Sroberto
58754359Sroberto	if (!stats_control)
58854359Sroberto		return;
58954359Sroberto
590132451Sroberto	get_systime(&now);
591132451Sroberto	filegen_setup(&loopstats, now.l_ui);
592132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
593132451Sroberto	now.l_ui %= 86400;
59454359Sroberto	if (loopstats.fp != NULL) {
595182007Sroberto		fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n",
596132451Sroberto		    day, ulfptoa(&now, 3), offset, freq * 1e6, jitter,
597285612Sdelphij		    wander * 1e6, spoll);
59854359Sroberto		fflush(loopstats.fp);
59954359Sroberto	}
60054359Sroberto}
60154359Sroberto
602285612Sdelphij
60354359Sroberto/*
60454359Sroberto * record_clock_stats - write clock statistics to file
60554359Sroberto *
60654359Sroberto * file format:
607285612Sdelphij * day (MJD)
60854359Sroberto * time (s past midnight)
609285612Sdelphij * IP address
61054359Sroberto * text message
61154359Sroberto */
61254359Srobertovoid
61354359Srobertorecord_clock_stats(
614285612Sdelphij	sockaddr_u *addr,
615285612Sdelphij	const char *text	/* timecode string */
61654359Sroberto	)
61754359Sroberto{
618132451Sroberto	l_fp	now;
619132451Sroberto	u_long	day;
62054359Sroberto
62154359Sroberto	if (!stats_control)
62254359Sroberto		return;
62354359Sroberto
624132451Sroberto	get_systime(&now);
625132451Sroberto	filegen_setup(&clockstats, now.l_ui);
626132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
627132451Sroberto	now.l_ui %= 86400;
62854359Sroberto	if (clockstats.fp != NULL) {
629285612Sdelphij		fprintf(clockstats.fp, "%lu %s %s %s\n", day,
630285612Sdelphij		    ulfptoa(&now, 3), stoa(addr), text);
63154359Sroberto		fflush(clockstats.fp);
63254359Sroberto	}
63354359Sroberto}
63454359Sroberto
635285612Sdelphij
63654359Sroberto/*
637285612Sdelphij * mprintf_clock_stats - write clock statistics to file with
638285612Sdelphij *			msnprintf-style formatting.
639285612Sdelphij */
640285612Sdelphijint
641285612Sdelphijmprintf_clock_stats(
642285612Sdelphij	sockaddr_u *addr,
643285612Sdelphij	const char *fmt,
644285612Sdelphij	...
645285612Sdelphij	)
646285612Sdelphij{
647285612Sdelphij	va_list	ap;
648285612Sdelphij	int	rc;
649285612Sdelphij	char	msg[512];
650285612Sdelphij
651285612Sdelphij	va_start(ap, fmt);
652285612Sdelphij	rc = mvsnprintf(msg, sizeof(msg), fmt, ap);
653285612Sdelphij	va_end(ap);
654285612Sdelphij	if (stats_control)
655285612Sdelphij		record_clock_stats(addr, msg);
656285612Sdelphij
657285612Sdelphij	return rc;
658285612Sdelphij}
659285612Sdelphij
660285612Sdelphij/*
66154359Sroberto * record_raw_stats - write raw timestamps to file
66254359Sroberto *
66354359Sroberto * file format
664285612Sdelphij * day (MJD)
66554359Sroberto * time (s past midnight)
66654359Sroberto * peer ip address
667285612Sdelphij * IP address
66854359Sroberto * t1 t2 t3 t4 timestamps
669330141Sdelphij * leap, version, mode, stratum, ppoll, precision, root delay, root dispersion, REFID
670330141Sdelphij * length and hex dump of any EFs and any legacy MAC.
67154359Sroberto */
67254359Srobertovoid
67354359Srobertorecord_raw_stats(
674285612Sdelphij	sockaddr_u *srcadr,
675285612Sdelphij	sockaddr_u *dstadr,
676285612Sdelphij	l_fp	*t1,		/* originate timestamp */
677285612Sdelphij	l_fp	*t2,		/* receive timestamp */
678285612Sdelphij	l_fp	*t3,		/* transmit timestamp */
679285612Sdelphij	l_fp	*t4,		/* destination timestamp */
680285612Sdelphij	int	leap,
681285612Sdelphij	int	version,
682285612Sdelphij	int	mode,
683285612Sdelphij	int	stratum,
684285612Sdelphij	int	ppoll,
685285612Sdelphij	int	precision,
686285612Sdelphij	double	root_delay,	/* seconds */
687285612Sdelphij	double	root_dispersion,/* seconds */
688330141Sdelphij	u_int32	refid,
689330141Sdelphij	int	len,
690330141Sdelphij	u_char	*extra
69154359Sroberto	)
69254359Sroberto{
693132451Sroberto	l_fp	now;
694132451Sroberto	u_long	day;
69554359Sroberto
69654359Sroberto	if (!stats_control)
69754359Sroberto		return;
69854359Sroberto
699132451Sroberto	get_systime(&now);
700132451Sroberto	filegen_setup(&rawstats, now.l_ui);
701132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
702132451Sroberto	now.l_ui %= 86400;
70354359Sroberto	if (rawstats.fp != NULL) {
704330141Sdelphij		fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s %d %d %d %d %d %d %.6f %.6f %s",
705285612Sdelphij		    day, ulfptoa(&now, 3),
706330141Sdelphij		    srcadr ? stoa(srcadr) : "-",
707330141Sdelphij		    dstadr ? stoa(dstadr) : "-",
708285612Sdelphij		    ulfptoa(t1, 9), ulfptoa(t2, 9),
709285612Sdelphij		    ulfptoa(t3, 9), ulfptoa(t4, 9),
710285612Sdelphij		    leap, version, mode, stratum, ppoll, precision,
711285612Sdelphij		    root_delay, root_dispersion, refid_str(refid, stratum));
712330141Sdelphij		if (len > 0) {
713330141Sdelphij			int i;
714330141Sdelphij
715330141Sdelphij			fprintf(rawstats.fp, " %d: ", len);
716330141Sdelphij			for (i = 0; i < len; ++i) {
717330141Sdelphij				fprintf(rawstats.fp, "%02x", extra[i]);
718330141Sdelphij			}
719330141Sdelphij		}
720330141Sdelphij		fprintf(rawstats.fp, "\n");
72154359Sroberto		fflush(rawstats.fp);
72254359Sroberto	}
72354359Sroberto}
72454359Sroberto
725132451Sroberto
72654359Sroberto/*
727132451Sroberto * record_sys_stats - write system statistics to file
728132451Sroberto *
729132451Sroberto * file format
730285612Sdelphij * day (MJD)
731132451Sroberto * time (s past midnight)
732285612Sdelphij * time since reset
733132451Sroberto * packets recieved
734285612Sdelphij * packets for this host
735132451Sroberto * current version
736285612Sdelphij * old version
737132451Sroberto * access denied
738132451Sroberto * bad length or format
739132451Sroberto * bad authentication
740285612Sdelphij * declined
741132451Sroberto * rate exceeded
742285612Sdelphij * KoD sent
743132451Sroberto */
744132451Srobertovoid
745132451Srobertorecord_sys_stats(void)
746132451Sroberto{
747132451Sroberto	l_fp	now;
748132451Sroberto	u_long	day;
749132451Sroberto
750132451Sroberto	if (!stats_control)
751132451Sroberto		return;
752132451Sroberto
753132451Sroberto	get_systime(&now);
754132451Sroberto	filegen_setup(&sysstats, now.l_ui);
755132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
756132451Sroberto	now.l_ui %= 86400;
757132451Sroberto	if (sysstats.fp != NULL) {
758285612Sdelphij		fprintf(sysstats.fp,
759285612Sdelphij		    "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
760285612Sdelphij		    day, ulfptoa(&now, 3), current_time - sys_stattime,
761285612Sdelphij		    sys_received, sys_processed, sys_newversion,
762285612Sdelphij		    sys_oldversion, sys_restricted, sys_badlength,
763285612Sdelphij		    sys_badauth, sys_declined, sys_limitrejected,
764285612Sdelphij		    sys_kodsent);
765132451Sroberto		fflush(sysstats.fp);
766132451Sroberto		proto_clr_stats();
767132451Sroberto	}
768132451Sroberto}
769132451Sroberto
770132451Sroberto
771132451Sroberto/*
772285612Sdelphij * record_proto_stats - write system statistics to file
773285612Sdelphij *
774285612Sdelphij * file format
775285612Sdelphij * day (MJD)
776285612Sdelphij * time (s past midnight)
777285612Sdelphij * text message
778285612Sdelphij */
779285612Sdelphijvoid
780285612Sdelphijrecord_proto_stats(
781285612Sdelphij	char	*str		/* text string */
782285612Sdelphij	)
783285612Sdelphij{
784285612Sdelphij	l_fp	now;
785285612Sdelphij	u_long	day;
786285612Sdelphij
787285612Sdelphij	if (!stats_control)
788285612Sdelphij		return;
789285612Sdelphij
790285612Sdelphij	get_systime(&now);
791285612Sdelphij	filegen_setup(&protostats, now.l_ui);
792285612Sdelphij	day = now.l_ui / 86400 + MJD_1900;
793285612Sdelphij	now.l_ui %= 86400;
794285612Sdelphij	if (protostats.fp != NULL) {
795285612Sdelphij		fprintf(protostats.fp, "%lu %s %s\n", day,
796285612Sdelphij		    ulfptoa(&now, 3), str);
797285612Sdelphij		fflush(protostats.fp);
798285612Sdelphij	}
799285612Sdelphij}
800285612Sdelphij
801285612Sdelphij
802285612Sdelphij#ifdef AUTOKEY
803285612Sdelphij/*
804132451Sroberto * record_crypto_stats - write crypto statistics to file
805132451Sroberto *
806132451Sroberto * file format:
807132451Sroberto * day (mjd)
808132451Sroberto * time (s past midnight)
809285612Sdelphij * peer ip address
810132451Sroberto * text message
811132451Sroberto */
812132451Srobertovoid
813132451Srobertorecord_crypto_stats(
814285612Sdelphij	sockaddr_u *addr,
815285612Sdelphij	const char *text	/* text message */
816132451Sroberto	)
817132451Sroberto{
818132451Sroberto	l_fp	now;
819132451Sroberto	u_long	day;
820132451Sroberto
821132451Sroberto	if (!stats_control)
822132451Sroberto		return;
823132451Sroberto
824132451Sroberto	get_systime(&now);
825132451Sroberto	filegen_setup(&cryptostats, now.l_ui);
826132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
827132451Sroberto	now.l_ui %= 86400;
828132451Sroberto	if (cryptostats.fp != NULL) {
829132451Sroberto		if (addr == NULL)
830285612Sdelphij			fprintf(cryptostats.fp, "%lu %s 0.0.0.0 %s\n",
831132451Sroberto			    day, ulfptoa(&now, 3), text);
832132451Sroberto		else
833132451Sroberto			fprintf(cryptostats.fp, "%lu %s %s %s\n",
834132451Sroberto			    day, ulfptoa(&now, 3), stoa(addr), text);
835132451Sroberto		fflush(cryptostats.fp);
836132451Sroberto	}
837132451Sroberto}
838285612Sdelphij#endif	/* AUTOKEY */
839132451Sroberto
840285612Sdelphij
841182007Sroberto#ifdef DEBUG_TIMING
842182007Sroberto/*
843285612Sdelphij * record_timing_stats - write timing statistics to file
844182007Sroberto *
845182007Sroberto * file format:
846182007Sroberto * day (mjd)
847182007Sroberto * time (s past midnight)
848182007Sroberto * text message
849182007Sroberto */
850182007Srobertovoid
851182007Srobertorecord_timing_stats(
852285612Sdelphij	const char *text	/* text message */
853182007Sroberto	)
854182007Sroberto{
855182007Sroberto	static unsigned int flshcnt;
856182007Sroberto	l_fp	now;
857182007Sroberto	u_long	day;
858132451Sroberto
859182007Sroberto	if (!stats_control)
860182007Sroberto		return;
861182007Sroberto
862182007Sroberto	get_systime(&now);
863182007Sroberto	filegen_setup(&timingstats, now.l_ui);
864182007Sroberto	day = now.l_ui / 86400 + MJD_1900;
865182007Sroberto	now.l_ui %= 86400;
866182007Sroberto	if (timingstats.fp != NULL) {
867285612Sdelphij		fprintf(timingstats.fp, "%lu %s %s\n", day, lfptoa(&now,
868285612Sdelphij		    3), text);
869182007Sroberto		if (++flshcnt % 100 == 0)
870182007Sroberto			fflush(timingstats.fp);
871182007Sroberto	}
872182007Sroberto}
873182007Sroberto#endif
874285612Sdelphij
875285612Sdelphij
876132451Sroberto/*
877285612Sdelphij * check_leap_file - See if the leapseconds file has been updated.
878285612Sdelphij *
879285612Sdelphij * Returns: n/a
880285612Sdelphij *
881285612Sdelphij * Note: This loads a new leapfile on the fly. Currently a leap file
882285612Sdelphij * without SHA1 signature is accepted, but if there is a signature line,
883285612Sdelphij * the signature must be valid or the file is rejected.
884285612Sdelphij */
885285612Sdelphijvoid
886285612Sdelphijcheck_leap_file(
887285612Sdelphij	int           is_daily_check,
888285612Sdelphij	uint32_t      ntptime       ,
889285612Sdelphij	const time_t *systime
890285612Sdelphij	)
891285612Sdelphij{
892285612Sdelphij	/* just do nothing if there is no leap file */
893285612Sdelphij	if ( ! (leapfile_name && *leapfile_name))
894285612Sdelphij		return;
895285612Sdelphij
896285612Sdelphij	/* try to load leapfile, force it if no leapfile loaded yet */
897285612Sdelphij	if (leapsec_load_file(
898285612Sdelphij		    leapfile_name, &leapfile_stat,
899285612Sdelphij		    !have_leapfile, is_daily_check))
900285612Sdelphij		have_leapfile = TRUE;
901285612Sdelphij	else if (!have_leapfile)
902285612Sdelphij		return;
903285612Sdelphij
904285612Sdelphij	check_leap_expiration(is_daily_check, ntptime, systime);
905285612Sdelphij}
906285612Sdelphij
907285612Sdelphij/*
908285612Sdelphij * check expiration of a loaded leap table
909285612Sdelphij */
910285612Sdelphijstatic void
911285612Sdelphijcheck_leap_expiration(
912285612Sdelphij	int           is_daily_check,
913285612Sdelphij	uint32_t      ntptime       ,
914285612Sdelphij	const time_t *systime
915285612Sdelphij	)
916285612Sdelphij{
917285612Sdelphij	static const char * const logPrefix = "leapsecond file";
918285612Sdelphij	int  rc;
919285612Sdelphij
920285612Sdelphij	/* test the expiration of the leap data and log with proper
921285612Sdelphij	 * level and frequency (once/hour or once/day, depending on the
922285612Sdelphij	 * state.
923285612Sdelphij	 */
924285612Sdelphij	rc = leapsec_daystolive(ntptime, systime);
925285612Sdelphij	if (rc == 0) {
926285612Sdelphij		msyslog(LOG_WARNING,
927285612Sdelphij			"%s ('%s'): will expire in less than one day",
928285612Sdelphij			logPrefix, leapfile_name);
929285612Sdelphij	} else if (is_daily_check && rc < 28) {
930285612Sdelphij		if (rc < 0)
931285612Sdelphij			msyslog(LOG_ERR,
932285612Sdelphij				"%s ('%s'): expired less than %d day%s ago",
933285612Sdelphij				logPrefix, leapfile_name, -rc, (rc == -1 ? "" : "s"));
934285612Sdelphij		else
935285612Sdelphij			msyslog(LOG_WARNING,
936285612Sdelphij				"%s ('%s'): will expire in less than %d days",
937285612Sdelphij				logPrefix, leapfile_name, 1+rc);
938285612Sdelphij	}
939285612Sdelphij}
940285612Sdelphij
941285612Sdelphij
942285612Sdelphij/*
94354359Sroberto * getauthkeys - read the authentication keys from the specified file
94454359Sroberto */
94554359Srobertovoid
94654359Srobertogetauthkeys(
947182007Sroberto	const char *keyfile
94854359Sroberto	)
94954359Sroberto{
950293650Sglebius	size_t len;
95154359Sroberto
95254359Sroberto	len = strlen(keyfile);
953285612Sdelphij	if (!len)
95454359Sroberto		return;
95554359Sroberto
95654359Sroberto#ifndef SYS_WINNT
957285612Sdelphij	key_file_name = erealloc(key_file_name, len + 1);
958285612Sdelphij	memcpy(key_file_name, keyfile, len + 1);
95954359Sroberto#else
960285612Sdelphij	key_file_name = erealloc(key_file_name, _MAX_PATH);
961285612Sdelphij	if (len + 1 > _MAX_PATH)
962285612Sdelphij		return;
963285612Sdelphij	if (!ExpandEnvironmentStrings(keyfile, key_file_name,
964285612Sdelphij				      _MAX_PATH)) {
96554359Sroberto		msyslog(LOG_ERR,
966285612Sdelphij			"ExpandEnvironmentStrings(KEY_FILE) failed: %m");
967285612Sdelphij		strlcpy(key_file_name, keyfile, _MAX_PATH);
96854359Sroberto	}
969285612Sdelphij	key_file_name = erealloc(key_file_name,
970285612Sdelphij				 1 + strlen(key_file_name));
97154359Sroberto#endif /* SYS_WINNT */
97254359Sroberto
97354359Sroberto	authreadkeys(key_file_name);
97454359Sroberto}
97554359Sroberto
97654359Sroberto
97754359Sroberto/*
97854359Sroberto * rereadkeys - read the authentication key file over again.
97954359Sroberto */
98054359Srobertovoid
98154359Srobertorereadkeys(void)
98254359Sroberto{
983285612Sdelphij	if (NULL != key_file_name)
984285612Sdelphij		authreadkeys(key_file_name);
98554359Sroberto}
986132451Sroberto
987285612Sdelphij
988285612Sdelphij#if notyet
989132451Sroberto/*
990285612Sdelphij * ntp_exit - document explicitly that ntpd has exited
991132451Sroberto */
992285612Sdelphijvoid
993285612Sdelphijntp_exit(int retval)
994285612Sdelphij{
995285612Sdelphij	msyslog(LOG_ERR, "EXITING with return code %d", retval);
996285612Sdelphij	exit(retval);
997285612Sdelphij}
998285612Sdelphij#endif
999285612Sdelphij
1000285612Sdelphij/*
1001285612Sdelphij * fstostr - prettyprint NTP seconds
1002285612Sdelphij */
1003285612Sdelphijchar * fstostr(
1004285612Sdelphij	time_t	ntp_stamp
1005132451Sroberto	)
1006132451Sroberto{
1007285612Sdelphij	char *		buf;
1008285612Sdelphij	struct calendar tm;
1009132451Sroberto
1010285612Sdelphij	LIB_GETBUF(buf);
1011285612Sdelphij	if (ntpcal_ntp_to_date(&tm, (u_int32)ntp_stamp, NULL) < 0)
1012285612Sdelphij		snprintf(buf, LIB_BUFLENGTH, "ntpcal_ntp_to_date: %ld: range error",
1013285612Sdelphij			 (long)ntp_stamp);
1014285612Sdelphij	else
1015285612Sdelphij		snprintf(buf, LIB_BUFLENGTH, "%04d%02d%02d%02d%02d",
1016285612Sdelphij			 tm.year, tm.month, tm.monthday,
1017285612Sdelphij			 tm.hour, tm.minute);
1018285612Sdelphij	return buf;
1019285612Sdelphij}
1020132451Sroberto
1021132451Sroberto
1022182007Sroberto/*
1023285612Sdelphij * ntpd_time_stepped is called back by step_systime(), allowing ntpd
1024285612Sdelphij * to do any one-time processing necessitated by the step.
1025182007Sroberto */
1026182007Srobertovoid
1027285612Sdelphijntpd_time_stepped(void)
1028182007Sroberto{
1029285612Sdelphij	u_int saved_mon_enabled;
1030285612Sdelphij
1031285612Sdelphij	/*
1032285612Sdelphij	 * flush the monitor MRU list which contains l_fp timestamps
1033285612Sdelphij	 * which should not be compared across the step.
1034285612Sdelphij	 */
1035285612Sdelphij	if (MON_OFF != mon_enabled) {
1036285612Sdelphij		saved_mon_enabled = mon_enabled;
1037285612Sdelphij		mon_stop(MON_OFF);
1038285612Sdelphij		mon_start(saved_mon_enabled);
1039285612Sdelphij	}
1040285612Sdelphij
1041285612Sdelphij	/* inform interpolating Windows code to allow time to go back */
1042285612Sdelphij#ifdef SYS_WINNT
1043285612Sdelphij	win_time_stepped();
1044285612Sdelphij#endif
1045182007Sroberto}
1046