1 /* 2 * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14 #include <sm/gen.h> 15 SM_RCSID("@(#)$Id: clock.c,v 1.34 2001/11/05 18:33:20 ca Exp $") 16 #include <unistd.h> 17 #include <time.h> 18 #include <errno.h> 19 #if SM_CONF_SETITIMER 20 # include <sys/time.h> 21 #endif /* SM_CONF_SETITIMER */ 22 #include <sm/heap.h> 23 #include <sm/debug.h> 24 #include <sm/bitops.h> 25 #include <sm/clock.h> 26 #include "local.h" 27 28 #ifndef sigmask 29 # define sigmask(s) (1 << ((s) - 1)) 30 #endif /* ! sigmask */ 31 32 static void sm_endsleep __P((void)); 33 34 35 /* 36 ** SM_SETEVENTM -- set an event to happen at a specific time in milliseconds. 37 ** 38 ** Events are stored in a sorted list for fast processing. 39 ** An event only applies to the process that set it. 40 ** Source is #ifdef'd to work with older OS's that don't have setitimer() 41 ** (that is, don't have a timer granularity less than 1 second). 42 ** 43 ** Parameters: 44 ** intvl -- interval until next event occurs (milliseconds). 45 ** func -- function to call on event. 46 ** arg -- argument to func on event. 47 ** 48 ** Returns: 49 ** On success returns the SM_EVENT entry created. 50 ** On failure returns NULL. 51 ** 52 ** Side Effects: 53 ** none. 54 */ 55 56 static SM_EVENT *volatile SmEventQueue; /* head of event queue */ 57 static SM_EVENT *volatile SmFreeEventList; /* list of free events */ 58 59 SM_EVENT * 60 sm_seteventm(intvl, func, arg) 61 int intvl; 62 void (*func)(); 63 int arg; 64 { 65 ENTER_CRITICAL(); 66 if (SmFreeEventList == NULL) 67 { 68 SmFreeEventList = (SM_EVENT *) sm_pmalloc_x(sizeof *SmFreeEventList); 69 SmFreeEventList->ev_link = NULL; 70 } 71 LEAVE_CRITICAL(); 72 73 return sm_sigsafe_seteventm(intvl, func, arg); 74 } 75 76 /* 77 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 78 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 79 ** DOING. 80 */ 81 82 SM_EVENT * 83 sm_sigsafe_seteventm(intvl, func, arg) 84 int intvl; 85 void (*func)(); 86 int arg; 87 { 88 register SM_EVENT **evp; 89 register SM_EVENT *ev; 90 #if SM_CONF_SETITIMER 91 auto struct timeval now, nowi, ival; 92 auto struct itimerval itime; 93 #else /* SM_CONF_SETITIMER */ 94 auto time_t now, nowi; 95 #endif /* SM_CONF_SETITIMER */ 96 int wasblocked; 97 98 /* negative times are not allowed */ 99 if (intvl <= 0) 100 return NULL; 101 102 wasblocked = sm_blocksignal(SIGALRM); 103 #if SM_CONF_SETITIMER 104 ival.tv_sec = intvl / 1000; 105 ival.tv_usec = (intvl - ival.tv_sec * 1000) * 10; 106 (void) gettimeofday(&now, NULL); 107 nowi = now; 108 timeradd(&now, &ival, &nowi); 109 #else /* SM_CONF_SETITIMER */ 110 now = time(NULL); 111 nowi = now + (time_t)(intvl / 1000); 112 #endif /* SM_CONF_SETITIMER */ 113 114 /* search event queue for correct position */ 115 for (evp = (SM_EVENT **) (&SmEventQueue); 116 (ev = *evp) != NULL; 117 evp = &ev->ev_link) 118 { 119 #if SM_CONF_SETITIMER 120 if (timercmp(&(ev->ev_time), &nowi, >=)) 121 #else /* SM_CONF_SETITIMER */ 122 if (ev->ev_time >= nowi) 123 #endif /* SM_CONF_SETITIMER */ 124 break; 125 } 126 127 ENTER_CRITICAL(); 128 if (SmFreeEventList == NULL) 129 { 130 /* 131 ** This shouldn't happen. If called from sm_seteventm(), 132 ** we have just malloced a SmFreeEventList entry. If 133 ** called from a signal handler, it should have been 134 ** from an existing event which sm_tick() just added to 135 ** SmFreeEventList. 136 */ 137 138 LEAVE_CRITICAL(); 139 return NULL; 140 } 141 else 142 { 143 ev = SmFreeEventList; 144 SmFreeEventList = ev->ev_link; 145 } 146 LEAVE_CRITICAL(); 147 148 /* insert new event */ 149 ev->ev_time = nowi; 150 ev->ev_func = func; 151 ev->ev_arg = arg; 152 ev->ev_pid = getpid(); 153 ENTER_CRITICAL(); 154 ev->ev_link = *evp; 155 *evp = ev; 156 LEAVE_CRITICAL(); 157 158 (void) sm_signal(SIGALRM, sm_tick); 159 # if SM_CONF_SETITIMER 160 timersub(&SmEventQueue->ev_time, &now, &itime.it_value); 161 itime.it_interval.tv_sec = 0; 162 itime.it_interval.tv_usec = 0; 163 if (itime.it_value.tv_sec == 0 && itime.it_value.tv_usec == 0) 164 itime.it_value.tv_usec = 1000; 165 (void) setitimer(ITIMER_REAL, &itime, NULL); 166 # else /* SM_CONF_SETITIMER */ 167 intvl = SmEventQueue->ev_time - now; 168 (void) alarm((unsigned) intvl < 1 ? 1 : intvl); 169 # endif /* SM_CONF_SETITIMER */ 170 if (wasblocked == 0) 171 (void) sm_releasesignal(SIGALRM); 172 return ev; 173 } 174 /* 175 ** SM_CLREVENT -- remove an event from the event queue. 176 ** 177 ** Parameters: 178 ** ev -- pointer to event to remove. 179 ** 180 ** Returns: 181 ** none. 182 ** 183 ** Side Effects: 184 ** arranges for event ev to not happen. 185 */ 186 187 void 188 sm_clrevent(ev) 189 register SM_EVENT *ev; 190 { 191 register SM_EVENT **evp; 192 int wasblocked; 193 # if SM_CONF_SETITIMER 194 struct itimerval clr; 195 # endif /* SM_CONF_SETITIMER */ 196 197 if (ev == NULL) 198 return; 199 200 /* find the parent event */ 201 wasblocked = sm_blocksignal(SIGALRM); 202 for (evp = (SM_EVENT **) (&SmEventQueue); 203 *evp != NULL; 204 evp = &(*evp)->ev_link) 205 { 206 if (*evp == ev) 207 break; 208 } 209 210 /* now remove it */ 211 if (*evp != NULL) 212 { 213 ENTER_CRITICAL(); 214 *evp = ev->ev_link; 215 ev->ev_link = SmFreeEventList; 216 SmFreeEventList = ev; 217 LEAVE_CRITICAL(); 218 } 219 220 /* restore clocks and pick up anything spare */ 221 if (wasblocked == 0) 222 (void) sm_releasesignal(SIGALRM); 223 if (SmEventQueue != NULL) 224 (void) kill(getpid(), SIGALRM); 225 else 226 { 227 /* nothing left in event queue, no need for an alarm */ 228 # if SM_CONF_SETITIMER 229 clr.it_interval.tv_sec = 0; 230 clr.it_interval.tv_usec = 0; 231 clr.it_value.tv_sec = 0; 232 clr.it_value.tv_usec = 0; 233 (void) setitimer(ITIMER_REAL, &clr, NULL); 234 # else /* SM_CONF_SETITIMER */ 235 (void) alarm(0); 236 # endif /* SM_CONF_SETITIMER */ 237 } 238 } 239 /* 240 ** SM_CLEAR_EVENTS -- remove all events from the event queue. 241 ** 242 ** Parameters: 243 ** none. 244 ** 245 ** Returns: 246 ** none. 247 */ 248 249 void 250 sm_clear_events() 251 { 252 register SM_EVENT *ev; 253 #if SM_CONF_SETITIMER 254 struct itimerval clr; 255 #endif /* SM_CONF_SETITIMER */ 256 int wasblocked; 257 258 if (SmEventQueue == NULL) 259 return; 260 261 /* nothing will be left in event queue, no need for an alarm */ 262 #if SM_CONF_SETITIMER 263 clr.it_interval.tv_sec = 0; 264 clr.it_interval.tv_usec = 0; 265 clr.it_value.tv_sec = 0; 266 clr.it_value.tv_usec = 0; 267 (void) setitimer(ITIMER_REAL, &clr, NULL); 268 #else /* SM_CONF_SETITIMER */ 269 (void) alarm(0); 270 #endif /* SM_CONF_SETITIMER */ 271 wasblocked = sm_blocksignal(SIGALRM); 272 273 /* find the end of the EventQueue */ 274 for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link) 275 continue; 276 277 ENTER_CRITICAL(); 278 ev->ev_link = SmFreeEventList; 279 SmFreeEventList = SmEventQueue; 280 SmEventQueue = NULL; 281 LEAVE_CRITICAL(); 282 283 /* restore clocks and pick up anything spare */ 284 if (wasblocked == 0) 285 (void) sm_releasesignal(SIGALRM); 286 } 287 /* 288 ** SM_TICK -- take a clock tick 289 ** 290 ** Called by the alarm clock. This routine runs events as needed. 291 ** Always called as a signal handler, so we assume that SIGALRM 292 ** has been blocked. 293 ** 294 ** Parameters: 295 ** One that is ignored; for compatibility with signal handlers. 296 ** 297 ** Returns: 298 ** none. 299 ** 300 ** Side Effects: 301 ** calls the next function in EventQueue. 302 ** 303 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 304 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 305 ** DOING. 306 */ 307 308 /* ARGSUSED */ 309 SIGFUNC_DECL 310 sm_tick(sig) 311 int sig; 312 { 313 register SM_EVENT *ev; 314 pid_t mypid; 315 int save_errno = errno; 316 #if SM_CONF_SETITIMER 317 struct itimerval clr; 318 struct timeval now; 319 #else /* SM_CONF_SETITIMER */ 320 register time_t now; 321 #endif /* SM_CONF_SETITIMER */ 322 323 #if SM_CONF_SETITIMER 324 clr.it_interval.tv_sec = 0; 325 clr.it_interval.tv_usec = 0; 326 clr.it_value.tv_sec = 0; 327 clr.it_value.tv_usec = 0; 328 (void) setitimer(ITIMER_REAL, &clr, NULL); 329 gettimeofday(&now, NULL); 330 #else /* SM_CONF_SETITIMER */ 331 (void) alarm(0); 332 now = time(NULL); 333 #endif /* SM_CONF_SETITIMER */ 334 335 FIX_SYSV_SIGNAL(sig, sm_tick); 336 errno = save_errno; 337 CHECK_CRITICAL(sig); 338 339 mypid = getpid(); 340 while (PendingSignal != 0) 341 { 342 int sigbit = 0; 343 int sig = 0; 344 345 if (bitset(PEND_SIGHUP, PendingSignal)) 346 { 347 sigbit = PEND_SIGHUP; 348 sig = SIGHUP; 349 } 350 else if (bitset(PEND_SIGINT, PendingSignal)) 351 { 352 sigbit = PEND_SIGINT; 353 sig = SIGINT; 354 } 355 else if (bitset(PEND_SIGTERM, PendingSignal)) 356 { 357 sigbit = PEND_SIGTERM; 358 sig = SIGTERM; 359 } 360 else if (bitset(PEND_SIGUSR1, PendingSignal)) 361 { 362 sigbit = PEND_SIGUSR1; 363 sig = SIGUSR1; 364 } 365 else 366 { 367 /* If we get here, we are in trouble */ 368 abort(); 369 } 370 PendingSignal &= ~sigbit; 371 kill(mypid, sig); 372 } 373 374 #if SM_CONF_SETITIMER 375 gettimeofday(&now, NULL); 376 #else /* SM_CONF_SETITIMER */ 377 now = time(NULL); 378 #endif /* SM_CONF_SETITIMER */ 379 while ((ev = SmEventQueue) != NULL && 380 (ev->ev_pid != mypid || 381 #if SM_CONF_SETITIMER 382 timercmp(&ev->ev_time, &now, <=) 383 #else /* SM_CONF_SETITIMER */ 384 ev->ev_time <= now 385 #endif /* SM_CONF_SETITIMER */ 386 )) 387 { 388 void (*f)(); 389 int arg; 390 pid_t pid; 391 392 /* process the event on the top of the queue */ 393 ev = SmEventQueue; 394 SmEventQueue = SmEventQueue->ev_link; 395 396 /* we must be careful in here because ev_func may not return */ 397 f = ev->ev_func; 398 arg = ev->ev_arg; 399 pid = ev->ev_pid; 400 ENTER_CRITICAL(); 401 ev->ev_link = SmFreeEventList; 402 SmFreeEventList = ev; 403 LEAVE_CRITICAL(); 404 if (pid != getpid()) 405 continue; 406 if (SmEventQueue != NULL) 407 { 408 #if SM_CONF_SETITIMER 409 if (timercmp(&SmEventQueue->ev_time, &now, >)) 410 { 411 timersub(&SmEventQueue->ev_time, &now, 412 &clr.it_value); 413 clr.it_interval.tv_sec = 0; 414 clr.it_interval.tv_usec = 0; 415 (void) setitimer(ITIMER_REAL, &clr, NULL); 416 } 417 else 418 { 419 clr.it_interval.tv_sec = 0; 420 clr.it_interval.tv_usec = 0; 421 clr.it_value.tv_sec = 3; 422 clr.it_value.tv_usec = 0; 423 (void) setitimer(ITIMER_REAL, &clr, NULL); 424 } 425 #else /* SM_CONF_SETITIMER */ 426 if (SmEventQueue->ev_time > now) 427 (void) alarm((unsigned) (SmEventQueue->ev_time 428 - now)); 429 else 430 (void) alarm(3); 431 #endif /* SM_CONF_SETITIMER */ 432 } 433 434 /* call ev_func */ 435 errno = save_errno; 436 (*f)(arg); 437 #if SM_CONF_SETITIMER 438 clr.it_interval.tv_sec = 0; 439 clr.it_interval.tv_usec = 0; 440 clr.it_value.tv_sec = 0; 441 clr.it_value.tv_usec = 0; 442 (void) setitimer(ITIMER_REAL, &clr, NULL); 443 gettimeofday(&now, NULL); 444 #else /* SM_CONF_SETITIMER */ 445 (void) alarm(0); 446 now = time(NULL); 447 #endif /* SM_CONF_SETITIMER */ 448 } 449 if (SmEventQueue != NULL) 450 { 451 #if SM_CONF_SETITIMER 452 timersub(&SmEventQueue->ev_time, &now, &clr.it_value); 453 clr.it_interval.tv_sec = 0; 454 clr.it_interval.tv_usec = 0; 455 (void) setitimer(ITIMER_REAL, &clr, NULL); 456 #else /* SM_CONF_SETITIMER */ 457 (void) alarm((unsigned) (SmEventQueue->ev_time - now)); 458 #endif /* SM_CONF_SETITIMER */ 459 } 460 errno = save_errno; 461 return SIGFUNC_RETURN; 462 } 463 /* 464 ** SLEEP -- a version of sleep that works with this stuff 465 ** 466 ** Because Unix sleep uses the alarm facility, I must reimplement 467 ** it here. 468 ** 469 ** Parameters: 470 ** intvl -- time to sleep. 471 ** 472 ** Returns: 473 ** zero. 474 ** 475 ** Side Effects: 476 ** waits for intvl time. However, other events can 477 ** be run during that interval. 478 */ 479 480 481 static bool volatile SmSleepDone; 482 483 #ifndef SLEEP_T 484 # define SLEEP_T unsigned int 485 #endif /* ! SLEEP_T */ 486 487 SLEEP_T 488 sleep(intvl) 489 unsigned int intvl; 490 { 491 int was_held; 492 493 if (intvl == 0) 494 return (SLEEP_T) 0; 495 SmSleepDone = false; 496 (void) sm_setevent((time_t) intvl, sm_endsleep, 0); 497 was_held = sm_releasesignal(SIGALRM); 498 while (!SmSleepDone) 499 (void) pause(); 500 if (was_held > 0) 501 (void) sm_blocksignal(SIGALRM); 502 return (SLEEP_T) 0; 503 } 504 505 static void 506 sm_endsleep() 507 { 508 /* 509 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 510 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 511 ** DOING. 512 */ 513 514 SmSleepDone = true; 515 } 516 517