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.
14 #pragma ident "%Z%%M% %I% %E% SMI"
17 SM_RCSID("@(#)$Id: clock.c,v 1.47 2005/06/14 23:07:20 ca Exp $")
23 #endif /* SM_CONF_SETITIMER */
26 #include <sm/bitops.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
34 #endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
37 # define sigmask(s) (1 << ((s) - 1))
38 #endif /* ! sigmask */
42 ** SM_SETEVENTM -- set an event to happen at a specific time in milliseconds.
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).
50 ** intvl -- interval until next event occurs (milliseconds).
51 ** func -- function to call on event.
52 ** arg -- argument to func on event.
55 ** On success returns the SM_EVENT entry created.
56 ** On failure returns NULL.
62 static SM_EVENT
*volatile SmEventQueue
; /* head of event queue */
63 static SM_EVENT
*volatile SmFreeEventList
; /* list of free events */
66 sm_seteventm(intvl
, func
, arg
)
68 void (*func
)__P((int));
72 if (SmFreeEventList
== NULL
)
74 SmFreeEventList
= (SM_EVENT
*) sm_pmalloc_x(sizeof *SmFreeEventList
);
75 SmFreeEventList
->ev_link
= NULL
;
79 return sm_sigsafe_seteventm(intvl
, func
, arg
);
83 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
84 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
89 sm_sigsafe_seteventm(intvl
, func
, arg
)
91 void (*func
)__P((int));
94 register SM_EVENT
**evp
;
95 register SM_EVENT
*ev
;
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 */
104 /* negative times are not allowed */
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
);
114 timeradd(&now
, &ival
, &nowi
);
115 #else /* SM_CONF_SETITIMER */
117 nowi
= now
+ (time_t)(intvl
/ 1000);
118 #endif /* SM_CONF_SETITIMER */
120 /* search event queue for correct position */
121 for (evp
= (SM_EVENT
**) (&SmEventQueue
);
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 */
134 if (SmFreeEventList
== NULL
)
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
146 (void) sm_releasesignal(SIGALRM
);
151 ev
= SmFreeEventList
;
152 SmFreeEventList
= ev
->ev_link
;
156 /* insert new event */
160 ev
->ev_pid
= getpid();
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 */
181 (void) sm_releasesignal(SIGALRM
);
185 ** SM_CLREVENT -- remove an event from the event queue.
188 ** ev -- pointer to event to remove.
194 ** arranges for event ev to not happen.
199 register SM_EVENT
*ev
;
201 register SM_EVENT
**evp
;
203 # if SM_CONF_SETITIMER
204 struct itimerval clr
;
205 # endif /* SM_CONF_SETITIMER */
210 /* find the parent event */
211 wasblocked
= sm_blocksignal(SIGALRM
);
212 for (evp
= (SM_EVENT
**) (&SmEventQueue
);
214 evp
= &(*evp
)->ev_link
)
225 ev
->ev_link
= SmFreeEventList
;
226 SmFreeEventList
= ev
;
230 /* restore clocks and pick up anything spare */
232 (void) sm_releasesignal(SIGALRM
);
233 if (SmEventQueue
!= NULL
)
234 (void) kill(getpid(), SIGALRM
);
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 */
246 # endif /* SM_CONF_SETITIMER */
250 ** SM_CLEAR_EVENTS -- remove all events from the event queue.
262 register SM_EVENT
*ev
;
263 #if SM_CONF_SETITIMER
264 struct itimerval clr
;
265 #endif /* SM_CONF_SETITIMER */
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 */
277 #endif /* SM_CONF_SETITIMER */
279 if (SmEventQueue
== NULL
)
282 wasblocked
= sm_blocksignal(SIGALRM
);
284 /* find the end of the EventQueue */
285 for (ev
= SmEventQueue
; ev
->ev_link
!= NULL
; ev
= ev
->ev_link
)
289 ev
->ev_link
= SmFreeEventList
;
290 SmFreeEventList
= SmEventQueue
;
294 /* restore clocks and pick up anything spare */
296 (void) sm_releasesignal(SIGALRM
);
299 ** SM_TICK -- take a clock tick
301 ** Called by the alarm clock. This routine runs events as needed.
302 ** Always called as a signal handler, so we assume that SIGALRM
306 ** One that is ignored; for compatibility with signal handlers.
312 ** calls the next function in EventQueue.
314 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
315 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
324 register SM_EVENT
*ev
;
326 int save_errno
= errno
;
327 #if SM_CONF_SETITIMER
328 struct itimerval clr
;
330 #else /* SM_CONF_SETITIMER */
332 #endif /* SM_CONF_SETITIMER */
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 */
344 #endif /* SM_CONF_SETITIMER */
346 FIX_SYSV_SIGNAL(sig
, sm_tick
);
351 while (PendingSignal
!= 0)
356 if (bitset(PEND_SIGHUP
, PendingSignal
))
358 sigbit
= PEND_SIGHUP
;
361 else if (bitset(PEND_SIGINT
, PendingSignal
))
363 sigbit
= PEND_SIGINT
;
366 else if (bitset(PEND_SIGTERM
, PendingSignal
))
368 sigbit
= PEND_SIGTERM
;
371 else if (bitset(PEND_SIGUSR1
, PendingSignal
))
373 sigbit
= PEND_SIGUSR1
;
378 /* If we get here, we are in trouble */
381 PendingSignal
&= ~sigbit
;
385 #if SM_CONF_SETITIMER
386 gettimeofday(&now
, NULL
);
387 #else /* SM_CONF_SETITIMER */
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 */
396 #endif /* SM_CONF_SETITIMER */
403 /* process the event on the top of the queue */
405 SmEventQueue
= SmEventQueue
->ev_link
;
407 /* we must be careful in here because ev_func may not return */
412 ev
->ev_link
= SmFreeEventList
;
413 SmFreeEventList
= ev
;
417 if (SmEventQueue
!= NULL
)
419 #if SM_CONF_SETITIMER
420 if (timercmp(&SmEventQueue
->ev_time
, &now
, >))
422 timersub(&SmEventQueue
->ev_time
, &now
,
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
);
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
);
441 #else /* SM_CONF_SETITIMER */
442 if (SmEventQueue
->ev_time
> now
)
443 (void) alarm((unsigned) (SmEventQueue
->ev_time
447 #endif /* SM_CONF_SETITIMER */
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 */
463 #endif /* SM_CONF_SETITIMER */
465 if (SmEventQueue
!= NULL
)
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 */
481 return SIGFUNC_RETURN
;
484 ** SLEEP -- a version of sleep that works with this stuff
486 ** Because Unix sleep uses the alarm facility, I must reimplement
490 ** intvl -- time to sleep.
496 ** waits for intvl time. However, other events can
497 ** be run during that interval.
502 static void sm_endsleep
__P((int));
503 static bool volatile SmSleepDone
;
504 # endif /* !HAVE_NANOSLEEP */
507 # define SLEEP_T unsigned int
508 #endif /* ! SLEEP_T */
515 struct timespec rqtp
;
521 nanosleep(&rqtp
, NULL
);
523 #else /* HAVE_NANOSLEEP */
526 #if _FFR_SLEEP_USE_SELECT > 0
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
536 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
537 #else /* SM_CONF_SETITIMER */
539 #endif /* SM_CONF_SETITIMER */
543 #if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
544 if (intvl
> _FFR_MAX_SLEEP_TIME
)
546 syslog(LOG_ERR
, "sleep: interval=%u exceeds max value %d",
547 intvl
, _FFR_MAX_SLEEP_TIME
);
549 SM_ASSERT(intvl
< (unsigned int) INT_MAX
);
551 intvl
= _FFR_MAX_SLEEP_TIME
;
553 #endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
556 #if SM_CONF_SETITIMER
557 # if _FFR_SLEEP_USE_SELECT > 0
560 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
561 (void) gettimeofday(&now
, NULL
);
563 #else /* SM_CONF_SETITIMER */
564 now
= begin
= time(NULL
);
565 #endif /* SM_CONF_SETITIMER */
567 ev
= sm_setevent((time_t) intvl
, sm_endsleep
, 0);
572 syslog(LOG_ERR
, "sleep: sm_setevent(%u) failed", intvl
);
576 was_held
= sm_releasesignal(SIGALRM
);
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))
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 */
593 ** Check whether time expired before signal is released.
594 ** Due to the granularity of time() add 1 to be on the
598 if (!(begin
+ (time_t) intvl
+ 1 > now
))
600 # if _FFR_SLEEP_USE_SELECT > 0
601 sm_io_to
.tv_sec
= intvl
- (now
- begin
);
602 if (sm_io_to
.tv_sec
<= 0)
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
)
610 r
= select(0, NULL
, NULL
, NULL
, &sm_io_to
);
615 #endif /* _FFR_SLEEP_USE_SELECT > 0 */
619 /* if out of the loop without the event being triggered remove it */
623 (void) sm_blocksignal(SIGALRM
);
625 #endif /* HAVE_NANOSLEEP */
634 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
635 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
641 #endif /* !HAVE_NANOSLEEP */