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