1/*
2 * ntp_util.c - stuff I didn't have any other place for
3 */
4#ifdef HAVE_CONFIG_H
5# include <config.h>
6#endif
7
8#include "ntpd.h"
9#include "ntp_io.h"
10#include "ntp_unixtime.h"
11#include "ntp_filegen.h"
12#include "ntp_if.h"
13#include "ntp_stdlib.h"
14#include "ntp_assert.h"
15
16#include <stdio.h>
17#include <ctype.h>
18#include <sys/types.h>
19#ifdef HAVE_SYS_IOCTL_H
20# include <sys/ioctl.h>
21#endif
22
23#ifdef HAVE_IEEEFP_H
24# include <ieeefp.h>
25#endif
26#ifdef HAVE_MATH_H
27# include <math.h>
28#endif
29
30#ifdef  DOSYNCTODR
31# if !defined(VMS)
32#  include <sys/resource.h>
33# endif /* VMS */
34#endif
35
36#if defined(VMS)
37# include <descrip.h>
38#endif /* VMS */
39
40#include <vproc.h>
41#include <sys/mman.h>     /* mmap */
42#include <signal.h>
43#include <sys/stat.h>
44
45#include <os/trace.h>
46
47/*
48 * Defines used by the leapseconds stuff
49 */
50#define	MAX_TAI	100			/* max TAI offset (s) */
51#define	L_DAY	86400UL			/* seconds per day */
52#define	L_YEAR	(L_DAY * 365)		/* days per year */
53#define	L_LYEAR	(L_YEAR + L_DAY)	/* days per leap year */
54#define	L_4YEAR	(L_LYEAR + 3 * L_YEAR)	/* days per leap cycle */
55#define	L_CENT	(L_4YEAR * 25)		/* days per century */
56
57/*
58 * This contains odds and ends, including the hourly stats, various
59 * configuration items, leapseconds stuff, etc.
60 */
61/*
62 * File names
63 */
64static	char *key_file_name;		/* keys file name */
65char	*leapseconds_file_name;		/* leapseconds file name */
66char	*stats_drift_file;		/* frequency file name */
67static	char *stats_temp_file;		/* temp frequency file name */
68double wander_resid;			/* wander threshold */
69double	wander_threshold = 1e-7;	/* initial wander threshold */
70int	drift_file_sw;			/* clock update switch */
71static	int drift_exists;
72
73/*
74 * Statistics file stuff
75 */
76#ifndef NTP_VAR
77# ifndef SYS_WINNT
78#  define NTP_VAR "/var/NTP/"		/* NOTE the trailing '/' */
79# else
80#  define NTP_VAR "c:\\var\\ntp\\"	/* NOTE the trailing '\\' */
81# endif /* SYS_WINNT */
82#endif
83
84#ifndef MAXPATHLEN
85# define MAXPATHLEN 256
86#endif
87
88#ifdef DEBUG_TIMING
89static FILEGEN timingstats;
90#endif
91#ifdef OPENSSL
92static FILEGEN cryptostats;
93#endif /* OPENSSL */
94
95static	char statsdir[MAXPATHLEN] = NTP_VAR;
96static FILEGEN peerstats;
97static FILEGEN loopstats;
98static FILEGEN clockstats;
99static FILEGEN rawstats;
100static FILEGEN sysstats;
101static FILEGEN protostats;
102
103/*
104 * This controls whether stats are written to the fileset. Provided
105 * so that ntpdc can turn off stats when the file system fills up.
106 */
107int stats_control;
108
109/*
110 * Initial frequency offset later passed to the loopfilter.
111 */
112double	old_drift = 1e9;		/* current frequency */
113static double prev_drift_comp;		/* last frequency update */
114
115/*
116 * Static prototypes
117 */
118static int leap_file(FILE *);
119static void record_sys_stats(void);
120
121/*
122 * Prototypes
123 */
124#ifdef DEBUG
125void	uninit_util(void);
126#endif
127
128
129/*
130 * uninit_util - free memory allocated by init_util
131 */
132#ifdef DEBUG
133void
134uninit_util(void)
135{
136#if defined(_MSC_VER) && defined (_DEBUG)
137	_CrtCheckMemory();
138#endif
139	if (stats_drift_file) {
140		free(stats_drift_file);
141		free(stats_temp_file);
142		stats_drift_file = NULL;
143		stats_temp_file = NULL;
144	}
145	if (key_file_name) {
146		free(key_file_name);
147		key_file_name = NULL;
148	}
149	filegen_unregister("peerstats");
150	filegen_unregister("loopstats");
151	filegen_unregister("clockstats");
152	filegen_unregister("rawstats");
153	filegen_unregister("sysstats");
154	filegen_unregister("protostats");
155#ifdef OPENSSL
156	filegen_unregister("cryptostats");
157#endif /* OPENSSL */
158#ifdef DEBUG_TIMING
159	filegen_unregister("timingstats");
160#endif /* DEBUG_TIMING */
161
162#if defined(_MSC_VER) && defined (_DEBUG)
163	_CrtCheckMemory();
164#endif
165}
166#endif /* DEBUG */
167
168
169/*
170 * init_util - initialize the utilities (ntpd included)
171 */
172void
173init_util(void)
174{
175	stats_drift_file = NULL;
176	stats_temp_file = NULL;
177	key_file_name = NULL;
178	filegen_register(statsdir, "peerstats",   &peerstats);
179	filegen_register(statsdir, "loopstats",   &loopstats);
180	filegen_register(statsdir, "clockstats",  &clockstats);
181	filegen_register(statsdir, "rawstats",    &rawstats);
182	filegen_register(statsdir, "sysstats",    &sysstats);
183	filegen_register(statsdir, "protostats",  &protostats);
184#ifdef OPENSSL
185	filegen_register(statsdir, "cryptostats", &cryptostats);
186#endif /* OPENSSL */
187#ifdef DEBUG_TIMING
188	filegen_register(statsdir, "timingstats", &timingstats);
189#endif /* DEBUG_TIMING */
190#ifdef DEBUG
191	atexit(uninit_util);
192#endif /* DEBUG */
193}
194
195int
196save_drift_file(
197	)
198{
199	FILE *fp;
200	int rc = TRUE;
201#if !TARGET_OS_EMBEDDED
202	vproc_transaction_t vt;
203#endif
204    struct stat statbuf;
205	static off_t stats_size = 0;
206	sigset_t sigterm, oset;
207
208	sigemptyset(&sigterm);
209	sigaddset(&sigterm, SIGTERM);
210	pthread_sigmask(SIG_BLOCK, &sigterm, &oset);
211#if !TARGET_OS_EMBEDDED
212	vt = vproc_transaction_begin(NULL);
213#endif
214	if (stat(stats_drift_file, &statbuf) == -1) {
215		if ((fp = fopen(stats_temp_file, "w")) == NULL) {
216			msyslog(LOG_ERR, "can't open %s: %m",
217			    stats_temp_file);
218			rc = FALSE;
219			goto done;
220		}
221		stats_size = fprintf(fp, "%.3f\n", drift_comp * 1e6);
222		(void)fclose(fp);
223		/* atomic */
224#ifdef SYS_WINNT
225		(void) _unlink(stats_drift_file); /* rename semantics differ under NT */
226#endif /* SYS_WINNT */
227
228#ifndef NO_RENAME
229		(void) rename(stats_temp_file, stats_drift_file);
230#else
231		/* we have no rename NFS of ftp in use*/
232		if ((fp = fopen(stats_drift_file, "w")) == NULL) {
233			msyslog(LOG_ERR, "can't open %s: %m",
234			    stats_drift_file);
235			rc = FALSE;
236		}
237
238#endif
239	        drift_exists++;
240#if defined(VMS)
241		/* PURGE */
242		{
243			$DESCRIPTOR(oldvers,";-1");
244			struct dsc$descriptor driftdsc = {
245				strlen(stats_drift_file),0,0,stats_drift_file };
246
247			while(lib$delete_file(&oldvers,&driftdsc) & 1) ;
248		}
249#endif
250	} else {
251		/* use mmap */
252		static void *mmap_addr;
253		if (mmap_addr == 0) {
254			int fd = open(stats_drift_file, O_RDWR);
255			if (fd >= 0) {
256				mmap_addr = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0);
257				if (mmap_addr == MAP_FAILED) {
258					msyslog(LOG_ERR, "can't mmap %s: %m", stats_drift_file);
259					mmap_addr = 0;
260					rc = FALSE;
261				} else {
262                    off_t n = snprintf(mmap_addr, getpagesize(), "%.3f\n", drift_comp * 1e6);
263					os_trace("drift: %.3f", drift_comp * 1e6);
264                    if (n != stats_size) {
265                        truncate(stats_drift_file, n);
266                        stats_size = n;
267                    }
268                }
269                close(fd);
270			} else {
271				msyslog(LOG_ERR, "can't open %s: %m", stats_drift_file);
272				rc = FALSE;
273			}
274		} else {
275			off_t n = snprintf(mmap_addr, getpagesize(), "%.3f\n", drift_comp * 1e6);
276			os_trace("drift: %.3f", drift_comp * 1e6);
277			if (n != stats_size) {
278				truncate(stats_drift_file, n);
279				stats_size = n;
280			}
281		}
282	}
283done:
284#if !TARGET_OS_EMBEDDED
285	vproc_transaction_end(NULL, vt);
286#endif
287	pthread_sigmask(SIG_SETMASK, &oset, NULL);
288	return rc;
289}
290
291
292/*
293 * hourly_stats - print some interesting stats
294 */
295void
296write_stats(void)
297{
298	double	ftemp;
299#ifdef DOSYNCTODR
300	struct timeval tv;
301#if !defined(VMS)
302	int	prio_set;
303#endif
304#ifdef HAVE_GETCLOCK
305        struct timespec ts;
306#endif
307	int	o_prio;
308
309	/*
310	 * Sometimes having a Sun can be a drag.
311	 *
312	 * The kernel variable dosynctodr controls whether the system's
313	 * soft clock is kept in sync with the battery clock. If it
314	 * is zero, then the soft clock is not synced, and the battery
315	 * clock is simply left to rot. That means that when the system
316	 * reboots, the battery clock (which has probably gone wacky)
317	 * sets the soft clock. That means ntpd starts off with a very
318	 * confused idea of what time it is. It then takes a large
319	 * amount of time to figure out just how wacky the battery clock
320	 * has made things drift, etc, etc. The solution is to make the
321	 * battery clock sync up to system time. The way to do THAT is
322	 * to simply set the time of day to the current time of day, but
323	 * as quickly as possible. This may, or may not be a sensible
324	 * thing to do.
325	 *
326	 * CAVEAT: settimeofday() steps the sun clock by about 800 us,
327	 *         so setting DOSYNCTODR seems a bad idea in the
328	 *         case of us resolution
329	 */
330
331#if !defined(VMS)
332	/*
333	 * (prr) getpriority returns -1 on error, but -1 is also a valid
334	 * return value (!), so instead we have to zero errno before the
335	 * call and check it for non-zero afterwards.
336	 */
337	errno = 0;
338	prio_set = 0;
339	o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */
340
341	/*
342	 * (prr) if getpriority succeeded, call setpriority to raise
343	 * scheduling priority as high as possible.  If that succeeds
344	 * as well, set the prio_set flag so we remember to reset
345	 * priority to its previous value below.  Note that on Solaris
346	 * 2.6 (and beyond?), both getpriority and setpriority will fail
347	 * with ESRCH, because sched_setscheduler (called from main) put
348	 * us in the real-time scheduling class which setpriority
349	 * doesn't know about. Being in the real-time class is better
350	 * than anything setpriority can do, anyhow, so this error is
351	 * silently ignored.
352	 */
353	if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
354		prio_set = 1;	/* overdrive */
355#endif /* VMS */
356#ifdef HAVE_GETCLOCK
357        (void) getclock(TIMEOFDAY, &ts);
358        tv.tv_sec = ts.tv_sec;
359        tv.tv_usec = ts.tv_nsec / 1000;
360#else /*  not HAVE_GETCLOCK */
361	GETTIMEOFDAY(&tv,(struct timezone *)NULL);
362#endif /* not HAVE_GETCLOCK */
363	if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0)
364		msyslog(LOG_ERR, "can't sync battery time: %m");
365#if !defined(VMS)
366	if (prio_set)
367		setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */
368#endif /* VMS */
369#endif /* DOSYNCTODR */
370	record_sys_stats();
371	ftemp = fabs(prev_drift_comp - drift_comp);
372	prev_drift_comp = drift_comp;
373	if (ftemp > clock_phi)
374		return;
375
376	if (stats_drift_file != 0 && drift_file_sw) {
377
378		/*
379		 * When the frequency file is written, initialize the
380		 * wander threshold to a configured initial value.
381		 * Thereafter reduce it by a factor of 0.5. When it
382		 * drops below the frequency wander, write the frequency
383		 * file. This adapts to the prevailing wander yet
384		 * minimizes the file writes.
385		 */
386		drift_file_sw = FALSE;
387		wander_resid *= 0.5;
388#ifdef DEBUG
389		if (debug)
390			msyslog(LOG_DEBUG, "write_stats: wander %.6lf thresh %.6lf, freq %.6lf\n",
391				clock_stability * 1e6, wander_resid * 1e6,
392				drift_comp * 1e6);
393#endif
394 		if (sys_leap != LEAP_NOTINSYNC && clock_stability >
395		    wander_resid) {
396			wander_resid = wander_threshold;
397			save_drift_file();
398			/* atomic */
399#ifdef SYS_WINNT
400			if (_unlink(stats_drift_file)) /* rename semantics differ under NT */
401				msyslog(LOG_WARNING,
402					"Unable to remove prior drift file %s, %m",
403					stats_drift_file);
404#endif /* SYS_WINNT */
405
406#if defined(VMS)
407			/* PURGE */
408			{
409				$DESCRIPTOR(oldvers,";-1");
410				struct dsc$descriptor driftdsc = {
411					strlen(stats_drift_file), 0, 0,
412					    stats_drift_file };
413				while(lib$delete_file(&oldvers,
414				    &driftdsc) & 1);
415			}
416#endif
417		} else if (drift_comp != 0.0) {
418			save_drift_file(); /* for pacemaker */
419			/* XXX: Log a message at INFO level */
420		}
421	}
422}
423
424
425/*
426 * stats_config - configure the stats operation
427 */
428void
429stats_config(
430	int item,
431	const char *invalue	/* only one type so far */
432	)
433{
434	FILE	*fp;
435	const char *value;
436	int	len;
437	char	tbuf[80];
438	char	str1[20], str2[20];
439
440	/*
441	 * Expand environment strings under Windows NT, since the
442	 * command interpreter doesn't do this, the program must.
443	 */
444#ifdef SYS_WINNT
445	char newvalue[MAX_PATH], parameter[MAX_PATH];
446
447	if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
448 		switch(item) {
449		    case STATS_FREQ_FILE:
450			strcpy(parameter,"STATS_FREQ_FILE");
451			break;
452
453		    case STATS_LEAP_FILE:
454			strcpy(parameter,"STATS_LEAP_FILE");
455			break;
456
457		    case STATS_STATSDIR:
458			strcpy(parameter,"STATS_STATSDIR");
459			break;
460
461		    case STATS_PID_FILE:
462			strcpy(parameter,"STATS_PID_FILE");
463			break;
464
465		    default:
466			strcpy(parameter,"UNKNOWN");
467			break;
468		}
469		value = invalue;
470		msyslog(LOG_ERR,
471		    "ExpandEnvironmentStrings(%s) failed: %m\n",
472		    parameter);
473	} else {
474		value = newvalue;
475	}
476#else
477	value = invalue;
478#endif /* SYS_WINNT */
479
480	switch(item) {
481
482	/*
483	 * Open and read frequency file.
484	 */
485	case STATS_FREQ_FILE:
486		if (!value || (len = strlen(value)) == 0)
487			break;
488
489		stats_drift_file = erealloc(stats_drift_file, len + 1);
490		stats_temp_file = erealloc(stats_temp_file,
491					   len + sizeof(".TEMP"));
492
493		memmove(stats_drift_file, value, (unsigned)(len+1));
494		memmove(stats_temp_file, value, (unsigned)len);
495		memmove(stats_temp_file + len,
496#if !defined(VMS)
497			".TEMP", sizeof(".TEMP"));
498#else
499			"-TEMP", sizeof("-TEMP"));
500#endif /* VMS */
501
502		/*
503		 * Open drift file and read frequency. If the file is
504		 * missing or contains errors, tell the loop to reset.
505		 */
506		if ((fp = fopen(stats_drift_file, "r")) == NULL)
507			break;
508
509		if (fscanf(fp, "%lf", &old_drift) != 1) {
510			msyslog(LOG_ERR,
511				"format error frequency file %s",
512				stats_drift_file);
513			fclose(fp);
514			break;
515
516		}
517		fclose(fp);
518
519		if (old_drift < -100.0 || old_drift > 100.0) {
520			os_trace_fault("Ignorning old drift %.3f", old_drift);
521			msyslog(LOG_ERR, "Ignoring old drift %.3f", old_drift);
522			break;
523		}
524
525		drift_exists++;
526		old_drift /= 1e6;
527		prev_drift_comp = old_drift;
528		break;
529
530	/*
531	 * Specify statistics directory.
532	 */
533	case STATS_STATSDIR:
534
535		/*
536		 * HMS: the following test is insufficient:
537		 * - value may be missing the DIR_SEP
538		 * - we still need the filename after it
539		 */
540		if (strlen(value) >= sizeof(statsdir)) {
541			msyslog(LOG_ERR,
542			    "statsdir too long (>%d, sigh)",
543			    (int)sizeof(statsdir) - 1);
544		} else {
545			l_fp now;
546			int add_dir_sep;
547			int value_l = strlen(value);
548
549			/* Add a DIR_SEP unless we already have one. */
550			if (value_l == 0)
551				add_dir_sep = 0;
552			else
553				add_dir_sep = (DIR_SEP !=
554				    value[value_l - 1]);
555
556			if (add_dir_sep)
557			    snprintf(statsdir, sizeof(statsdir),
558				"%s%c", value, DIR_SEP);
559			else
560			    snprintf(statsdir, sizeof(statsdir),
561				"%s", value);
562
563			get_systime(&now);
564			if(peerstats.prefix == &statsdir[0] &&
565			    peerstats.fp != NULL) {
566				fclose(peerstats.fp);
567				peerstats.fp = NULL;
568				filegen_setup(&peerstats, now.l_ui);
569			}
570			if(loopstats.prefix == &statsdir[0] &&
571			    loopstats.fp != NULL) {
572				fclose(loopstats.fp);
573				loopstats.fp = NULL;
574				filegen_setup(&loopstats, now.l_ui);
575			}
576			if(clockstats.prefix == &statsdir[0] &&
577			    clockstats.fp != NULL) {
578				fclose(clockstats.fp);
579				clockstats.fp = NULL;
580				filegen_setup(&clockstats, now.l_ui);
581			}
582			if(rawstats.prefix == &statsdir[0] &&
583			    rawstats.fp != NULL) {
584				fclose(rawstats.fp);
585				rawstats.fp = NULL;
586				filegen_setup(&rawstats, now.l_ui);
587			}
588			if(sysstats.prefix == &statsdir[0] &&
589			    sysstats.fp != NULL) {
590				fclose(sysstats.fp);
591				sysstats.fp = NULL;
592				filegen_setup(&sysstats, now.l_ui);
593			}
594			if(protostats.prefix == &statsdir[0] &&
595			    protostats.fp != NULL) {
596				fclose(protostats.fp);
597				protostats.fp = NULL;
598				filegen_setup(&protostats, now.l_ui);
599			}
600#ifdef OPENSSL
601			if(cryptostats.prefix == &statsdir[0] &&
602			    cryptostats.fp != NULL) {
603				fclose(cryptostats.fp);
604				cryptostats.fp = NULL;
605				filegen_setup(&cryptostats, now.l_ui);
606			}
607#endif /* OPENSSL */
608#ifdef DEBUG_TIMING
609			if(timingstats.prefix == &statsdir[0] &&
610			    timingstats.fp != NULL) {
611				fclose(timingstats.fp);
612				timingstats.fp = NULL;
613				filegen_setup(&timingstats, now.l_ui);
614			}
615#endif /* DEBUG_TIMING */
616		}
617		break;
618
619	/*
620	 * Open pid file.
621	 */
622	case STATS_PID_FILE:
623		if ((fp = fopen(value, "w")) == NULL) {
624			msyslog(LOG_ERR, "pid file %s: %m",
625			    value);
626			break;
627		}
628		fprintf(fp, "%d", (int)getpid());
629		fclose(fp);;
630		break;
631
632	/*
633	 * Read leapseconds file.
634	 */
635	case STATS_LEAP_FILE:
636		if ((fp = fopen(value, "r")) == NULL) {
637			msyslog(LOG_ERR, "leapseconds file %s: %m",
638			    value);
639			break;
640		}
641
642		if (leap_file(fp) < 0) {
643			msyslog(LOG_ERR,
644			    "format error leapseconds file %s",
645			    value);
646		} else {
647			strcpy(str1, fstostr(leap_sec));
648			strcpy(str2, fstostr(leap_expire));
649			snprintf(tbuf, sizeof(tbuf),
650			    "%d leap %s expire %s", leap_tai, str1,
651			    str2);
652			report_event(EVNT_TAI, NULL, tbuf);
653		}
654		fclose(fp);
655		break;
656
657	default:
658		/* oh well */
659		break;
660	}
661}
662
663
664/*
665 * record_peer_stats - write peer statistics to file
666 *
667 * file format:
668 * day (MJD)
669 * time (s past UTC midnight)
670 * IP address
671 * status word (hex)
672 * offset
673 * delay
674 * dispersion
675 * jitter
676*/
677void
678record_peer_stats(
679	sockaddr_u *addr,
680	int	status,
681	double	offset,		/* offset */
682	double	delay,		/* delay */
683	double	dispersion,	/* dispersion */
684	double	jitter		/* jitter */
685	)
686{
687	l_fp	now;
688	u_long	day;
689
690	if (!stats_control)
691		return;
692
693	get_systime(&now);
694	filegen_setup(&peerstats, now.l_ui);
695	day = now.l_ui / 86400 + MJD_1900;
696	now.l_ui %= 86400;
697	if (peerstats.fp != NULL) {
698		fprintf(peerstats.fp,
699		    "%lu %s %s %x %.9f %.9f %.9f %.9f\n", day,
700		    ulfptoa(&now, 3), stoa(addr), status, offset,
701		    delay, dispersion, jitter);
702		fflush(peerstats.fp);
703	}
704}
705
706
707/*
708 * record_loop_stats - write loop filter statistics to file
709 *
710 * file format:
711 * day (MJD)
712 * time (s past midnight)
713 * offset
714 * frequency (PPM)
715 * jitter
716 * wnder (PPM)
717 * time constant (log2)
718 */
719void
720record_loop_stats(
721	double	offset,		/* offset */
722	double	freq,		/* frequency (PPM) */
723	double	jitter,		/* jitter */
724	double	wander,		/* wander (PPM) */
725	int spoll
726	)
727{
728	l_fp	now;
729	u_long	day;
730
731	if (!stats_control)
732		return;
733
734	get_systime(&now);
735	filegen_setup(&loopstats, now.l_ui);
736	day = now.l_ui / 86400 + MJD_1900;
737	now.l_ui %= 86400;
738	if (loopstats.fp != NULL) {
739		fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n",
740		    day, ulfptoa(&now, 3), offset, freq * 1e6, jitter,
741		    wander * 1e6, spoll);
742		fflush(loopstats.fp);
743	}
744}
745
746
747/*
748 * record_clock_stats - write clock statistics to file
749 *
750 * file format:
751 * day (MJD)
752 * time (s past midnight)
753 * IP address
754 * text message
755 */
756void
757record_clock_stats(
758	sockaddr_u *addr,
759	const char *text	/* timecode string */
760	)
761{
762	l_fp	now;
763	u_long	day;
764
765	if (!stats_control)
766		return;
767
768	get_systime(&now);
769	filegen_setup(&clockstats, now.l_ui);
770	day = now.l_ui / 86400 + MJD_1900;
771	now.l_ui %= 86400;
772	if (clockstats.fp != NULL) {
773		fprintf(clockstats.fp, "%lu %s %s %s\n", day,
774		    ulfptoa(&now, 3), stoa(addr), text);
775		fflush(clockstats.fp);
776	}
777}
778
779
780/*
781 * record_raw_stats - write raw timestamps to file
782 *
783 * file format
784 * day (MJD)
785 * time (s past midnight)
786 * peer ip address
787 * IP address
788 * t1 t2 t3 t4 timestamps
789 */
790void
791record_raw_stats(
792	sockaddr_u *srcadr,
793	sockaddr_u *dstadr,
794	l_fp	*t1,		/* originate timestamp */
795	l_fp	*t2,		/* receive timestamp */
796	l_fp	*t3,		/* transmit timestamp */
797	l_fp	*t4		/* destination timestamp */
798	)
799{
800	l_fp	now;
801	u_long	day;
802
803	if (!stats_control)
804		return;
805
806	get_systime(&now);
807	filegen_setup(&rawstats, now.l_ui);
808	day = now.l_ui / 86400 + MJD_1900;
809	now.l_ui %= 86400;
810	if (rawstats.fp != NULL) {
811                fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s\n", day,
812		    ulfptoa(&now, 3), stoa(srcadr), dstadr ?
813		    stoa(dstadr) : "-",	ulfptoa(t1, 9), ulfptoa(t2, 9),
814		    ulfptoa(t3, 9), ulfptoa(t4, 9));
815		fflush(rawstats.fp);
816	}
817}
818
819
820/*
821 * record_sys_stats - write system statistics to file
822 *
823 * file format
824 * day (MJD)
825 * time (s past midnight)
826 * time since reset
827 * packets recieved
828 * packets for this host
829 * current version
830 * old version
831 * access denied
832 * bad length or format
833 * bad authentication
834 * declined
835 * rate exceeded
836 * KoD sent
837 */
838void
839record_sys_stats(void)
840{
841	l_fp	now;
842	u_long	day;
843
844	if (!stats_control)
845		return;
846
847	get_systime(&now);
848	filegen_setup(&sysstats, now.l_ui);
849	day = now.l_ui / 86400 + MJD_1900;
850	now.l_ui %= 86400;
851	if (sysstats.fp != NULL) {
852                fprintf(sysstats.fp,
853		    "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
854		    day, ulfptoa(&now, 3), current_time - sys_stattime,
855		    sys_received, sys_processed, sys_newversion,
856		    sys_oldversion, sys_restricted, sys_badlength,
857		    sys_badauth, sys_declined, sys_limitrejected,
858		    sys_kodsent);
859		fflush(sysstats.fp);
860		proto_clr_stats();
861	}
862}
863
864
865/*
866 * record_proto_stats - write system statistics to file
867 *
868 * file format
869 * day (MJD)
870 * time (s past midnight)
871 * text message
872 */
873void
874record_proto_stats(
875	char	*str		/* text string */
876	)
877{
878	l_fp	now;
879	u_long	day;
880
881	if (!stats_control)
882		return;
883
884	get_systime(&now);
885	filegen_setup(&protostats, now.l_ui);
886	day = now.l_ui / 86400 + MJD_1900;
887	now.l_ui %= 86400;
888	if (protostats.fp != NULL) {
889		fprintf(protostats.fp, "%lu %s %s\n", day,
890		    ulfptoa(&now, 3), str);
891		fflush(protostats.fp);
892	}
893}
894
895
896#ifdef OPENSSL
897/*
898 * record_crypto_stats - write crypto statistics to file
899 *
900 * file format:
901 * day (mjd)
902 * time (s past midnight)
903 * peer ip address
904 * text message
905 */
906void
907record_crypto_stats(
908	sockaddr_u *addr,
909	const char *text	/* text message */
910	)
911{
912	l_fp	now;
913	u_long	day;
914
915	if (!stats_control)
916		return;
917
918	get_systime(&now);
919	filegen_setup(&cryptostats, now.l_ui);
920	day = now.l_ui / 86400 + MJD_1900;
921	now.l_ui %= 86400;
922	if (cryptostats.fp != NULL) {
923		if (addr == NULL)
924			fprintf(cryptostats.fp, "%lu %s 0.0.0.0 %s\n",
925			    day, ulfptoa(&now, 3), text);
926		else
927			fprintf(cryptostats.fp, "%lu %s %s %s\n",
928			    day, ulfptoa(&now, 3), stoa(addr), text);
929		fflush(cryptostats.fp);
930	}
931}
932#endif /* OPENSSL */
933
934
935#ifdef DEBUG_TIMING
936/*
937 * record_timing_stats - write timing statistics to file
938 *
939 * file format:
940 * day (mjd)
941 * time (s past midnight)
942 * text message
943 */
944void
945record_timing_stats(
946	const char *text	/* text message */
947	)
948{
949	static unsigned int flshcnt;
950	l_fp	now;
951	u_long	day;
952
953	if (!stats_control)
954		return;
955
956	get_systime(&now);
957	filegen_setup(&timingstats, now.l_ui);
958	day = now.l_ui / 86400 + MJD_1900;
959	now.l_ui %= 86400;
960	if (timingstats.fp != NULL) {
961		fprintf(timingstats.fp, "%lu %s %s\n", day, lfptoa(&now,
962		    3), text);
963		if (++flshcnt % 100 == 0)
964			fflush(timingstats.fp);
965	}
966}
967#endif
968
969
970/*
971 * leap_file - read leapseconds file
972 *
973 * Read the ERTS leapsecond file in NIST text format and extract the
974 * NTP seconds of the latest leap and TAI offset after the leap.
975 */
976static int
977leap_file(
978	FILE	*fp		/* file handle */
979	)
980{
981	char	buf[NTP_MAXSTRLEN]; /* file line buffer */
982	u_long	leap;		/* NTP time at leap */
983	u_long	expire;		/* NTP time when file expires */
984	int	offset;		/* TAI offset at leap (s) */
985	int	i;
986
987	/*
988	 * Read and parse the leapseconds file. Empty lines and comments
989	 * are ignored. A line beginning with #@ contains the file
990	 * expiration time in NTP seconds. Other lines begin with two
991	 * integers followed by junk or comments. The first integer is
992	 * the NTP seconds at the leap, the second is the TAI offset
993	 * after the leap.
994 	 */
995	offset = 0;
996	leap = 0;
997	expire = 0;
998	i = 10;
999	while (fgets(buf, NTP_MAXSTRLEN - 1, fp) != NULL) {
1000		if (strlen(buf) < 1)
1001			continue;
1002
1003		if (buf[0] == '#') {
1004			if (strlen(buf) < 3)
1005				continue;
1006
1007			/*
1008			 * Note the '@' flag was used only in the 2006
1009			 * table; previious to that the flag was '$'.
1010			 */
1011			if (buf[1] == '@' || buf[1] == '$') {
1012				if (sscanf(&buf[2], "%lu", &expire) !=
1013				    1)
1014					return (-1);
1015
1016				continue;
1017			}
1018		}
1019		if (sscanf(buf, "%lu %d", &leap, &offset) == 2) {
1020
1021			/*
1022			 * Valid offsets must increase by one for each
1023			 * leap.
1024			 */
1025			if (i++ != offset)
1026				return (-1);
1027		}
1028	}
1029
1030	/*
1031	 * There must be at least one leap.
1032	 */
1033	if (i == 10)
1034		return (-1);
1035
1036	leap_tai = offset;
1037	leap_sec = leap;
1038	leap_expire = expire;
1039	return (0);
1040}
1041
1042
1043/*
1044 * leap_month - returns seconds until the end of the month.
1045 */
1046u_long
1047leap_month(
1048	u_long	sec		/* current NTP second */
1049	)
1050{
1051	u_long	ltemp;
1052	u_long	*ptr;
1053	u_long	year[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
1054		    31};
1055	u_long	lyear[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30,
1056		    31};
1057
1058	/*
1059	 * Find current leap cycle.
1060	 */
1061	ltemp = sec;
1062	while (ltemp >= L_CENT)
1063		ltemp -= L_CENT;
1064	while (ltemp >= L_4YEAR)
1065		ltemp -= L_4YEAR;
1066
1067	/*
1068	 * We are within four years of the target. If in leap year, use
1069	 * leap year month table; otherwise, use year month table.
1070	 */
1071	if (ltemp < L_LYEAR) {
1072		ptr = lyear;
1073	} else {
1074		ptr = year;
1075		ltemp -= L_LYEAR;
1076		while (ltemp >= L_YEAR)
1077			ltemp -= L_YEAR;
1078	}
1079
1080	/*
1081	 * We are within one year of the target. Find the month of the
1082	 * leap.
1083	 */
1084	while (ltemp >= *ptr * L_DAY)
1085		ltemp -= *ptr++ * L_DAY;
1086
1087	/*
1088	 * The result is the number of seconds until the end of the
1089	 * month when the leap is to occur.
1090	 */
1091	return (*ptr * L_DAY - ltemp - L_DAY);
1092}
1093
1094
1095/*
1096 * getauthkeys - read the authentication keys from the specified file
1097 */
1098void
1099getauthkeys(
1100	const char *keyfile
1101	)
1102{
1103	int len;
1104
1105	len = strlen(keyfile);
1106	if (!len)
1107		return;
1108
1109#ifndef SYS_WINNT
1110	key_file_name = erealloc(key_file_name, len + 1);
1111	memmove(key_file_name, keyfile, len + 1);
1112#else
1113	key_file_name = erealloc(key_file_name, _MAX_PATH);
1114	if (len + 1 > _MAX_PATH)
1115		return;
1116	if (!ExpandEnvironmentStrings(keyfile, key_file_name,
1117				      _MAX_PATH)) {
1118		msyslog(LOG_ERR,
1119			"ExpandEnvironmentStrings(KEY_FILE) failed: %m");
1120		strncpy(key_file_name, keyfile, _MAX_PATH);
1121	}
1122#endif /* SYS_WINNT */
1123
1124	authreadkeys(key_file_name);
1125}
1126
1127
1128/*
1129 * rereadkeys - read the authentication key file over again.
1130 */
1131void
1132rereadkeys(void)
1133{
1134	if (NULL != key_file_name)
1135		authreadkeys(key_file_name);
1136}
1137
1138
1139/*
1140 * sock_hash - hash a sockaddr_u structure
1141 */
1142u_short
1143sock_hash(
1144	sockaddr_u *addr
1145	)
1146{
1147	u_int hashVal;
1148	u_int j;
1149	size_t len;
1150	u_char *pch;
1151	hashVal = 0;
1152	len = 0;
1153
1154	/*
1155	 * We can't just hash the whole thing because there are hidden
1156	 * fields in sockaddr_in6 that might be filled in by recvfrom(),
1157	 * so just use the family, port and address.
1158	 */
1159	pch = (u_char *)&AF(addr);
1160	hashVal = 37 * hashVal + *pch;
1161	if (sizeof(AF(addr)) > 1) {
1162		pch++;
1163		hashVal = 37 * hashVal + *pch;
1164	}
1165	switch(AF(addr)) {
1166	case AF_INET:
1167		pch = (u_char *)&SOCK_ADDR4(addr);
1168		len = sizeof(SOCK_ADDR4(addr));
1169		break;
1170
1171	case AF_INET6:
1172		pch = (u_char *)&SOCK_ADDR6(addr);
1173		len = sizeof(SOCK_ADDR6(addr));
1174		break;
1175	}
1176
1177	for (j = 0; j < len ; j++)
1178		hashVal = 37 * hashVal + pch[j];
1179
1180	hashVal = hashVal & NTP_HASH_MASK;
1181
1182	return (u_short)hashVal;
1183}
1184
1185
1186#if notyet
1187/*
1188 * ntp_exit - document explicitly that ntpd has exited
1189 */
1190void
1191ntp_exit(int retval)
1192{
1193	msyslog(LOG_ERR, "EXITING with return code %d", retval);
1194	exit(retval);
1195}
1196#endif
1197
1198/*
1199 * fstostr - prettyprint NTP seconds
1200 */
1201char * fstostr(
1202	time_t	ntp_stamp
1203	)
1204{
1205	static char	str[20];
1206	struct tm *	tm;
1207	time_t		unix_stamp;
1208
1209	unix_stamp = ntp_stamp - JAN_1970;
1210	tm = gmtime(&unix_stamp);
1211	if (NULL != tm)
1212		snprintf(str, sizeof(str),
1213			 "%04d%02d%02d%02d%02d",
1214			 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1215			 tm->tm_hour, tm->tm_min);
1216	else
1217		strcpy(str, "gmtime() error");
1218
1219	return str;
1220}
1221