xref: /freebsd/contrib/ntp/ntpd/ntp_timer.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
1 /*
2  * ntp_timer.c - event timer support routines
3  */
4 #ifdef HAVE_CONFIG_H
5 # include <config.h>
6 #endif
7 
8 #include "ntp_machine.h"
9 #include "ntpd.h"
10 #include "ntp_stdlib.h"
11 
12 #include <stdio.h>
13 #include <signal.h>
14 #ifdef HAVE_SYS_SIGNAL_H
15 # include <sys/signal.h>
16 #endif
17 #ifdef HAVE_UNISTD_H
18 # include <unistd.h>
19 #endif
20 
21 #if defined(HAVE_IO_COMPLETION_PORT)
22 # include "ntp_iocompletionport.h"
23 # include "ntp_timer.h"
24 #endif
25 
26 /*
27  * These routines provide support for the event timer.	The timer is
28  * implemented by an interrupt routine which sets a flag once every
29  * 2**EVENT_TIMEOUT seconds (currently 4), and a timer routine which
30  * is called when the mainline code gets around to seeing the flag.
31  * The timer routine dispatches the clock adjustment code if its time
32  * has come, then searches the timer queue for expiries which are
33  * dispatched to the transmit procedure.  Finally, we call the hourly
34  * procedure to do cleanup and print a message.
35  */
36 
37 /*
38  * Alarm flag.	The mainline code imports this.
39  */
40 volatile int alarm_flag;
41 
42 /*
43  * The counters
44  */
45 static	u_long adjust_timer;		/* second timer */
46 static	u_long keys_timer;		/* minute timer */
47 static	u_long hourly_timer;		/* hour timer */
48 static	u_long huffpuff_timer;		/* huff-n'-puff timer */
49 #ifdef OPENSSL
50 static	u_long revoke_timer;		/* keys revoke timer */
51 u_char	sys_revoke = KEY_REVOKE;	/* keys revoke timeout (log2 s) */
52 #endif /* OPENSSL */
53 
54 /*
55  * Statistics counter for the interested.
56  */
57 volatile u_long alarm_overflow;
58 
59 #define MINUTE	60
60 #define HOUR	(60*60)
61 
62 u_long current_time;
63 
64 /*
65  * Stats.  Number of overflows and number of calls to transmit().
66  */
67 u_long timer_timereset;
68 u_long timer_overflows;
69 u_long timer_xmtcalls;
70 
71 #if defined(VMS)
72 static int vmstimer[2]; 	/* time for next timer AST */
73 static int vmsinc[2];		/* timer increment */
74 #endif /* VMS */
75 
76 #if defined SYS_WINNT
77 static HANDLE WaitableTimerHandle = NULL;
78 #else
79 static	RETSIGTYPE alarming P((int));
80 #endif /* SYS_WINNT */
81 
82 #if !defined(VMS)
83 # if !defined SYS_WINNT || defined(SYS_CYGWIN32)
84 #  ifndef HAVE_TIMER_SETTIME
85 	struct itimerval itimer;
86 #  else
87 	static timer_t ntpd_timerid;
88 	struct itimerspec itimer;
89 #  endif /* HAVE_TIMER_SETTIME */
90 # endif /* SYS_WINNT */
91 #endif /* VMS */
92 
93 /*
94  * reinit_timer - reinitialize interval timer.
95  */
96 void
97 reinit_timer(void)
98 {
99 #if !defined(SYS_WINNT) && !defined(VMS)
100 #  if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
101 	timer_gettime(ntpd_timerid, &itimer);
102 	if (itimer.it_value.tv_sec < 0 || itimer.it_value.tv_sec > (1<<EVENT_TIMEOUT)) {
103 		itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
104 	}
105 	if (itimer.it_value.tv_nsec < 0 ) {
106 		itimer.it_value.tv_nsec = 0;
107 	}
108 	if (itimer.it_value.tv_sec == 0 && itimer.it_value.tv_nsec == 0) {
109 		itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
110 		itimer.it_value.tv_nsec = 0;
111 	}
112 	itimer.it_interval.tv_sec = (1<<EVENT_TIMEOUT);
113 	itimer.it_interval.tv_nsec = 0;
114 	timer_settime(ntpd_timerid, 0 /*!TIMER_ABSTIME*/, &itimer, NULL);
115 #  else
116 	getitimer(ITIMER_REAL, &itimer);
117 	if (itimer.it_value.tv_sec < 0 || itimer.it_value.tv_sec > (1<<EVENT_TIMEOUT)) {
118 		itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
119 	}
120 	if (itimer.it_value.tv_usec < 0 ) {
121 		itimer.it_value.tv_usec = 0;
122 	}
123 	if (itimer.it_value.tv_sec == 0 && itimer.it_value.tv_usec == 0) {
124 		itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
125 		itimer.it_value.tv_usec = 0;
126 	}
127 	itimer.it_interval.tv_sec = (1<<EVENT_TIMEOUT);
128 	itimer.it_interval.tv_usec = 0;
129 	setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
130 #  endif
131 # endif /* VMS */
132 }
133 
134 /*
135  * init_timer - initialize the timer data structures
136  */
137 void
138 init_timer(void)
139 {
140 # if defined SYS_WINNT & !defined(SYS_CYGWIN32)
141 	HANDLE hToken;
142 	TOKEN_PRIVILEGES tkp;
143 # endif /* SYS_WINNT */
144 
145 	/*
146 	 * Initialize...
147 	 */
148 	alarm_flag = 0;
149 	alarm_overflow = 0;
150 	adjust_timer = 1;
151 	hourly_timer = HOUR;
152 	huffpuff_timer = 0;
153 	current_time = 0;
154 	timer_overflows = 0;
155 	timer_xmtcalls = 0;
156 	timer_timereset = 0;
157 
158 #if !defined(SYS_WINNT)
159 	/*
160 	 * Set up the alarm interrupt.	The first comes 2**EVENT_TIMEOUT
161 	 * seconds from now and they continue on every 2**EVENT_TIMEOUT
162 	 * seconds.
163 	 */
164 # if !defined(VMS)
165 #  if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
166 	if (timer_create (CLOCK_REALTIME, NULL, &ntpd_timerid) ==
167 #	ifdef SYS_VXWORKS
168 		ERROR
169 #	else
170 		-1
171 #	endif
172 	   )
173 	{
174 		fprintf (stderr, "timer create FAILED\n");
175 		exit (0);
176 	}
177 	(void) signal_no_reset(SIGALRM, alarming);
178 	itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
179 	itimer.it_interval.tv_nsec = itimer.it_value.tv_nsec = 0;
180 	timer_settime(ntpd_timerid, 0 /*!TIMER_ABSTIME*/, &itimer, NULL);
181 #  else
182 	(void) signal_no_reset(SIGALRM, alarming);
183 	itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
184 	itimer.it_interval.tv_usec = itimer.it_value.tv_usec = 0;
185 	setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
186 #  endif
187 # else /* VMS */
188 	vmsinc[0] = 10000000;		/* 1 sec */
189 	vmsinc[1] = 0;
190 	lib$emul(&(1<<EVENT_TIMEOUT), &vmsinc, &0, &vmsinc);
191 
192 	sys$gettim(&vmstimer);	/* that's "now" as abstime */
193 
194 	lib$addx(&vmsinc, &vmstimer, &vmstimer);
195 	sys$setimr(0, &vmstimer, alarming, alarming, 0);
196 # endif /* VMS */
197 #else /* SYS_WINNT */
198 	_tzset();
199 
200 	/*
201 	 * Get privileges needed for fiddling with the clock
202 	 */
203 
204 	/* get the current process token handle */
205 	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
206 		msyslog(LOG_ERR, "OpenProcessToken failed: %m");
207 		exit(1);
208 	}
209 	/* get the LUID for system-time privilege. */
210 	LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);
211 	tkp.PrivilegeCount = 1;  /* one privilege to set */
212 	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
213 	/* get set-time privilege for this process. */
214 	AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
215 	/* cannot test return value of AdjustTokenPrivileges. */
216 	if (GetLastError() != ERROR_SUCCESS) {
217 		msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m");
218 	}
219 
220 	/*
221 	 * Set up timer interrupts for every 2**EVENT_TIMEOUT seconds
222 	 * Under Windows/NT,
223 	 */
224 
225 	WaitableTimerHandle = CreateWaitableTimer(NULL, FALSE, NULL);
226 	if (WaitableTimerHandle == NULL) {
227 		msyslog(LOG_ERR, "CreateWaitableTimer failed: %m");
228 		exit(1);
229 	}
230 	else {
231 		DWORD Period = (1<<EVENT_TIMEOUT) * 1000;
232 		LARGE_INTEGER DueTime;
233 		DueTime.QuadPart = Period * 10000i64;
234 		if (!SetWaitableTimer(WaitableTimerHandle, &DueTime, Period, NULL, NULL, FALSE) != NO_ERROR) {
235 			msyslog(LOG_ERR, "SetWaitableTimer failed: %m");
236 			exit(1);
237 		}
238 	}
239 
240 #endif /* SYS_WINNT */
241 }
242 
243 #if defined(SYS_WINNT)
244 extern HANDLE
245 get_timer_handle(void)
246 {
247 	return WaitableTimerHandle;
248 }
249 #endif
250 
251 /*
252  * timer - dispatch anyone who needs to be
253  */
254 void
255 timer(void)
256 {
257 	register struct peer *peer, *next_peer;
258 #ifdef OPENSSL
259 	char	statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
260 #endif /* OPENSSL */
261 	u_int n;
262 
263 	current_time += (1<<EVENT_TIMEOUT);
264 
265 	/*
266 	 * Adjustment timeout first.
267 	 */
268 	if (adjust_timer <= current_time) {
269 		adjust_timer += 1;
270 		adj_host_clock();
271 		kod_proto();
272 	}
273 
274 	/*
275 	 * Now dispatch any peers whose event timer has expired. Be careful
276 	 * here, since the peer structure might go away as the result of
277 	 * the call.
278 	 */
279 	for (n = 0; n < HASH_SIZE; n++) {
280 		for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
281 			next_peer = peer->next;
282 			if (peer->action && peer->nextaction <= current_time)
283 	  			peer->action(peer);
284 			if (peer->nextdate <= current_time) {
285 #ifdef REFCLOCK
286 				if (peer->flags & FLAG_REFCLOCK)
287 					refclock_transmit(peer);
288 				else
289 					transmit(peer);
290 #else /* REFCLOCK */
291 				transmit(peer);
292 #endif /* REFCLOCK */
293 			}
294 		}
295 	}
296 
297 	/*
298 	 * Garbage collect expired keys.
299 	 */
300 	if (keys_timer <= current_time) {
301 		keys_timer += MINUTE;
302 		auth_agekeys();
303 	}
304 
305 	/*
306 	 * Huff-n'-puff filter
307 	 */
308 	if (huffpuff_timer <= current_time) {
309 		huffpuff_timer += HUFFPUFF;
310 		huffpuff();
311 	}
312 
313 #ifdef OPENSSL
314 	/*
315 	 * Garbage collect old keys and generate new private value
316 	 */
317 	if (revoke_timer <= current_time) {
318 		revoke_timer += RANDPOLL(sys_revoke);
319 		expire_all();
320 		sprintf(statstr, "refresh ts %u", ntohl(hostval.tstamp));
321 		record_crypto_stats(NULL, statstr);
322 #ifdef DEBUG
323 		if (debug)
324 			printf("timer: %s\n", statstr);
325 #endif
326 	}
327 #endif /* OPENSSL */
328 
329 	/*
330 	 * Finally, call the hourly routine.
331 	 */
332 	if (hourly_timer <= current_time) {
333 		hourly_timer += HOUR;
334 		hourly_stats();
335 	}
336 }
337 
338 
339 #ifndef SYS_WINNT
340 /*
341  * alarming - tell the world we've been alarmed
342  */
343 static RETSIGTYPE
344 alarming(
345 	int sig
346 	)
347 {
348 #if !defined(VMS)
349 	if (initializing)
350 		return;
351 	if (alarm_flag)
352 		alarm_overflow++;
353 	else
354 		alarm_flag++;
355 #else /* VMS AST routine */
356 	if (!initializing) {
357 		if (alarm_flag) alarm_overflow++;
358 		else alarm_flag = 1;	/* increment is no good */
359 	}
360 	lib$addx(&vmsinc,&vmstimer,&vmstimer);
361 	sys$setimr(0,&vmstimer,alarming,alarming,0);
362 #endif /* VMS */
363 }
364 #endif /* SYS_WINNT */
365 
366 
367 /*
368  * timer_clr_stats - clear timer module stat counters
369  */
370 void
371 timer_clr_stats(void)
372 {
373 	timer_overflows = 0;
374 	timer_xmtcalls = 0;
375 	timer_timereset = current_time;
376 }
377 
378