1 | /* |
---|
2 | * ntp_event.c - event timer support routines |
---|
3 | */ |
---|
4 | #ifdef HAVE_CONFIG_H |
---|
5 | #include <config.h> |
---|
6 | #endif |
---|
7 | |
---|
8 | #include <stdio.h> |
---|
9 | #include <errno.h> |
---|
10 | #include <sys/types.h> |
---|
11 | #include <sys/time.h> |
---|
12 | #include <signal.h> |
---|
13 | #include <sys/signal.h> |
---|
14 | |
---|
15 | #include "ntpd.h" |
---|
16 | #include "ntp_stdlib.h" |
---|
17 | |
---|
18 | /* |
---|
19 | * These routines provide support for the event timer. The timer is |
---|
20 | * implemented by an interrupt routine which sets a flag once every |
---|
21 | * 2**EVENT_TIMEOUT seconds (currently 4), and a timer routine which |
---|
22 | * is called when the mainline code gets around to seeing the flag. |
---|
23 | * The timer routine dispatches the clock adjustment code if its time |
---|
24 | * has come, then searches the timer queue for expiries which are |
---|
25 | * dispatched to the transmit procedure. Finally, we call the hourly |
---|
26 | * procedure to do cleanup and print a message. |
---|
27 | */ |
---|
28 | |
---|
29 | /* |
---|
30 | * Alarm flag. The mainline code imports this. |
---|
31 | */ |
---|
32 | volatile int alarm_flag; |
---|
33 | |
---|
34 | /* |
---|
35 | * adjust and hourly counters |
---|
36 | */ |
---|
37 | static u_long adjust_timer; |
---|
38 | static u_long hourly_timer; |
---|
39 | |
---|
40 | /* |
---|
41 | * Imported from the leap module. The leap timer. |
---|
42 | */ |
---|
43 | extern u_long leap_timer; |
---|
44 | |
---|
45 | /* |
---|
46 | * Statistics counter for the interested. |
---|
47 | */ |
---|
48 | volatile u_long alarm_overflow; |
---|
49 | |
---|
50 | #define HOUR (60*60) |
---|
51 | |
---|
52 | /* |
---|
53 | * Current_time holds the number of seconds since we started, in |
---|
54 | * increments of 2**EVENT_TIMEOUT seconds. The timer queue is the |
---|
55 | * hash into which we sort timer entries. |
---|
56 | */ |
---|
57 | u_long current_time; |
---|
58 | struct event timerqueue[TIMER_NSLOTS]; |
---|
59 | |
---|
60 | /* |
---|
61 | * Stats. Number of overflows and number of calls to transmit(). |
---|
62 | */ |
---|
63 | u_long timer_timereset; |
---|
64 | u_long timer_overflows; |
---|
65 | u_long timer_xmtcalls; |
---|
66 | |
---|
67 | #ifndef SYS_WINNT |
---|
68 | static RETSIGTYPE alarming P((int)); |
---|
69 | #else |
---|
70 | void PASCAL alarming P((UINT,UINT,DWORD,DWORD,DWORD)); |
---|
71 | #endif /* SYS_WINNT */ |
---|
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 | /* |
---|
79 | * init_timer - initialize the timer data structures |
---|
80 | */ |
---|
81 | void |
---|
82 | init_timer() |
---|
83 | { |
---|
84 | register int i; |
---|
85 | #if !defined(VMS) |
---|
86 | # ifndef SYS_WINNT |
---|
87 | #ifndef HAVE_TIMER_SETTIME |
---|
88 | struct itimerval itimer; |
---|
89 | #else |
---|
90 | static timer_t xntpd_timerid; /* should be global if we ever want to kill |
---|
91 | timer without rebooting ... */ |
---|
92 | struct itimerspec itimer; |
---|
93 | #endif |
---|
94 | |
---|
95 | # else /* SYS_WINNT */ |
---|
96 | TIMECAPS tc; |
---|
97 | HANDLE hToken; |
---|
98 | TOKEN_PRIVILEGES tkp; |
---|
99 | UINT wTimerRes, wTimerID; |
---|
100 | extern HANDLE hMutex; |
---|
101 | # endif /* SYS_WINNT */ |
---|
102 | #endif /* VMS */ |
---|
103 | |
---|
104 | /* |
---|
105 | * Initialize... |
---|
106 | */ |
---|
107 | alarm_flag = 0; |
---|
108 | alarm_overflow = 0; |
---|
109 | adjust_timer = 1; |
---|
110 | hourly_timer = HOUR; |
---|
111 | current_time = 0; |
---|
112 | timer_overflows = 0; |
---|
113 | timer_xmtcalls = 0; |
---|
114 | timer_timereset = 0; |
---|
115 | |
---|
116 | for (i = 0; i < TIMER_NSLOTS; i++) { |
---|
117 | /* |
---|
118 | * Queue pointers should point at themselves. Event |
---|
119 | * times must be set to 0 since this is used to |
---|
120 | * detect the queue end. |
---|
121 | */ |
---|
122 | timerqueue[i].next = &timerqueue[i]; |
---|
123 | timerqueue[i].prev = &timerqueue[i]; |
---|
124 | timerqueue[i].event_time = 0; |
---|
125 | } |
---|
126 | |
---|
127 | #ifndef SYS_WINNT |
---|
128 | /* |
---|
129 | * Set up the alarm interrupt. The first comes 2**EVENT_TIMEOUT |
---|
130 | * seconds from now and they continue on every 2**EVENT_TIMEOUT |
---|
131 | * seconds. |
---|
132 | */ |
---|
133 | # if !defined(VMS) |
---|
134 | #if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME) |
---|
135 | if (timer_create (CLOCK_REALTIME, NULL, &xntpd_timerid) == |
---|
136 | #ifdef SYS_VXWORKS |
---|
137 | ERROR |
---|
138 | #else |
---|
139 | -1 |
---|
140 | #endif |
---|
141 | ) |
---|
142 | { |
---|
143 | fprintf (stderr, "timer create FAILED\n"); |
---|
144 | exit (0); |
---|
145 | } |
---|
146 | (void) signal_no_reset(SIGALRM, alarming); |
---|
147 | itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT); |
---|
148 | itimer.it_interval.tv_nsec = itimer.it_value.tv_nsec = 0; |
---|
149 | timer_settime(xntpd_timerid, 0 /*!TIMER_ABSTIME*/, &itimer, NULL); |
---|
150 | #else |
---|
151 | (void) signal_no_reset(SIGALRM, alarming); |
---|
152 | itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT); |
---|
153 | itimer.it_interval.tv_usec = itimer.it_value.tv_usec = 0; |
---|
154 | setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0); |
---|
155 | #endif |
---|
156 | |
---|
157 | # else /* VMS */ |
---|
158 | vmsinc[0] = 10000000; /* 1 sec */ |
---|
159 | vmsinc[1] = 0; |
---|
160 | lib$emul(&(1<<EVENT_TIMEOUT), &vmsinc, &0, &vmsinc); |
---|
161 | |
---|
162 | sys$gettim(&vmstimer); /* that's "now" as abstime */ |
---|
163 | |
---|
164 | lib$addx(&vmsinc, &vmstimer, &vmstimer); |
---|
165 | sys$setimr(0, &vmstimer, alarming, alarming, 0); |
---|
166 | # endif /* VMS */ |
---|
167 | #else /* SYS_WINNT */ |
---|
168 | _tzset(); |
---|
169 | |
---|
170 | /* |
---|
171 | * Get privileges needed for fiddling with the clock |
---|
172 | */ |
---|
173 | |
---|
174 | /* get the current process token handle */ |
---|
175 | if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { |
---|
176 | msyslog(LOG_ERR, "OpenProcessToken failed: %m"); |
---|
177 | exit(1); |
---|
178 | } |
---|
179 | /* get the LUID for system-time privilege. */ |
---|
180 | LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid); |
---|
181 | tkp.PrivilegeCount = 1; /* one privilege to set */ |
---|
182 | tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
---|
183 | /* get set-time privilege for this process. */ |
---|
184 | AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0); |
---|
185 | /* cannot test return value of AdjustTokenPrivileges. */ |
---|
186 | if (GetLastError() != ERROR_SUCCESS) |
---|
187 | msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m"); |
---|
188 | |
---|
189 | /* |
---|
190 | * Set up timer interrupts for every 2**EVENT_TIMEOUT seconds |
---|
191 | * Under Windows/NT, expiry of timer interval leads to invocation |
---|
192 | * of a callback function (on a different thread) rather than |
---|
193 | * generating an alarm signal |
---|
194 | */ |
---|
195 | |
---|
196 | /* determine max and min resolution supported */ |
---|
197 | if(timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { |
---|
198 | msyslog(LOG_ERR, "timeGetDevCaps failed: %m"); |
---|
199 | exit(1); |
---|
200 | } |
---|
201 | wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax); |
---|
202 | /* establish the minimum timer resolution that we'll use */ |
---|
203 | timeBeginPeriod(wTimerRes); |
---|
204 | |
---|
205 | hMutex = CreateMutex( |
---|
206 | NULL, /* no security attributes */ |
---|
207 | FALSE, /* initially not owned */ |
---|
208 | "MutexForNTP"); /* name of mutex */ |
---|
209 | if (hMutex == NULL) { |
---|
210 | msyslog(LOG_ERR, "cannot create a mutex: %m\n"); |
---|
211 | exit(1); |
---|
212 | } |
---|
213 | |
---|
214 | /* start the timer event */ |
---|
215 | wTimerID = timeSetEvent( |
---|
216 | (1<<EVENT_TIMEOUT) * 1000, /* Delay in ms */ |
---|
217 | wTimerRes, /* Resolution */ |
---|
218 | (LPTIMECALLBACK) alarming, /* Callback function */ |
---|
219 | (DWORD) 0, /* User data */ |
---|
220 | TIME_PERIODIC); /* Event type (periodic) */ |
---|
221 | if (wTimerID == 0) { |
---|
222 | msyslog(LOG_ERR, "timeSetEvent failed: %m"); |
---|
223 | exit(1); |
---|
224 | } |
---|
225 | #endif /* SYS_WINNT */ |
---|
226 | } |
---|
227 | |
---|
228 | |
---|
229 | /* |
---|
230 | * timer - dispatch anyone who needs to be |
---|
231 | */ |
---|
232 | void |
---|
233 | timer() |
---|
234 | { |
---|
235 | register struct event *ev; |
---|
236 | register struct event *tq; |
---|
237 | #ifdef SYS_WINNT |
---|
238 | extern HANDLE hMutex; |
---|
239 | #endif |
---|
240 | |
---|
241 | current_time += (1<<EVENT_TIMEOUT); |
---|
242 | |
---|
243 | /* |
---|
244 | * Adjustment timeout first |
---|
245 | */ |
---|
246 | if (adjust_timer <= current_time) { |
---|
247 | adjust_timer += 1; |
---|
248 | adj_host_clock(); |
---|
249 | } |
---|
250 | |
---|
251 | #ifdef SYS_WINNT |
---|
252 | if (!ReleaseMutex(hMutex)) { |
---|
253 | msyslog(LOG_ERR, "alarming cannot release mutex: %m\n"); |
---|
254 | exit(1); |
---|
255 | } |
---|
256 | #endif /* SYS_WINNT */ |
---|
257 | |
---|
258 | /* |
---|
259 | * Leap timer next. |
---|
260 | */ |
---|
261 | if (leap_timer != 0 && leap_timer <= current_time) |
---|
262 | leap_process(); |
---|
263 | |
---|
264 | /* |
---|
265 | * Now dispatch any peers whose event timer has expired. |
---|
266 | */ |
---|
267 | |
---|
268 | /* Added mutex to prevent race condition among threads under Windows NT */ |
---|
269 | #ifdef SYS_WINNT |
---|
270 | WaitForSingleObject(m_hListMutex,INFINITE); |
---|
271 | #endif /* SYS_WINNT */ |
---|
272 | |
---|
273 | #ifdef TIMERQUEUE_DEBUG |
---|
274 | { |
---|
275 | int i; |
---|
276 | int j; |
---|
277 | |
---|
278 | for (i = 0; i < TIMER_NSLOTS; ++i) |
---|
279 | { |
---|
280 | struct event *qh; |
---|
281 | |
---|
282 | qh = ev = &timerqueue[i]; |
---|
283 | if (qh->event_time != 0) |
---|
284 | msyslog(LOG_ERR, "timerqueue[%d].event_time is %d instead of 0!", |
---|
285 | i, timerqueue[i].event_time); |
---|
286 | j = 0; |
---|
287 | do |
---|
288 | { |
---|
289 | if (ev->prev->next != ev) |
---|
290 | msyslog(LOG_ERR, "timerqueue[%d]: #%d: ev->prev->next != ev", |
---|
291 | i, j); |
---|
292 | if (ev->next->prev != ev) |
---|
293 | msyslog(LOG_ERR, "timerqueue[%d]: #%d: ev->next->prev != ev", |
---|
294 | i, j); |
---|
295 | ++j; |
---|
296 | ev = ev->next; |
---|
297 | } |
---|
298 | while (qh != ev); |
---|
299 | } |
---|
300 | } |
---|
301 | #endif /* TIMERQUEUE_DEBUG */ |
---|
302 | |
---|
303 | tq = &timerqueue[TIMER_SLOT(current_time)]; |
---|
304 | if (tq) { |
---|
305 | ev = tq->next; |
---|
306 | while (ev |
---|
307 | && ev->event_time != 0 |
---|
308 | && ev->event_time < (current_time + (1<<EVENT_TIMEOUT))) { |
---|
309 | tq->next = ev->next; |
---|
310 | tq->next->prev = tq; |
---|
311 | ev->prev = ev->next = 0; |
---|
312 | timer_xmtcalls++; |
---|
313 | ev->event_handler(ev->peer); |
---|
314 | ev = tq->next; |
---|
315 | } |
---|
316 | if (!ev) |
---|
317 | msyslog(LOG_ERR, "timer: ev was NIL!"); |
---|
318 | } else { |
---|
319 | msyslog(LOG_ERR, "timer: tq was NIL!"); |
---|
320 | } |
---|
321 | |
---|
322 | /* Added mutex to prevent race condition among threads under Windows NT */ |
---|
323 | #ifdef SYS_WINNT |
---|
324 | ReleaseMutex(m_hListMutex); |
---|
325 | #endif /* SYS_WINNT */ |
---|
326 | |
---|
327 | /* |
---|
328 | * Finally, call the hourly routine |
---|
329 | */ |
---|
330 | if (hourly_timer <= current_time) { |
---|
331 | hourly_timer += HOUR; |
---|
332 | hourly_stats(); |
---|
333 | } |
---|
334 | } |
---|
335 | |
---|
336 | |
---|
337 | #ifndef SYS_WINNT |
---|
338 | /* |
---|
339 | * alarming - tell the world we've been alarmed |
---|
340 | */ |
---|
341 | static RETSIGTYPE |
---|
342 | alarming(sig) |
---|
343 | int sig; |
---|
344 | { |
---|
345 | extern int initializing; /* from main line code */ |
---|
346 | |
---|
347 | #if !defined(VMS) |
---|
348 | if (initializing) |
---|
349 | return; |
---|
350 | if (alarm_flag) |
---|
351 | alarm_overflow++; |
---|
352 | else |
---|
353 | alarm_flag++; |
---|
354 | #else /* VMS AST routine */ |
---|
355 | if (!initializing) { |
---|
356 | if (alarm_flag) alarm_overflow++; |
---|
357 | else alarm_flag = 1; /* increment is no good */ |
---|
358 | } |
---|
359 | lib$addx(&vmsinc,&vmstimer,&vmstimer); |
---|
360 | sys$setimr(0,&vmstimer,alarming,alarming,0); |
---|
361 | #endif /* VMS */ |
---|
362 | } |
---|
363 | #else /* SYS_WINNT */ |
---|
364 | /* |
---|
365 | * alarming for WinNT - invoke the timer() routine after grabbing the mutex |
---|
366 | */ |
---|
367 | void PASCAL alarming (UINT wTimerID, UINT msg, |
---|
368 | DWORD dwUser, DWORD dw1, DWORD dw2) |
---|
369 | { |
---|
370 | extern int debug; |
---|
371 | static int initializing2 = 1; |
---|
372 | extern HANDLE hMutex; |
---|
373 | DWORD dwWaitResult; |
---|
374 | extern HANDLE TimerThreadHandle; |
---|
375 | static DWORD threadID; |
---|
376 | #ifdef DEBUG |
---|
377 | SYSTEMTIME st; |
---|
378 | #endif |
---|
379 | |
---|
380 | /* |
---|
381 | * set the priority for timer() thread to be higher than the main thread |
---|
382 | */ |
---|
383 | if (initializing2) { |
---|
384 | TimerThreadHandle = GetCurrentThread(); |
---|
385 | if (!SetThreadPriority(TimerThreadHandle, (DWORD) THREAD_PRIORITY_HIGHEST)) |
---|
386 | msyslog(LOG_ERR, "SetThreadPriority failed: %m"); |
---|
387 | threadID = GetCurrentThreadId(); |
---|
388 | initializing2 = 0; |
---|
389 | } |
---|
390 | |
---|
391 | #ifdef DEBUG |
---|
392 | if (debug > 9) { |
---|
393 | GetSystemTime(&st); |
---|
394 | printf("thread %u (timer callback): time %02u:%02u:%02u:%03u\n", |
---|
395 | threadID, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); |
---|
396 | fflush(stdout); |
---|
397 | } |
---|
398 | #endif |
---|
399 | |
---|
400 | dwWaitResult = WaitForSingleObject( |
---|
401 | hMutex, /* handle of mutex */ |
---|
402 | 5000L); /* five-second time-out interval */ |
---|
403 | |
---|
404 | switch (dwWaitResult) { |
---|
405 | case WAIT_OBJECT_0: |
---|
406 | /* The thread got mutex ownership. */ |
---|
407 | /* the mutex is released in the timer() routine */ |
---|
408 | timer(); |
---|
409 | break; |
---|
410 | default: |
---|
411 | /* Cannot get mutex ownership due to time-out. */ |
---|
412 | msyslog(LOG_ERR, "alarming error with mutex: %m\n"); |
---|
413 | exit(1); |
---|
414 | } |
---|
415 | |
---|
416 | UNREFERENCED_PARAMETER(dw1); |
---|
417 | UNREFERENCED_PARAMETER(dw2); |
---|
418 | UNREFERENCED_PARAMETER(dwUser); |
---|
419 | UNREFERENCED_PARAMETER(msg); |
---|
420 | UNREFERENCED_PARAMETER(wTimerID); |
---|
421 | } |
---|
422 | #endif /* SYS_WINNT */ |
---|
423 | |
---|
424 | |
---|
425 | /* |
---|
426 | * timer_clr_stats - clear timer module stat counters |
---|
427 | */ |
---|
428 | void |
---|
429 | timer_clr_stats() |
---|
430 | { |
---|
431 | timer_overflows = 0; |
---|
432 | timer_xmtcalls = 0; |
---|
433 | timer_timereset = current_time; |
---|
434 | } |
---|