2 * Copyright (c) 1998-2004 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.47 2005/06/14 23:07:20 ca Exp $")
21 #endif /* SM_CONF_SETITIMER */
24 #include <sm/bitops.h>
27 #if _FFR_SLEEP_USE_SELECT > 0
28 # include <sys/types.h>
29 #endif /* _FFR_SLEEP_USE_SELECT > 0 */
30 #if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
32 #endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
35 # define sigmask(s) (1 << ((s) - 1))
36 #endif /* ! sigmask */
40 ** SM_SETEVENTM -- set an event to happen at a specific time in milliseconds.
42 ** Events are stored in a sorted list for fast processing.
43 ** An event only applies to the process that set it.
44 ** Source is #ifdef'd to work with older OS's that don't have setitimer()
45 ** (that is, don't have a timer granularity less than 1 second).
48 ** intvl -- interval until next event occurs (milliseconds).
49 ** func -- function to call on event.
50 ** arg -- argument to func on event.
53 ** On success returns the SM_EVENT entry created.
54 ** On failure returns NULL.
60 static SM_EVENT
*volatile SmEventQueue
; /* head of event queue */
61 static SM_EVENT
*volatile SmFreeEventList
; /* list of free events */
64 sm_seteventm(intvl
, func
, arg
)
66 void (*func
)__P((int));
70 if (SmFreeEventList
== NULL
)
72 SmFreeEventList
= (SM_EVENT
*) sm_pmalloc_x(sizeof *SmFreeEventList
);
73 SmFreeEventList
->ev_link
= NULL
;
77 return sm_sigsafe_seteventm(intvl
, func
, arg
);
81 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
82 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
87 sm_sigsafe_seteventm(intvl
, func
, arg
)
89 void (*func
)__P((int));
92 register SM_EVENT
**evp
;
93 register SM_EVENT
*ev
;
95 auto struct timeval now
, nowi
, ival
;
96 auto struct itimerval itime
;
97 #else /* SM_CONF_SETITIMER */
98 auto time_t now
, nowi
;
99 #endif /* SM_CONF_SETITIMER */
102 /* negative times are not allowed */
106 wasblocked
= sm_blocksignal(SIGALRM
);
107 #if SM_CONF_SETITIMER
108 ival
.tv_sec
= intvl
/ 1000;
109 ival
.tv_usec
= (intvl
- ival
.tv_sec
* 1000) * 10;
110 (void) gettimeofday(&now
, NULL
);
112 timeradd(&now
, &ival
, &nowi
);
113 #else /* SM_CONF_SETITIMER */
115 nowi
= now
+ (time_t)(intvl
/ 1000);
116 #endif /* SM_CONF_SETITIMER */
118 /* search event queue for correct position */
119 for (evp
= (SM_EVENT
**) (&SmEventQueue
);
123 #if SM_CONF_SETITIMER
124 if (timercmp(&(ev
->ev_time
), &nowi
, >=))
125 #else /* SM_CONF_SETITIMER */
126 if (ev
->ev_time
>= nowi
)
127 #endif /* SM_CONF_SETITIMER */
132 if (SmFreeEventList
== NULL
)
135 ** This shouldn't happen. If called from sm_seteventm(),
136 ** we have just malloced a SmFreeEventList entry. If
137 ** called from a signal handler, it should have been
138 ** from an existing event which sm_tick() just added to
144 (void) sm_releasesignal(SIGALRM
);
149 ev
= SmFreeEventList
;
150 SmFreeEventList
= ev
->ev_link
;
154 /* insert new event */
158 ev
->ev_pid
= getpid();
164 (void) sm_signal(SIGALRM
, sm_tick
);
165 # if SM_CONF_SETITIMER
166 timersub(&SmEventQueue
->ev_time
, &now
, &itime
.it_value
);
167 itime
.it_interval
.tv_sec
= 0;
168 itime
.it_interval
.tv_usec
= 0;
169 if (itime
.it_value
.tv_sec
< 0)
170 itime
.it_value
.tv_sec
= 0;
171 if (itime
.it_value
.tv_sec
== 0 && itime
.it_value
.tv_usec
== 0)
172 itime
.it_value
.tv_usec
= 1000;
173 (void) setitimer(ITIMER_REAL
, &itime
, NULL
);
174 # else /* SM_CONF_SETITIMER */
175 intvl
= SmEventQueue
->ev_time
- now
;
176 (void) alarm((unsigned) (intvl
< 1 ? 1 : intvl
));
177 # endif /* SM_CONF_SETITIMER */
179 (void) sm_releasesignal(SIGALRM
);
183 ** SM_CLREVENT -- remove an event from the event queue.
186 ** ev -- pointer to event to remove.
192 ** arranges for event ev to not happen.
197 register SM_EVENT
*ev
;
199 register SM_EVENT
**evp
;
201 # if SM_CONF_SETITIMER
202 struct itimerval clr
;
203 # endif /* SM_CONF_SETITIMER */
208 /* find the parent event */
209 wasblocked
= sm_blocksignal(SIGALRM
);
210 for (evp
= (SM_EVENT
**) (&SmEventQueue
);
212 evp
= &(*evp
)->ev_link
)
223 ev
->ev_link
= SmFreeEventList
;
224 SmFreeEventList
= ev
;
228 /* restore clocks and pick up anything spare */
230 (void) sm_releasesignal(SIGALRM
);
231 if (SmEventQueue
!= NULL
)
232 (void) kill(getpid(), SIGALRM
);
235 /* nothing left in event queue, no need for an alarm */
236 # if SM_CONF_SETITIMER
237 clr
.it_interval
.tv_sec
= 0;
238 clr
.it_interval
.tv_usec
= 0;
239 clr
.it_value
.tv_sec
= 0;
240 clr
.it_value
.tv_usec
= 0;
241 (void) setitimer(ITIMER_REAL
, &clr
, NULL
);
242 # else /* SM_CONF_SETITIMER */
244 # endif /* SM_CONF_SETITIMER */
248 ** SM_CLEAR_EVENTS -- remove all events from the event queue.
260 register SM_EVENT
*ev
;
261 #if SM_CONF_SETITIMER
262 struct itimerval clr
;
263 #endif /* SM_CONF_SETITIMER */
266 /* nothing will be left in event queue, no need for an alarm */
267 #if SM_CONF_SETITIMER
268 clr
.it_interval
.tv_sec
= 0;
269 clr
.it_interval
.tv_usec
= 0;
270 clr
.it_value
.tv_sec
= 0;
271 clr
.it_value
.tv_usec
= 0;
272 (void) setitimer(ITIMER_REAL
, &clr
, NULL
);
273 #else /* SM_CONF_SETITIMER */
275 #endif /* SM_CONF_SETITIMER */
277 if (SmEventQueue
== NULL
)
280 wasblocked
= sm_blocksignal(SIGALRM
);
282 /* find the end of the EventQueue */
283 for (ev
= SmEventQueue
; ev
->ev_link
!= NULL
; ev
= ev
->ev_link
)
287 ev
->ev_link
= SmFreeEventList
;
288 SmFreeEventList
= SmEventQueue
;
292 /* restore clocks and pick up anything spare */
294 (void) sm_releasesignal(SIGALRM
);
297 ** SM_TICK -- take a clock tick
299 ** Called by the alarm clock. This routine runs events as needed.
300 ** Always called as a signal handler, so we assume that SIGALRM
304 ** One that is ignored; for compatibility with signal handlers.
310 ** calls the next function in EventQueue.
312 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
313 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
322 register SM_EVENT
*ev
;
324 int save_errno
= errno
;
325 #if SM_CONF_SETITIMER
326 struct itimerval clr
;
328 #else /* SM_CONF_SETITIMER */
330 #endif /* SM_CONF_SETITIMER */
332 #if SM_CONF_SETITIMER
333 clr
.it_interval
.tv_sec
= 0;
334 clr
.it_interval
.tv_usec
= 0;
335 clr
.it_value
.tv_sec
= 0;
336 clr
.it_value
.tv_usec
= 0;
337 (void) setitimer(ITIMER_REAL
, &clr
, NULL
);
338 gettimeofday(&now
, NULL
);
339 #else /* SM_CONF_SETITIMER */
342 #endif /* SM_CONF_SETITIMER */
344 FIX_SYSV_SIGNAL(sig
, sm_tick
);
349 while (PendingSignal
!= 0)
354 if (bitset(PEND_SIGHUP
, PendingSignal
))
356 sigbit
= PEND_SIGHUP
;
359 else if (bitset(PEND_SIGINT
, PendingSignal
))
361 sigbit
= PEND_SIGINT
;
364 else if (bitset(PEND_SIGTERM
, PendingSignal
))
366 sigbit
= PEND_SIGTERM
;
369 else if (bitset(PEND_SIGUSR1
, PendingSignal
))
371 sigbit
= PEND_SIGUSR1
;
376 /* If we get here, we are in trouble */
379 PendingSignal
&= ~sigbit
;
383 #if SM_CONF_SETITIMER
384 gettimeofday(&now
, NULL
);
385 #else /* SM_CONF_SETITIMER */
387 #endif /* SM_CONF_SETITIMER */
388 while ((ev
= SmEventQueue
) != NULL
&&
389 (ev
->ev_pid
!= mypid
||
390 #if SM_CONF_SETITIMER
391 timercmp(&ev
->ev_time
, &now
, <=)
392 #else /* SM_CONF_SETITIMER */
394 #endif /* SM_CONF_SETITIMER */
401 /* process the event on the top of the queue */
403 SmEventQueue
= SmEventQueue
->ev_link
;
405 /* we must be careful in here because ev_func may not return */
410 ev
->ev_link
= SmFreeEventList
;
411 SmFreeEventList
= ev
;
415 if (SmEventQueue
!= NULL
)
417 #if SM_CONF_SETITIMER
418 if (timercmp(&SmEventQueue
->ev_time
, &now
, >))
420 timersub(&SmEventQueue
->ev_time
, &now
,
422 clr
.it_interval
.tv_sec
= 0;
423 clr
.it_interval
.tv_usec
= 0;
424 if (clr
.it_value
.tv_sec
< 0)
425 clr
.it_value
.tv_sec
= 0;
426 if (clr
.it_value
.tv_sec
== 0 &&
427 clr
.it_value
.tv_usec
== 0)
428 clr
.it_value
.tv_usec
= 1000;
429 (void) setitimer(ITIMER_REAL
, &clr
, NULL
);
433 clr
.it_interval
.tv_sec
= 0;
434 clr
.it_interval
.tv_usec
= 0;
435 clr
.it_value
.tv_sec
= 3;
436 clr
.it_value
.tv_usec
= 0;
437 (void) setitimer(ITIMER_REAL
, &clr
, NULL
);
439 #else /* SM_CONF_SETITIMER */
440 if (SmEventQueue
->ev_time
> now
)
441 (void) alarm((unsigned) (SmEventQueue
->ev_time
445 #endif /* SM_CONF_SETITIMER */
451 #if SM_CONF_SETITIMER
452 clr
.it_interval
.tv_sec
= 0;
453 clr
.it_interval
.tv_usec
= 0;
454 clr
.it_value
.tv_sec
= 0;
455 clr
.it_value
.tv_usec
= 0;
456 (void) setitimer(ITIMER_REAL
, &clr
, NULL
);
457 gettimeofday(&now
, NULL
);
458 #else /* SM_CONF_SETITIMER */
461 #endif /* SM_CONF_SETITIMER */
463 if (SmEventQueue
!= NULL
)
465 #if SM_CONF_SETITIMER
466 timersub(&SmEventQueue
->ev_time
, &now
, &clr
.it_value
);
467 clr
.it_interval
.tv_sec
= 0;
468 clr
.it_interval
.tv_usec
= 0;
469 if (clr
.it_value
.tv_sec
< 0)
470 clr
.it_value
.tv_sec
= 0;
471 if (clr
.it_value
.tv_sec
== 0 && clr
.it_value
.tv_usec
== 0)
472 clr
.it_value
.tv_usec
= 1000;
473 (void) setitimer(ITIMER_REAL
, &clr
, NULL
);
474 #else /* SM_CONF_SETITIMER */
475 (void) alarm((unsigned) (SmEventQueue
->ev_time
- now
));
476 #endif /* SM_CONF_SETITIMER */
479 return SIGFUNC_RETURN
;
482 ** SLEEP -- a version of sleep that works with this stuff
484 ** Because Unix sleep uses the alarm facility, I must reimplement
488 ** intvl -- time to sleep.
494 ** waits for intvl time. However, other events can
495 ** be run during that interval.
500 static void sm_endsleep
__P((int));
501 static bool volatile SmSleepDone
;
502 # endif /* !HAVE_NANOSLEEP */
505 # define SLEEP_T unsigned int
506 #endif /* ! SLEEP_T */
513 struct timespec rqtp
;
519 nanosleep(&rqtp
, NULL
);
521 #else /* HAVE_NANOSLEEP */
524 #if _FFR_SLEEP_USE_SELECT > 0
526 # if _FFR_SLEEP_USE_SELECT > 0
527 struct timeval sm_io_to
;
528 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
529 #endif /* _FFR_SLEEP_USE_SELECT > 0 */
530 #if SM_CONF_SETITIMER
531 struct timeval now
, begin
, diff
;
532 # if _FFR_SLEEP_USE_SELECT > 0
534 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
535 #else /* SM_CONF_SETITIMER */
537 #endif /* SM_CONF_SETITIMER */
541 #if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
542 if (intvl
> _FFR_MAX_SLEEP_TIME
)
544 syslog(LOG_ERR
, "sleep: interval=%u exceeds max value %d",
545 intvl
, _FFR_MAX_SLEEP_TIME
);
547 SM_ASSERT(intvl
< (unsigned int) INT_MAX
);
549 intvl
= _FFR_MAX_SLEEP_TIME
;
551 #endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
554 #if SM_CONF_SETITIMER
555 # if _FFR_SLEEP_USE_SELECT > 0
558 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
559 (void) gettimeofday(&now
, NULL
);
561 #else /* SM_CONF_SETITIMER */
562 now
= begin
= time(NULL
);
563 #endif /* SM_CONF_SETITIMER */
565 ev
= sm_setevent((time_t) intvl
, sm_endsleep
, 0);
570 syslog(LOG_ERR
, "sleep: sm_setevent(%u) failed", intvl
);
574 was_held
= sm_releasesignal(SIGALRM
);
578 #if SM_CONF_SETITIMER
579 (void) gettimeofday(&now
, NULL
);
580 timersub(&now
, &begin
, &diff
);
581 if (diff
.tv_sec
< 0 ||
582 (diff
.tv_sec
== 0 && diff
.tv_usec
== 0))
584 # if _FFR_SLEEP_USE_SELECT > 0
585 timersub(&slpv
, &diff
, &sm_io_to
);
586 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
587 #else /* SM_CONF_SETITIMER */
591 ** Check whether time expired before signal is released.
592 ** Due to the granularity of time() add 1 to be on the
596 if (!(begin
+ (time_t) intvl
+ 1 > now
))
598 # if _FFR_SLEEP_USE_SELECT > 0
599 sm_io_to
.tv_sec
= intvl
- (now
- begin
);
600 if (sm_io_to
.tv_sec
<= 0)
602 sm_io_to
.tv_usec
= 0;
603 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
604 #endif /* SM_CONF_SETITIMER */
605 #if _FFR_SLEEP_USE_SELECT > 0
606 if (intvl
<= _FFR_SLEEP_USE_SELECT
)
608 r
= select(0, NULL
, NULL
, NULL
, &sm_io_to
);
613 #endif /* _FFR_SLEEP_USE_SELECT > 0 */
617 /* if out of the loop without the event being triggered remove it */
621 (void) sm_blocksignal(SIGALRM
);
623 #endif /* HAVE_NANOSLEEP */
632 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
633 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
639 #endif /* !HAVE_NANOSLEEP */