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