clock.c revision 112810
1/* 2 * Copyright (c) 1998-2003 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> 15SM_RCSID("@(#)$Id: clock.c,v 1.35.2.3 2003/03/03 19:57:40 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 32static 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 56static SM_EVENT *volatile SmEventQueue; /* head of event queue */ 57static SM_EVENT *volatile SmFreeEventList; /* list of free events */ 58 59SM_EVENT * 60sm_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 82SM_EVENT * 83sm_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) 164 itime.it_value.tv_sec = 0; 165 if (itime.it_value.tv_sec == 0 && itime.it_value.tv_usec == 0) 166 itime.it_value.tv_usec = 1000; 167 (void) setitimer(ITIMER_REAL, &itime, NULL); 168# else /* SM_CONF_SETITIMER */ 169 intvl = SmEventQueue->ev_time - now; 170 (void) alarm((unsigned) (intvl < 1 ? 1 : intvl)); 171# endif /* SM_CONF_SETITIMER */ 172 if (wasblocked == 0) 173 (void) sm_releasesignal(SIGALRM); 174 return ev; 175} 176/* 177** SM_CLREVENT -- remove an event from the event queue. 178** 179** Parameters: 180** ev -- pointer to event to remove. 181** 182** Returns: 183** none. 184** 185** Side Effects: 186** arranges for event ev to not happen. 187*/ 188 189void 190sm_clrevent(ev) 191 register SM_EVENT *ev; 192{ 193 register SM_EVENT **evp; 194 int wasblocked; 195# if SM_CONF_SETITIMER 196 struct itimerval clr; 197# endif /* SM_CONF_SETITIMER */ 198 199 if (ev == NULL) 200 return; 201 202 /* find the parent event */ 203 wasblocked = sm_blocksignal(SIGALRM); 204 for (evp = (SM_EVENT **) (&SmEventQueue); 205 *evp != NULL; 206 evp = &(*evp)->ev_link) 207 { 208 if (*evp == ev) 209 break; 210 } 211 212 /* now remove it */ 213 if (*evp != NULL) 214 { 215 ENTER_CRITICAL(); 216 *evp = ev->ev_link; 217 ev->ev_link = SmFreeEventList; 218 SmFreeEventList = ev; 219 LEAVE_CRITICAL(); 220 } 221 222 /* restore clocks and pick up anything spare */ 223 if (wasblocked == 0) 224 (void) sm_releasesignal(SIGALRM); 225 if (SmEventQueue != NULL) 226 (void) kill(getpid(), SIGALRM); 227 else 228 { 229 /* nothing left in event queue, no need for an alarm */ 230# if SM_CONF_SETITIMER 231 clr.it_interval.tv_sec = 0; 232 clr.it_interval.tv_usec = 0; 233 clr.it_value.tv_sec = 0; 234 clr.it_value.tv_usec = 0; 235 (void) setitimer(ITIMER_REAL, &clr, NULL); 236# else /* SM_CONF_SETITIMER */ 237 (void) alarm(0); 238# endif /* SM_CONF_SETITIMER */ 239 } 240} 241/* 242** SM_CLEAR_EVENTS -- remove all events from the event queue. 243** 244** Parameters: 245** none. 246** 247** Returns: 248** none. 249*/ 250 251void 252sm_clear_events() 253{ 254 register SM_EVENT *ev; 255#if SM_CONF_SETITIMER 256 struct itimerval clr; 257#endif /* SM_CONF_SETITIMER */ 258 int wasblocked; 259 260 /* nothing will be left in event queue, no need for an alarm */ 261#if SM_CONF_SETITIMER 262 clr.it_interval.tv_sec = 0; 263 clr.it_interval.tv_usec = 0; 264 clr.it_value.tv_sec = 0; 265 clr.it_value.tv_usec = 0; 266 (void) setitimer(ITIMER_REAL, &clr, NULL); 267#else /* SM_CONF_SETITIMER */ 268 (void) alarm(0); 269#endif /* SM_CONF_SETITIMER */ 270 271 if (SmEventQueue == NULL) 272 return; 273 274 wasblocked = sm_blocksignal(SIGALRM); 275 276 /* find the end of the EventQueue */ 277 for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link) 278 continue; 279 280 ENTER_CRITICAL(); 281 ev->ev_link = SmFreeEventList; 282 SmFreeEventList = SmEventQueue; 283 SmEventQueue = NULL; 284 LEAVE_CRITICAL(); 285 286 /* restore clocks and pick up anything spare */ 287 if (wasblocked == 0) 288 (void) sm_releasesignal(SIGALRM); 289} 290/* 291** SM_TICK -- take a clock tick 292** 293** Called by the alarm clock. This routine runs events as needed. 294** Always called as a signal handler, so we assume that SIGALRM 295** has been blocked. 296** 297** Parameters: 298** One that is ignored; for compatibility with signal handlers. 299** 300** Returns: 301** none. 302** 303** Side Effects: 304** calls the next function in EventQueue. 305** 306** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 307** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 308** DOING. 309*/ 310 311/* ARGSUSED */ 312SIGFUNC_DECL 313sm_tick(sig) 314 int sig; 315{ 316 register SM_EVENT *ev; 317 pid_t mypid; 318 int save_errno = errno; 319#if SM_CONF_SETITIMER 320 struct itimerval clr; 321 struct timeval now; 322#else /* SM_CONF_SETITIMER */ 323 register time_t now; 324#endif /* SM_CONF_SETITIMER */ 325 326#if SM_CONF_SETITIMER 327 clr.it_interval.tv_sec = 0; 328 clr.it_interval.tv_usec = 0; 329 clr.it_value.tv_sec = 0; 330 clr.it_value.tv_usec = 0; 331 (void) setitimer(ITIMER_REAL, &clr, NULL); 332 gettimeofday(&now, NULL); 333#else /* SM_CONF_SETITIMER */ 334 (void) alarm(0); 335 now = time(NULL); 336#endif /* SM_CONF_SETITIMER */ 337 338 FIX_SYSV_SIGNAL(sig, sm_tick); 339 errno = save_errno; 340 CHECK_CRITICAL(sig); 341 342 mypid = getpid(); 343 while (PendingSignal != 0) 344 { 345 int sigbit = 0; 346 int sig = 0; 347 348 if (bitset(PEND_SIGHUP, PendingSignal)) 349 { 350 sigbit = PEND_SIGHUP; 351 sig = SIGHUP; 352 } 353 else if (bitset(PEND_SIGINT, PendingSignal)) 354 { 355 sigbit = PEND_SIGINT; 356 sig = SIGINT; 357 } 358 else if (bitset(PEND_SIGTERM, PendingSignal)) 359 { 360 sigbit = PEND_SIGTERM; 361 sig = SIGTERM; 362 } 363 else if (bitset(PEND_SIGUSR1, PendingSignal)) 364 { 365 sigbit = PEND_SIGUSR1; 366 sig = SIGUSR1; 367 } 368 else 369 { 370 /* If we get here, we are in trouble */ 371 abort(); 372 } 373 PendingSignal &= ~sigbit; 374 kill(mypid, sig); 375 } 376 377#if SM_CONF_SETITIMER 378 gettimeofday(&now, NULL); 379#else /* SM_CONF_SETITIMER */ 380 now = time(NULL); 381#endif /* SM_CONF_SETITIMER */ 382 while ((ev = SmEventQueue) != NULL && 383 (ev->ev_pid != mypid || 384#if SM_CONF_SETITIMER 385 timercmp(&ev->ev_time, &now, <=) 386#else /* SM_CONF_SETITIMER */ 387 ev->ev_time <= now 388#endif /* SM_CONF_SETITIMER */ 389 )) 390 { 391 void (*f)(); 392 int arg; 393 pid_t pid; 394 395 /* process the event on the top of the queue */ 396 ev = SmEventQueue; 397 SmEventQueue = SmEventQueue->ev_link; 398 399 /* we must be careful in here because ev_func may not return */ 400 f = ev->ev_func; 401 arg = ev->ev_arg; 402 pid = ev->ev_pid; 403 ENTER_CRITICAL(); 404 ev->ev_link = SmFreeEventList; 405 SmFreeEventList = ev; 406 LEAVE_CRITICAL(); 407 if (pid != getpid()) 408 continue; 409 if (SmEventQueue != NULL) 410 { 411#if SM_CONF_SETITIMER 412 if (timercmp(&SmEventQueue->ev_time, &now, >)) 413 { 414 timersub(&SmEventQueue->ev_time, &now, 415 &clr.it_value); 416 clr.it_interval.tv_sec = 0; 417 clr.it_interval.tv_usec = 0; 418 if (clr.it_value.tv_sec < 0) 419 clr.it_value.tv_sec = 0; 420 if (clr.it_value.tv_sec == 0 && 421 clr.it_value.tv_usec == 0) 422 clr.it_value.tv_usec = 1000; 423 (void) setitimer(ITIMER_REAL, &clr, NULL); 424 } 425 else 426 { 427 clr.it_interval.tv_sec = 0; 428 clr.it_interval.tv_usec = 0; 429 clr.it_value.tv_sec = 3; 430 clr.it_value.tv_usec = 0; 431 (void) setitimer(ITIMER_REAL, &clr, NULL); 432 } 433#else /* SM_CONF_SETITIMER */ 434 if (SmEventQueue->ev_time > now) 435 (void) alarm((unsigned) (SmEventQueue->ev_time 436 - now)); 437 else 438 (void) alarm(3); 439#endif /* SM_CONF_SETITIMER */ 440 } 441 442 /* call ev_func */ 443 errno = save_errno; 444 (*f)(arg); 445#if SM_CONF_SETITIMER 446 clr.it_interval.tv_sec = 0; 447 clr.it_interval.tv_usec = 0; 448 clr.it_value.tv_sec = 0; 449 clr.it_value.tv_usec = 0; 450 (void) setitimer(ITIMER_REAL, &clr, NULL); 451 gettimeofday(&now, NULL); 452#else /* SM_CONF_SETITIMER */ 453 (void) alarm(0); 454 now = time(NULL); 455#endif /* SM_CONF_SETITIMER */ 456 } 457 if (SmEventQueue != NULL) 458 { 459#if SM_CONF_SETITIMER 460 timersub(&SmEventQueue->ev_time, &now, &clr.it_value); 461 clr.it_interval.tv_sec = 0; 462 clr.it_interval.tv_usec = 0; 463 if (clr.it_value.tv_sec < 0) 464 clr.it_value.tv_sec = 0; 465 if (clr.it_value.tv_sec == 0 && clr.it_value.tv_usec == 0) 466 clr.it_value.tv_usec = 1000; 467 (void) setitimer(ITIMER_REAL, &clr, NULL); 468#else /* SM_CONF_SETITIMER */ 469 (void) alarm((unsigned) (SmEventQueue->ev_time - now)); 470#endif /* SM_CONF_SETITIMER */ 471 } 472 errno = save_errno; 473 return SIGFUNC_RETURN; 474} 475/* 476** SLEEP -- a version of sleep that works with this stuff 477** 478** Because Unix sleep uses the alarm facility, I must reimplement 479** it here. 480** 481** Parameters: 482** intvl -- time to sleep. 483** 484** Returns: 485** zero. 486** 487** Side Effects: 488** waits for intvl time. However, other events can 489** be run during that interval. 490*/ 491 492 493static bool volatile SmSleepDone; 494 495#ifndef SLEEP_T 496# define SLEEP_T unsigned int 497#endif /* ! SLEEP_T */ 498 499SLEEP_T 500sleep(intvl) 501 unsigned int intvl; 502{ 503 int was_held; 504 505 if (intvl == 0) 506 return (SLEEP_T) 0; 507 SmSleepDone = false; 508 (void) sm_setevent((time_t) intvl, sm_endsleep, 0); 509 was_held = sm_releasesignal(SIGALRM); 510 while (!SmSleepDone) 511 (void) pause(); 512 if (was_held > 0) 513 (void) sm_blocksignal(SIGALRM); 514 return (SLEEP_T) 0; 515} 516 517static void 518sm_endsleep() 519{ 520 /* 521 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 522 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 523 ** DOING. 524 */ 525 526 SmSleepDone = true; 527} 528 529