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