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