Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / ntp / ntpd / ntp_timer.c
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