154359Sroberto/*
254359Sroberto * ntp_timer.c - event timer support routines
354359Sroberto */
454359Sroberto#ifdef HAVE_CONFIG_H
554359Sroberto# include <config.h>
654359Sroberto#endif
754359Sroberto
882498Sroberto#include "ntp_machine.h"
982498Sroberto#include "ntpd.h"
1082498Sroberto#include "ntp_stdlib.h"
11285612Sdelphij#include "ntp_calendar.h"
12285612Sdelphij#include "ntp_leapsec.h"
1382498Sroberto
14285612Sdelphij#if defined(HAVE_IO_COMPLETION_PORT)
15285612Sdelphij# include "ntp_iocompletionport.h"
16285612Sdelphij# include "ntp_timer.h"
17285612Sdelphij#endif
18285612Sdelphij
1954359Sroberto#include <stdio.h>
2054359Sroberto#include <signal.h>
21106163Sroberto#ifdef HAVE_SYS_SIGNAL_H
22106163Sroberto# include <sys/signal.h>
23106163Sroberto#endif
2482498Sroberto#ifdef HAVE_UNISTD_H
2582498Sroberto# include <unistd.h>
2682498Sroberto#endif
2754359Sroberto
28285612Sdelphij#ifdef KERNEL_PLL
29285612Sdelphij#include "ntp_syscall.h"
30285612Sdelphij#endif /* KERNEL_PLL */
31285612Sdelphij
32285612Sdelphij#ifdef AUTOKEY
33285612Sdelphij#include <openssl/rand.h>
34285612Sdelphij#endif	/* AUTOKEY */
35285612Sdelphij
36285612Sdelphij
37285612Sdelphij/* TC_ERR represents the timer_create() error return value. */
38285612Sdelphij#ifdef SYS_VXWORKS
39285612Sdelphij#define	TC_ERR	ERROR
40285612Sdelphij#else
41285612Sdelphij#define	TC_ERR	(-1)
4254359Sroberto#endif
4354359Sroberto
44285612Sdelphij
45285612Sdelphijstatic void check_leapsec(u_int32, const time_t*, int/*BOOL*/);
46285612Sdelphij
4754359Sroberto/*
48285612Sdelphij * These routines provide support for the event timer.  The timer is
4954359Sroberto * implemented by an interrupt routine which sets a flag once every
50285612Sdelphij * second, and a timer routine which is called when the mainline code
51285612Sdelphij * gets around to seeing the flag.  The timer routine dispatches the
52285612Sdelphij * clock adjustment code if its time has come, then searches the timer
53285612Sdelphij * queue for expiries which are dispatched to the transmit procedure.
54285612Sdelphij * Finally, we call the hourly procedure to do cleanup and print a
55285612Sdelphij * message.
5654359Sroberto */
57285612Sdelphijvolatile int interface_interval;     /* init_io() sets def. 300s */
5854359Sroberto
5954359Sroberto/*
60289997Sglebius * Initializing flag.  All async routines watch this and only do their
61289997Sglebius * thing when it is clear.
62289997Sglebius */
63289997Sglebiusint initializing;
64289997Sglebius
65289997Sglebius/*
66285612Sdelphij * Alarm flag. The mainline code imports this.
6754359Sroberto */
6854359Srobertovolatile int alarm_flag;
6954359Sroberto
7054359Sroberto/*
71285612Sdelphij * The counters and timeouts
7254359Sroberto */
73285612Sdelphijstatic  u_long interface_timer;	/* interface update timer */
74285612Sdelphijstatic	u_long adjust_timer;	/* second timer */
75285612Sdelphijstatic	u_long stats_timer;	/* stats timer */
76285612Sdelphijstatic	u_long leapf_timer;	/* Report leapfile problems once/day */
77285612Sdelphijstatic	u_long huffpuff_timer;	/* huff-n'-puff timer */
78285612Sdelphijstatic	u_long worker_idle_timer;/* next check for idle intres */
79285612Sdelphiju_long	leapsec;	        /* seconds to next leap (proximity class) */
80285612Sdelphijint     leapdif;                /* TAI difference step at next leap second*/
81285612Sdelphiju_long	orphwait; 		/* orphan wait time */
82285612Sdelphij#ifdef AUTOKEY
83285612Sdelphijstatic	u_long revoke_timer;	/* keys revoke timer */
84285612Sdelphijstatic	u_long keys_timer;	/* session key timer */
85285612Sdelphiju_long	sys_revoke = KEY_REVOKE; /* keys revoke timeout (log2 s) */
86285612Sdelphiju_long	sys_automax = NTP_AUTOMAX; /* key list timeout (log2 s) */
87285612Sdelphij#endif	/* AUTOKEY */
8854359Sroberto
8954359Sroberto/*
9054359Sroberto * Statistics counter for the interested.
9154359Sroberto */
9254359Srobertovolatile u_long alarm_overflow;
9354359Sroberto
94285612Sdelphiju_long current_time;		/* seconds since startup */
9554359Sroberto
9654359Sroberto/*
9754359Sroberto * Stats.  Number of overflows and number of calls to transmit().
9854359Sroberto */
9954359Srobertou_long timer_timereset;
10054359Srobertou_long timer_overflows;
10154359Srobertou_long timer_xmtcalls;
10254359Sroberto
10354359Sroberto#if defined(VMS)
10454359Srobertostatic int vmstimer[2]; 	/* time for next timer AST */
10554359Srobertostatic int vmsinc[2];		/* timer increment */
10654359Sroberto#endif /* VMS */
10754359Sroberto
108285612Sdelphij#ifdef SYS_WINNT
109285612SdelphijHANDLE WaitableTimerHandle;
11054359Sroberto#else
111285612Sdelphijstatic	RETSIGTYPE alarming (int);
11254359Sroberto#endif /* SYS_WINNT */
11354359Sroberto
114132451Sroberto#if !defined(VMS)
115132451Sroberto# if !defined SYS_WINNT || defined(SYS_CYGWIN32)
116285612Sdelphij#  ifdef HAVE_TIMER_CREATE
117285612Sdelphijstatic timer_t timer_id;
118285612Sdelphijtypedef struct itimerspec intervaltimer;
119285612Sdelphij#   define	itv_frac	tv_nsec
120285612Sdelphij#  else
121285612Sdelphijtypedef struct itimerval intervaltimer;
122285612Sdelphij#   define	itv_frac	tv_usec
123285612Sdelphij#  endif
124285612Sdelphijintervaltimer itimer;
125285612Sdelphij# endif
126285612Sdelphij#endif
12754359Sroberto
128285612Sdelphij#if !defined(SYS_WINNT) && !defined(VMS)
129285612Sdelphijvoid	set_timer_or_die(const intervaltimer *);
130285612Sdelphij#endif
131285612Sdelphij
132285612Sdelphij
133285612Sdelphij#if !defined(SYS_WINNT) && !defined(VMS)
134285612Sdelphijvoid
135285612Sdelphijset_timer_or_die(
136285612Sdelphij	const intervaltimer *	ptimer
137285612Sdelphij	)
138285612Sdelphij{
139285612Sdelphij	const char *	setfunc;
140285612Sdelphij	int		rc;
141285612Sdelphij
142285612Sdelphij# ifdef HAVE_TIMER_CREATE
143285612Sdelphij	setfunc = "timer_settime";
144285612Sdelphij	rc = timer_settime(timer_id, 0, &itimer, NULL);
145285612Sdelphij# else
146285612Sdelphij	setfunc = "setitimer";
147285612Sdelphij	rc = setitimer(ITIMER_REAL, &itimer, NULL);
148285612Sdelphij# endif
149285612Sdelphij	if (-1 == rc) {
150285612Sdelphij		msyslog(LOG_ERR, "interval timer %s failed, %m",
151285612Sdelphij			setfunc);
152285612Sdelphij		exit(1);
153285612Sdelphij	}
154285612Sdelphij}
155285612Sdelphij#endif	/* !SYS_WINNT && !VMS */
156285612Sdelphij
157285612Sdelphij
15854359Sroberto/*
159285612Sdelphij * reinit_timer - reinitialize interval timer after a clock step.
160132451Sroberto */
161285612Sdelphijvoid
162132451Srobertoreinit_timer(void)
163132451Sroberto{
164132451Sroberto#if !defined(SYS_WINNT) && !defined(VMS)
165285612Sdelphij	ZERO(itimer);
166285612Sdelphij# ifdef HAVE_TIMER_CREATE
167285612Sdelphij	timer_gettime(timer_id, &itimer);
168285612Sdelphij# else
169132451Sroberto	getitimer(ITIMER_REAL, &itimer);
170285612Sdelphij# endif
171285612Sdelphij	if (itimer.it_value.tv_sec < 0 ||
172285612Sdelphij	    itimer.it_value.tv_sec > (1 << EVENT_TIMEOUT))
173285612Sdelphij		itimer.it_value.tv_sec = (1 << EVENT_TIMEOUT);
174285612Sdelphij	if (itimer.it_value.itv_frac < 0)
175285612Sdelphij		itimer.it_value.itv_frac = 0;
176285612Sdelphij	if (0 == itimer.it_value.tv_sec &&
177285612Sdelphij	    0 == itimer.it_value.itv_frac)
178285612Sdelphij		itimer.it_value.tv_sec = (1 << EVENT_TIMEOUT);
179285612Sdelphij	itimer.it_interval.tv_sec = (1 << EVENT_TIMEOUT);
180285612Sdelphij	itimer.it_interval.itv_frac = 0;
181285612Sdelphij	set_timer_or_die(&itimer);
182132451Sroberto# endif /* VMS */
183132451Sroberto}
184132451Sroberto
185285612Sdelphij
186132451Sroberto/*
18754359Sroberto * init_timer - initialize the timer data structures
18854359Sroberto */
18954359Srobertovoid
19054359Srobertoinit_timer(void)
19154359Sroberto{
19254359Sroberto	/*
19354359Sroberto	 * Initialize...
19454359Sroberto	 */
195285612Sdelphij	alarm_flag = FALSE;
19654359Sroberto	alarm_overflow = 0;
19754359Sroberto	adjust_timer = 1;
198285612Sdelphij	stats_timer = SECSPERHR;
199285612Sdelphij	leapf_timer = SECSPERDAY;
20082498Sroberto	huffpuff_timer = 0;
201182007Sroberto	interface_timer = 0;
20254359Sroberto	current_time = 0;
20354359Sroberto	timer_overflows = 0;
20454359Sroberto	timer_xmtcalls = 0;
20554359Sroberto	timer_timereset = 0;
20654359Sroberto
207285612Sdelphij#ifndef SYS_WINNT
20854359Sroberto	/*
20954359Sroberto	 * Set up the alarm interrupt.	The first comes 2**EVENT_TIMEOUT
21054359Sroberto	 * seconds from now and they continue on every 2**EVENT_TIMEOUT
21154359Sroberto	 * seconds.
21254359Sroberto	 */
213285612Sdelphij# ifndef VMS
214285612Sdelphij#  ifdef HAVE_TIMER_CREATE
215285612Sdelphij	if (TC_ERR == timer_create(CLOCK_REALTIME, NULL, &timer_id)) {
216285612Sdelphij		msyslog(LOG_ERR, "timer_create failed, %m");
217285612Sdelphij		exit(1);
21854359Sroberto	}
21954359Sroberto#  endif
220285612Sdelphij	signal_no_reset(SIGALRM, alarming);
221285612Sdelphij	itimer.it_interval.tv_sec =
222285612Sdelphij		itimer.it_value.tv_sec = (1 << EVENT_TIMEOUT);
223285612Sdelphij	itimer.it_interval.itv_frac = itimer.it_value.itv_frac = 0;
224285612Sdelphij	set_timer_or_die(&itimer);
225285612Sdelphij# else	/* VMS follows */
22654359Sroberto	vmsinc[0] = 10000000;		/* 1 sec */
22754359Sroberto	vmsinc[1] = 0;
22854359Sroberto	lib$emul(&(1<<EVENT_TIMEOUT), &vmsinc, &0, &vmsinc);
22954359Sroberto
23054359Sroberto	sys$gettim(&vmstimer);	/* that's "now" as abstime */
23154359Sroberto
23254359Sroberto	lib$addx(&vmsinc, &vmstimer, &vmstimer);
23354359Sroberto	sys$setimr(0, &vmstimer, alarming, alarming, 0);
234285612Sdelphij# endif	/* VMS */
235285612Sdelphij#else	/* SYS_WINNT follows */
23654359Sroberto	/*
23754359Sroberto	 * Set up timer interrupts for every 2**EVENT_TIMEOUT seconds
238285612Sdelphij	 * Under Windows/NT,
23954359Sroberto	 */
24054359Sroberto
24154359Sroberto	WaitableTimerHandle = CreateWaitableTimer(NULL, FALSE, NULL);
24254359Sroberto	if (WaitableTimerHandle == NULL) {
24354359Sroberto		msyslog(LOG_ERR, "CreateWaitableTimer failed: %m");
24454359Sroberto		exit(1);
24554359Sroberto	}
24654359Sroberto	else {
247285612Sdelphij		DWORD		Period;
248285612Sdelphij		LARGE_INTEGER	DueTime;
249285612Sdelphij		BOOL		rc;
250285612Sdelphij
251285612Sdelphij		Period = (1 << EVENT_TIMEOUT) * 1000;
25254359Sroberto		DueTime.QuadPart = Period * 10000i64;
253285612Sdelphij		rc = SetWaitableTimer(WaitableTimerHandle, &DueTime,
254285612Sdelphij				      Period, NULL, NULL, FALSE);
255285612Sdelphij		if (!rc) {
25654359Sroberto			msyslog(LOG_ERR, "SetWaitableTimer failed: %m");
25754359Sroberto			exit(1);
25854359Sroberto		}
25954359Sroberto	}
26054359Sroberto
261285612Sdelphij#endif	/* SYS_WINNT */
26254359Sroberto}
26354359Sroberto
264285612Sdelphij
265285612Sdelphij/*
266285612Sdelphij * intres_timeout_req(s) is invoked in the parent to schedule an idle
267285612Sdelphij * timeout to fire in s seconds, if not reset earlier by a call to
268285612Sdelphij * intres_timeout_req(0), which clears any pending timeout.  When the
269285612Sdelphij * timeout expires, worker_idle_timer_fired() is invoked (again, in the
270285612Sdelphij * parent).
271285612Sdelphij *
272285612Sdelphij * sntp and ntpd each provide implementations adapted to their timers.
273285612Sdelphij */
274285612Sdelphijvoid
275285612Sdelphijintres_timeout_req(
276285612Sdelphij	u_int	seconds		/* 0 cancels */
277285612Sdelphij	)
27854359Sroberto{
279298770Sdelphij#if defined(HAVE_DROPROOT) && defined(NEED_EARLY_FORK)
280298770Sdelphij	if (droproot) {
281298770Sdelphij		worker_idle_timer = 0;
282298770Sdelphij		return;
283298770Sdelphij	}
284298770Sdelphij#endif
285285612Sdelphij	if (0 == seconds) {
286285612Sdelphij		worker_idle_timer = 0;
287285612Sdelphij		return;
288285612Sdelphij	}
289285612Sdelphij	worker_idle_timer = current_time + seconds;
29054359Sroberto}
29154359Sroberto
292285612Sdelphij
29354359Sroberto/*
294285612Sdelphij * timer - event timer
29554359Sroberto */
29654359Srobertovoid
29754359Srobertotimer(void)
29854359Sroberto{
299285612Sdelphij	struct peer *	p;
300285612Sdelphij	struct peer *	next_peer;
301285612Sdelphij	l_fp		now;
302285612Sdelphij	time_t          tnow;
30354359Sroberto
30454359Sroberto	/*
305285612Sdelphij	 * The basic timerevent is one second.  This is used to adjust the
306285612Sdelphij	 * system clock in time and frequency, implement the kiss-o'-death
307285612Sdelphij	 * function and the association polling function.
30854359Sroberto	 */
309285612Sdelphij	current_time++;
31054359Sroberto	if (adjust_timer <= current_time) {
31154359Sroberto		adjust_timer += 1;
31254359Sroberto		adj_host_clock();
313182007Sroberto#ifdef REFCLOCK
314285612Sdelphij		for (p = peer_list; p != NULL; p = next_peer) {
315285612Sdelphij			next_peer = p->p_link;
316285612Sdelphij			if (FLAG_REFCLOCK & p->flags)
317285612Sdelphij				refclock_timer(p);
318182007Sroberto		}
319182007Sroberto#endif /* REFCLOCK */
32054359Sroberto	}
32154359Sroberto
32254359Sroberto	/*
323285612Sdelphij	 * Now dispatch any peers whose event timer has expired. Be
324285612Sdelphij	 * careful here, since the peer structure might go away as the
325285612Sdelphij	 * result of the call.
32654359Sroberto	 */
327285612Sdelphij	for (p = peer_list; p != NULL; p = next_peer) {
328285612Sdelphij		next_peer = p->p_link;
329285612Sdelphij
330285612Sdelphij		/*
331285612Sdelphij		 * Restrain the non-burst packet rate not more
332285612Sdelphij		 * than one packet every 16 seconds. This is
333285612Sdelphij		 * usually tripped using iburst and minpoll of
334285612Sdelphij		 * 128 s or less.
335285612Sdelphij		 */
336285612Sdelphij		if (p->throttle > 0)
337285612Sdelphij			p->throttle--;
338285612Sdelphij		if (p->nextdate <= current_time) {
33954359Sroberto#ifdef REFCLOCK
340285612Sdelphij			if (FLAG_REFCLOCK & p->flags)
341285612Sdelphij				refclock_transmit(p);
342285612Sdelphij			else
343285612Sdelphij#endif	/* REFCLOCK */
344285612Sdelphij				transmit(p);
34554359Sroberto		}
34654359Sroberto	}
34754359Sroberto
34854359Sroberto	/*
349285612Sdelphij	 * Orphan mode is active when enabled and when no servers less
350285612Sdelphij	 * than the orphan stratum are available. A server with no other
351285612Sdelphij	 * synchronization source is an orphan. It shows offset zero and
352285612Sdelphij	 * reference ID the loopback address.
35354359Sroberto	 */
354285612Sdelphij	if (sys_orphan < STRATUM_UNSPEC && sys_peer == NULL &&
355285612Sdelphij	    current_time > orphwait) {
356285612Sdelphij		if (sys_leap == LEAP_NOTINSYNC) {
357285612Sdelphij			set_sys_leap(LEAP_NOWARNING);
358285612Sdelphij#ifdef AUTOKEY
359285612Sdelphij			if (crypto_flags)
360285612Sdelphij				crypto_update();
361285612Sdelphij#endif	/* AUTOKEY */
362285612Sdelphij		}
363285612Sdelphij		sys_stratum = (u_char)sys_orphan;
364285612Sdelphij		if (sys_stratum > 1)
365285612Sdelphij			sys_refid = htonl(LOOPBACKADR);
366285612Sdelphij		else
367285612Sdelphij			memcpy(&sys_refid, "LOOP", 4);
368285612Sdelphij		sys_offset = 0;
369285612Sdelphij		sys_rootdelay = 0;
370285612Sdelphij		sys_rootdisp = 0;
37154359Sroberto	}
37254359Sroberto
373285612Sdelphij	get_systime(&now);
374285612Sdelphij	time(&tnow);
375285612Sdelphij
37654359Sroberto	/*
377285612Sdelphij	 * Leapseconds. Get time and defer to worker if either something
378285612Sdelphij	 * is imminent or every 8th second.
37954359Sroberto	 */
380285612Sdelphij	if (leapsec > LSPROX_NOWARN || 0 == (current_time & 7))
381285612Sdelphij		check_leapsec(now.l_ui, &tnow,
382285612Sdelphij                                (sys_leap == LEAP_NOTINSYNC));
383285612Sdelphij        if (sys_leap != LEAP_NOTINSYNC) {
384285612Sdelphij                if (leapsec >= LSPROX_ANNOUNCE && leapdif) {
385285612Sdelphij		        if (leapdif > 0)
386285612Sdelphij			        set_sys_leap(LEAP_ADDSECOND);
387285612Sdelphij		        else
388285612Sdelphij			        set_sys_leap(LEAP_DELSECOND);
389285612Sdelphij                } else {
390285612Sdelphij                        set_sys_leap(LEAP_NOWARNING);
391285612Sdelphij                }
392285612Sdelphij	}
393285612Sdelphij
394285612Sdelphij	/*
395285612Sdelphij	 * Update huff-n'-puff filter.
396285612Sdelphij	 */
39782498Sroberto	if (huffpuff_timer <= current_time) {
39882498Sroberto		huffpuff_timer += HUFFPUFF;
39982498Sroberto		huffpuff();
40082498Sroberto	}
40182498Sroberto
402285612Sdelphij#ifdef AUTOKEY
40382498Sroberto	/*
404285612Sdelphij	 * Garbage collect expired keys.
40582498Sroberto	 */
406285612Sdelphij	if (keys_timer <= current_time) {
407285612Sdelphij		keys_timer += 1 << sys_automax;
408285612Sdelphij		auth_agekeys();
40954359Sroberto	}
41054359Sroberto
41154359Sroberto	/*
412285612Sdelphij	 * Generate new private value. This causes all associations
413285612Sdelphij	 * to regenerate cookies.
41454359Sroberto	 */
415285612Sdelphij	if (revoke_timer && revoke_timer <= current_time) {
416285612Sdelphij		revoke_timer += 1 << sys_revoke;
417285612Sdelphij		RAND_bytes((u_char *)&sys_private, 4);
418285612Sdelphij	}
419285612Sdelphij#endif	/* AUTOKEY */
420285612Sdelphij
421285612Sdelphij	/*
422285612Sdelphij	 * Interface update timer
423285612Sdelphij	 */
424182007Sroberto	if (interface_interval && interface_timer <= current_time) {
425285612Sdelphij		timer_interfacetimeout(current_time +
426285612Sdelphij		    interface_interval);
427285612Sdelphij		DPRINTF(2, ("timer: interface update\n"));
428200576Sroberto		interface_update(NULL, NULL);
42954359Sroberto	}
430285612Sdelphij
431285612Sdelphij	if (worker_idle_timer && worker_idle_timer <= current_time)
432285612Sdelphij		worker_idle_timer_fired();
433285612Sdelphij
434182007Sroberto	/*
435285612Sdelphij	 * Finally, write hourly stats and do the hourly
436285612Sdelphij	 * and daily leapfile checks.
437182007Sroberto	 */
438182007Sroberto	if (stats_timer <= current_time) {
439285612Sdelphij		stats_timer += SECSPERHR;
440285612Sdelphij		write_stats();
441285612Sdelphij		if (leapf_timer <= current_time) {
442285612Sdelphij			leapf_timer += SECSPERDAY;
443285612Sdelphij			check_leap_file(TRUE, now.l_ui, &tnow);
444285612Sdelphij		} else {
445285612Sdelphij			check_leap_file(FALSE, now.l_ui, &tnow);
446285612Sdelphij		}
447182007Sroberto	}
44854359Sroberto}
44954359Sroberto
45054359Sroberto
45154359Sroberto#ifndef SYS_WINNT
45254359Sroberto/*
45354359Sroberto * alarming - tell the world we've been alarmed
45454359Sroberto */
45554359Srobertostatic RETSIGTYPE
45654359Srobertoalarming(
45754359Sroberto	int sig
45854359Sroberto	)
45954359Sroberto{
460285612Sdelphij# ifdef DEBUG
461285612Sdelphij	const char *msg = "alarming: initializing TRUE\n";
462285612Sdelphij# endif
463285612Sdelphij
46454359Sroberto	if (!initializing) {
465285612Sdelphij		if (alarm_flag) {
466285612Sdelphij			alarm_overflow++;
467285612Sdelphij# ifdef DEBUG
468285612Sdelphij			msg = "alarming: overflow\n";
469285612Sdelphij# endif
470285612Sdelphij		} else {
471285612Sdelphij# ifndef VMS
472285612Sdelphij			alarm_flag++;
473285612Sdelphij# else
474285612Sdelphij			/* VMS AST routine, increment is no good */
475285612Sdelphij			alarm_flag = 1;
476285612Sdelphij# endif
477285612Sdelphij# ifdef DEBUG
478285612Sdelphij			msg = "alarming: normal\n";
479285612Sdelphij# endif
480285612Sdelphij		}
48154359Sroberto	}
482285612Sdelphij# ifdef VMS
483285612Sdelphij	lib$addx(&vmsinc, &vmstimer, &vmstimer);
484285612Sdelphij	sys$setimr(0, &vmstimer, alarming, alarming, 0);
485285612Sdelphij# endif
486285612Sdelphij# ifdef DEBUG
487285612Sdelphij	if (debug >= 4)
488285612Sdelphij		(void)(-1 == write(1, msg, strlen(msg)));
489285612Sdelphij# endif
49054359Sroberto}
49154359Sroberto#endif /* SYS_WINNT */
49254359Sroberto
493285612Sdelphij
494182007Srobertovoid
495182007Srobertotimer_interfacetimeout(u_long timeout)
496182007Sroberto{
497182007Sroberto	interface_timer = timeout;
498182007Sroberto}
49954359Sroberto
500182007Sroberto
50154359Sroberto/*
50254359Sroberto * timer_clr_stats - clear timer module stat counters
50354359Sroberto */
50454359Srobertovoid
50554359Srobertotimer_clr_stats(void)
50654359Sroberto{
50754359Sroberto	timer_overflows = 0;
50854359Sroberto	timer_xmtcalls = 0;
50954359Sroberto	timer_timereset = current_time;
51054359Sroberto}
51154359Sroberto
512285612Sdelphij
513285612Sdelphijstatic void
514285612Sdelphijcheck_leap_sec_in_progress( const leap_result_t *lsdata ) {
515285612Sdelphij	int prv_leap_sec_in_progress = leap_sec_in_progress;
516285612Sdelphij	leap_sec_in_progress = lsdata->tai_diff && (lsdata->ddist < 3);
517285612Sdelphij
518285612Sdelphij	/* if changed we may have to update the leap status sent to clients */
519285612Sdelphij	if (leap_sec_in_progress != prv_leap_sec_in_progress)
520285612Sdelphij		set_sys_leap(sys_leap);
521285612Sdelphij}
522285612Sdelphij
523285612Sdelphij
524285612Sdelphijstatic void
525285612Sdelphijcheck_leapsec(
526285612Sdelphij	u_int32        now  ,
527285612Sdelphij	const time_t * tpiv ,
528285612Sdelphij        int/*BOOL*/    reset)
529285612Sdelphij{
530285612Sdelphij	static const char leapmsg_p_step[] =
531285612Sdelphij	    "Positive leap second, stepped backward.";
532285612Sdelphij	static const char leapmsg_p_slew[] =
533285612Sdelphij	    "Positive leap second, no step correction. "
534285612Sdelphij	    "System clock will be inaccurate for a long time.";
535285612Sdelphij
536285612Sdelphij	static const char leapmsg_n_step[] =
537285612Sdelphij	    "Negative leap second, stepped forward.";
538285612Sdelphij	static const char leapmsg_n_slew[] =
539285612Sdelphij	    "Negative leap second, no step correction. "
540285612Sdelphij	    "System clock will be inaccurate for a long time.";
541285612Sdelphij
542285612Sdelphij	leap_result_t lsdata;
543285612Sdelphij	u_int32       lsprox;
544285612Sdelphij#ifdef AUTOKEY
545285612Sdelphij	int/*BOOL*/   update_autokey = FALSE;
546285612Sdelphij#endif
547285612Sdelphij
548285612Sdelphij#ifndef SYS_WINNT  /* WinNT port has its own leap second handling */
549285612Sdelphij# ifdef KERNEL_PLL
550285612Sdelphij	leapsec_electric(pll_control && kern_enable);
551285612Sdelphij# else
552285612Sdelphij	leapsec_electric(0);
553285612Sdelphij# endif
554285612Sdelphij#endif
555285612Sdelphij#ifdef LEAP_SMEAR
556285612Sdelphij	leap_smear.enabled = leap_smear_intv != 0;
557285612Sdelphij#endif
558294569Sdelphij	if (reset) {
559285612Sdelphij		lsprox = LSPROX_NOWARN;
560285612Sdelphij		leapsec_reset_frame();
561285612Sdelphij		memset(&lsdata, 0, sizeof(lsdata));
562285612Sdelphij	} else {
563294569Sdelphij	  int fired;
564285612Sdelphij
565294569Sdelphij	  fired = leapsec_query(&lsdata, now, tpiv);
566294569Sdelphij
567294569Sdelphij	  DPRINTF(3, ("*** leapsec_query: fired %i, now %u (0x%08X), tai_diff %i, ddist %u\n",
568285612Sdelphij		  fired, now, now, lsdata.tai_diff, lsdata.ddist));
569285612Sdelphij
570285612Sdelphij#ifdef LEAP_SMEAR
571285612Sdelphij	  leap_smear.in_progress = 0;
572285612Sdelphij	  leap_smear.doffset = 0.0;
573285612Sdelphij
574285612Sdelphij	  if (leap_smear.enabled) {
575285612Sdelphij		if (lsdata.tai_diff) {
576285612Sdelphij			if (leap_smear.interval == 0) {
577285612Sdelphij				leap_smear.interval = leap_smear_intv;
578285612Sdelphij				leap_smear.intv_end = lsdata.ttime.Q_s;
579285612Sdelphij				leap_smear.intv_start = leap_smear.intv_end - leap_smear.interval;
580285612Sdelphij				DPRINTF(1, ("*** leapsec_query: setting leap_smear interval %li, begin %.0f, end %.0f\n",
581285612Sdelphij					leap_smear.interval, leap_smear.intv_start, leap_smear.intv_end));
582285612Sdelphij			}
583294569Sdelphij		} else {
584285612Sdelphij			if (leap_smear.interval)
585285612Sdelphij				DPRINTF(1, ("*** leapsec_query: clearing leap_smear interval\n"));
586285612Sdelphij			leap_smear.interval = 0;
587285612Sdelphij		}
588285612Sdelphij
589285612Sdelphij		if (leap_smear.interval) {
590285612Sdelphij			double dtemp = now;
591285612Sdelphij			if (dtemp >= leap_smear.intv_start && dtemp <= leap_smear.intv_end) {
592285612Sdelphij				double leap_smear_time = dtemp - leap_smear.intv_start;
593285612Sdelphij				/*
594285612Sdelphij				 * For now we just do a linear interpolation over the smear interval
595285612Sdelphij				 */
596285612Sdelphij#if 0
597285612Sdelphij				// linear interpolation
598285612Sdelphij				leap_smear.doffset = -(leap_smear_time * lsdata.tai_diff / leap_smear.interval);
599285612Sdelphij#else
600285612Sdelphij				// Google approach: lie(t) = (1.0 - cos(pi * t / w)) / 2.0
601285612Sdelphij				leap_smear.doffset = -((double) lsdata.tai_diff - cos( M_PI * leap_smear_time / leap_smear.interval)) / 2.0;
602285612Sdelphij#endif
603285612Sdelphij				/*
604285612Sdelphij				 * TODO see if we're inside an inserted leap second, so we need to compute
605285612Sdelphij				 * leap_smear.doffset = 1.0 - leap_smear.doffset
606285612Sdelphij				 */
607285612Sdelphij				leap_smear.in_progress = 1;
608285612Sdelphij#if 0 && defined( DEBUG )
609285612Sdelphij				msyslog(LOG_NOTICE, "*** leapsec_query: [%.0f:%.0f] (%li), now %u (%.0f), smear offset %.6f ms\n",
610285612Sdelphij					leap_smear.intv_start, leap_smear.intv_end, leap_smear.interval,
611285612Sdelphij					now, leap_smear_time, leap_smear.doffset);
612285612Sdelphij#else
613285612Sdelphij				DPRINTF(1, ("*** leapsec_query: [%.0f:%.0f] (%li), now %u (%.0f), smear offset %.6f ms\n",
614285612Sdelphij					leap_smear.intv_start, leap_smear.intv_end, leap_smear.interval,
615285612Sdelphij					now, leap_smear_time, leap_smear.doffset));
616285612Sdelphij#endif
617285612Sdelphij
618285612Sdelphij			}
619285612Sdelphij		}
620285612Sdelphij	  }
621285612Sdelphij	  else
622285612Sdelphij		leap_smear.interval = 0;
623285612Sdelphij
624285612Sdelphij	  /*
625285612Sdelphij	   * Update the current leap smear offset, eventually 0.0 if outside smear interval.
626285612Sdelphij	   */
627285612Sdelphij	  DTOLFP(leap_smear.doffset, &leap_smear.offset);
628285612Sdelphij
629285612Sdelphij#endif	/* LEAP_SMEAR */
630285612Sdelphij
631285612Sdelphij	  if (fired) {
632285612Sdelphij		/* Full hit. Eventually step the clock, but always
633285612Sdelphij		 * announce the leap event has happened.
634285612Sdelphij		 */
635285612Sdelphij		const char *leapmsg = NULL;
636293650Sglebius		double      lswarp  = lsdata.warped;
637293650Sglebius		if (lswarp < 0.0) {
638285612Sdelphij			if (clock_max_back > 0.0 &&
639293650Sglebius			    clock_max_back < -lswarp) {
640293650Sglebius				step_systime(lswarp);
641285612Sdelphij				leapmsg = leapmsg_p_step;
642285612Sdelphij			} else {
643285612Sdelphij				leapmsg = leapmsg_p_slew;
644285612Sdelphij			}
645293650Sglebius		} else 	if (lswarp > 0.0) {
646285612Sdelphij			if (clock_max_fwd > 0.0 &&
647293650Sglebius			    clock_max_fwd < lswarp) {
648293650Sglebius				step_systime(lswarp);
649285612Sdelphij				leapmsg = leapmsg_n_step;
650285612Sdelphij			} else {
651285612Sdelphij				leapmsg = leapmsg_n_slew;
652285612Sdelphij			}
653285612Sdelphij		}
654285612Sdelphij		if (leapmsg)
655285612Sdelphij			msyslog(LOG_NOTICE, "%s", leapmsg);
656285612Sdelphij		report_event(EVNT_LEAP, NULL, NULL);
657285612Sdelphij#ifdef AUTOKEY
658285612Sdelphij		update_autokey = TRUE;
659285612Sdelphij#endif
660285612Sdelphij		lsprox  = LSPROX_NOWARN;
661285612Sdelphij		leapsec = LSPROX_NOWARN;
662285612Sdelphij		sys_tai = lsdata.tai_offs;
663285612Sdelphij	  } else {
664285612Sdelphij#ifdef AUTOKEY
665294569Sdelphij		  update_autokey = (sys_tai != (u_int)lsdata.tai_offs);
666285612Sdelphij#endif
667294569Sdelphij		  lsprox  = lsdata.proximity;
668294569Sdelphij		  sys_tai = lsdata.tai_offs;
669285612Sdelphij	  }
670285612Sdelphij	}
671285612Sdelphij
672285612Sdelphij	/* We guard against panic alarming during the red alert phase.
673285612Sdelphij	 * Strange and evil things might happen if we go from stone cold
674285612Sdelphij	 * to piping hot in one step. If things are already that wobbly,
675285612Sdelphij	 * we let the normal clock correction take over, even if a jump
676285612Sdelphij	 * is involved.
677285612Sdelphij         * Also make sure the alarming events are edge-triggered, that is,
678285612Sdelphij         * ceated only when the threshold is crossed.
679285612Sdelphij         */
680285612Sdelphij	if (  (leapsec > 0 || lsprox < LSPROX_ALERT)
681285612Sdelphij	    && leapsec < lsprox                     ) {
682285612Sdelphij		if (  leapsec < LSPROX_SCHEDULE
683285612Sdelphij                   && lsprox >= LSPROX_SCHEDULE) {
684285612Sdelphij			if (lsdata.dynamic)
685285612Sdelphij				report_event(PEVNT_ARMED, sys_peer, NULL);
686285612Sdelphij			else
687285612Sdelphij				report_event(EVNT_ARMED, NULL, NULL);
688285612Sdelphij		}
689285612Sdelphij		leapsec = lsprox;
690285612Sdelphij	}
691285612Sdelphij	if (leapsec > lsprox) {
692285612Sdelphij		if (  leapsec >= LSPROX_SCHEDULE
693285612Sdelphij                   && lsprox   < LSPROX_SCHEDULE) {
694285612Sdelphij			report_event(EVNT_DISARMED, NULL, NULL);
695285612Sdelphij		}
696285612Sdelphij		leapsec = lsprox;
697285612Sdelphij	}
698285612Sdelphij
699285612Sdelphij	if (leapsec >= LSPROX_SCHEDULE)
700285612Sdelphij		leapdif = lsdata.tai_diff;
701285612Sdelphij	else
702285612Sdelphij		leapdif = 0;
703285612Sdelphij
704285612Sdelphij	check_leap_sec_in_progress(&lsdata);
705285612Sdelphij
706285612Sdelphij#ifdef AUTOKEY
707285612Sdelphij	if (update_autokey)
708285612Sdelphij		crypto_update_taichange();
709285612Sdelphij#endif
710285612Sdelphij}
711