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> 17SM_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 62static SM_EVENT *volatile SmEventQueue; /* head of event queue */ 63static SM_EVENT *volatile SmFreeEventList; /* list of free events */ 64 65SM_EVENT * 66sm_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 88SM_EVENT * 89sm_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 197void 198sm_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 259void 260sm_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 */ 320SIGFUNC_DECL 321sm_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 502static void sm_endsleep __P((int)); 503static bool volatile SmSleepDone; 504# endif /* !HAVE_NANOSLEEP */ 505 506#ifndef SLEEP_T 507# define SLEEP_T unsigned int 508#endif /* ! SLEEP_T */ 509 510SLEEP_T 511sleep(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 629static void 630sm_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