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"
13280849Scy#include "ntp_assert.h"
14280849Scy#include "ntp_calendar.h"
15280849Scy#include "ntp_leapsec.h"
16280849Scy#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
24280849Scy#ifdef HAVE_UNISTD_H
25280849Scy# include <unistd.h>
26280849Scy#endif
27280849Scy#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/*
41280849Scy * Defines used by the leapseconds stuff
4254359Sroberto */
43280849Scy#define	MAX_TAI	100			/* max TAI offset (s) */
44280849Scy#define	L_DAY	86400UL			/* seconds per day */
45280849Scy#define	L_YEAR	(L_DAY * 365)		/* days per year */
46280849Scy#define	L_LYEAR	(L_YEAR + L_DAY)	/* days per leap year */
47280849Scy#define	L_4YEAR	(L_LYEAR + 3 * L_YEAR)	/* days per leap cycle */
48280849Scy#define	L_CENT	(L_4YEAR * 25)		/* days per century */
49280849Scy
5054359Sroberto/*
51280849Scy * This contains odds and ends, including the hourly stats, various
52280849Scy * configuration items, leapseconds stuff, etc.
5354359Sroberto */
5454359Sroberto/*
55280849Scy * File names
5654359Sroberto */
57280849Scystatic	char *key_file_name;		/* keys file name */
58280849Scystatic char	  *leapfile_name;		/* leapseconds file name */
59280849Scystatic struct stat leapfile_stat;	/* leapseconds file stat() buffer */
60280849Scystatic int /*BOOL*/have_leapfile = FALSE;
61358659Scystatic int /*BOOL*/chck_leaphash = TRUE;
62280849Scychar	*stats_drift_file;		/* frequency file name */
63280849Scystatic	char *stats_temp_file;		/* temp frequency file name */
64280849Scystatic double wander_resid;		/* last frequency update */
65280849Scydouble	wander_threshold = 1e-7;	/* initial frequency threshold */
6654359Sroberto
6754359Sroberto/*
6854359Sroberto * Statistics file stuff
6954359Sroberto */
7054359Sroberto#ifndef NTP_VAR
71182007Sroberto# ifndef SYS_WINNT
72182007Sroberto#  define NTP_VAR "/var/NTP/"		/* NOTE the trailing '/' */
73182007Sroberto# else
74280849Scy#  define NTP_VAR "c:\\var\\ntp\\"	/* NOTE the trailing '\\' */
75182007Sroberto# endif /* SYS_WINNT */
7654359Sroberto#endif
7754359Sroberto
7854359Sroberto
79280849Scychar statsdir[MAXFILENAME] = NTP_VAR;
8054359Srobertostatic FILEGEN peerstats;
8154359Srobertostatic FILEGEN loopstats;
8254359Srobertostatic FILEGEN clockstats;
8354359Srobertostatic FILEGEN rawstats;
84132451Srobertostatic FILEGEN sysstats;
85280849Scystatic FILEGEN protostats;
86280849Scystatic FILEGEN cryptostats;
87182007Srobertostatic FILEGEN timingstats;
8854359Sroberto
8954359Sroberto/*
9054359Sroberto * This controls whether stats are written to the fileset. Provided
91358659Scy * so that ntpdc can turn off stats when the file system fills up.
9254359Sroberto */
9354359Srobertoint stats_control;
9454359Sroberto
9554359Sroberto/*
96280849Scy * Last frequency written to file.
97182007Sroberto */
98280849Scystatic double prev_drift_comp;		/* last frequency update */
99182007Sroberto
100182007Sroberto/*
101280849Scy * Function prototypes
10254359Sroberto */
103280849Scystatic	void	record_sys_stats(void);
104280849Scy	void	ntpd_time_stepped(void);
105280849Scystatic  void	check_leap_expiration(int, uint32_t, const time_t*);
106280849Scy
107358659Scy/*
108280849Scy * Prototypes
109280849Scy */
110280849Scy#ifdef DEBUG
111280849Scyvoid	uninit_util(void);
112280849Scy#endif
113280849Scy
114280849Scy/*
115280849Scy * uninit_util - free memory allocated by init_util
116280849Scy */
117280849Scy#ifdef DEBUG
11854359Srobertovoid
119280849Scyuninit_util(void)
12054359Sroberto{
121280849Scy#if defined(_MSC_VER) && defined (_DEBUG)
122280849Scy	_CrtCheckMemory();
123280849Scy#endif
124280849Scy	if (stats_drift_file) {
125280849Scy		free(stats_drift_file);
126280849Scy		free(stats_temp_file);
127280849Scy		stats_drift_file = NULL;
128280849Scy		stats_temp_file = NULL;
129280849Scy	}
130280849Scy	if (key_file_name) {
131280849Scy		free(key_file_name);
132280849Scy		key_file_name = NULL;
133280849Scy	}
134280849Scy	filegen_unregister("peerstats");
135280849Scy	filegen_unregister("loopstats");
136280849Scy	filegen_unregister("clockstats");
137280849Scy	filegen_unregister("rawstats");
138280849Scy	filegen_unregister("sysstats");
139280849Scy	filegen_unregister("protostats");
140280849Scy#ifdef AUTOKEY
141280849Scy	filegen_unregister("cryptostats");
142280849Scy#endif	/* AUTOKEY */
143280849Scy#ifdef DEBUG_TIMING
144280849Scy	filegen_unregister("timingstats");
145280849Scy#endif	/* DEBUG_TIMING */
14654359Sroberto
147280849Scy#if defined(_MSC_VER) && defined (_DEBUG)
148280849Scy	_CrtCheckMemory();
149280849Scy#endif
150280849Scy}
151280849Scy#endif /* DEBUG */
152132451Sroberto
15354359Sroberto
154280849Scy/*
155280849Scy * init_util - initialize the util module of ntpd
156280849Scy */
157280849Scyvoid
158280849Scyinit_util(void)
159280849Scy{
160280849Scy	filegen_register(statsdir, "peerstats",	  &peerstats);
161280849Scy	filegen_register(statsdir, "loopstats",	  &loopstats);
162280849Scy	filegen_register(statsdir, "clockstats",  &clockstats);
163280849Scy	filegen_register(statsdir, "rawstats",	  &rawstats);
164280849Scy	filegen_register(statsdir, "sysstats",	  &sysstats);
165280849Scy	filegen_register(statsdir, "protostats",  &protostats);
166280849Scy	filegen_register(statsdir, "cryptostats", &cryptostats);
167280849Scy	filegen_register(statsdir, "timingstats", &timingstats);
168280849Scy	/*
169280849Scy	 * register with libntp ntp_set_tod() to call us back
170280849Scy	 * when time is stepped.
171280849Scy	 */
172280849Scy	step_callback = &ntpd_time_stepped;
173280849Scy#ifdef DEBUG
174280849Scy	atexit(&uninit_util);
175280849Scy#endif /* DEBUG */
17654359Sroberto}
17754359Sroberto
17854359Sroberto
17954359Sroberto/*
18054359Sroberto * hourly_stats - print some interesting stats
18154359Sroberto */
18254359Srobertovoid
183182007Srobertowrite_stats(void)
18454359Sroberto{
185280849Scy	FILE	*fp;
18654359Sroberto#ifdef DOSYNCTODR
18754359Sroberto	struct timeval tv;
18854359Sroberto#if !defined(VMS)
189280849Scy	int	prio_set;
19054359Sroberto#endif
19154359Sroberto#ifdef HAVE_GETCLOCK
192280849Scy	struct timespec ts;
19354359Sroberto#endif
194280849Scy	int	o_prio;
19554359Sroberto
19654359Sroberto	/*
19754359Sroberto	 * Sometimes having a Sun can be a drag.
19854359Sroberto	 *
19954359Sroberto	 * The kernel variable dosynctodr controls whether the system's
20054359Sroberto	 * soft clock is kept in sync with the battery clock. If it
20154359Sroberto	 * is zero, then the soft clock is not synced, and the battery
20254359Sroberto	 * clock is simply left to rot. That means that when the system
20354359Sroberto	 * reboots, the battery clock (which has probably gone wacky)
20454359Sroberto	 * sets the soft clock. That means ntpd starts off with a very
20554359Sroberto	 * confused idea of what time it is. It then takes a large
20654359Sroberto	 * amount of time to figure out just how wacky the battery clock
20754359Sroberto	 * has made things drift, etc, etc. The solution is to make the
20854359Sroberto	 * battery clock sync up to system time. The way to do THAT is
20954359Sroberto	 * to simply set the time of day to the current time of day, but
21054359Sroberto	 * as quickly as possible. This may, or may not be a sensible
21154359Sroberto	 * thing to do.
21254359Sroberto	 *
21354359Sroberto	 * CAVEAT: settimeofday() steps the sun clock by about 800 us,
214280849Scy	 *	   so setting DOSYNCTODR seems a bad idea in the
215280849Scy	 *	   case of us resolution
21654359Sroberto	 */
21754359Sroberto
21854359Sroberto#if !defined(VMS)
219280849Scy	/*
220280849Scy	 * (prr) getpriority returns -1 on error, but -1 is also a valid
221132451Sroberto	 * return value (!), so instead we have to zero errno before the
222132451Sroberto	 * call and check it for non-zero afterwards.
22354359Sroberto	 */
22454359Sroberto	errno = 0;
22554359Sroberto	prio_set = 0;
22654359Sroberto	o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */
22754359Sroberto
228132451Sroberto	/*
229132451Sroberto	 * (prr) if getpriority succeeded, call setpriority to raise
23054359Sroberto	 * scheduling priority as high as possible.  If that succeeds
23154359Sroberto	 * as well, set the prio_set flag so we remember to reset
232132451Sroberto	 * priority to its previous value below.  Note that on Solaris
233132451Sroberto	 * 2.6 (and beyond?), both getpriority and setpriority will fail
234132451Sroberto	 * with ESRCH, because sched_setscheduler (called from main) put
235132451Sroberto	 * us in the real-time scheduling class which setpriority
236132451Sroberto	 * doesn't know about. Being in the real-time class is better
237132451Sroberto	 * than anything setpriority can do, anyhow, so this error is
238132451Sroberto	 * silently ignored.
23954359Sroberto	 */
24054359Sroberto	if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
241132451Sroberto		prio_set = 1;	/* overdrive */
24254359Sroberto#endif /* VMS */
24354359Sroberto#ifdef HAVE_GETCLOCK
244280849Scy	(void) getclock(TIMEOFDAY, &ts);
245280849Scy	tv.tv_sec = ts.tv_sec;
246280849Scy	tv.tv_usec = ts.tv_nsec / 1000;
24754359Sroberto#else /*  not HAVE_GETCLOCK */
24854359Sroberto	GETTIMEOFDAY(&tv,(struct timezone *)NULL);
24954359Sroberto#endif /* not HAVE_GETCLOCK */
250280849Scy	if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0)
25154359Sroberto		msyslog(LOG_ERR, "can't sync battery time: %m");
25254359Sroberto#if !defined(VMS)
25354359Sroberto	if (prio_set)
254132451Sroberto		setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */
25554359Sroberto#endif /* VMS */
25654359Sroberto#endif /* DOSYNCTODR */
257132451Sroberto	record_sys_stats();
25854359Sroberto	if (stats_drift_file != 0) {
259280849Scy
260280849Scy		/*
261280849Scy		 * When the frequency file is written, initialize the
262280849Scy		 * prev_drift_comp and wander_resid. Thereafter,
263280849Scy		 * reduce the wander_resid by half each hour. When
264280849Scy		 * the difference between the prev_drift_comp and
265280849Scy		 * drift_comp is less than the wander_resid, update
266280849Scy		 * the frequncy file. This minimizes the file writes to
267280849Scy		 * nonvolaile storage.
268280849Scy		 */
269280849Scy#ifdef DEBUG
270280849Scy		if (debug)
271280849Scy			printf("write_stats: frequency %.6lf thresh %.6lf, freq %.6lf\n",
272280849Scy			    (prev_drift_comp - drift_comp) * 1e6, wander_resid *
273280849Scy			    1e6, drift_comp * 1e6);
274280849Scy#endif
275280849Scy		if (fabs(prev_drift_comp - drift_comp) < wander_resid) {
276280849Scy			wander_resid *= 0.5;
277280849Scy			return;
278280849Scy		}
279280849Scy		prev_drift_comp = drift_comp;
280280849Scy		wander_resid = wander_threshold;
28154359Sroberto		if ((fp = fopen(stats_temp_file, "w")) == NULL) {
282280849Scy			msyslog(LOG_ERR, "frequency file %s: %m",
28354359Sroberto			    stats_temp_file);
28454359Sroberto			return;
28554359Sroberto		}
28654359Sroberto		fprintf(fp, "%.3f\n", drift_comp * 1e6);
28754359Sroberto		(void)fclose(fp);
28854359Sroberto		/* atomic */
28954359Sroberto#ifdef SYS_WINNT
290280849Scy		if (_unlink(stats_drift_file)) /* rename semantics differ under NT */
291358659Scy			msyslog(LOG_WARNING,
292358659Scy				"Unable to remove prior drift file %s, %m",
293280849Scy				stats_drift_file);
29454359Sroberto#endif /* SYS_WINNT */
29554359Sroberto
29654359Sroberto#ifndef NO_RENAME
297280849Scy		if (rename(stats_temp_file, stats_drift_file))
298358659Scy			msyslog(LOG_WARNING,
299358659Scy				"Unable to rename temp drift file %s to %s, %m",
300280849Scy				stats_temp_file, stats_drift_file);
30154359Sroberto#else
302182007Sroberto		/* we have no rename NFS of ftp in use */
303280849Scy		if ((fp = fopen(stats_drift_file, "w")) ==
304280849Scy		    NULL) {
305280849Scy			msyslog(LOG_ERR,
306280849Scy			    "frequency file %s: %m",
30754359Sroberto			    stats_drift_file);
30854359Sroberto			return;
30954359Sroberto		}
31054359Sroberto#endif
31154359Sroberto
31254359Sroberto#if defined(VMS)
31354359Sroberto		/* PURGE */
31454359Sroberto		{
31554359Sroberto			$DESCRIPTOR(oldvers,";-1");
31654359Sroberto			struct dsc$descriptor driftdsc = {
317280849Scy				strlen(stats_drift_file), 0, 0,
318280849Scy				    stats_drift_file };
319280849Scy			while(lib$delete_file(&oldvers,
320280849Scy			    &driftdsc) & 1);
32154359Sroberto		}
32254359Sroberto#endif
32354359Sroberto	}
32454359Sroberto}
32554359Sroberto
32654359Sroberto
32754359Sroberto/*
32854359Sroberto * stats_config - configure the stats operation
32954359Sroberto */
33054359Srobertovoid
33154359Srobertostats_config(
33254359Sroberto	int item,
333358659Scy	const char *invalue,	/* only one type so far */
334358659Scy	int optflag
33554359Sroberto	)
33654359Sroberto{
337280849Scy	FILE	*fp;
338182007Sroberto	const char *value;
339293423Sdelphij	size_t	len;
340280849Scy	double	old_drift;
341280849Scy	l_fp	now;
342280849Scy	time_t  ttnow;
343280849Scy#ifndef VMS
344280849Scy	const char temp_ext[] = ".TEMP";
345280849Scy#else
346280849Scy	const char temp_ext[] = "-TEMP";
347280849Scy#endif
34854359Sroberto
349132451Sroberto	/*
350132451Sroberto	 * Expand environment strings under Windows NT, since the
351132451Sroberto	 * command interpreter doesn't do this, the program must.
35254359Sroberto	 */
35354359Sroberto#ifdef SYS_WINNT
35454359Sroberto	char newvalue[MAX_PATH], parameter[MAX_PATH];
35554359Sroberto
356132451Sroberto	if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
357280849Scy		switch (item) {
358280849Scy		case STATS_FREQ_FILE:
359280849Scy			strlcpy(parameter, "STATS_FREQ_FILE",
360280849Scy				sizeof(parameter));
36154359Sroberto			break;
362280849Scy
363280849Scy		case STATS_LEAP_FILE:
364280849Scy			strlcpy(parameter, "STATS_LEAP_FILE",
365280849Scy				sizeof(parameter));
36654359Sroberto			break;
367280849Scy
368280849Scy		case STATS_STATSDIR:
369280849Scy			strlcpy(parameter, "STATS_STATSDIR",
370280849Scy				sizeof(parameter));
37154359Sroberto			break;
372280849Scy
373280849Scy		case STATS_PID_FILE:
374280849Scy			strlcpy(parameter, "STATS_PID_FILE",
375280849Scy				sizeof(parameter));
37654359Sroberto			break;
377280849Scy
378280849Scy		default:
379280849Scy			strlcpy(parameter, "UNKNOWN",
380280849Scy				sizeof(parameter));
381280849Scy			break;
38254359Sroberto		}
38354359Sroberto		value = invalue;
38454359Sroberto		msyslog(LOG_ERR,
385280849Scy			"ExpandEnvironmentStrings(%s) failed: %m\n",
386280849Scy			parameter);
387132451Sroberto	} else {
388132451Sroberto		value = newvalue;
38954359Sroberto	}
390358659Scy#else
39154359Sroberto	value = invalue;
39254359Sroberto#endif /* SYS_WINNT */
39354359Sroberto
394280849Scy	switch (item) {
39554359Sroberto
396280849Scy	/*
397280849Scy	 * Open and read frequency file.
398280849Scy	 */
399280849Scy	case STATS_FREQ_FILE:
400280849Scy		if (!value || (len = strlen(value)) == 0)
401280849Scy			break;
40254359Sroberto
403280849Scy		stats_drift_file = erealloc(stats_drift_file, len + 1);
404358659Scy		stats_temp_file = erealloc(stats_temp_file,
405280849Scy		    len + sizeof(".TEMP"));
406280849Scy		memcpy(stats_drift_file, value, (size_t)(len+1));
407280849Scy		memcpy(stats_temp_file, value, (size_t)len);
408280849Scy		memcpy(stats_temp_file + len, temp_ext, sizeof(temp_ext));
40954359Sroberto
41054359Sroberto		/*
411132451Sroberto		 * Open drift file and read frequency. If the file is
412132451Sroberto		 * missing or contains errors, tell the loop to reset.
41354359Sroberto		 */
414280849Scy		if ((fp = fopen(stats_drift_file, "r")) == NULL)
41554359Sroberto			break;
416280849Scy
41754359Sroberto		if (fscanf(fp, "%lf", &old_drift) != 1) {
418280849Scy			msyslog(LOG_ERR,
419358659Scy				"format error frequency file %s",
420280849Scy				stats_drift_file);
421132451Sroberto			fclose(fp);
42254359Sroberto			break;
423280849Scy
42454359Sroberto		}
425132451Sroberto		fclose(fp);
426280849Scy		loop_config(LOOP_FREQ, old_drift);
427280849Scy		prev_drift_comp = drift_comp;
42854359Sroberto		break;
429280849Scy
430280849Scy	/*
431280849Scy	 * Specify statistics directory.
432280849Scy	 */
433280849Scy	case STATS_STATSDIR:
434280849Scy
435280849Scy		/* - 1 since value may be missing the DIR_SEP. */
436280849Scy		if (strlen(value) >= sizeof(statsdir) - 1) {
43754359Sroberto			msyslog(LOG_ERR,
438280849Scy			    "statsdir too long (>%d, sigh)",
439280849Scy			    (int)sizeof(statsdir) - 2);
44054359Sroberto		} else {
441280849Scy			int add_dir_sep;
442293423Sdelphij			size_t value_l;
44354359Sroberto
444280849Scy			/* Add a DIR_SEP unless we already have one. */
445280849Scy			value_l = strlen(value);
446280849Scy			if (0 == value_l)
447280849Scy				add_dir_sep = FALSE;
448280849Scy			else
449280849Scy				add_dir_sep = (DIR_SEP !=
450280849Scy				    value[value_l - 1]);
451280849Scy
452280849Scy			if (add_dir_sep)
453280849Scy				snprintf(statsdir, sizeof(statsdir),
454280849Scy				    "%s%c", value, DIR_SEP);
455280849Scy			else
456280849Scy				snprintf(statsdir, sizeof(statsdir),
457280849Scy				    "%s", value);
458280849Scy			filegen_statsdir();
45954359Sroberto		}
46054359Sroberto		break;
46154359Sroberto
462280849Scy	/*
463280849Scy	 * Open pid file.
464280849Scy	 */
465280849Scy	case STATS_PID_FILE:
46654359Sroberto		if ((fp = fopen(value, "w")) == NULL) {
467280849Scy			msyslog(LOG_ERR, "pid file %s: %m",
468280849Scy			    value);
46954359Sroberto			break;
47054359Sroberto		}
471280849Scy		fprintf(fp, "%d", (int)getpid());
472280849Scy		fclose(fp);
47354359Sroberto		break;
47454359Sroberto
475280849Scy	/*
476280849Scy	 * Read leapseconds file.
477280849Scy	 *
478280849Scy	 * Note: Currently a leap file without SHA1 signature is
479280849Scy	 * accepted, but if there is a signature line, the signature
480280849Scy	 * must be valid or the file is rejected.
481280849Scy	 */
482280849Scy	case STATS_LEAP_FILE:
483280849Scy		if (!value || (len = strlen(value)) == 0)
484280849Scy			break;
485280849Scy
486280849Scy		leapfile_name = erealloc(leapfile_name, len + 1);
487280849Scy		memcpy(leapfile_name, value, len + 1);
488358659Scy		chck_leaphash = optflag;
489280849Scy
490280849Scy		if (leapsec_load_file(
491358659Scy			    leapfile_name, &leapfile_stat,
492358659Scy			    TRUE, TRUE, chck_leaphash))
493280849Scy		{
494280849Scy			leap_signature_t lsig;
495280849Scy
496280849Scy			get_systime(&now);
497280849Scy			time(&ttnow);
498280849Scy			leapsec_getsig(&lsig);
499280849Scy			mprintf_event(EVNT_TAI, NULL,
500280849Scy				      "%d leap %s %s %s",
501280849Scy				      lsig.taiof,
502280849Scy				      fstostr(lsig.ttime),
503280849Scy				      leapsec_expired(now.l_ui, NULL)
504280849Scy					  ? "expired"
505280849Scy					  : "expires",
506280849Scy				      fstostr(lsig.etime));
507280849Scy
508280849Scy			have_leapfile = TRUE;
509280849Scy
510280849Scy			/* force an immediate daily expiration check of
511280849Scy			 * the leap seconds table
512280849Scy			 */
513280849Scy			check_leap_expiration(TRUE, now.l_ui, &ttnow);
514280849Scy		}
515280849Scy		break;
516280849Scy
517280849Scy	default:
51854359Sroberto		/* oh well */
51954359Sroberto		break;
52054359Sroberto	}
52154359Sroberto}
52254359Sroberto
523280849Scy
52454359Sroberto/*
52554359Sroberto * record_peer_stats - write peer statistics to file
52654359Sroberto *
52754359Sroberto * file format:
528280849Scy * day (MJD)
52954359Sroberto * time (s past UTC midnight)
530280849Scy * IP address
531280849Scy * status word (hex)
532280849Scy * offset
533280849Scy * delay
534280849Scy * dispersion
535280849Scy * jitter
53654359Sroberto*/
53754359Srobertovoid
53854359Srobertorecord_peer_stats(
539280849Scy	sockaddr_u *addr,
540132451Sroberto	int	status,
541280849Scy	double	offset,		/* offset */
542280849Scy	double	delay,		/* delay */
543280849Scy	double	dispersion,	/* dispersion */
544280849Scy	double	jitter		/* jitter */
54554359Sroberto	)
54654359Sroberto{
547132451Sroberto	l_fp	now;
548132451Sroberto	u_long	day;
54954359Sroberto
55054359Sroberto	if (!stats_control)
55154359Sroberto		return;
55254359Sroberto
553132451Sroberto	get_systime(&now);
554132451Sroberto	filegen_setup(&peerstats, now.l_ui);
555132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
556132451Sroberto	now.l_ui %= 86400;
55754359Sroberto	if (peerstats.fp != NULL) {
55854359Sroberto		fprintf(peerstats.fp,
559280849Scy		    "%lu %s %s %x %.9f %.9f %.9f %.9f\n", day,
560280849Scy		    ulfptoa(&now, 3), stoa(addr), status, offset,
561280849Scy		    delay, dispersion, jitter);
56254359Sroberto		fflush(peerstats.fp);
56354359Sroberto	}
56454359Sroberto}
565182007Sroberto
566280849Scy
56754359Sroberto/*
56854359Sroberto * record_loop_stats - write loop filter statistics to file
56954359Sroberto *
57054359Sroberto * file format:
571280849Scy * day (MJD)
57254359Sroberto * time (s past midnight)
573280849Scy * offset
574280849Scy * frequency (PPM)
575280849Scy * jitter
576280849Scy * wnder (PPM)
577280849Scy * time constant (log2)
57854359Sroberto */
57954359Srobertovoid
58082498Srobertorecord_loop_stats(
581280849Scy	double	offset,		/* offset */
582280849Scy	double	freq,		/* frequency (PPM) */
583280849Scy	double	jitter,		/* jitter */
584280849Scy	double	wander,		/* wander (PPM) */
585132451Sroberto	int spoll
58682498Sroberto	)
58754359Sroberto{
588132451Sroberto	l_fp	now;
589132451Sroberto	u_long	day;
59054359Sroberto
59154359Sroberto	if (!stats_control)
59254359Sroberto		return;
59354359Sroberto
594132451Sroberto	get_systime(&now);
595132451Sroberto	filegen_setup(&loopstats, now.l_ui);
596132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
597132451Sroberto	now.l_ui %= 86400;
59854359Sroberto	if (loopstats.fp != NULL) {
599182007Sroberto		fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n",
600132451Sroberto		    day, ulfptoa(&now, 3), offset, freq * 1e6, jitter,
601280849Scy		    wander * 1e6, spoll);
60254359Sroberto		fflush(loopstats.fp);
60354359Sroberto	}
60454359Sroberto}
60554359Sroberto
606280849Scy
60754359Sroberto/*
60854359Sroberto * record_clock_stats - write clock statistics to file
60954359Sroberto *
61054359Sroberto * file format:
611280849Scy * day (MJD)
61254359Sroberto * time (s past midnight)
613280849Scy * IP address
61454359Sroberto * text message
61554359Sroberto */
61654359Srobertovoid
61754359Srobertorecord_clock_stats(
618280849Scy	sockaddr_u *addr,
619280849Scy	const char *text	/* timecode string */
62054359Sroberto	)
62154359Sroberto{
622132451Sroberto	l_fp	now;
623132451Sroberto	u_long	day;
62454359Sroberto
62554359Sroberto	if (!stats_control)
62654359Sroberto		return;
62754359Sroberto
628132451Sroberto	get_systime(&now);
629132451Sroberto	filegen_setup(&clockstats, now.l_ui);
630132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
631132451Sroberto	now.l_ui %= 86400;
63254359Sroberto	if (clockstats.fp != NULL) {
633280849Scy		fprintf(clockstats.fp, "%lu %s %s %s\n", day,
634280849Scy		    ulfptoa(&now, 3), stoa(addr), text);
63554359Sroberto		fflush(clockstats.fp);
63654359Sroberto	}
63754359Sroberto}
63854359Sroberto
639280849Scy
64054359Sroberto/*
641280849Scy * mprintf_clock_stats - write clock statistics to file with
642280849Scy *			msnprintf-style formatting.
643280849Scy */
644280849Scyint
645280849Scymprintf_clock_stats(
646280849Scy	sockaddr_u *addr,
647280849Scy	const char *fmt,
648280849Scy	...
649280849Scy	)
650280849Scy{
651280849Scy	va_list	ap;
652280849Scy	int	rc;
653280849Scy	char	msg[512];
654280849Scy
655280849Scy	va_start(ap, fmt);
656280849Scy	rc = mvsnprintf(msg, sizeof(msg), fmt, ap);
657280849Scy	va_end(ap);
658280849Scy	if (stats_control)
659280849Scy		record_clock_stats(addr, msg);
660280849Scy
661280849Scy	return rc;
662280849Scy}
663280849Scy
664280849Scy/*
66554359Sroberto * record_raw_stats - write raw timestamps to file
66654359Sroberto *
66754359Sroberto * file format
668280849Scy * day (MJD)
66954359Sroberto * time (s past midnight)
67054359Sroberto * peer ip address
671280849Scy * IP address
67254359Sroberto * t1 t2 t3 t4 timestamps
673330106Sdelphij * leap, version, mode, stratum, ppoll, precision, root delay, root dispersion, REFID
674330106Sdelphij * length and hex dump of any EFs and any legacy MAC.
67554359Sroberto */
67654359Srobertovoid
67754359Srobertorecord_raw_stats(
678280849Scy	sockaddr_u *srcadr,
679280849Scy	sockaddr_u *dstadr,
680280849Scy	l_fp	*t1,		/* originate timestamp */
681280849Scy	l_fp	*t2,		/* receive timestamp */
682280849Scy	l_fp	*t3,		/* transmit timestamp */
683280849Scy	l_fp	*t4,		/* destination timestamp */
684280849Scy	int	leap,
685280849Scy	int	version,
686280849Scy	int	mode,
687280849Scy	int	stratum,
688280849Scy	int	ppoll,
689280849Scy	int	precision,
690280849Scy	double	root_delay,	/* seconds */
691280849Scy	double	root_dispersion,/* seconds */
692330106Sdelphij	u_int32	refid,
693330106Sdelphij	int	len,
694330106Sdelphij	u_char	*extra
69554359Sroberto	)
69654359Sroberto{
697132451Sroberto	l_fp	now;
698132451Sroberto	u_long	day;
69954359Sroberto
70054359Sroberto	if (!stats_control)
70154359Sroberto		return;
70254359Sroberto
703132451Sroberto	get_systime(&now);
704132451Sroberto	filegen_setup(&rawstats, now.l_ui);
705132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
706132451Sroberto	now.l_ui %= 86400;
70754359Sroberto	if (rawstats.fp != NULL) {
708330106Sdelphij		fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s %d %d %d %d %d %d %.6f %.6f %s",
709280849Scy		    day, ulfptoa(&now, 3),
710330106Sdelphij		    srcadr ? stoa(srcadr) : "-",
711330106Sdelphij		    dstadr ? stoa(dstadr) : "-",
712280849Scy		    ulfptoa(t1, 9), ulfptoa(t2, 9),
713280849Scy		    ulfptoa(t3, 9), ulfptoa(t4, 9),
714280849Scy		    leap, version, mode, stratum, ppoll, precision,
715280849Scy		    root_delay, root_dispersion, refid_str(refid, stratum));
716330106Sdelphij		if (len > 0) {
717330106Sdelphij			int i;
718330106Sdelphij
719330106Sdelphij			fprintf(rawstats.fp, " %d: ", len);
720330106Sdelphij			for (i = 0; i < len; ++i) {
721330106Sdelphij				fprintf(rawstats.fp, "%02x", extra[i]);
722330106Sdelphij			}
723330106Sdelphij		}
724330106Sdelphij		fprintf(rawstats.fp, "\n");
72554359Sroberto		fflush(rawstats.fp);
72654359Sroberto	}
72754359Sroberto}
72854359Sroberto
729132451Sroberto
73054359Sroberto/*
731132451Sroberto * record_sys_stats - write system statistics to file
732132451Sroberto *
733132451Sroberto * file format
734280849Scy * day (MJD)
735132451Sroberto * time (s past midnight)
736280849Scy * time since reset
737132451Sroberto * packets recieved
738280849Scy * packets for this host
739132451Sroberto * current version
740280849Scy * old version
741132451Sroberto * access denied
742132451Sroberto * bad length or format
743132451Sroberto * bad authentication
744280849Scy * declined
745132451Sroberto * rate exceeded
746280849Scy * KoD sent
747132451Sroberto */
748132451Srobertovoid
749132451Srobertorecord_sys_stats(void)
750132451Sroberto{
751132451Sroberto	l_fp	now;
752132451Sroberto	u_long	day;
753132451Sroberto
754132451Sroberto	if (!stats_control)
755132451Sroberto		return;
756132451Sroberto
757132451Sroberto	get_systime(&now);
758132451Sroberto	filegen_setup(&sysstats, now.l_ui);
759132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
760132451Sroberto	now.l_ui %= 86400;
761132451Sroberto	if (sysstats.fp != NULL) {
762280849Scy		fprintf(sysstats.fp,
763280849Scy		    "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
764280849Scy		    day, ulfptoa(&now, 3), current_time - sys_stattime,
765280849Scy		    sys_received, sys_processed, sys_newversion,
766280849Scy		    sys_oldversion, sys_restricted, sys_badlength,
767280849Scy		    sys_badauth, sys_declined, sys_limitrejected,
768280849Scy		    sys_kodsent);
769132451Sroberto		fflush(sysstats.fp);
770132451Sroberto		proto_clr_stats();
771132451Sroberto	}
772132451Sroberto}
773132451Sroberto
774132451Sroberto
775132451Sroberto/*
776280849Scy * record_proto_stats - write system statistics to file
777280849Scy *
778280849Scy * file format
779280849Scy * day (MJD)
780280849Scy * time (s past midnight)
781280849Scy * text message
782280849Scy */
783280849Scyvoid
784280849Scyrecord_proto_stats(
785280849Scy	char	*str		/* text string */
786280849Scy	)
787280849Scy{
788280849Scy	l_fp	now;
789280849Scy	u_long	day;
790280849Scy
791280849Scy	if (!stats_control)
792280849Scy		return;
793280849Scy
794280849Scy	get_systime(&now);
795280849Scy	filegen_setup(&protostats, now.l_ui);
796280849Scy	day = now.l_ui / 86400 + MJD_1900;
797280849Scy	now.l_ui %= 86400;
798280849Scy	if (protostats.fp != NULL) {
799280849Scy		fprintf(protostats.fp, "%lu %s %s\n", day,
800280849Scy		    ulfptoa(&now, 3), str);
801280849Scy		fflush(protostats.fp);
802280849Scy	}
803280849Scy}
804280849Scy
805280849Scy
806280849Scy#ifdef AUTOKEY
807280849Scy/*
808132451Sroberto * record_crypto_stats - write crypto statistics to file
809132451Sroberto *
810132451Sroberto * file format:
811132451Sroberto * day (mjd)
812132451Sroberto * time (s past midnight)
813280849Scy * peer ip address
814132451Sroberto * text message
815132451Sroberto */
816132451Srobertovoid
817132451Srobertorecord_crypto_stats(
818280849Scy	sockaddr_u *addr,
819280849Scy	const char *text	/* text message */
820132451Sroberto	)
821132451Sroberto{
822132451Sroberto	l_fp	now;
823132451Sroberto	u_long	day;
824132451Sroberto
825132451Sroberto	if (!stats_control)
826132451Sroberto		return;
827132451Sroberto
828132451Sroberto	get_systime(&now);
829132451Sroberto	filegen_setup(&cryptostats, now.l_ui);
830132451Sroberto	day = now.l_ui / 86400 + MJD_1900;
831132451Sroberto	now.l_ui %= 86400;
832132451Sroberto	if (cryptostats.fp != NULL) {
833132451Sroberto		if (addr == NULL)
834280849Scy			fprintf(cryptostats.fp, "%lu %s 0.0.0.0 %s\n",
835132451Sroberto			    day, ulfptoa(&now, 3), text);
836132451Sroberto		else
837132451Sroberto			fprintf(cryptostats.fp, "%lu %s %s %s\n",
838132451Sroberto			    day, ulfptoa(&now, 3), stoa(addr), text);
839132451Sroberto		fflush(cryptostats.fp);
840132451Sroberto	}
841132451Sroberto}
842280849Scy#endif	/* AUTOKEY */
843132451Sroberto
844280849Scy
845182007Sroberto#ifdef DEBUG_TIMING
846182007Sroberto/*
847280849Scy * record_timing_stats - write timing statistics to file
848182007Sroberto *
849182007Sroberto * file format:
850182007Sroberto * day (mjd)
851182007Sroberto * time (s past midnight)
852182007Sroberto * text message
853182007Sroberto */
854182007Srobertovoid
855182007Srobertorecord_timing_stats(
856280849Scy	const char *text	/* text message */
857182007Sroberto	)
858182007Sroberto{
859182007Sroberto	static unsigned int flshcnt;
860182007Sroberto	l_fp	now;
861182007Sroberto	u_long	day;
862132451Sroberto
863182007Sroberto	if (!stats_control)
864182007Sroberto		return;
865182007Sroberto
866182007Sroberto	get_systime(&now);
867182007Sroberto	filegen_setup(&timingstats, now.l_ui);
868182007Sroberto	day = now.l_ui / 86400 + MJD_1900;
869182007Sroberto	now.l_ui %= 86400;
870182007Sroberto	if (timingstats.fp != NULL) {
871280849Scy		fprintf(timingstats.fp, "%lu %s %s\n", day, lfptoa(&now,
872280849Scy		    3), text);
873182007Sroberto		if (++flshcnt % 100 == 0)
874182007Sroberto			fflush(timingstats.fp);
875182007Sroberto	}
876182007Sroberto}
877182007Sroberto#endif
878280849Scy
879280849Scy
880132451Sroberto/*
881280849Scy * check_leap_file - See if the leapseconds file has been updated.
882280849Scy *
883280849Scy * Returns: n/a
884280849Scy *
885280849Scy * Note: This loads a new leapfile on the fly. Currently a leap file
886280849Scy * without SHA1 signature is accepted, but if there is a signature line,
887280849Scy * the signature must be valid or the file is rejected.
888280849Scy */
889280849Scyvoid
890280849Scycheck_leap_file(
891280849Scy	int           is_daily_check,
892280849Scy	uint32_t      ntptime       ,
893280849Scy	const time_t *systime
894280849Scy	)
895280849Scy{
896280849Scy	/* just do nothing if there is no leap file */
897280849Scy	if ( ! (leapfile_name && *leapfile_name))
898280849Scy		return;
899358659Scy
900280849Scy	/* try to load leapfile, force it if no leapfile loaded yet */
901280849Scy	if (leapsec_load_file(
902280849Scy		    leapfile_name, &leapfile_stat,
903358659Scy		    !have_leapfile, is_daily_check, chck_leaphash))
904280849Scy		have_leapfile = TRUE;
905280849Scy	else if (!have_leapfile)
906280849Scy		return;
907280849Scy
908280849Scy	check_leap_expiration(is_daily_check, ntptime, systime);
909280849Scy}
910280849Scy
911280849Scy/*
912280849Scy * check expiration of a loaded leap table
913280849Scy */
914280849Scystatic void
915280849Scycheck_leap_expiration(
916280849Scy	int           is_daily_check,
917280849Scy	uint32_t      ntptime       ,
918280849Scy	const time_t *systime
919280849Scy	)
920280849Scy{
921280849Scy	static const char * const logPrefix = "leapsecond file";
922280849Scy	int  rc;
923280849Scy
924280849Scy	/* test the expiration of the leap data and log with proper
925280849Scy	 * level and frequency (once/hour or once/day, depending on the
926280849Scy	 * state.
927280849Scy	 */
928358659Scy	rc = leapsec_daystolive(ntptime, systime);
929280849Scy	if (rc == 0) {
930280849Scy		msyslog(LOG_WARNING,
931280849Scy			"%s ('%s'): will expire in less than one day",
932280849Scy			logPrefix, leapfile_name);
933280849Scy	} else if (is_daily_check && rc < 28) {
934280849Scy		if (rc < 0)
935280849Scy			msyslog(LOG_ERR,
936358659Scy				"%s ('%s'): expired %d day%s ago",
937280849Scy				logPrefix, leapfile_name, -rc, (rc == -1 ? "" : "s"));
938280849Scy		else
939280849Scy			msyslog(LOG_WARNING,
940280849Scy				"%s ('%s'): will expire in less than %d days",
941280849Scy				logPrefix, leapfile_name, 1+rc);
942280849Scy	}
943280849Scy}
944280849Scy
945280849Scy
946280849Scy/*
94754359Sroberto * getauthkeys - read the authentication keys from the specified file
94854359Sroberto */
94954359Srobertovoid
95054359Srobertogetauthkeys(
951182007Sroberto	const char *keyfile
95254359Sroberto	)
95354359Sroberto{
954293423Sdelphij	size_t len;
95554359Sroberto
95654359Sroberto	len = strlen(keyfile);
957280849Scy	if (!len)
95854359Sroberto		return;
959358659Scy
96054359Sroberto#ifndef SYS_WINNT
961280849Scy	key_file_name = erealloc(key_file_name, len + 1);
962280849Scy	memcpy(key_file_name, keyfile, len + 1);
96354359Sroberto#else
964280849Scy	key_file_name = erealloc(key_file_name, _MAX_PATH);
965280849Scy	if (len + 1 > _MAX_PATH)
966280849Scy		return;
967280849Scy	if (!ExpandEnvironmentStrings(keyfile, key_file_name,
968280849Scy				      _MAX_PATH)) {
96954359Sroberto		msyslog(LOG_ERR,
970280849Scy			"ExpandEnvironmentStrings(KEY_FILE) failed: %m");
971280849Scy		strlcpy(key_file_name, keyfile, _MAX_PATH);
97254359Sroberto	}
973280849Scy	key_file_name = erealloc(key_file_name,
974280849Scy				 1 + strlen(key_file_name));
97554359Sroberto#endif /* SYS_WINNT */
97654359Sroberto
97754359Sroberto	authreadkeys(key_file_name);
97854359Sroberto}
97954359Sroberto
98054359Sroberto
98154359Sroberto/*
98254359Sroberto * rereadkeys - read the authentication key file over again.
98354359Sroberto */
98454359Srobertovoid
98554359Srobertorereadkeys(void)
98654359Sroberto{
987280849Scy	if (NULL != key_file_name)
988280849Scy		authreadkeys(key_file_name);
98954359Sroberto}
990132451Sroberto
991280849Scy
992280849Scy#if notyet
993132451Sroberto/*
994280849Scy * ntp_exit - document explicitly that ntpd has exited
995132451Sroberto */
996280849Scyvoid
997280849Scyntp_exit(int retval)
998280849Scy{
999280849Scy	msyslog(LOG_ERR, "EXITING with return code %d", retval);
1000280849Scy	exit(retval);
1001280849Scy}
1002280849Scy#endif
1003280849Scy
1004280849Scy/*
1005280849Scy * fstostr - prettyprint NTP seconds
1006280849Scy */
1007280849Scychar * fstostr(
1008280849Scy	time_t	ntp_stamp
1009132451Sroberto	)
1010132451Sroberto{
1011280849Scy	char *		buf;
1012280849Scy	struct calendar tm;
1013132451Sroberto
1014280849Scy	LIB_GETBUF(buf);
1015280849Scy	if (ntpcal_ntp_to_date(&tm, (u_int32)ntp_stamp, NULL) < 0)
1016280849Scy		snprintf(buf, LIB_BUFLENGTH, "ntpcal_ntp_to_date: %ld: range error",
1017280849Scy			 (long)ntp_stamp);
1018280849Scy	else
1019280849Scy		snprintf(buf, LIB_BUFLENGTH, "%04d%02d%02d%02d%02d",
1020280849Scy			 tm.year, tm.month, tm.monthday,
1021280849Scy			 tm.hour, tm.minute);
1022280849Scy	return buf;
1023280849Scy}
1024132451Sroberto
1025132451Sroberto
1026182007Sroberto/*
1027280849Scy * ntpd_time_stepped is called back by step_systime(), allowing ntpd
1028280849Scy * to do any one-time processing necessitated by the step.
1029182007Sroberto */
1030182007Srobertovoid
1031280849Scyntpd_time_stepped(void)
1032182007Sroberto{
1033280849Scy	u_int saved_mon_enabled;
1034280849Scy
1035280849Scy	/*
1036280849Scy	 * flush the monitor MRU list which contains l_fp timestamps
1037280849Scy	 * which should not be compared across the step.
1038280849Scy	 */
1039280849Scy	if (MON_OFF != mon_enabled) {
1040280849Scy		saved_mon_enabled = mon_enabled;
1041280849Scy		mon_stop(MON_OFF);
1042280849Scy		mon_start(saved_mon_enabled);
1043280849Scy	}
1044280849Scy
1045280849Scy	/* inform interpolating Windows code to allow time to go back */
1046280849Scy#ifdef SYS_WINNT
1047280849Scy	win_time_stepped();
1048280849Scy#endif
1049182007Sroberto}
1050