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