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> 15 SM_RCSID("@(#)$Id: clock.c,v 1.45 2004/01/19 19:32:08 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 #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 60 static SM_EVENT *volatile SmEventQueue; /* head of event queue */ 61 static SM_EVENT *volatile SmFreeEventList; /* list of free events */ 62 63 SM_EVENT * 64 sm_seteventm(intvl, func, arg) 65 int intvl; 66 void (*func)(); 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 86 SM_EVENT * 87 sm_sigsafe_seteventm(intvl, func, arg) 88 int intvl; 89 void (*func)(); 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 195 void 196 sm_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 257 void 258 sm_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 */ 318 SIGFUNC_DECL 319 sm_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)(); 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 500 static void sm_endsleep __P((void)); 501 static bool volatile SmSleepDone; 502 # endif /* !HAVE_NANOSLEEP */ 503 504 #ifndef SLEEP_T 505 # define SLEEP_T unsigned int 506 #endif /* ! SLEEP_T */ 507 508 SLEEP_T 509 sleep(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 627 static void 628 sm_endsleep() 629 { 630 /* 631 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 632 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 633 ** DOING. 634 */ 635 636 SmSleepDone = true; 637 } 638 #endif /* !HAVE_NANOSLEEP */ 639 640