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