clock.c revision 95154
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> 15SM_RCSID("@(#)$Id: clock.c,v 1.1.1.2 2002/04/10 03:04:55 gshapiro 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 if (SmEventQueue == NULL) 261 return; 262 263 /* nothing will be left in event queue, no need for an alarm */ 264#if SM_CONF_SETITIMER 265 clr.it_interval.tv_sec = 0; 266 clr.it_interval.tv_usec = 0; 267 clr.it_value.tv_sec = 0; 268 clr.it_value.tv_usec = 0; 269 (void) setitimer(ITIMER_REAL, &clr, NULL); 270#else /* SM_CONF_SETITIMER */ 271 (void) alarm(0); 272#endif /* SM_CONF_SETITIMER */ 273 wasblocked = sm_blocksignal(SIGALRM); 274 275 /* find the end of the EventQueue */ 276 for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link) 277 continue; 278 279 ENTER_CRITICAL(); 280 ev->ev_link = SmFreeEventList; 281 SmFreeEventList = SmEventQueue; 282 SmEventQueue = NULL; 283 LEAVE_CRITICAL(); 284 285 /* restore clocks and pick up anything spare */ 286 if (wasblocked == 0) 287 (void) sm_releasesignal(SIGALRM); 288} 289/* 290** SM_TICK -- take a clock tick 291** 292** Called by the alarm clock. This routine runs events as needed. 293** Always called as a signal handler, so we assume that SIGALRM 294** has been blocked. 295** 296** Parameters: 297** One that is ignored; for compatibility with signal handlers. 298** 299** Returns: 300** none. 301** 302** Side Effects: 303** calls the next function in EventQueue. 304** 305** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 306** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 307** DOING. 308*/ 309 310/* ARGSUSED */ 311SIGFUNC_DECL 312sm_tick(sig) 313 int sig; 314{ 315 register SM_EVENT *ev; 316 pid_t mypid; 317 int save_errno = errno; 318#if SM_CONF_SETITIMER 319 struct itimerval clr; 320 struct timeval now; 321#else /* SM_CONF_SETITIMER */ 322 register time_t now; 323#endif /* SM_CONF_SETITIMER */ 324 325#if SM_CONF_SETITIMER 326 clr.it_interval.tv_sec = 0; 327 clr.it_interval.tv_usec = 0; 328 clr.it_value.tv_sec = 0; 329 clr.it_value.tv_usec = 0; 330 (void) setitimer(ITIMER_REAL, &clr, NULL); 331 gettimeofday(&now, NULL); 332#else /* SM_CONF_SETITIMER */ 333 (void) alarm(0); 334 now = time(NULL); 335#endif /* SM_CONF_SETITIMER */ 336 337 FIX_SYSV_SIGNAL(sig, sm_tick); 338 errno = save_errno; 339 CHECK_CRITICAL(sig); 340 341 mypid = getpid(); 342 while (PendingSignal != 0) 343 { 344 int sigbit = 0; 345 int sig = 0; 346 347 if (bitset(PEND_SIGHUP, PendingSignal)) 348 { 349 sigbit = PEND_SIGHUP; 350 sig = SIGHUP; 351 } 352 else if (bitset(PEND_SIGINT, PendingSignal)) 353 { 354 sigbit = PEND_SIGINT; 355 sig = SIGINT; 356 } 357 else if (bitset(PEND_SIGTERM, PendingSignal)) 358 { 359 sigbit = PEND_SIGTERM; 360 sig = SIGTERM; 361 } 362 else if (bitset(PEND_SIGUSR1, PendingSignal)) 363 { 364 sigbit = PEND_SIGUSR1; 365 sig = SIGUSR1; 366 } 367 else 368 { 369 /* If we get here, we are in trouble */ 370 abort(); 371 } 372 PendingSignal &= ~sigbit; 373 kill(mypid, sig); 374 } 375 376#if SM_CONF_SETITIMER 377 gettimeofday(&now, NULL); 378#else /* SM_CONF_SETITIMER */ 379 now = time(NULL); 380#endif /* SM_CONF_SETITIMER */ 381 while ((ev = SmEventQueue) != NULL && 382 (ev->ev_pid != mypid || 383#if SM_CONF_SETITIMER 384 timercmp(&ev->ev_time, &now, <=) 385#else /* SM_CONF_SETITIMER */ 386 ev->ev_time <= now 387#endif /* SM_CONF_SETITIMER */ 388 )) 389 { 390 void (*f)(); 391 int arg; 392 pid_t pid; 393 394 /* process the event on the top of the queue */ 395 ev = SmEventQueue; 396 SmEventQueue = SmEventQueue->ev_link; 397 398 /* we must be careful in here because ev_func may not return */ 399 f = ev->ev_func; 400 arg = ev->ev_arg; 401 pid = ev->ev_pid; 402 ENTER_CRITICAL(); 403 ev->ev_link = SmFreeEventList; 404 SmFreeEventList = ev; 405 LEAVE_CRITICAL(); 406 if (pid != getpid()) 407 continue; 408 if (SmEventQueue != NULL) 409 { 410#if SM_CONF_SETITIMER 411 if (timercmp(&SmEventQueue->ev_time, &now, >)) 412 { 413 timersub(&SmEventQueue->ev_time, &now, 414 &clr.it_value); 415 clr.it_interval.tv_sec = 0; 416 clr.it_interval.tv_usec = 0; 417 if (clr.it_value.tv_sec < 0) 418 clr.it_value.tv_sec = 0; 419 if (clr.it_value.tv_sec == 0 && 420 clr.it_value.tv_usec == 0) 421 clr.it_value.tv_usec = 1000; 422 (void) setitimer(ITIMER_REAL, &clr, NULL); 423 } 424 else 425 { 426 clr.it_interval.tv_sec = 0; 427 clr.it_interval.tv_usec = 0; 428 clr.it_value.tv_sec = 3; 429 clr.it_value.tv_usec = 0; 430 (void) setitimer(ITIMER_REAL, &clr, NULL); 431 } 432#else /* SM_CONF_SETITIMER */ 433 if (SmEventQueue->ev_time > now) 434 (void) alarm((unsigned) (SmEventQueue->ev_time 435 - now)); 436 else 437 (void) alarm(3); 438#endif /* SM_CONF_SETITIMER */ 439 } 440 441 /* call ev_func */ 442 errno = save_errno; 443 (*f)(arg); 444#if SM_CONF_SETITIMER 445 clr.it_interval.tv_sec = 0; 446 clr.it_interval.tv_usec = 0; 447 clr.it_value.tv_sec = 0; 448 clr.it_value.tv_usec = 0; 449 (void) setitimer(ITIMER_REAL, &clr, NULL); 450 gettimeofday(&now, NULL); 451#else /* SM_CONF_SETITIMER */ 452 (void) alarm(0); 453 now = time(NULL); 454#endif /* SM_CONF_SETITIMER */ 455 } 456 if (SmEventQueue != NULL) 457 { 458#if SM_CONF_SETITIMER 459 timersub(&SmEventQueue->ev_time, &now, &clr.it_value); 460 clr.it_interval.tv_sec = 0; 461 clr.it_interval.tv_usec = 0; 462 if (clr.it_value.tv_sec < 0) 463 clr.it_value.tv_sec = 0; 464 if (clr.it_value.tv_sec == 0 && clr.it_value.tv_usec == 0) 465 clr.it_value.tv_usec = 1000; 466 (void) setitimer(ITIMER_REAL, &clr, NULL); 467#else /* SM_CONF_SETITIMER */ 468 (void) alarm((unsigned) (SmEventQueue->ev_time - now)); 469#endif /* SM_CONF_SETITIMER */ 470 } 471 errno = save_errno; 472 return SIGFUNC_RETURN; 473} 474/* 475** SLEEP -- a version of sleep that works with this stuff 476** 477** Because Unix sleep uses the alarm facility, I must reimplement 478** it here. 479** 480** Parameters: 481** intvl -- time to sleep. 482** 483** Returns: 484** zero. 485** 486** Side Effects: 487** waits for intvl time. However, other events can 488** be run during that interval. 489*/ 490 491 492static bool volatile SmSleepDone; 493 494#ifndef SLEEP_T 495# define SLEEP_T unsigned int 496#endif /* ! SLEEP_T */ 497 498SLEEP_T 499sleep(intvl) 500 unsigned int intvl; 501{ 502 int was_held; 503 504 if (intvl == 0) 505 return (SLEEP_T) 0; 506 SmSleepDone = false; 507 (void) sm_setevent((time_t) intvl, sm_endsleep, 0); 508 was_held = sm_releasesignal(SIGALRM); 509 while (!SmSleepDone) 510 (void) pause(); 511 if (was_held > 0) 512 (void) sm_blocksignal(SIGALRM); 513 return (SLEEP_T) 0; 514} 515 516static void 517sm_endsleep() 518{ 519 /* 520 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 521 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 522 ** DOING. 523 */ 524 525 SmSleepDone = true; 526} 527 528