clock.c revision 182352
1/* 2 * Copyright (c) 1998-2004 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.47 2005/06/14 23:07:20 ca Exp $") 16#include <unistd.h> 17#include <time.h> 18#include <errno.h> 19#if SM_CONF_SETITIMER 20# include <sm/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#if _FFR_SLEEP_USE_SELECT > 0 28# include <sys/types.h> 29#endif /* _FFR_SLEEP_USE_SELECT > 0 */ 30#if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 31# include <syslog.h> 32#endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */ 33 34#ifndef sigmask 35# define sigmask(s) (1 << ((s) - 1)) 36#endif /* ! sigmask */ 37 38 39/* 40** SM_SETEVENTM -- set an event to happen at a specific time in milliseconds. 41** 42** Events are stored in a sorted list for fast processing. 43** An event only applies to the process that set it. 44** Source is #ifdef'd to work with older OS's that don't have setitimer() 45** (that is, don't have a timer granularity less than 1 second). 46** 47** Parameters: 48** intvl -- interval until next event occurs (milliseconds). 49** func -- function to call on event. 50** arg -- argument to func on event. 51** 52** Returns: 53** On success returns the SM_EVENT entry created. 54** On failure returns NULL. 55** 56** Side Effects: 57** none. 58*/ 59 60static SM_EVENT *volatile SmEventQueue; /* head of event queue */ 61static SM_EVENT *volatile SmFreeEventList; /* list of free events */ 62 63SM_EVENT * 64sm_seteventm(intvl, func, arg) 65 int intvl; 66 void (*func)__P((int)); 67 int arg; 68{ 69 ENTER_CRITICAL(); 70 if (SmFreeEventList == NULL) 71 { 72 SmFreeEventList = (SM_EVENT *) sm_pmalloc_x(sizeof *SmFreeEventList); 73 SmFreeEventList->ev_link = NULL; 74 } 75 LEAVE_CRITICAL(); 76 77 return sm_sigsafe_seteventm(intvl, func, arg); 78} 79 80/* 81** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 82** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 83** DOING. 84*/ 85 86SM_EVENT * 87sm_sigsafe_seteventm(intvl, func, arg) 88 int intvl; 89 void (*func)__P((int)); 90 int arg; 91{ 92 register SM_EVENT **evp; 93 register SM_EVENT *ev; 94#if SM_CONF_SETITIMER 95 auto struct timeval now, nowi, ival; 96 auto struct itimerval itime; 97#else /* SM_CONF_SETITIMER */ 98 auto time_t now, nowi; 99#endif /* SM_CONF_SETITIMER */ 100 int wasblocked; 101 102 /* negative times are not allowed */ 103 if (intvl <= 0) 104 return NULL; 105 106 wasblocked = sm_blocksignal(SIGALRM); 107#if SM_CONF_SETITIMER 108 ival.tv_sec = intvl / 1000; 109 ival.tv_usec = (intvl - ival.tv_sec * 1000) * 10; 110 (void) gettimeofday(&now, NULL); 111 nowi = now; 112 timeradd(&now, &ival, &nowi); 113#else /* SM_CONF_SETITIMER */ 114 now = time(NULL); 115 nowi = now + (time_t)(intvl / 1000); 116#endif /* SM_CONF_SETITIMER */ 117 118 /* search event queue for correct position */ 119 for (evp = (SM_EVENT **) (&SmEventQueue); 120 (ev = *evp) != NULL; 121 evp = &ev->ev_link) 122 { 123#if SM_CONF_SETITIMER 124 if (timercmp(&(ev->ev_time), &nowi, >=)) 125#else /* SM_CONF_SETITIMER */ 126 if (ev->ev_time >= nowi) 127#endif /* SM_CONF_SETITIMER */ 128 break; 129 } 130 131 ENTER_CRITICAL(); 132 if (SmFreeEventList == NULL) 133 { 134 /* 135 ** This shouldn't happen. If called from sm_seteventm(), 136 ** we have just malloced a SmFreeEventList entry. If 137 ** called from a signal handler, it should have been 138 ** from an existing event which sm_tick() just added to 139 ** SmFreeEventList. 140 */ 141 142 LEAVE_CRITICAL(); 143 if (wasblocked == 0) 144 (void) sm_releasesignal(SIGALRM); 145 return NULL; 146 } 147 else 148 { 149 ev = SmFreeEventList; 150 SmFreeEventList = ev->ev_link; 151 } 152 LEAVE_CRITICAL(); 153 154 /* insert new event */ 155 ev->ev_time = nowi; 156 ev->ev_func = func; 157 ev->ev_arg = arg; 158 ev->ev_pid = getpid(); 159 ENTER_CRITICAL(); 160 ev->ev_link = *evp; 161 *evp = ev; 162 LEAVE_CRITICAL(); 163 164 (void) sm_signal(SIGALRM, sm_tick); 165# if SM_CONF_SETITIMER 166 timersub(&SmEventQueue->ev_time, &now, &itime.it_value); 167 itime.it_interval.tv_sec = 0; 168 itime.it_interval.tv_usec = 0; 169 if (itime.it_value.tv_sec < 0) 170 itime.it_value.tv_sec = 0; 171 if (itime.it_value.tv_sec == 0 && itime.it_value.tv_usec == 0) 172 itime.it_value.tv_usec = 1000; 173 (void) setitimer(ITIMER_REAL, &itime, NULL); 174# else /* SM_CONF_SETITIMER */ 175 intvl = SmEventQueue->ev_time - now; 176 (void) alarm((unsigned) (intvl < 1 ? 1 : intvl)); 177# endif /* SM_CONF_SETITIMER */ 178 if (wasblocked == 0) 179 (void) sm_releasesignal(SIGALRM); 180 return ev; 181} 182/* 183** SM_CLREVENT -- remove an event from the event queue. 184** 185** Parameters: 186** ev -- pointer to event to remove. 187** 188** Returns: 189** none. 190** 191** Side Effects: 192** arranges for event ev to not happen. 193*/ 194 195void 196sm_clrevent(ev) 197 register SM_EVENT *ev; 198{ 199 register SM_EVENT **evp; 200 int wasblocked; 201# if SM_CONF_SETITIMER 202 struct itimerval clr; 203# endif /* SM_CONF_SETITIMER */ 204 205 if (ev == NULL) 206 return; 207 208 /* find the parent event */ 209 wasblocked = sm_blocksignal(SIGALRM); 210 for (evp = (SM_EVENT **) (&SmEventQueue); 211 *evp != NULL; 212 evp = &(*evp)->ev_link) 213 { 214 if (*evp == ev) 215 break; 216 } 217 218 /* now remove it */ 219 if (*evp != NULL) 220 { 221 ENTER_CRITICAL(); 222 *evp = ev->ev_link; 223 ev->ev_link = SmFreeEventList; 224 SmFreeEventList = ev; 225 LEAVE_CRITICAL(); 226 } 227 228 /* restore clocks and pick up anything spare */ 229 if (wasblocked == 0) 230 (void) sm_releasesignal(SIGALRM); 231 if (SmEventQueue != NULL) 232 (void) kill(getpid(), SIGALRM); 233 else 234 { 235 /* nothing left in event queue, no need for an alarm */ 236# if SM_CONF_SETITIMER 237 clr.it_interval.tv_sec = 0; 238 clr.it_interval.tv_usec = 0; 239 clr.it_value.tv_sec = 0; 240 clr.it_value.tv_usec = 0; 241 (void) setitimer(ITIMER_REAL, &clr, NULL); 242# else /* SM_CONF_SETITIMER */ 243 (void) alarm(0); 244# endif /* SM_CONF_SETITIMER */ 245 } 246} 247/* 248** SM_CLEAR_EVENTS -- remove all events from the event queue. 249** 250** Parameters: 251** none. 252** 253** Returns: 254** none. 255*/ 256 257void 258sm_clear_events() 259{ 260 register SM_EVENT *ev; 261#if SM_CONF_SETITIMER 262 struct itimerval clr; 263#endif /* SM_CONF_SETITIMER */ 264 int wasblocked; 265 266 /* nothing will be left in event queue, no need for an alarm */ 267#if SM_CONF_SETITIMER 268 clr.it_interval.tv_sec = 0; 269 clr.it_interval.tv_usec = 0; 270 clr.it_value.tv_sec = 0; 271 clr.it_value.tv_usec = 0; 272 (void) setitimer(ITIMER_REAL, &clr, NULL); 273#else /* SM_CONF_SETITIMER */ 274 (void) alarm(0); 275#endif /* SM_CONF_SETITIMER */ 276 277 if (SmEventQueue == NULL) 278 return; 279 280 wasblocked = sm_blocksignal(SIGALRM); 281 282 /* find the end of the EventQueue */ 283 for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link) 284 continue; 285 286 ENTER_CRITICAL(); 287 ev->ev_link = SmFreeEventList; 288 SmFreeEventList = SmEventQueue; 289 SmEventQueue = NULL; 290 LEAVE_CRITICAL(); 291 292 /* restore clocks and pick up anything spare */ 293 if (wasblocked == 0) 294 (void) sm_releasesignal(SIGALRM); 295} 296/* 297** SM_TICK -- take a clock tick 298** 299** Called by the alarm clock. This routine runs events as needed. 300** Always called as a signal handler, so we assume that SIGALRM 301** has been blocked. 302** 303** Parameters: 304** One that is ignored; for compatibility with signal handlers. 305** 306** Returns: 307** none. 308** 309** Side Effects: 310** calls the next function in EventQueue. 311** 312** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 313** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 314** DOING. 315*/ 316 317/* ARGSUSED */ 318SIGFUNC_DECL 319sm_tick(sig) 320 int sig; 321{ 322 register SM_EVENT *ev; 323 pid_t mypid; 324 int save_errno = errno; 325#if SM_CONF_SETITIMER 326 struct itimerval clr; 327 struct timeval now; 328#else /* SM_CONF_SETITIMER */ 329 register time_t now; 330#endif /* SM_CONF_SETITIMER */ 331 332#if SM_CONF_SETITIMER 333 clr.it_interval.tv_sec = 0; 334 clr.it_interval.tv_usec = 0; 335 clr.it_value.tv_sec = 0; 336 clr.it_value.tv_usec = 0; 337 (void) setitimer(ITIMER_REAL, &clr, NULL); 338 gettimeofday(&now, NULL); 339#else /* SM_CONF_SETITIMER */ 340 (void) alarm(0); 341 now = time(NULL); 342#endif /* SM_CONF_SETITIMER */ 343 344 FIX_SYSV_SIGNAL(sig, sm_tick); 345 errno = save_errno; 346 CHECK_CRITICAL(sig); 347 348 mypid = getpid(); 349 while (PendingSignal != 0) 350 { 351 int sigbit = 0; 352 int sig = 0; 353 354 if (bitset(PEND_SIGHUP, PendingSignal)) 355 { 356 sigbit = PEND_SIGHUP; 357 sig = SIGHUP; 358 } 359 else if (bitset(PEND_SIGINT, PendingSignal)) 360 { 361 sigbit = PEND_SIGINT; 362 sig = SIGINT; 363 } 364 else if (bitset(PEND_SIGTERM, PendingSignal)) 365 { 366 sigbit = PEND_SIGTERM; 367 sig = SIGTERM; 368 } 369 else if (bitset(PEND_SIGUSR1, PendingSignal)) 370 { 371 sigbit = PEND_SIGUSR1; 372 sig = SIGUSR1; 373 } 374 else 375 { 376 /* If we get here, we are in trouble */ 377 abort(); 378 } 379 PendingSignal &= ~sigbit; 380 kill(mypid, sig); 381 } 382 383#if SM_CONF_SETITIMER 384 gettimeofday(&now, NULL); 385#else /* SM_CONF_SETITIMER */ 386 now = time(NULL); 387#endif /* SM_CONF_SETITIMER */ 388 while ((ev = SmEventQueue) != NULL && 389 (ev->ev_pid != mypid || 390#if SM_CONF_SETITIMER 391 timercmp(&ev->ev_time, &now, <=) 392#else /* SM_CONF_SETITIMER */ 393 ev->ev_time <= now 394#endif /* SM_CONF_SETITIMER */ 395 )) 396 { 397 void (*f)__P((int)); 398 int arg; 399 pid_t pid; 400 401 /* process the event on the top of the queue */ 402 ev = SmEventQueue; 403 SmEventQueue = SmEventQueue->ev_link; 404 405 /* we must be careful in here because ev_func may not return */ 406 f = ev->ev_func; 407 arg = ev->ev_arg; 408 pid = ev->ev_pid; 409 ENTER_CRITICAL(); 410 ev->ev_link = SmFreeEventList; 411 SmFreeEventList = ev; 412 LEAVE_CRITICAL(); 413 if (pid != getpid()) 414 continue; 415 if (SmEventQueue != NULL) 416 { 417#if SM_CONF_SETITIMER 418 if (timercmp(&SmEventQueue->ev_time, &now, >)) 419 { 420 timersub(&SmEventQueue->ev_time, &now, 421 &clr.it_value); 422 clr.it_interval.tv_sec = 0; 423 clr.it_interval.tv_usec = 0; 424 if (clr.it_value.tv_sec < 0) 425 clr.it_value.tv_sec = 0; 426 if (clr.it_value.tv_sec == 0 && 427 clr.it_value.tv_usec == 0) 428 clr.it_value.tv_usec = 1000; 429 (void) setitimer(ITIMER_REAL, &clr, NULL); 430 } 431 else 432 { 433 clr.it_interval.tv_sec = 0; 434 clr.it_interval.tv_usec = 0; 435 clr.it_value.tv_sec = 3; 436 clr.it_value.tv_usec = 0; 437 (void) setitimer(ITIMER_REAL, &clr, NULL); 438 } 439#else /* SM_CONF_SETITIMER */ 440 if (SmEventQueue->ev_time > now) 441 (void) alarm((unsigned) (SmEventQueue->ev_time 442 - now)); 443 else 444 (void) alarm(3); 445#endif /* SM_CONF_SETITIMER */ 446 } 447 448 /* call ev_func */ 449 errno = save_errno; 450 (*f)(arg); 451#if SM_CONF_SETITIMER 452 clr.it_interval.tv_sec = 0; 453 clr.it_interval.tv_usec = 0; 454 clr.it_value.tv_sec = 0; 455 clr.it_value.tv_usec = 0; 456 (void) setitimer(ITIMER_REAL, &clr, NULL); 457 gettimeofday(&now, NULL); 458#else /* SM_CONF_SETITIMER */ 459 (void) alarm(0); 460 now = time(NULL); 461#endif /* SM_CONF_SETITIMER */ 462 } 463 if (SmEventQueue != NULL) 464 { 465#if SM_CONF_SETITIMER 466 timersub(&SmEventQueue->ev_time, &now, &clr.it_value); 467 clr.it_interval.tv_sec = 0; 468 clr.it_interval.tv_usec = 0; 469 if (clr.it_value.tv_sec < 0) 470 clr.it_value.tv_sec = 0; 471 if (clr.it_value.tv_sec == 0 && clr.it_value.tv_usec == 0) 472 clr.it_value.tv_usec = 1000; 473 (void) setitimer(ITIMER_REAL, &clr, NULL); 474#else /* SM_CONF_SETITIMER */ 475 (void) alarm((unsigned) (SmEventQueue->ev_time - now)); 476#endif /* SM_CONF_SETITIMER */ 477 } 478 errno = save_errno; 479 return SIGFUNC_RETURN; 480} 481/* 482** SLEEP -- a version of sleep that works with this stuff 483** 484** Because Unix sleep uses the alarm facility, I must reimplement 485** it here. 486** 487** Parameters: 488** intvl -- time to sleep. 489** 490** Returns: 491** zero. 492** 493** Side Effects: 494** waits for intvl time. However, other events can 495** be run during that interval. 496*/ 497 498 499# if !HAVE_NANOSLEEP 500static void sm_endsleep __P((int)); 501static bool volatile SmSleepDone; 502# endif /* !HAVE_NANOSLEEP */ 503 504#ifndef SLEEP_T 505# define SLEEP_T unsigned int 506#endif /* ! SLEEP_T */ 507 508SLEEP_T 509sleep(intvl) 510 unsigned int intvl; 511{ 512#if HAVE_NANOSLEEP 513 struct timespec rqtp; 514 515 if (intvl == 0) 516 return (SLEEP_T) 0; 517 rqtp.tv_sec = intvl; 518 rqtp.tv_nsec = 0; 519 nanosleep(&rqtp, NULL); 520 return (SLEEP_T) 0; 521#else /* HAVE_NANOSLEEP */ 522 int was_held; 523 SM_EVENT *ev; 524#if _FFR_SLEEP_USE_SELECT > 0 525 int r; 526# if _FFR_SLEEP_USE_SELECT > 0 527 struct timeval sm_io_to; 528# endif /* _FFR_SLEEP_USE_SELECT > 0 */ 529#endif /* _FFR_SLEEP_USE_SELECT > 0 */ 530#if SM_CONF_SETITIMER 531 struct timeval now, begin, diff; 532# if _FFR_SLEEP_USE_SELECT > 0 533 struct timeval slpv; 534# endif /* _FFR_SLEEP_USE_SELECT > 0 */ 535#else /* SM_CONF_SETITIMER */ 536 time_t begin, now; 537#endif /* SM_CONF_SETITIMER */ 538 539 if (intvl == 0) 540 return (SLEEP_T) 0; 541#if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 542 if (intvl > _FFR_MAX_SLEEP_TIME) 543 { 544 syslog(LOG_ERR, "sleep: interval=%u exceeds max value %d", 545 intvl, _FFR_MAX_SLEEP_TIME); 546# if 0 547 SM_ASSERT(intvl < (unsigned int) INT_MAX); 548# endif /* 0 */ 549 intvl = _FFR_MAX_SLEEP_TIME; 550 } 551#endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */ 552 SmSleepDone = false; 553 554#if SM_CONF_SETITIMER 555# if _FFR_SLEEP_USE_SELECT > 0 556 slpv.tv_sec = intvl; 557 slpv.tv_usec = 0; 558# endif /* _FFR_SLEEP_USE_SELECT > 0 */ 559 (void) gettimeofday(&now, NULL); 560 begin = now; 561#else /* SM_CONF_SETITIMER */ 562 now = begin = time(NULL); 563#endif /* SM_CONF_SETITIMER */ 564 565 ev = sm_setevent((time_t) intvl, sm_endsleep, 0); 566 if (ev == NULL) 567 { 568 /* COMPLAIN */ 569#if 0 570 syslog(LOG_ERR, "sleep: sm_setevent(%u) failed", intvl); 571#endif /* 0 */ 572 SmSleepDone = true; 573 } 574 was_held = sm_releasesignal(SIGALRM); 575 576 while (!SmSleepDone) 577 { 578#if SM_CONF_SETITIMER 579 (void) gettimeofday(&now, NULL); 580 timersub(&now, &begin, &diff); 581 if (diff.tv_sec < 0 || 582 (diff.tv_sec == 0 && diff.tv_usec == 0)) 583 break; 584# if _FFR_SLEEP_USE_SELECT > 0 585 timersub(&slpv, &diff, &sm_io_to); 586# endif /* _FFR_SLEEP_USE_SELECT > 0 */ 587#else /* SM_CONF_SETITIMER */ 588 now = time(NULL); 589 590 /* 591 ** Check whether time expired before signal is released. 592 ** Due to the granularity of time() add 1 to be on the 593 ** safe side. 594 */ 595 596 if (!(begin + (time_t) intvl + 1 > now)) 597 break; 598# if _FFR_SLEEP_USE_SELECT > 0 599 sm_io_to.tv_sec = intvl - (now - begin); 600 if (sm_io_to.tv_sec <= 0) 601 sm_io_to.tv_sec = 1; 602 sm_io_to.tv_usec = 0; 603# endif /* _FFR_SLEEP_USE_SELECT > 0 */ 604#endif /* SM_CONF_SETITIMER */ 605#if _FFR_SLEEP_USE_SELECT > 0 606 if (intvl <= _FFR_SLEEP_USE_SELECT) 607 { 608 r = select(0, NULL, NULL, NULL, &sm_io_to); 609 if (r == 0) 610 break; 611 } 612 else 613#endif /* _FFR_SLEEP_USE_SELECT > 0 */ 614 (void) pause(); 615 } 616 617 /* if out of the loop without the event being triggered remove it */ 618 if (!SmSleepDone) 619 sm_clrevent(ev); 620 if (was_held > 0) 621 (void) sm_blocksignal(SIGALRM); 622 return (SLEEP_T) 0; 623#endif /* HAVE_NANOSLEEP */ 624} 625 626#if !HAVE_NANOSLEEP 627static void 628sm_endsleep(ignore) 629 int ignore; 630{ 631 /* 632 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 633 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 634 ** DOING. 635 */ 636 637 SmSleepDone = true; 638} 639#endif /* !HAVE_NANOSLEEP */ 640 641