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