ntp_util.c revision 290000
1228692Sdes/*
2236109Sdes * ntp_util.c - stuff I didn't have any other place for
3228692Sdes */
4228692Sdes#ifdef HAVE_CONFIG_H
5174832Sdes# include <config.h>
6236109Sdes#endif
7228692Sdes
8228692Sdes#include "ntpd.h"
9228692Sdes#include "ntp_unixtime.h"
10228692Sdes#include "ntp_filegen.h"
11117610Sdes#include "ntp_if.h"
12117610Sdes#include "ntp_stdlib.h"
13117610Sdes#include "ntp_assert.h"
14117610Sdes#include "ntp_calendar.h"
15228692Sdes#include "ntp_leapsec.h"
16228692Sdes#include "lib_strbuf.h"
17228692Sdes
18228692Sdes#include <stdio.h>
19228692Sdes#include <ctype.h>
20228692Sdes#include <sys/types.h>
21117610Sdes#ifdef HAVE_SYS_IOCTL_H
22117610Sdes# include <sys/ioctl.h>
23117610Sdes#endif
24117610Sdes#ifdef HAVE_UNISTD_H
25117610Sdes# include <unistd.h>
26228692Sdes#endif
27228692Sdes#include <sys/stat.h>
28228692Sdes
29228692Sdes#ifdef HAVE_IEEEFP_H
30228692Sdes# include <ieeefp.h>
31228692Sdes#endif
32117610Sdes#ifdef HAVE_MATH_H
33228692Sdes# include <math.h>
34228692Sdes#endif
35228692Sdes
36228692Sdes#if defined(VMS)
37228692Sdes# include <descrip.h>
38228692Sdes#endif /* VMS */
39228692Sdes
40228692Sdes/*
41228692Sdes * Defines used by the leapseconds stuff
42228692Sdes */
43228692Sdes#define	MAX_TAI	100			/* max TAI offset (s) */
44236109Sdes#define	L_DAY	86400UL			/* seconds per day */
45228692Sdes#define	L_YEAR	(L_DAY * 365)		/* days per year */
46228692Sdes#define	L_LYEAR	(L_YEAR + L_DAY)	/* days per leap year */
47228692Sdes#define	L_4YEAR	(L_LYEAR + 3 * L_YEAR)	/* days per leap cycle */
48228692Sdes#define	L_CENT	(L_4YEAR * 25)		/* days per century */
49228692Sdes
50228692Sdes/*
51228692Sdes * This contains odds and ends, including the hourly stats, various
52228692Sdes * configuration items, leapseconds stuff, etc.
53228692Sdes */
54228692Sdes/*
55228692Sdes * File names
56228692Sdes */
57228692Sdesstatic	char *key_file_name;		/* keys file name */
58228692Sdesstatic char	  *leapfile_name;		/* leapseconds file name */
59228692Sdesstatic struct stat leapfile_stat;	/* leapseconds file stat() buffer */
60228692Sdesstatic int /*BOOL*/have_leapfile = FALSE;
61228692Sdeschar	*stats_drift_file;		/* frequency file name */
62228692Sdesstatic	char *stats_temp_file;		/* temp frequency file name */
63228692Sdesstatic double wander_resid;		/* last frequency update */
64228692Sdesdouble	wander_threshold = 1e-7;	/* initial frequency threshold */
65228692Sdes
66228692Sdes/*
67228692Sdes * Statistics file stuff
68228692Sdes */
69228692Sdes#ifndef NTP_VAR
70228692Sdes# ifndef SYS_WINNT
71228692Sdes#  define NTP_VAR "/var/NTP/"		/* NOTE the trailing '/' */
72228692Sdes# else
73236109Sdes#  define NTP_VAR "c:\\var\\ntp\\"	/* NOTE the trailing '\\' */
74228692Sdes# endif /* SYS_WINNT */
75228692Sdes#endif
76228692Sdes
77228692Sdes
78228692Sdeschar statsdir[MAXFILENAME] = NTP_VAR;
79228692Sdesstatic FILEGEN peerstats;
80117610Sdesstatic FILEGEN loopstats;
81228692Sdesstatic FILEGEN clockstats;
82141098Sdesstatic FILEGEN rawstats;
83236109Sdesstatic FILEGEN sysstats;
84228692Sdesstatic FILEGEN protostats;
85236109Sdesstatic FILEGEN cryptostats;
86141098Sdesstatic FILEGEN timingstats;
87228692Sdes
88174832Sdes/*
89174832Sdes * This controls whether stats are written to the fileset. Provided
90174832Sdes * so that ntpdc can turn off stats when the file system fills up.
91174832Sdes */
92174832Sdesint stats_control;
93174832Sdes
94141098Sdes/*
95174832Sdes * Last frequency written to file.
96174832Sdes */
97141098Sdesstatic double prev_drift_comp;		/* last frequency update */
98174832Sdes
99174832Sdes/*
100141098Sdes * Function prototypes
101228692Sdes */
102228692Sdesstatic	void	record_sys_stats(void);
103228692Sdes	void	ntpd_time_stepped(void);
104228692Sdesstatic  void	check_leap_expiration(int, uint32_t, const time_t*);
105228692Sdes
106228692Sdes/*
107228692Sdes * Prototypes
108117610Sdes */
109228692Sdes#ifdef DEBUG
110228692Sdesvoid	uninit_util(void);
111228692Sdes#endif
112228692Sdes
113174832Sdes/*
114174832Sdes * uninit_util - free memory allocated by init_util
115228692Sdes */
116228692Sdes#ifdef DEBUG
117174832Sdesvoid
118228692Sdesuninit_util(void)
119228692Sdes{
120174832Sdes#if defined(_MSC_VER) && defined (_DEBUG)
121174832Sdes	_CrtCheckMemory();
122228692Sdes#endif
123228692Sdes	if (stats_drift_file) {
124228692Sdes		free(stats_drift_file);
125117610Sdes		free(stats_temp_file);
126228692Sdes		stats_drift_file = NULL;
127228692Sdes		stats_temp_file = NULL;
128228692Sdes	}
129228692Sdes	if (key_file_name) {
130228692Sdes		free(key_file_name);
131228692Sdes		key_file_name = NULL;
132228692Sdes	}
133228692Sdes	filegen_unregister("peerstats");
134228692Sdes	filegen_unregister("loopstats");
135228692Sdes	filegen_unregister("clockstats");
136228692Sdes	filegen_unregister("rawstats");
137228692Sdes	filegen_unregister("sysstats");
138228692Sdes	filegen_unregister("protostats");
139228692Sdes#ifdef AUTOKEY
140228692Sdes	filegen_unregister("cryptostats");
141228692Sdes#endif	/* AUTOKEY */
142228692Sdes#ifdef DEBUG_TIMING
143228692Sdes	filegen_unregister("timingstats");
144228692Sdes#endif	/* DEBUG_TIMING */
145228692Sdes
146228692Sdes#if defined(_MSC_VER) && defined (_DEBUG)
147228692Sdes	_CrtCheckMemory();
148228692Sdes#endif
149228692Sdes}
150228692Sdes#endif /* DEBUG */
151228692Sdes
152228692Sdes
153228692Sdes/*
154228692Sdes * init_util - initialize the util module of ntpd
155125647Sdes */
156174832Sdesvoid
157174832Sdesinit_util(void)
158174832Sdes{
159117610Sdes	filegen_register(statsdir, "peerstats",	  &peerstats);
160228692Sdes	filegen_register(statsdir, "loopstats",	  &loopstats);
161228692Sdes	filegen_register(statsdir, "clockstats",  &clockstats);
162117610Sdes	filegen_register(statsdir, "rawstats",	  &rawstats);
163228692Sdes	filegen_register(statsdir, "sysstats",	  &sysstats);
164228692Sdes	filegen_register(statsdir, "protostats",  &protostats);
165228692Sdes	filegen_register(statsdir, "cryptostats", &cryptostats);
166228692Sdes	filegen_register(statsdir, "timingstats", &timingstats);
167228692Sdes	/*
168228692Sdes	 * register with libntp ntp_set_tod() to call us back
169228692Sdes	 * when time is stepped.
170228692Sdes	 */
171228692Sdes	step_callback = &ntpd_time_stepped;
172228692Sdes#ifdef DEBUG
173228692Sdes	atexit(&uninit_util);
174228692Sdes#endif /* DEBUG */
175117610Sdes}
176141098Sdes
177228692Sdes
178228692Sdes/*
179228692Sdes * hourly_stats - print some interesting stats
180228692Sdes */
181228692Sdesvoid
182228692Sdeswrite_stats(void)
183228692Sdes{
184228692Sdes	FILE	*fp;
185228692Sdes#ifdef DOSYNCTODR
186228692Sdes	struct timeval tv;
187228692Sdes#if !defined(VMS)
188228692Sdes	int	prio_set;
189228692Sdes#endif
190228692Sdes#ifdef HAVE_GETCLOCK
191228692Sdes	struct timespec ts;
192228692Sdes#endif
193228692Sdes	int	o_prio;
194228692Sdes
195228692Sdes	/*
196228692Sdes	 * Sometimes having a Sun can be a drag.
197228692Sdes	 *
198228692Sdes	 * The kernel variable dosynctodr controls whether the system's
199228692Sdes	 * soft clock is kept in sync with the battery clock. If it
200228692Sdes	 * is zero, then the soft clock is not synced, and the battery
201228692Sdes	 * clock is simply left to rot. That means that when the system
202228692Sdes	 * reboots, the battery clock (which has probably gone wacky)
203228692Sdes	 * sets the soft clock. That means ntpd starts off with a very
204228692Sdes	 * confused idea of what time it is. It then takes a large
205228692Sdes	 * amount of time to figure out just how wacky the battery clock
206228692Sdes	 * has made things drift, etc, etc. The solution is to make the
207228692Sdes	 * battery clock sync up to system time. The way to do THAT is
208228692Sdes	 * to simply set the time of day to the current time of day, but
209228692Sdes	 * as quickly as possible. This may, or may not be a sensible
210228692Sdes	 * thing to do.
211228692Sdes	 *
212228692Sdes	 * CAVEAT: settimeofday() steps the sun clock by about 800 us,
213228692Sdes	 *	   so setting DOSYNCTODR seems a bad idea in the
214228692Sdes	 *	   case of us resolution
215228692Sdes	 */
216228692Sdes
217228692Sdes#if !defined(VMS)
218228692Sdes	/*
219228692Sdes	 * (prr) getpriority returns -1 on error, but -1 is also a valid
220228692Sdes	 * return value (!), so instead we have to zero errno before the
221228692Sdes	 * call and check it for non-zero afterwards.
222228692Sdes	 */
223228692Sdes	errno = 0;
224228692Sdes	prio_set = 0;
225228692Sdes	o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */
226228692Sdes
227228692Sdes	/*
228228692Sdes	 * (prr) if getpriority succeeded, call setpriority to raise
229228692Sdes	 * scheduling priority as high as possible.  If that succeeds
230228692Sdes	 * as well, set the prio_set flag so we remember to reset
231228692Sdes	 * priority to its previous value below.  Note that on Solaris
232228692Sdes	 * 2.6 (and beyond?), both getpriority and setpriority will fail
233228692Sdes	 * with ESRCH, because sched_setscheduler (called from main) put
234228692Sdes	 * us in the real-time scheduling class which setpriority
235228692Sdes	 * doesn't know about. Being in the real-time class is better
236228692Sdes	 * than anything setpriority can do, anyhow, so this error is
237228692Sdes	 * silently ignored.
238228692Sdes	 */
239228692Sdes	if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
240228692Sdes		prio_set = 1;	/* overdrive */
241228692Sdes#endif /* VMS */
242228692Sdes#ifdef HAVE_GETCLOCK
243228692Sdes	(void) getclock(TIMEOFDAY, &ts);
244228692Sdes	tv.tv_sec = ts.tv_sec;
245228692Sdes	tv.tv_usec = ts.tv_nsec / 1000;
246228692Sdes#else /*  not HAVE_GETCLOCK */
247228692Sdes	GETTIMEOFDAY(&tv,(struct timezone *)NULL);
248228692Sdes#endif /* not HAVE_GETCLOCK */
249228692Sdes	if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0)
250228692Sdes		msyslog(LOG_ERR, "can't sync battery time: %m");
251228692Sdes#if !defined(VMS)
252228692Sdes	if (prio_set)
253228692Sdes		setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */
254228692Sdes#endif /* VMS */
255228692Sdes#endif /* DOSYNCTODR */
256228692Sdes	record_sys_stats();
257228692Sdes	if (stats_drift_file != 0) {
258228692Sdes
259228692Sdes		/*
260228692Sdes		 * When the frequency file is written, initialize the
261228692Sdes		 * prev_drift_comp and wander_resid. Thereafter,
262228692Sdes		 * reduce the wander_resid by half each hour. When
263228692Sdes		 * the difference between the prev_drift_comp and
264228692Sdes		 * drift_comp is less than the wander_resid, update
265228692Sdes		 * the frequncy file. This minimizes the file writes to
266228692Sdes		 * nonvolaile storage.
267228692Sdes		 */
268228692Sdes#ifdef DEBUG
269228692Sdes		if (debug)
270228692Sdes			printf("write_stats: frequency %.6lf thresh %.6lf, freq %.6lf\n",
271228692Sdes			    (prev_drift_comp - drift_comp) * 1e6, wander_resid *
272228692Sdes			    1e6, drift_comp * 1e6);
273228692Sdes#endif
274228692Sdes		if (fabs(prev_drift_comp - drift_comp) < wander_resid) {
275228692Sdes			wander_resid *= 0.5;
276228692Sdes			return;
277228692Sdes		}
278228692Sdes		prev_drift_comp = drift_comp;
279228692Sdes		wander_resid = wander_threshold;
280228692Sdes		if ((fp = fopen(stats_temp_file, "w")) == NULL) {
281228692Sdes			msyslog(LOG_ERR, "frequency file %s: %m",
282228692Sdes			    stats_temp_file);
283228692Sdes			return;
284228692Sdes		}
285228692Sdes		fprintf(fp, "%.3f\n", drift_comp * 1e6);
286228692Sdes		(void)fclose(fp);
287228692Sdes		/* atomic */
288228692Sdes#ifdef SYS_WINNT
289228692Sdes		if (_unlink(stats_drift_file)) /* rename semantics differ under NT */
290228692Sdes			msyslog(LOG_WARNING,
291228692Sdes				"Unable to remove prior drift file %s, %m",
292228692Sdes				stats_drift_file);
293228692Sdes#endif /* SYS_WINNT */
294228692Sdes
295228692Sdes#ifndef NO_RENAME
296228692Sdes		if (rename(stats_temp_file, stats_drift_file))
297228692Sdes			msyslog(LOG_WARNING,
298228692Sdes				"Unable to rename temp drift file %s to %s, %m",
299228692Sdes				stats_temp_file, stats_drift_file);
300228692Sdes#else
301228692Sdes		/* we have no rename NFS of ftp in use */
302228692Sdes		if ((fp = fopen(stats_drift_file, "w")) ==
303228692Sdes		    NULL) {
304228692Sdes			msyslog(LOG_ERR,
305228692Sdes			    "frequency file %s: %m",
306228692Sdes			    stats_drift_file);
307228692Sdes			return;
308228692Sdes		}
309228692Sdes#endif
310228692Sdes
311228692Sdes#if defined(VMS)
312228692Sdes		/* PURGE */
313228692Sdes		{
314228692Sdes			$DESCRIPTOR(oldvers,";-1");
315228692Sdes			struct dsc$descriptor driftdsc = {
316228692Sdes				strlen(stats_drift_file), 0, 0,
317228692Sdes				    stats_drift_file };
318228692Sdes			while(lib$delete_file(&oldvers,
319228692Sdes			    &driftdsc) & 1);
320228692Sdes		}
321228692Sdes#endif
322228692Sdes	}
323228692Sdes}
324228692Sdes
325228692Sdes
326228692Sdes/*
327228692Sdes * stats_config - configure the stats operation
328228692Sdes */
329228692Sdesvoid
330228692Sdesstats_config(
331228692Sdes	int item,
332228692Sdes	const char *invalue	/* only one type so far */
333228692Sdes	)
334228692Sdes{
335228692Sdes	FILE	*fp;
336228692Sdes	const char *value;
337228692Sdes	int	len;
338228692Sdes	double	old_drift;
339228692Sdes	l_fp	now;
340228692Sdes	time_t  ttnow;
341228692Sdes#ifndef VMS
342228692Sdes	const char temp_ext[] = ".TEMP";
343228692Sdes#else
344228692Sdes	const char temp_ext[] = "-TEMP";
345228692Sdes#endif
346228692Sdes
347228692Sdes	/*
348228692Sdes	 * Expand environment strings under Windows NT, since the
349228692Sdes	 * command interpreter doesn't do this, the program must.
350228692Sdes	 */
351228692Sdes#ifdef SYS_WINNT
352228692Sdes	char newvalue[MAX_PATH], parameter[MAX_PATH];
353228692Sdes
354228692Sdes	if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
355228692Sdes		switch (item) {
356228692Sdes		case STATS_FREQ_FILE:
357228692Sdes			strlcpy(parameter, "STATS_FREQ_FILE",
358228692Sdes				sizeof(parameter));
359228692Sdes			break;
360228692Sdes
361228692Sdes		case STATS_LEAP_FILE:
362228692Sdes			strlcpy(parameter, "STATS_LEAP_FILE",
363228692Sdes				sizeof(parameter));
364228692Sdes			break;
365228692Sdes
366228692Sdes		case STATS_STATSDIR:
367228692Sdes			strlcpy(parameter, "STATS_STATSDIR",
368228692Sdes				sizeof(parameter));
369228692Sdes			break;
370228692Sdes
371228692Sdes		case STATS_PID_FILE:
372228692Sdes			strlcpy(parameter, "STATS_PID_FILE",
373228692Sdes				sizeof(parameter));
374228692Sdes			break;
375228692Sdes
376228692Sdes		default:
377228692Sdes			strlcpy(parameter, "UNKNOWN",
378228692Sdes				sizeof(parameter));
379228692Sdes			break;
380228692Sdes		}
381228692Sdes		value = invalue;
382228692Sdes		msyslog(LOG_ERR,
383228692Sdes			"ExpandEnvironmentStrings(%s) failed: %m\n",
384228692Sdes			parameter);
385228692Sdes	} else {
386236109Sdes		value = newvalue;
387228692Sdes	}
388228692Sdes#else
389228692Sdes	value = invalue;
390228692Sdes#endif /* SYS_WINNT */
391228692Sdes
392228692Sdes	switch (item) {
393228692Sdes
394228692Sdes	/*
395228692Sdes	 * Open and read frequency file.
396228692Sdes	 */
397228692Sdes	case STATS_FREQ_FILE:
398228692Sdes		if (!value || (len = strlen(value)) == 0)
399228692Sdes			break;
400228692Sdes
401228692Sdes		stats_drift_file = erealloc(stats_drift_file, len + 1);
402228692Sdes		stats_temp_file = erealloc(stats_temp_file,
403228692Sdes		    len + sizeof(".TEMP"));
404228692Sdes		memcpy(stats_drift_file, value, (size_t)(len+1));
405228692Sdes		memcpy(stats_temp_file, value, (size_t)len);
406228692Sdes		memcpy(stats_temp_file + len, temp_ext, sizeof(temp_ext));
407228692Sdes
408228692Sdes		/*
409228692Sdes		 * Open drift file and read frequency. If the file is
410228692Sdes		 * missing or contains errors, tell the loop to reset.
411228692Sdes		 */
412228692Sdes		if ((fp = fopen(stats_drift_file, "r")) == NULL)
413228692Sdes			break;
414228692Sdes
415228692Sdes		if (fscanf(fp, "%lf", &old_drift) != 1) {
416228692Sdes			msyslog(LOG_ERR,
417228692Sdes				"format error frequency file %s",
418228692Sdes				stats_drift_file);
419228692Sdes			fclose(fp);
420228692Sdes			break;
421228692Sdes
422228692Sdes		}
423228692Sdes		fclose(fp);
424228692Sdes		loop_config(LOOP_FREQ, old_drift);
425228692Sdes		prev_drift_comp = drift_comp;
426228692Sdes		break;
427228692Sdes
428228692Sdes	/*
429228692Sdes	 * Specify statistics directory.
430228692Sdes	 */
431228692Sdes	case STATS_STATSDIR:
432228692Sdes
433228692Sdes		/* - 1 since value may be missing the DIR_SEP. */
434228692Sdes		if (strlen(value) >= sizeof(statsdir) - 1) {
435228692Sdes			msyslog(LOG_ERR,
436228692Sdes			    "statsdir too long (>%d, sigh)",
437228692Sdes			    (int)sizeof(statsdir) - 2);
438228692Sdes		} else {
439228692Sdes			int add_dir_sep;
440228692Sdes			int value_l;
441228692Sdes
442228692Sdes			/* Add a DIR_SEP unless we already have one. */
443228692Sdes			value_l = strlen(value);
444228692Sdes			if (0 == value_l)
445228692Sdes				add_dir_sep = FALSE;
446228692Sdes			else
447228692Sdes				add_dir_sep = (DIR_SEP !=
448228692Sdes				    value[value_l - 1]);
449228692Sdes
450228692Sdes			if (add_dir_sep)
451228692Sdes				snprintf(statsdir, sizeof(statsdir),
452228692Sdes				    "%s%c", value, DIR_SEP);
453228692Sdes			else
454228692Sdes				snprintf(statsdir, sizeof(statsdir),
455228692Sdes				    "%s", value);
456228692Sdes			filegen_statsdir();
457228692Sdes		}
458228692Sdes		break;
459228692Sdes
460228692Sdes	/*
461228692Sdes	 * Open pid file.
462228692Sdes	 */
463228692Sdes	case STATS_PID_FILE:
464228692Sdes		if ((fp = fopen(value, "w")) == NULL) {
465228692Sdes			msyslog(LOG_ERR, "pid file %s: %m",
466228692Sdes			    value);
467228692Sdes			break;
468228692Sdes		}
469228692Sdes		fprintf(fp, "%d", (int)getpid());
470228692Sdes		fclose(fp);
471228692Sdes		break;
472228692Sdes
473228692Sdes	/*
474228692Sdes	 * Read leapseconds file.
475228692Sdes	 *
476228692Sdes	 * Note: Currently a leap file without SHA1 signature is
477228692Sdes	 * accepted, but if there is a signature line, the signature
478228692Sdes	 * must be valid or the file is rejected.
479228692Sdes	 */
480228692Sdes	case STATS_LEAP_FILE:
481228692Sdes		if (!value || (len = strlen(value)) == 0)
482228692Sdes			break;
483228692Sdes
484228692Sdes		leapfile_name = erealloc(leapfile_name, len + 1);
485228692Sdes		memcpy(leapfile_name, value, len + 1);
486228692Sdes
487228692Sdes		if (leapsec_load_file(
488228692Sdes			    leapfile_name, &leapfile_stat, TRUE, TRUE))
489228692Sdes		{
490228692Sdes			leap_signature_t lsig;
491228692Sdes
492228692Sdes			get_systime(&now);
493228692Sdes			time(&ttnow);
494228692Sdes			leapsec_getsig(&lsig);
495228692Sdes			mprintf_event(EVNT_TAI, NULL,
496228692Sdes				      "%d leap %s %s %s",
497228692Sdes				      lsig.taiof,
498228692Sdes				      fstostr(lsig.ttime),
499228692Sdes				      leapsec_expired(now.l_ui, NULL)
500228692Sdes					  ? "expired"
501228692Sdes					  : "expires",
502228692Sdes				      fstostr(lsig.etime));
503228692Sdes
504228692Sdes			have_leapfile = TRUE;
505228692Sdes
506228692Sdes			/* force an immediate daily expiration check of
507228692Sdes			 * the leap seconds table
508228692Sdes			 */
509228692Sdes			check_leap_expiration(TRUE, now.l_ui, &ttnow);
510228692Sdes		}
511228692Sdes		break;
512228692Sdes
513228692Sdes	default:
514228692Sdes		/* oh well */
515228692Sdes		break;
516228692Sdes	}
517228692Sdes}
518228692Sdes
519228692Sdes
520228692Sdes/*
521228692Sdes * record_peer_stats - write peer statistics to file
522228692Sdes *
523228692Sdes * file format:
524228692Sdes * day (MJD)
525228692Sdes * time (s past UTC midnight)
526228692Sdes * IP address
527228692Sdes * status word (hex)
528228692Sdes * offset
529228692Sdes * delay
530228692Sdes * dispersion
531228692Sdes * jitter
532228692Sdes*/
533228692Sdesvoid
534228692Sdesrecord_peer_stats(
535228692Sdes	sockaddr_u *addr,
536228692Sdes	int	status,
537228692Sdes	double	offset,		/* offset */
538228692Sdes	double	delay,		/* delay */
539228692Sdes	double	dispersion,	/* dispersion */
540228692Sdes	double	jitter		/* jitter */
541228692Sdes	)
542228692Sdes{
543228692Sdes	l_fp	now;
544228692Sdes	u_long	day;
545228692Sdes
546228692Sdes	if (!stats_control)
547228692Sdes		return;
548228692Sdes
549228692Sdes	get_systime(&now);
550228692Sdes	filegen_setup(&peerstats, now.l_ui);
551228692Sdes	day = now.l_ui / 86400 + MJD_1900;
552228692Sdes	now.l_ui %= 86400;
553174832Sdes	if (peerstats.fp != NULL) {
554174832Sdes		fprintf(peerstats.fp,
555174832Sdes		    "%lu %s %s %x %.9f %.9f %.9f %.9f\n", day,
556174832Sdes		    ulfptoa(&now, 3), stoa(addr), status, offset,
557174832Sdes		    delay, dispersion, jitter);
558174832Sdes		fflush(peerstats.fp);
559174832Sdes	}
560174832Sdes}
561228692Sdes
562174832Sdes
563174832Sdes/*
564174832Sdes * record_loop_stats - write loop filter statistics to file
565174832Sdes *
566174832Sdes * file format:
567174832Sdes * day (MJD)
568174832Sdes * time (s past midnight)
569174832Sdes * offset
570228692Sdes * frequency (PPM)
571228692Sdes * jitter
572174832Sdes * wnder (PPM)
573228692Sdes * time constant (log2)
574228692Sdes */
575228692Sdesvoid
576228692Sdesrecord_loop_stats(
577174832Sdes	double	offset,		/* offset */
578174832Sdes	double	freq,		/* frequency (PPM) */
579174832Sdes	double	jitter,		/* jitter */
580228692Sdes	double	wander,		/* wander (PPM) */
581228692Sdes	int spoll
582174832Sdes	)
583174832Sdes{
584228692Sdes	l_fp	now;
585174832Sdes	u_long	day;
586174832Sdes
587174832Sdes	if (!stats_control)
588228692Sdes		return;
589228692Sdes
590228692Sdes	get_systime(&now);
591228692Sdes	filegen_setup(&loopstats, now.l_ui);
592228692Sdes	day = now.l_ui / 86400 + MJD_1900;
593228692Sdes	now.l_ui %= 86400;
594228692Sdes	if (loopstats.fp != NULL) {
595147455Sdes		fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n",
596228692Sdes		    day, ulfptoa(&now, 3), offset, freq * 1e6, jitter,
597228692Sdes		    wander * 1e6, spoll);
598228692Sdes		fflush(loopstats.fp);
599228692Sdes	}
600228692Sdes}
601228692Sdes
602228692Sdes
603228692Sdes/*
604228692Sdes * record_clock_stats - write clock statistics to file
605228692Sdes *
606228692Sdes * file format:
607228692Sdes * day (MJD)
608228692Sdes * time (s past midnight)
609228692Sdes * IP address
610228692Sdes * text message
611228692Sdes */
612228692Sdesvoid
613228692Sdesrecord_clock_stats(
614228692Sdes	sockaddr_u *addr,
615228692Sdes	const char *text	/* timecode string */
616228692Sdes	)
617228692Sdes{
618228692Sdes	l_fp	now;
619228692Sdes	u_long	day;
620228692Sdes
621228692Sdes	if (!stats_control)
622228692Sdes		return;
623228692Sdes
624228692Sdes	get_systime(&now);
625228692Sdes	filegen_setup(&clockstats, now.l_ui);
626228692Sdes	day = now.l_ui / 86400 + MJD_1900;
627228692Sdes	now.l_ui %= 86400;
628228692Sdes	if (clockstats.fp != NULL) {
629228692Sdes		fprintf(clockstats.fp, "%lu %s %s %s\n", day,
630228692Sdes		    ulfptoa(&now, 3), stoa(addr), text);
631228692Sdes		fflush(clockstats.fp);
632228692Sdes	}
633228692Sdes}
634228692Sdes
635228692Sdes
636228692Sdes/*
637228692Sdes * mprintf_clock_stats - write clock statistics to file with
638228692Sdes *			msnprintf-style formatting.
639228692Sdes */
640228692Sdesint
641228692Sdesmprintf_clock_stats(
642228692Sdes	sockaddr_u *addr,
643228692Sdes	const char *fmt,
644228692Sdes	...
645228692Sdes	)
646228692Sdes{
647228692Sdes	va_list	ap;
648228692Sdes	int	rc;
649228692Sdes	char	msg[512];
650228692Sdes
651228692Sdes	va_start(ap, fmt);
652228692Sdes	rc = mvsnprintf(msg, sizeof(msg), fmt, ap);
653228692Sdes	va_end(ap);
654228692Sdes	if (stats_control)
655228692Sdes		record_clock_stats(addr, msg);
656228692Sdes
657228692Sdes	return rc;
658228692Sdes}
659228692Sdes
660228692Sdes/*
661228692Sdes * record_raw_stats - write raw timestamps to file
662228692Sdes *
663228692Sdes * file format
664228692Sdes * day (MJD)
665141098Sdes * time (s past midnight)
666228692Sdes * peer ip address
667228692Sdes * IP address
668228692Sdes * t1 t2 t3 t4 timestamps
669228692Sdes */
670228692Sdesvoid
671228692Sdesrecord_raw_stats(
672228692Sdes	sockaddr_u *srcadr,
673228692Sdes	sockaddr_u *dstadr,
674228692Sdes	l_fp	*t1,		/* originate timestamp */
675228692Sdes	l_fp	*t2,		/* receive timestamp */
676228692Sdes	l_fp	*t3,		/* transmit timestamp */
677228692Sdes	l_fp	*t4,		/* destination timestamp */
678228692Sdes	int	leap,
679228692Sdes	int	version,
680228692Sdes	int	mode,
681228692Sdes	int	stratum,
682228692Sdes	int	ppoll,
683228692Sdes	int	precision,
684228692Sdes	double	root_delay,	/* seconds */
685228692Sdes	double	root_dispersion,/* seconds */
686228692Sdes	u_int32	refid
687228692Sdes	)
688228692Sdes{
689228692Sdes	l_fp	now;
690228692Sdes	u_long	day;
691228692Sdes
692228692Sdes	if (!stats_control)
693228692Sdes		return;
694228692Sdes
695228692Sdes	get_systime(&now);
696228692Sdes	filegen_setup(&rawstats, now.l_ui);
697228692Sdes	day = now.l_ui / 86400 + MJD_1900;
698228692Sdes	now.l_ui %= 86400;
699228692Sdes	if (rawstats.fp != NULL) {
700228692Sdes		fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s %d %d %d %d %d %d %.6f %.6f %s\n",
701228692Sdes		    day, ulfptoa(&now, 3),
702228692Sdes		    stoa(srcadr), dstadr ?  stoa(dstadr) : "-",
703228692Sdes		    ulfptoa(t1, 9), ulfptoa(t2, 9),
704141098Sdes		    ulfptoa(t3, 9), ulfptoa(t4, 9),
705228692Sdes		    leap, version, mode, stratum, ppoll, precision,
706228692Sdes		    root_delay, root_dispersion, refid_str(refid, stratum));
707141098Sdes		fflush(rawstats.fp);
708228692Sdes	}
709228692Sdes}
710228692Sdes
711228692Sdes
712228692Sdes/*
713228692Sdes * record_sys_stats - write system statistics to file
714228692Sdes *
715228692Sdes * file format
716228692Sdes * day (MJD)
717228692Sdes * time (s past midnight)
718228692Sdes * time since reset
719228692Sdes * packets recieved
720228692Sdes * packets for this host
721228692Sdes * current version
722228692Sdes * old version
723228692Sdes * access denied
724228692Sdes * bad length or format
725228692Sdes * bad authentication
726228692Sdes * declined
727228692Sdes * rate exceeded
728228692Sdes * KoD sent
729228692Sdes */
730228692Sdesvoid
731228692Sdesrecord_sys_stats(void)
732228692Sdes{
733228692Sdes	l_fp	now;
734228692Sdes	u_long	day;
735228692Sdes
736228692Sdes	if (!stats_control)
737228692Sdes		return;
738228692Sdes
739228692Sdes	get_systime(&now);
740228692Sdes	filegen_setup(&sysstats, now.l_ui);
741228692Sdes	day = now.l_ui / 86400 + MJD_1900;
742228692Sdes	now.l_ui %= 86400;
743228692Sdes	if (sysstats.fp != NULL) {
744228692Sdes		fprintf(sysstats.fp,
745228692Sdes		    "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
746228692Sdes		    day, ulfptoa(&now, 3), current_time - sys_stattime,
747228692Sdes		    sys_received, sys_processed, sys_newversion,
748228692Sdes		    sys_oldversion, sys_restricted, sys_badlength,
749228692Sdes		    sys_badauth, sys_declined, sys_limitrejected,
750228692Sdes		    sys_kodsent);
751228692Sdes		fflush(sysstats.fp);
752228692Sdes		proto_clr_stats();
753228692Sdes	}
754228692Sdes}
755228692Sdes
756228692Sdes
757228692Sdes/*
758228692Sdes * record_proto_stats - write system statistics to file
759228692Sdes *
760228692Sdes * file format
761228692Sdes * day (MJD)
762228692Sdes * time (s past midnight)
763228692Sdes * text message
764228692Sdes */
765228692Sdesvoid
766228692Sdesrecord_proto_stats(
767228692Sdes	char	*str		/* text string */
768228692Sdes	)
769228692Sdes{
770236109Sdes	l_fp	now;
771236109Sdes	u_long	day;
772228692Sdes
773228692Sdes	if (!stats_control)
774228692Sdes		return;
775228692Sdes
776228692Sdes	get_systime(&now);
777228692Sdes	filegen_setup(&protostats, now.l_ui);
778228692Sdes	day = now.l_ui / 86400 + MJD_1900;
779228692Sdes	now.l_ui %= 86400;
780228692Sdes	if (protostats.fp != NULL) {
781228692Sdes		fprintf(protostats.fp, "%lu %s %s\n", day,
782228692Sdes		    ulfptoa(&now, 3), str);
783228692Sdes		fflush(protostats.fp);
784228692Sdes	}
785228692Sdes}
786228692Sdes
787228692Sdes
788228692Sdes#ifdef AUTOKEY
789228692Sdes/*
790228692Sdes * record_crypto_stats - write crypto statistics to file
791228692Sdes *
792228692Sdes * file format:
793228692Sdes * day (mjd)
794228692Sdes * time (s past midnight)
795228692Sdes * peer ip address
796228692Sdes * text message
797228692Sdes */
798228692Sdesvoid
799228692Sdesrecord_crypto_stats(
800228692Sdes	sockaddr_u *addr,
801228692Sdes	const char *text	/* text message */
802228692Sdes	)
803228692Sdes{
804228692Sdes	l_fp	now;
805228692Sdes	u_long	day;
806228692Sdes
807228692Sdes	if (!stats_control)
808228692Sdes		return;
809228692Sdes
810228692Sdes	get_systime(&now);
811228692Sdes	filegen_setup(&cryptostats, now.l_ui);
812228692Sdes	day = now.l_ui / 86400 + MJD_1900;
813228692Sdes	now.l_ui %= 86400;
814228692Sdes	if (cryptostats.fp != NULL) {
815228692Sdes		if (addr == NULL)
816228692Sdes			fprintf(cryptostats.fp, "%lu %s 0.0.0.0 %s\n",
817228692Sdes			    day, ulfptoa(&now, 3), text);
818228692Sdes		else
819228692Sdes			fprintf(cryptostats.fp, "%lu %s %s %s\n",
820228692Sdes			    day, ulfptoa(&now, 3), stoa(addr), text);
821228692Sdes		fflush(cryptostats.fp);
822228692Sdes	}
823228692Sdes}
824228692Sdes#endif	/* AUTOKEY */
825228692Sdes
826228692Sdes
827228692Sdes#ifdef DEBUG_TIMING
828228692Sdes/*
829228692Sdes * record_timing_stats - write timing statistics to file
830228692Sdes *
831228692Sdes * file format:
832228692Sdes * day (mjd)
833228692Sdes * time (s past midnight)
834228692Sdes * text message
835228692Sdes */
836228692Sdesvoid
837228692Sdesrecord_timing_stats(
838228692Sdes	const char *text	/* text message */
839228692Sdes	)
840228692Sdes{
841228692Sdes	static unsigned int flshcnt;
842228692Sdes	l_fp	now;
843228692Sdes	u_long	day;
844228692Sdes
845228692Sdes	if (!stats_control)
846228692Sdes		return;
847228692Sdes
848228692Sdes	get_systime(&now);
849228692Sdes	filegen_setup(&timingstats, now.l_ui);
850228692Sdes	day = now.l_ui / 86400 + MJD_1900;
851228692Sdes	now.l_ui %= 86400;
852228692Sdes	if (timingstats.fp != NULL) {
853228692Sdes		fprintf(timingstats.fp, "%lu %s %s\n", day, lfptoa(&now,
854228692Sdes		    3), text);
855228692Sdes		if (++flshcnt % 100 == 0)
856228692Sdes			fflush(timingstats.fp);
857228692Sdes	}
858228692Sdes}
859228692Sdes#endif
860228692Sdes
861228692Sdes
862228692Sdes/*
863228692Sdes * check_leap_file - See if the leapseconds file has been updated.
864228692Sdes *
865228692Sdes * Returns: n/a
866228692Sdes *
867228692Sdes * Note: This loads a new leapfile on the fly. Currently a leap file
868228692Sdes * without SHA1 signature is accepted, but if there is a signature line,
869228692Sdes * the signature must be valid or the file is rejected.
870228692Sdes */
871228692Sdesvoid
872228692Sdescheck_leap_file(
873228692Sdes	int           is_daily_check,
874228692Sdes	uint32_t      ntptime       ,
875228692Sdes	const time_t *systime
876228692Sdes	)
877228692Sdes{
878228692Sdes	/* just do nothing if there is no leap file */
879228692Sdes	if ( ! (leapfile_name && *leapfile_name))
880228692Sdes		return;
881228692Sdes
882228692Sdes	/* try to load leapfile, force it if no leapfile loaded yet */
883228692Sdes	if (leapsec_load_file(
884228692Sdes		    leapfile_name, &leapfile_stat,
885228692Sdes		    !have_leapfile, is_daily_check))
886228692Sdes		have_leapfile = TRUE;
887228692Sdes	else if (!have_leapfile)
888228692Sdes		return;
889228692Sdes
890228692Sdes	check_leap_expiration(is_daily_check, ntptime, systime);
891228692Sdes}
892228692Sdes
893228692Sdes/*
894228692Sdes * check expiration of a loaded leap table
895228692Sdes */
896228692Sdesstatic void
897228692Sdescheck_leap_expiration(
898228692Sdes	int           is_daily_check,
899228692Sdes	uint32_t      ntptime       ,
900228692Sdes	const time_t *systime
901228692Sdes	)
902228692Sdes{
903228692Sdes	static const char * const logPrefix = "leapsecond file";
904228692Sdes	int  rc;
905228692Sdes
906228692Sdes	/* test the expiration of the leap data and log with proper
907228692Sdes	 * level and frequency (once/hour or once/day, depending on the
908228692Sdes	 * state.
909228692Sdes	 */
910228692Sdes	rc = leapsec_daystolive(ntptime, systime);
911228692Sdes	if (rc == 0) {
912228692Sdes		msyslog(LOG_WARNING,
913228692Sdes			"%s ('%s'): will expire in less than one day",
914228692Sdes			logPrefix, leapfile_name);
915228692Sdes	} else if (is_daily_check && rc < 28) {
916228692Sdes		if (rc < 0)
917228692Sdes			msyslog(LOG_ERR,
918228692Sdes				"%s ('%s'): expired less than %d day%s ago",
919228692Sdes				logPrefix, leapfile_name, -rc, (rc == -1 ? "" : "s"));
920228692Sdes		else
921228692Sdes			msyslog(LOG_WARNING,
922228692Sdes				"%s ('%s'): will expire in less than %d days",
923228692Sdes				logPrefix, leapfile_name, 1+rc);
924228692Sdes	}
925228692Sdes}
926228692Sdes
927228692Sdes
928228692Sdes/*
929228692Sdes * getauthkeys - read the authentication keys from the specified file
930228692Sdes */
931228692Sdesvoid
932228692Sdesgetauthkeys(
933228692Sdes	const char *keyfile
934228692Sdes	)
935228692Sdes{
936228692Sdes	int len;
937228692Sdes
938228692Sdes	len = strlen(keyfile);
939228692Sdes	if (!len)
940228692Sdes		return;
941228692Sdes
942228692Sdes#ifndef SYS_WINNT
943228692Sdes	key_file_name = erealloc(key_file_name, len + 1);
944228692Sdes	memcpy(key_file_name, keyfile, len + 1);
945228692Sdes#else
946228692Sdes	key_file_name = erealloc(key_file_name, _MAX_PATH);
947228692Sdes	if (len + 1 > _MAX_PATH)
948228692Sdes		return;
949228692Sdes	if (!ExpandEnvironmentStrings(keyfile, key_file_name,
950228692Sdes				      _MAX_PATH)) {
951228692Sdes		msyslog(LOG_ERR,
952228692Sdes			"ExpandEnvironmentStrings(KEY_FILE) failed: %m");
953228692Sdes		strlcpy(key_file_name, keyfile, _MAX_PATH);
954141098Sdes	}
955228692Sdes	key_file_name = erealloc(key_file_name,
956228692Sdes				 1 + strlen(key_file_name));
957228692Sdes#endif /* SYS_WINNT */
958228692Sdes
959228692Sdes	authreadkeys(key_file_name);
960228692Sdes}
961228692Sdes
962228692Sdes
963228692Sdes/*
964228692Sdes * rereadkeys - read the authentication key file over again.
965228692Sdes */
966228692Sdesvoid
967228692Sdesrereadkeys(void)
968228692Sdes{
969228692Sdes	if (NULL != key_file_name)
970228692Sdes		authreadkeys(key_file_name);
971228692Sdes}
972228692Sdes
973228692Sdes
974228692Sdes#if notyet
975228692Sdes/*
976228692Sdes * ntp_exit - document explicitly that ntpd has exited
977228692Sdes */
978228692Sdesvoid
979228692Sdesntp_exit(int retval)
980228692Sdes{
981228692Sdes	msyslog(LOG_ERR, "EXITING with return code %d", retval);
982228692Sdes	exit(retval);
983228692Sdes}
984228692Sdes#endif
985228692Sdes
986228692Sdes/*
987228692Sdes * fstostr - prettyprint NTP seconds
988228692Sdes */
989228692Sdeschar * fstostr(
990228692Sdes	time_t	ntp_stamp
991228692Sdes	)
992228692Sdes{
993228692Sdes	char *		buf;
994228692Sdes	struct calendar tm;
995228692Sdes
996228692Sdes	LIB_GETBUF(buf);
997228692Sdes	if (ntpcal_ntp_to_date(&tm, (u_int32)ntp_stamp, NULL) < 0)
998228692Sdes		snprintf(buf, LIB_BUFLENGTH, "ntpcal_ntp_to_date: %ld: range error",
999228692Sdes			 (long)ntp_stamp);
1000228692Sdes	else
1001228692Sdes		snprintf(buf, LIB_BUFLENGTH, "%04d%02d%02d%02d%02d",
1002228692Sdes			 tm.year, tm.month, tm.monthday,
1003228692Sdes			 tm.hour, tm.minute);
1004228692Sdes	return buf;
1005228692Sdes}
1006228692Sdes
1007228692Sdes
1008228692Sdes/*
1009228692Sdes * ntpd_time_stepped is called back by step_systime(), allowing ntpd
1010228692Sdes * to do any one-time processing necessitated by the step.
1011228692Sdes */
1012228692Sdesvoid
1013228692Sdesntpd_time_stepped(void)
1014228692Sdes{
1015228692Sdes	u_int saved_mon_enabled;
1016228692Sdes
1017228692Sdes	/*
1018228692Sdes	 * flush the monitor MRU list which contains l_fp timestamps
1019228692Sdes	 * which should not be compared across the step.
1020228692Sdes	 */
1021228692Sdes	if (MON_OFF != mon_enabled) {
1022228692Sdes		saved_mon_enabled = mon_enabled;
1023228692Sdes		mon_stop(MON_OFF);
1024228692Sdes		mon_start(saved_mon_enabled);
1025228692Sdes	}
1026228692Sdes
1027228692Sdes	/* inform interpolating Windows code to allow time to go back */
1028228692Sdes#ifdef SYS_WINNT
1029228692Sdes	win_time_stepped();
1030228692Sdes#endif
1031228692Sdes}
1032228692Sdes