2 * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
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.
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.
15 SM_RCSID("@(#)$Id: clock.c,v 1.35.2.3 2003/03/03 19:57:40 ca Exp $")
20 # include <sys/time.h>
21 #endif /* SM_CONF_SETITIMER */
24 #include <sm/bitops.h>
29 # define sigmask(s) (1 << ((s) - 1))
30 #endif /* ! sigmask */
32 static void sm_endsleep __P((void));
36 ** SM_SETEVENTM -- set an event to happen at a specific time in milliseconds.
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).
44 ** intvl -- interval until next event occurs (milliseconds).
45 ** func -- function to call on event.
46 ** arg -- argument to func on event.
49 ** On success returns the SM_EVENT entry created.
50 ** On failure returns NULL.
56 static SM_EVENT *volatile SmEventQueue; /* head of event queue */
57 static SM_EVENT *volatile SmFreeEventList; /* list of free events */
60 sm_seteventm(intvl, func, arg)
66 if (SmFreeEventList == NULL)
68 SmFreeEventList = (SM_EVENT *) sm_pmalloc_x(sizeof *SmFreeEventList);
69 SmFreeEventList->ev_link = NULL;
73 return sm_sigsafe_seteventm(intvl, func, arg);
77 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
78 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
83 sm_sigsafe_seteventm(intvl, func, arg)
88 register SM_EVENT **evp;
89 register SM_EVENT *ev;
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 */
98 /* negative times are not allowed */
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);
108 timeradd(&now, &ival, &nowi);
109 #else /* SM_CONF_SETITIMER */
111 nowi = now + (time_t)(intvl / 1000);
112 #endif /* SM_CONF_SETITIMER */
114 /* search event queue for correct position */
115 for (evp = (SM_EVENT **) (&SmEventQueue);
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 */
128 if (SmFreeEventList == NULL)
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
143 ev = SmFreeEventList;
144 SmFreeEventList = ev->ev_link;
148 /* insert new event */
152 ev->ev_pid = getpid();
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 */
173 (void) sm_releasesignal(SIGALRM);
177 ** SM_CLREVENT -- remove an event from the event queue.
180 ** ev -- pointer to event to remove.
186 ** arranges for event ev to not happen.
191 register SM_EVENT *ev;
193 register SM_EVENT **evp;
195 # if SM_CONF_SETITIMER
196 struct itimerval clr;
197 # endif /* SM_CONF_SETITIMER */
202 /* find the parent event */
203 wasblocked = sm_blocksignal(SIGALRM);
204 for (evp = (SM_EVENT **) (&SmEventQueue);
206 evp = &(*evp)->ev_link)
217 ev->ev_link = SmFreeEventList;
218 SmFreeEventList = ev;
222 /* restore clocks and pick up anything spare */
224 (void) sm_releasesignal(SIGALRM);
225 if (SmEventQueue != NULL)
226 (void) kill(getpid(), SIGALRM);
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 */
238 # endif /* SM_CONF_SETITIMER */
242 ** SM_CLEAR_EVENTS -- remove all events from the event queue.
254 register SM_EVENT *ev;
255 #if SM_CONF_SETITIMER
256 struct itimerval clr;
257 #endif /* SM_CONF_SETITIMER */
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 */
269 #endif /* SM_CONF_SETITIMER */
271 if (SmEventQueue == NULL)
274 wasblocked = sm_blocksignal(SIGALRM);
276 /* find the end of the EventQueue */
277 for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link)
281 ev->ev_link = SmFreeEventList;
282 SmFreeEventList = SmEventQueue;
286 /* restore clocks and pick up anything spare */
288 (void) sm_releasesignal(SIGALRM);
291 ** SM_TICK -- take a clock tick
293 ** Called by the alarm clock. This routine runs events as needed.
294 ** Always called as a signal handler, so we assume that SIGALRM
298 ** One that is ignored; for compatibility with signal handlers.
304 ** calls the next function in EventQueue.
306 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
307 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
316 register SM_EVENT *ev;
318 int save_errno = errno;
319 #if SM_CONF_SETITIMER
320 struct itimerval clr;
322 #else /* SM_CONF_SETITIMER */
324 #endif /* SM_CONF_SETITIMER */
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 */
336 #endif /* SM_CONF_SETITIMER */
338 FIX_SYSV_SIGNAL(sig, sm_tick);
343 while (PendingSignal != 0)
348 if (bitset(PEND_SIGHUP, PendingSignal))
350 sigbit = PEND_SIGHUP;
353 else if (bitset(PEND_SIGINT, PendingSignal))
355 sigbit = PEND_SIGINT;
358 else if (bitset(PEND_SIGTERM, PendingSignal))
360 sigbit = PEND_SIGTERM;
363 else if (bitset(PEND_SIGUSR1, PendingSignal))
365 sigbit = PEND_SIGUSR1;
370 /* If we get here, we are in trouble */
373 PendingSignal &= ~sigbit;
377 #if SM_CONF_SETITIMER
378 gettimeofday(&now, NULL);
379 #else /* SM_CONF_SETITIMER */
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 */
388 #endif /* SM_CONF_SETITIMER */
395 /* process the event on the top of the queue */
397 SmEventQueue = SmEventQueue->ev_link;
399 /* we must be careful in here because ev_func may not return */
404 ev->ev_link = SmFreeEventList;
405 SmFreeEventList = ev;
409 if (SmEventQueue != NULL)
411 #if SM_CONF_SETITIMER
412 if (timercmp(&SmEventQueue->ev_time, &now, >))
414 timersub(&SmEventQueue->ev_time, &now,
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);
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);
433 #else /* SM_CONF_SETITIMER */
434 if (SmEventQueue->ev_time > now)
435 (void) alarm((unsigned) (SmEventQueue->ev_time
439 #endif /* SM_CONF_SETITIMER */
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 */
455 #endif /* SM_CONF_SETITIMER */
457 if (SmEventQueue != NULL)
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 */
473 return SIGFUNC_RETURN;
476 ** SLEEP -- a version of sleep that works with this stuff
478 ** Because Unix sleep uses the alarm facility, I must reimplement
482 ** intvl -- time to sleep.
488 ** waits for intvl time. However, other events can
489 ** be run during that interval.
493 static bool volatile SmSleepDone;
496 # define SLEEP_T unsigned int
497 #endif /* ! SLEEP_T */
508 (void) sm_setevent((time_t) intvl, sm_endsleep, 0);
509 was_held = sm_releasesignal(SIGALRM);
513 (void) sm_blocksignal(SIGALRM);
521 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
522 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE