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"
13290000Sglebius#include "ntp_assert.h"
14290000Sglebius#include "ntp_calendar.h"
15290000Sglebius#include "ntp_leapsec.h"
16290000Sglebius#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
24290000Sglebius#ifdef HAVE_UNISTD_H
25290000Sglebius# include <unistd.h>
26290000Sglebius#endif
27290000Sglebius#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/*
41290000Sglebius * Defines used by the leapseconds stuff
4254359Sroberto */
43290000Sglebius#define	MAX_TAI	100			/* max TAI offset (s) */
44290000Sglebius#define	L_DAY	86400UL			/* seconds per day */
45290000Sglebius#define	L_YEAR	(L_DAY * 365)		/* days per year */
46290000Sglebius#define	L_LYEAR	(L_YEAR + L_DAY)	/* days per leap year */
47290000Sglebius#define	L_4YEAR	(L_LYEAR + 3 * L_YEAR)	/* days per leap cycle */
48290000Sglebius#define	L_CENT	(L_4YEAR * 25)		/* days per century */
49290000Sglebius
5054359Sroberto/*
51290000Sglebius * This contains odds and ends, including the hourly stats, various
52290000Sglebius * configuration items, leapseconds stuff, etc.
5354359Sroberto */
5454359Sroberto/*
55290000Sglebius * File names
5654359Sroberto */
57290000Sglebiusstatic	char *key_file_name;		/* keys file name */
58290000Sglebiusstatic char	  *leapfile_name;		/* leapseconds file name */
59290000Sglebiusstatic struct stat leapfile_stat;	/* leapseconds file stat() buffer */
60290000Sglebiusstatic int /*BOOL*/have_leapfile = FALSE;
61290000Sglebiuschar	*stats_drift_file;		/* frequency file name */
62290000Sglebiusstatic	char *stats_temp_file;		/* temp frequency file name */
63290000Sglebiusstatic double wander_resid;		/* last frequency update */
64290000Sglebiusdouble	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
73290000Sglebius#  define NTP_VAR "c:\\var\\ntp\\"	/* NOTE the trailing '\\' */
74182007Sroberto# endif /* SYS_WINNT */
7554359Sroberto#endif
7654359Sroberto
7754359Sroberto
78290000Sglebiuschar statsdir[MAXFILENAME] = NTP_VAR;
7954359Srobertostatic FILEGEN peerstats;
8054359Srobertostatic FILEGEN loopstats;
8154359Srobertostatic FILEGEN clockstats;
8254359Srobertostatic FILEGEN rawstats;
83132451Srobertostatic FILEGEN sysstats;
84290000Sglebiusstatic FILEGEN protostats;
85290000Sglebiusstatic 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/*
95290000Sglebius * Last frequency written to file.
96182007Sroberto */
97290000Sglebiusstatic double prev_drift_comp;		/* last frequency update */
98182007Sroberto
99182007Sroberto/*
100290000Sglebius * Function prototypes
10154359Sroberto */
102290000Sglebiusstatic	void	record_sys_stats(void);
103290000Sglebius	void	ntpd_time_stepped(void);
104290000Sglebiusstatic  void	check_leap_expiration(int, uint32_t, const time_t*);
105290000Sglebius
106290000Sglebius/*
107290000Sglebius * Prototypes
108290000Sglebius */
109290000Sglebius#ifdef DEBUG
110290000Sglebiusvoid	uninit_util(void);
111290000Sglebius#endif
112290000Sglebius
113290000Sglebius/*
114290000Sglebius * uninit_util - free memory allocated by init_util
115290000Sglebius */
116290000Sglebius#ifdef DEBUG
11754359Srobertovoid
118290000Sglebiusuninit_util(void)
11954359Sroberto{
120290000Sglebius#if defined(_MSC_VER) && defined (_DEBUG)
121290000Sglebius	_CrtCheckMemory();
122290000Sglebius#endif
123290000Sglebius	if (stats_drift_file) {
124290000Sglebius		free(stats_drift_file);
125290000Sglebius		free(stats_temp_file);
126290000Sglebius		stats_drift_file = NULL;
127290000Sglebius		stats_temp_file = NULL;
128290000Sglebius	}
129290000Sglebius	if (key_file_name) {
130290000Sglebius		free(key_file_name);
131290000Sglebius		key_file_name = NULL;
132290000Sglebius	}
133290000Sglebius	filegen_unregister("peerstats");
134290000Sglebius	filegen_unregister("loopstats");
135290000Sglebius	filegen_unregister("clockstats");
136290000Sglebius	filegen_unregister("rawstats");
137290000Sglebius	filegen_unregister("sysstats");
138290000Sglebius	filegen_unregister("protostats");
139290000Sglebius#ifdef AUTOKEY
140290000Sglebius	filegen_unregister("cryptostats");
141290000Sglebius#endif	/* AUTOKEY */
142290000Sglebius#ifdef DEBUG_TIMING
143290000Sglebius	filegen_unregister("timingstats");
144290000Sglebius#endif	/* DEBUG_TIMING */
14554359Sroberto
146290000Sglebius#if defined(_MSC_VER) && defined (_DEBUG)
147290000Sglebius	_CrtCheckMemory();
148290000Sglebius#endif
149290000Sglebius}
150290000Sglebius#endif /* DEBUG */
151132451Sroberto
15254359Sroberto
153290000Sglebius/*
154290000Sglebius * init_util - initialize the util module of ntpd
155290000Sglebius */
156290000Sglebiusvoid
157290000Sglebiusinit_util(void)
158290000Sglebius{
159290000Sglebius	filegen_register(statsdir, "peerstats",	  &peerstats);
160290000Sglebius	filegen_register(statsdir, "loopstats",	  &loopstats);
161290000Sglebius	filegen_register(statsdir, "clockstats",  &clockstats);
162290000Sglebius	filegen_register(statsdir, "rawstats",	  &rawstats);
163290000Sglebius	filegen_register(statsdir, "sysstats",	  &sysstats);
164290000Sglebius	filegen_register(statsdir, "protostats",  &protostats);
165290000Sglebius	filegen_register(statsdir, "cryptostats", &cryptostats);
166290000Sglebius	filegen_register(statsdir, "timingstats", &timingstats);
167290000Sglebius	/*
168290000Sglebius	 * register with libntp ntp_set_tod() to call us back
169290000Sglebius	 * when time is stepped.
170290000Sglebius	 */
171290000Sglebius	step_callback = &ntpd_time_stepped;
172290000Sglebius#ifdef DEBUG
173290000Sglebius	atexit(&uninit_util);
174290000Sglebius#endif /* DEBUG */
17554359Sroberto}
17654359Sroberto
17754359Sroberto
17854359Sroberto/*
17954359Sroberto * hourly_stats - print some interesting stats
18054359Sroberto */
18154359Srobertovoid
182182007Srobertowrite_stats(void)
18354359Sroberto{
184290000Sglebius	FILE	*fp;
18554359Sroberto#ifdef DOSYNCTODR
18654359Sroberto	struct timeval tv;
18754359Sroberto#if !defined(VMS)
188290000Sglebius	int	prio_set;
18954359Sroberto#endif
19054359Sroberto#ifdef HAVE_GETCLOCK
191290000Sglebius	struct timespec ts;
19254359Sroberto#endif
193290000Sglebius	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,
213290000Sglebius	 *	   so setting DOSYNCTODR seems a bad idea in the
214290000Sglebius	 *	   case of us resolution
21554359Sroberto	 */
21654359Sroberto
21754359Sroberto#if !defined(VMS)
218290000Sglebius	/*
219290000Sglebius	 * (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
243290000Sglebius	(void) getclock(TIMEOFDAY, &ts);
244290000Sglebius	tv.tv_sec = ts.tv_sec;
245290000Sglebius	tv.tv_usec = ts.tv_nsec / 1000;
24654359Sroberto#else /*  not HAVE_GETCLOCK */
24754359Sroberto	GETTIMEOFDAY(&tv,(struct timezone *)NULL);
24854359Sroberto#endif /* not HAVE_GETCLOCK */
249290000Sglebius	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) {
258290000Sglebius
259290000Sglebius		/*
260290000Sglebius		 * When the frequency file is written, initialize the
261290000Sglebius		 * prev_drift_comp and wander_resid. Thereafter,
262290000Sglebius		 * reduce the wander_resid by half each hour. When
263290000Sglebius		 * the difference between the prev_drift_comp and
264290000Sglebius		 * drift_comp is less than the wander_resid, update
265290000Sglebius		 * the frequncy file. This minimizes the file writes to
266290000Sglebius		 * nonvolaile storage.
267290000Sglebius		 */
268290000Sglebius#ifdef DEBUG
269290000Sglebius		if (debug)
270290000Sglebius			printf("write_stats: frequency %.6lf thresh %.6lf, freq %.6lf\n",
271290000Sglebius			    (prev_drift_comp - drift_comp) * 1e6, wander_resid *
272290000Sglebius			    1e6, drift_comp * 1e6);
273290000Sglebius#endif
274290000Sglebius		if (fabs(prev_drift_comp - drift_comp) < wander_resid) {
275290000Sglebius			wander_resid *= 0.5;
276290000Sglebius			return;
277290000Sglebius		}
278290000Sglebius		prev_drift_comp = drift_comp;
279290000Sglebius		wander_resid = wander_threshold;
28054359Sroberto		if ((fp = fopen(stats_temp_file, "w")) == NULL) {
281290000Sglebius			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
289290000Sglebius		if (_unlink(stats_drift_file)) /* rename semantics differ under NT */
290290000Sglebius			msyslog(LOG_WARNING,
291290000Sglebius				"Unable to remove prior drift file %s, %m",
292290000Sglebius				stats_drift_file);
29354359Sroberto#endif /* SYS_WINNT */
29454359Sroberto
29554359Sroberto#ifndef NO_RENAME
296290000Sglebius		if (rename(stats_temp_file, stats_drift_file))
297290000Sglebius			msyslog(LOG_WARNING,
298290000Sglebius				"Unable to rename temp drift file %s to %s, %m",
299290000Sglebius				stats_temp_file, stats_drift_file);
30054359Sroberto#else
301182007Sroberto		/* we have no rename NFS of ftp in use */
302290000Sglebius		if ((fp = fopen(stats_drift_file, "w")) ==
303290000Sglebius		    NULL) {
304290000Sglebius			msyslog(LOG_ERR,
305290000Sglebius			    "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 = {
316290000Sglebius				strlen(stats_drift_file), 0, 0,
317290000Sglebius				    stats_drift_file };
318290000Sglebius			while(lib$delete_file(&oldvers,
319290000Sglebius			    &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{
335290000Sglebius	FILE	*fp;
336182007Sroberto	const char *value;
337293894Sglebius	size_t	len;
338290000Sglebius	double	old_drift;
339290000Sglebius	l_fp	now;
340290000Sglebius	time_t  ttnow;
341290000Sglebius#ifndef VMS
342290000Sglebius	const char temp_ext[] = ".TEMP";
343290000Sglebius#else
344290000Sglebius	const char temp_ext[] = "-TEMP";
345290000Sglebius#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)) {
355290000Sglebius		switch (item) {
356290000Sglebius		case STATS_FREQ_FILE:
357290000Sglebius			strlcpy(parameter, "STATS_FREQ_FILE",
358290000Sglebius				sizeof(parameter));
35954359Sroberto			break;
360290000Sglebius
361290000Sglebius		case STATS_LEAP_FILE:
362290000Sglebius			strlcpy(parameter, "STATS_LEAP_FILE",
363290000Sglebius				sizeof(parameter));
36454359Sroberto			break;
365290000Sglebius
366290000Sglebius		case STATS_STATSDIR:
367290000Sglebius			strlcpy(parameter, "STATS_STATSDIR",
368290000Sglebius				sizeof(parameter));
36954359Sroberto			break;
370290000Sglebius
371290000Sglebius		case STATS_PID_FILE:
372290000Sglebius			strlcpy(parameter, "STATS_PID_FILE",
373290000Sglebius				sizeof(parameter));
37454359Sroberto			break;
375290000Sglebius
376290000Sglebius		default:
377290000Sglebius			strlcpy(parameter, "UNKNOWN",
378290000Sglebius				sizeof(parameter));
379290000Sglebius			break;
38054359Sroberto		}
38154359Sroberto		value = invalue;
38254359Sroberto		msyslog(LOG_ERR,
383290000Sglebius			"ExpandEnvironmentStrings(%s) failed: %m\n",
384290000Sglebius			parameter);
385132451Sroberto	} else {
386132451Sroberto		value = newvalue;
38754359Sroberto	}
388290000Sglebius#else
38954359Sroberto	value = invalue;
39054359Sroberto#endif /* SYS_WINNT */
39154359Sroberto
392290000Sglebius	switch (item) {
39354359Sroberto
394290000Sglebius	/*
395290000Sglebius	 * Open and read frequency file.
396290000Sglebius	 */
397290000Sglebius	case STATS_FREQ_FILE:
398290000Sglebius		if (!value || (len = strlen(value)) == 0)
399290000Sglebius			break;
40054359Sroberto
401290000Sglebius		stats_drift_file = erealloc(stats_drift_file, len + 1);
402290000Sglebius		stats_temp_file = erealloc(stats_temp_file,
403290000Sglebius		    len + sizeof(".TEMP"));
404290000Sglebius		memcpy(stats_drift_file, value, (size_t)(len+1));
405290000Sglebius		memcpy(stats_temp_file, value, (size_t)len);
406290000Sglebius		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		 */
412290000Sglebius		if ((fp = fopen(stats_drift_file, "r")) == NULL)
41354359Sroberto			break;
414290000Sglebius
41554359Sroberto		if (fscanf(fp, "%lf", &old_drift) != 1) {
416290000Sglebius			msyslog(LOG_ERR,
417290000Sglebius				"format error frequency file %s",
418290000Sglebius				stats_drift_file);
419132451Sroberto			fclose(fp);
42054359Sroberto			break;
421290000Sglebius
42254359Sroberto		}
423132451Sroberto		fclose(fp);
424290000Sglebius		loop_config(LOOP_FREQ, old_drift);
425290000Sglebius		prev_drift_comp = drift_comp;
42654359Sroberto		break;
427290000Sglebius
428290000Sglebius	/*
429290000Sglebius	 * Specify statistics directory.
430290000Sglebius	 */
431290000Sglebius	case STATS_STATSDIR:
432290000Sglebius
433290000Sglebius		/* - 1 since value may be missing the DIR_SEP. */
434290000Sglebius		if (strlen(value) >= sizeof(statsdir) - 1) {
43554359Sroberto			msyslog(LOG_ERR,
436290000Sglebius			    "statsdir too long (>%d, sigh)",
437290000Sglebius			    (int)sizeof(statsdir) - 2);
43854359Sroberto		} else {
439290000Sglebius			int add_dir_sep;
440293894Sglebius			size_t value_l;
44154359Sroberto
442290000Sglebius			/* Add a DIR_SEP unless we already have one. */
443290000Sglebius			value_l = strlen(value);
444290000Sglebius			if (0 == value_l)
445290000Sglebius				add_dir_sep = FALSE;
446290000Sglebius			else
447290000Sglebius				add_dir_sep = (DIR_SEP !=
448290000Sglebius				    value[value_l - 1]);
449290000Sglebius
450290000Sglebius			if (add_dir_sep)
451290000Sglebius				snprintf(statsdir, sizeof(statsdir),
452290000Sglebius				    "%s%c", value, DIR_SEP);
453290000Sglebius			else
454290000Sglebius				snprintf(statsdir, sizeof(statsdir),
455290000Sglebius				    "%s", value);
456290000Sglebius			filegen_statsdir();
45754359Sroberto		}
45854359Sroberto		break;
45954359Sroberto
460290000Sglebius	/*
461290000Sglebius	 * Open pid file.
462290000Sglebius	 */
463290000Sglebius	case STATS_PID_FILE:
46454359Sroberto		if ((fp = fopen(value, "w")) == NULL) {
465290000Sglebius			msyslog(LOG_ERR, "pid file %s: %m",
466290000Sglebius			    value);
46754359Sroberto			break;
46854359Sroberto		}
469290000Sglebius		fprintf(fp, "%d", (int)getpid());
470290000Sglebius		fclose(fp);
47154359Sroberto		break;
47254359Sroberto
473290000Sglebius	/*
474290000Sglebius	 * Read leapseconds file.
475290000Sglebius	 *
476290000Sglebius	 * Note: Currently a leap file without SHA1 signature is
477290000Sglebius	 * accepted, but if there is a signature line, the signature
478290000Sglebius	 * must be valid or the file is rejected.
479290000Sglebius	 */
480290000Sglebius	case STATS_LEAP_FILE:
481290000Sglebius		if (!value || (len = strlen(value)) == 0)
482290000Sglebius			break;
483290000Sglebius
484290000Sglebius		leapfile_name = erealloc(leapfile_name, len + 1);
485290000Sglebius		memcpy(leapfile_name, value, len + 1);
486290000Sglebius
487290000Sglebius		if (leapsec_load_file(
488290000Sglebius			    leapfile_name, &leapfile_stat, TRUE, TRUE))
489290000Sglebius		{
490290000Sglebius			leap_signature_t lsig;
491290000Sglebius
492290000Sglebius			get_systime(&now);
493290000Sglebius			time(&ttnow);
494290000Sglebius			leapsec_getsig(&lsig);
495290000Sglebius			mprintf_event(EVNT_TAI, NULL,
496290000Sglebius				      "%d leap %s %s %s",
497290000Sglebius				      lsig.taiof,
498290000Sglebius				      fstostr(lsig.ttime),
499290000Sglebius				      leapsec_expired(now.l_ui, NULL)
500290000Sglebius					  ? "expired"
501290000Sglebius					  : "expires",
502290000Sglebius				      fstostr(lsig.etime));
503290000Sglebius
504290000Sglebius			have_leapfile = TRUE;
505290000Sglebius
506290000Sglebius			/* force an immediate daily expiration check of
507290000Sglebius			 * the leap seconds table
508290000Sglebius			 */
509290000Sglebius			check_leap_expiration(TRUE, now.l_ui, &ttnow);
510290000Sglebius		}
511290000Sglebius		break;
512290000Sglebius
513290000Sglebius	default:
51454359Sroberto		/* oh well */
51554359Sroberto		break;
51654359Sroberto	}
51754359Sroberto}
51854359Sroberto
519290000Sglebius
52054359Sroberto/*
52154359Sroberto * record_peer_stats - write peer statistics to file
52254359Sroberto *
52354359Sroberto * file format:
524290000Sglebius * day (MJD)
52554359Sroberto * time (s past UTC midnight)
526290000Sglebius * IP address
527290000Sglebius * status word (hex)
528290000Sglebius * offset
529290000Sglebius * delay
530290000Sglebius * dispersion
531290000Sglebius * jitter
53254359Sroberto*/
53354359Srobertovoid
53454359Srobertorecord_peer_stats(
535290000Sglebius	sockaddr_u *addr,
536132451Sroberto	int	status,
537290000Sglebius	double	offset,		/* offset */
538290000Sglebius	double	delay,		/* delay */
539290000Sglebius	double	dispersion,	/* dispersion */
540290000Sglebius	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,
555290000Sglebius		    "%lu %s %s %x %.9f %.9f %.9f %.9f\n", day,
556290000Sglebius		    ulfptoa(&now, 3), stoa(addr), status, offset,
557290000Sglebius		    delay, dispersion, jitter);
55854359Sroberto		fflush(peerstats.fp);
55954359Sroberto	}
56054359Sroberto}
561182007Sroberto
562290000Sglebius
56354359Sroberto/*
56454359Sroberto * record_loop_stats - write loop filter statistics to file
56554359Sroberto *
56654359Sroberto * file format:
567290000Sglebius * day (MJD)
56854359Sroberto * time (s past midnight)
569290000Sglebius * offset
570290000Sglebius * frequency (PPM)
571290000Sglebius * jitter
572290000Sglebius * wnder (PPM)
573290000Sglebius * time constant (log2)
57454359Sroberto */
57554359Srobertovoid
57682498Srobertorecord_loop_stats(
577290000Sglebius	double	offset,		/* offset */
578290000Sglebius	double	freq,		/* frequency (PPM) */
579290000Sglebius	double	jitter,		/* jitter */
580290000Sglebius	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,
597290000Sglebius		    wander * 1e6, spoll);
59854359Sroberto		fflush(loopstats.fp);
59954359Sroberto	}
60054359Sroberto}
60154359Sroberto
602290000Sglebius
60354359Sroberto/*
60454359Sroberto * record_clock_stats - write clock statistics to file
60554359Sroberto *
60654359Sroberto * file format:
607290000Sglebius * day (MJD)
60854359Sroberto * time (s past midnight)
609290000Sglebius * IP address
61054359Sroberto * text message
61154359Sroberto */
61254359Srobertovoid
61354359Srobertorecord_clock_stats(
614290000Sglebius	sockaddr_u *addr,
615290000Sglebius	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) {
629290000Sglebius		fprintf(clockstats.fp, "%lu %s %s %s\n", day,
630290000Sglebius		    ulfptoa(&now, 3), stoa(addr), text);
63154359Sroberto		fflush(clockstats.fp);
63254359Sroberto	}
63354359Sroberto}
63454359Sroberto
635290000Sglebius
63654359Sroberto/*
637290000Sglebius * mprintf_clock_stats - write clock statistics to file with
638290000Sglebius *			msnprintf-style formatting.
639290000Sglebius */
640290000Sglebiusint
641290000Sglebiusmprintf_clock_stats(
642290000Sglebius	sockaddr_u *addr,
643290000Sglebius	const char *fmt,
644290000Sglebius	...
645290000Sglebius	)
646290000Sglebius{
647290000Sglebius	va_list	ap;
648290000Sglebius	int	rc;
649290000Sglebius	char	msg[512];
650290000Sglebius
651290000Sglebius	va_start(ap, fmt);
652290000Sglebius	rc = mvsnprintf(msg, sizeof(msg), fmt, ap);
653290000Sglebius	va_end(ap);
654290000Sglebius	if (stats_control)
655290000Sglebius		record_clock_stats(addr, msg);
656290000Sglebius
657290000Sglebius	return rc;
658290000Sglebius}
659290000Sglebius
660290000Sglebius/*
66154359Sroberto * record_raw_stats - write raw timestamps to file
66254359Sroberto *
66354359Sroberto * file format
664290000Sglebius * day (MJD)
66554359Sroberto * time (s past midnight)
66654359Sroberto * peer ip address
667290000Sglebius * IP address
66854359Sroberto * t1 t2 t3 t4 timestamps
66954359Sroberto */
67054359Srobertovoid
67154359Srobertorecord_raw_stats(
672290000Sglebius	sockaddr_u *srcadr,
673290000Sglebius	sockaddr_u *dstadr,
674290000Sglebius	l_fp	*t1,		/* originate timestamp */
675290000Sglebius	l_fp	*t2,		/* receive timestamp */
676290000Sglebius	l_fp	*t3,		/* transmit timestamp */
677290000Sglebius	l_fp	*t4,		/* destination timestamp */
678290000Sglebius	int	leap,
679290000Sglebius	int	version,
680290000Sglebius	int	mode,
681290000Sglebius	int	stratum,
682290000Sglebius	int	ppoll,
683290000Sglebius	int	precision,
684290000Sglebius	double	root_delay,	/* seconds */
685290000Sglebius	double	root_dispersion,/* seconds */
686290000Sglebius	u_int32	refid
68754359Sroberto	)
68854359Sroberto{
689132451Sroberto	l_fp	now;
690132451Sroberto	u_long	day;
69154359Sroberto
69254359Sroberto	if (!stats_control)
69354359Sroberto		return;
69454359Sroberto
695132451Sroberto	get_systime(&now);
696132451Sroberto	filegen_setup(&rawstats, now.l_ui);
697132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
698132451Sroberto	now.l_ui %= 86400;
69954359Sroberto	if (rawstats.fp != NULL) {
700290000Sglebius		fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s %d %d %d %d %d %d %.6f %.6f %s\n",
701290000Sglebius		    day, ulfptoa(&now, 3),
702290000Sglebius		    stoa(srcadr), dstadr ?  stoa(dstadr) : "-",
703290000Sglebius		    ulfptoa(t1, 9), ulfptoa(t2, 9),
704290000Sglebius		    ulfptoa(t3, 9), ulfptoa(t4, 9),
705290000Sglebius		    leap, version, mode, stratum, ppoll, precision,
706290000Sglebius		    root_delay, root_dispersion, refid_str(refid, stratum));
70754359Sroberto		fflush(rawstats.fp);
70854359Sroberto	}
70954359Sroberto}
71054359Sroberto
711132451Sroberto
71254359Sroberto/*
713132451Sroberto * record_sys_stats - write system statistics to file
714132451Sroberto *
715132451Sroberto * file format
716290000Sglebius * day (MJD)
717132451Sroberto * time (s past midnight)
718290000Sglebius * time since reset
719132451Sroberto * packets recieved
720290000Sglebius * packets for this host
721132451Sroberto * current version
722290000Sglebius * old version
723132451Sroberto * access denied
724132451Sroberto * bad length or format
725132451Sroberto * bad authentication
726290000Sglebius * declined
727132451Sroberto * rate exceeded
728290000Sglebius * KoD sent
729132451Sroberto */
730132451Srobertovoid
731132451Srobertorecord_sys_stats(void)
732132451Sroberto{
733132451Sroberto	l_fp	now;
734132451Sroberto	u_long	day;
735132451Sroberto
736132451Sroberto	if (!stats_control)
737132451Sroberto		return;
738132451Sroberto
739132451Sroberto	get_systime(&now);
740132451Sroberto	filegen_setup(&sysstats, now.l_ui);
741132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
742132451Sroberto	now.l_ui %= 86400;
743132451Sroberto	if (sysstats.fp != NULL) {
744290000Sglebius		fprintf(sysstats.fp,
745290000Sglebius		    "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
746290000Sglebius		    day, ulfptoa(&now, 3), current_time - sys_stattime,
747290000Sglebius		    sys_received, sys_processed, sys_newversion,
748290000Sglebius		    sys_oldversion, sys_restricted, sys_badlength,
749290000Sglebius		    sys_badauth, sys_declined, sys_limitrejected,
750290000Sglebius		    sys_kodsent);
751132451Sroberto		fflush(sysstats.fp);
752132451Sroberto		proto_clr_stats();
753132451Sroberto	}
754132451Sroberto}
755132451Sroberto
756132451Sroberto
757132451Sroberto/*
758290000Sglebius * record_proto_stats - write system statistics to file
759290000Sglebius *
760290000Sglebius * file format
761290000Sglebius * day (MJD)
762290000Sglebius * time (s past midnight)
763290000Sglebius * text message
764290000Sglebius */
765290000Sglebiusvoid
766290000Sglebiusrecord_proto_stats(
767290000Sglebius	char	*str		/* text string */
768290000Sglebius	)
769290000Sglebius{
770290000Sglebius	l_fp	now;
771290000Sglebius	u_long	day;
772290000Sglebius
773290000Sglebius	if (!stats_control)
774290000Sglebius		return;
775290000Sglebius
776290000Sglebius	get_systime(&now);
777290000Sglebius	filegen_setup(&protostats, now.l_ui);
778290000Sglebius	day = now.l_ui / 86400 + MJD_1900;
779290000Sglebius	now.l_ui %= 86400;
780290000Sglebius	if (protostats.fp != NULL) {
781290000Sglebius		fprintf(protostats.fp, "%lu %s %s\n", day,
782290000Sglebius		    ulfptoa(&now, 3), str);
783290000Sglebius		fflush(protostats.fp);
784290000Sglebius	}
785290000Sglebius}
786290000Sglebius
787290000Sglebius
788290000Sglebius#ifdef AUTOKEY
789290000Sglebius/*
790132451Sroberto * record_crypto_stats - write crypto statistics to file
791132451Sroberto *
792132451Sroberto * file format:
793132451Sroberto * day (mjd)
794132451Sroberto * time (s past midnight)
795290000Sglebius * peer ip address
796132451Sroberto * text message
797132451Sroberto */
798132451Srobertovoid
799132451Srobertorecord_crypto_stats(
800290000Sglebius	sockaddr_u *addr,
801290000Sglebius	const char *text	/* text message */
802132451Sroberto	)
803132451Sroberto{
804132451Sroberto	l_fp	now;
805132451Sroberto	u_long	day;
806132451Sroberto
807132451Sroberto	if (!stats_control)
808132451Sroberto		return;
809132451Sroberto
810132451Sroberto	get_systime(&now);
811132451Sroberto	filegen_setup(&cryptostats, now.l_ui);
812132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
813132451Sroberto	now.l_ui %= 86400;
814132451Sroberto	if (cryptostats.fp != NULL) {
815132451Sroberto		if (addr == NULL)
816290000Sglebius			fprintf(cryptostats.fp, "%lu %s 0.0.0.0 %s\n",
817132451Sroberto			    day, ulfptoa(&now, 3), text);
818132451Sroberto		else
819132451Sroberto			fprintf(cryptostats.fp, "%lu %s %s %s\n",
820132451Sroberto			    day, ulfptoa(&now, 3), stoa(addr), text);
821132451Sroberto		fflush(cryptostats.fp);
822132451Sroberto	}
823132451Sroberto}
824290000Sglebius#endif	/* AUTOKEY */
825132451Sroberto
826290000Sglebius
827182007Sroberto#ifdef DEBUG_TIMING
828182007Sroberto/*
829290000Sglebius * record_timing_stats - write timing statistics to file
830182007Sroberto *
831182007Sroberto * file format:
832182007Sroberto * day (mjd)
833182007Sroberto * time (s past midnight)
834182007Sroberto * text message
835182007Sroberto */
836182007Srobertovoid
837182007Srobertorecord_timing_stats(
838290000Sglebius	const char *text	/* text message */
839182007Sroberto	)
840182007Sroberto{
841182007Sroberto	static unsigned int flshcnt;
842182007Sroberto	l_fp	now;
843182007Sroberto	u_long	day;
844132451Sroberto
845182007Sroberto	if (!stats_control)
846182007Sroberto		return;
847182007Sroberto
848182007Sroberto	get_systime(&now);
849182007Sroberto	filegen_setup(&timingstats, now.l_ui);
850182007Sroberto	day = now.l_ui / 86400 + MJD_1900;
851182007Sroberto	now.l_ui %= 86400;
852182007Sroberto	if (timingstats.fp != NULL) {
853290000Sglebius		fprintf(timingstats.fp, "%lu %s %s\n", day, lfptoa(&now,
854290000Sglebius		    3), text);
855182007Sroberto		if (++flshcnt % 100 == 0)
856182007Sroberto			fflush(timingstats.fp);
857182007Sroberto	}
858182007Sroberto}
859182007Sroberto#endif
860290000Sglebius
861290000Sglebius
862132451Sroberto/*
863290000Sglebius * check_leap_file - See if the leapseconds file has been updated.
864290000Sglebius *
865290000Sglebius * Returns: n/a
866290000Sglebius *
867290000Sglebius * Note: This loads a new leapfile on the fly. Currently a leap file
868290000Sglebius * without SHA1 signature is accepted, but if there is a signature line,
869290000Sglebius * the signature must be valid or the file is rejected.
870290000Sglebius */
871290000Sglebiusvoid
872290000Sglebiuscheck_leap_file(
873290000Sglebius	int           is_daily_check,
874290000Sglebius	uint32_t      ntptime       ,
875290000Sglebius	const time_t *systime
876290000Sglebius	)
877290000Sglebius{
878290000Sglebius	/* just do nothing if there is no leap file */
879290000Sglebius	if ( ! (leapfile_name && *leapfile_name))
880290000Sglebius		return;
881290000Sglebius
882290000Sglebius	/* try to load leapfile, force it if no leapfile loaded yet */
883290000Sglebius	if (leapsec_load_file(
884290000Sglebius		    leapfile_name, &leapfile_stat,
885290000Sglebius		    !have_leapfile, is_daily_check))
886290000Sglebius		have_leapfile = TRUE;
887290000Sglebius	else if (!have_leapfile)
888290000Sglebius		return;
889290000Sglebius
890290000Sglebius	check_leap_expiration(is_daily_check, ntptime, systime);
891290000Sglebius}
892290000Sglebius
893290000Sglebius/*
894290000Sglebius * check expiration of a loaded leap table
895290000Sglebius */
896290000Sglebiusstatic void
897290000Sglebiuscheck_leap_expiration(
898290000Sglebius	int           is_daily_check,
899290000Sglebius	uint32_t      ntptime       ,
900290000Sglebius	const time_t *systime
901290000Sglebius	)
902290000Sglebius{
903290000Sglebius	static const char * const logPrefix = "leapsecond file";
904290000Sglebius	int  rc;
905290000Sglebius
906290000Sglebius	/* test the expiration of the leap data and log with proper
907290000Sglebius	 * level and frequency (once/hour or once/day, depending on the
908290000Sglebius	 * state.
909290000Sglebius	 */
910290000Sglebius	rc = leapsec_daystolive(ntptime, systime);
911290000Sglebius	if (rc == 0) {
912290000Sglebius		msyslog(LOG_WARNING,
913290000Sglebius			"%s ('%s'): will expire in less than one day",
914290000Sglebius			logPrefix, leapfile_name);
915290000Sglebius	} else if (is_daily_check && rc < 28) {
916290000Sglebius		if (rc < 0)
917290000Sglebius			msyslog(LOG_ERR,
918290000Sglebius				"%s ('%s'): expired less than %d day%s ago",
919290000Sglebius				logPrefix, leapfile_name, -rc, (rc == -1 ? "" : "s"));
920290000Sglebius		else
921290000Sglebius			msyslog(LOG_WARNING,
922290000Sglebius				"%s ('%s'): will expire in less than %d days",
923290000Sglebius				logPrefix, leapfile_name, 1+rc);
924290000Sglebius	}
925290000Sglebius}
926290000Sglebius
927290000Sglebius
928290000Sglebius/*
92954359Sroberto * getauthkeys - read the authentication keys from the specified file
93054359Sroberto */
93154359Srobertovoid
93254359Srobertogetauthkeys(
933182007Sroberto	const char *keyfile
93454359Sroberto	)
93554359Sroberto{
936293894Sglebius	size_t len;
93754359Sroberto
93854359Sroberto	len = strlen(keyfile);
939290000Sglebius	if (!len)
94054359Sroberto		return;
94154359Sroberto
94254359Sroberto#ifndef SYS_WINNT
943290000Sglebius	key_file_name = erealloc(key_file_name, len + 1);
944290000Sglebius	memcpy(key_file_name, keyfile, len + 1);
94554359Sroberto#else
946290000Sglebius	key_file_name = erealloc(key_file_name, _MAX_PATH);
947290000Sglebius	if (len + 1 > _MAX_PATH)
948290000Sglebius		return;
949290000Sglebius	if (!ExpandEnvironmentStrings(keyfile, key_file_name,
950290000Sglebius				      _MAX_PATH)) {
95154359Sroberto		msyslog(LOG_ERR,
952290000Sglebius			"ExpandEnvironmentStrings(KEY_FILE) failed: %m");
953290000Sglebius		strlcpy(key_file_name, keyfile, _MAX_PATH);
95454359Sroberto	}
955290000Sglebius	key_file_name = erealloc(key_file_name,
956290000Sglebius				 1 + strlen(key_file_name));
95754359Sroberto#endif /* SYS_WINNT */
95854359Sroberto
95954359Sroberto	authreadkeys(key_file_name);
96054359Sroberto}
96154359Sroberto
96254359Sroberto
96354359Sroberto/*
96454359Sroberto * rereadkeys - read the authentication key file over again.
96554359Sroberto */
96654359Srobertovoid
96754359Srobertorereadkeys(void)
96854359Sroberto{
969290000Sglebius	if (NULL != key_file_name)
970290000Sglebius		authreadkeys(key_file_name);
97154359Sroberto}
972132451Sroberto
973290000Sglebius
974290000Sglebius#if notyet
975132451Sroberto/*
976290000Sglebius * ntp_exit - document explicitly that ntpd has exited
977132451Sroberto */
978290000Sglebiusvoid
979290000Sglebiusntp_exit(int retval)
980290000Sglebius{
981290000Sglebius	msyslog(LOG_ERR, "EXITING with return code %d", retval);
982290000Sglebius	exit(retval);
983290000Sglebius}
984290000Sglebius#endif
985290000Sglebius
986290000Sglebius/*
987290000Sglebius * fstostr - prettyprint NTP seconds
988290000Sglebius */
989290000Sglebiuschar * fstostr(
990290000Sglebius	time_t	ntp_stamp
991132451Sroberto	)
992132451Sroberto{
993290000Sglebius	char *		buf;
994290000Sglebius	struct calendar tm;
995132451Sroberto
996290000Sglebius	LIB_GETBUF(buf);
997290000Sglebius	if (ntpcal_ntp_to_date(&tm, (u_int32)ntp_stamp, NULL) < 0)
998290000Sglebius		snprintf(buf, LIB_BUFLENGTH, "ntpcal_ntp_to_date: %ld: range error",
999290000Sglebius			 (long)ntp_stamp);
1000290000Sglebius	else
1001290000Sglebius		snprintf(buf, LIB_BUFLENGTH, "%04d%02d%02d%02d%02d",
1002290000Sglebius			 tm.year, tm.month, tm.monthday,
1003290000Sglebius			 tm.hour, tm.minute);
1004290000Sglebius	return buf;
1005290000Sglebius}
1006132451Sroberto
1007132451Sroberto
1008182007Sroberto/*
1009290000Sglebius * ntpd_time_stepped is called back by step_systime(), allowing ntpd
1010290000Sglebius * to do any one-time processing necessitated by the step.
1011182007Sroberto */
1012182007Srobertovoid
1013290000Sglebiusntpd_time_stepped(void)
1014182007Sroberto{
1015290000Sglebius	u_int saved_mon_enabled;
1016290000Sglebius
1017290000Sglebius	/*
1018290000Sglebius	 * flush the monitor MRU list which contains l_fp timestamps
1019290000Sglebius	 * which should not be compared across the step.
1020290000Sglebius	 */
1021290000Sglebius	if (MON_OFF != mon_enabled) {
1022290000Sglebius		saved_mon_enabled = mon_enabled;
1023290000Sglebius		mon_stop(MON_OFF);
1024290000Sglebius		mon_start(saved_mon_enabled);
1025290000Sglebius	}
1026290000Sglebius
1027290000Sglebius	/* inform interpolating Windows code to allow time to go back */
1028290000Sglebius#ifdef SYS_WINNT
1029290000Sglebius	win_time_stepped();
1030290000Sglebius#endif
1031182007Sroberto}
1032