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