1 /*
2 * Copyright (c) 1998-2004 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.47 2005/06/14 23:07:20 ca Exp $")
16 #include <unistd.h>
17 #include <time.h>
18 #include <errno.h>
19 #if SM_CONF_SETITIMER
20 # include <sm/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 #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
31 # include <syslog.h>
32 #endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
33
34 #ifndef sigmask
35 # define sigmask(s) (1 << ((s) - 1))
36 #endif /* ! sigmask */
37
38 #ifdef WIN32
39 static void (*alarm_handler)(int);
40 #endif /* WIN32 */
41
42 /*
43 ** SM_SETEVENTM -- set an event to happen at a specific time in milliseconds.
44 **
45 ** Events are stored in a sorted list for fast processing.
46 ** An event only applies to the process that set it.
47 ** Source is #ifdef'd to work with older OS's that don't have setitimer()
48 ** (that is, don't have a timer granularity less than 1 second).
49 **
50 ** Parameters:
51 ** intvl -- interval until next event occurs (milliseconds).
52 ** func -- function to call on event.
53 ** arg -- argument to func on event.
54 **
55 ** Returns:
56 ** On success returns the SM_EVENT entry created.
57 ** On failure returns NULL.
58 **
59 ** Side Effects:
60 ** none.
61 */
62
63 static SM_EVENT *volatile SmEventQueue; /* head of event queue */
64 static SM_EVENT *volatile SmFreeEventList; /* list of free events */
65
66 SM_EVENT *
sm_seteventm(intvl,func,arg)67 sm_seteventm(intvl, func, arg)
68 int intvl;
69 void (*func)__P((int));
70 int arg;
71 {
72 ENTER_CRITICAL();
73 if (SmFreeEventList == NULL)
74 {
75 SmFreeEventList = (SM_EVENT *) sm_pmalloc_x(sizeof *SmFreeEventList);
76 SmFreeEventList->ev_link = NULL;
77 }
78 LEAVE_CRITICAL();
79
80 return sm_sigsafe_seteventm(intvl, func, arg);
81 }
82
83 /*
84 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
85 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
86 ** DOING.
87 */
88
89 SM_EVENT *
sm_sigsafe_seteventm(intvl,func,arg)90 sm_sigsafe_seteventm(intvl, func, arg)
91 int intvl;
92 void (*func)__P((int));
93 int arg;
94 {
95 register SM_EVENT **evp;
96 register SM_EVENT *ev;
97 #if SM_CONF_SETITIMER
98 auto struct timeval now, nowi, ival;
99 auto struct itimerval itime;
100 #else /* SM_CONF_SETITIMER */
101 auto time_t now, nowi;
102 #endif /* SM_CONF_SETITIMER */
103 #ifndef WIN32
104 int wasblocked;
105 #endif /* ! WIN32 */
106
107 /* negative times are not allowed */
108 if (intvl <= 0)
109 return NULL;
110
111 #ifndef WIN32
112 wasblocked = sm_blocksignal(SIGALRM);
113 #else /* ! WIN32 */
114 alarm_handler = NULL;
115 #endif /* ! WIN32 */
116 #if SM_CONF_SETITIMER
117 ival.tv_sec = intvl / 1000;
118 ival.tv_usec = (intvl - ival.tv_sec * 1000) * 10;
119 (void) gettimeofday(&now, NULL);
120 nowi = now;
121 timeradd(&now, &ival, &nowi);
122 #else /* SM_CONF_SETITIMER */
123 now = time(NULL);
124 nowi = now + (time_t)(intvl / 1000);
125 #endif /* SM_CONF_SETITIMER */
126
127 /* search event queue for correct position */
128 for (evp = (SM_EVENT **) (&SmEventQueue);
129 (ev = *evp) != NULL;
130 evp = &ev->ev_link)
131 {
132 #if SM_CONF_SETITIMER
133 if (timercmp(&(ev->ev_time), &nowi, >=))
134 #else /* SM_CONF_SETITIMER */
135 if (ev->ev_time >= nowi)
136 #endif /* SM_CONF_SETITIMER */
137 break;
138 }
139
140 ENTER_CRITICAL();
141 if (SmFreeEventList == NULL)
142 {
143 /*
144 ** This shouldn't happen. If called from sm_seteventm(),
145 ** we have just malloced a SmFreeEventList entry. If
146 ** called from a signal handler, it should have been
147 ** from an existing event which sm_tick() just added to
148 ** SmFreeEventList.
149 */
150
151 LEAVE_CRITICAL();
152 #ifndef WIN32
153 if (wasblocked == 0)
154 (void) sm_releasesignal(SIGALRM);
155 #endif /* ! WIN32 */
156 return NULL;
157 }
158 else
159 {
160 ev = SmFreeEventList;
161 SmFreeEventList = ev->ev_link;
162 }
163 LEAVE_CRITICAL();
164
165 /* insert new event */
166 ev->ev_time = nowi;
167 #ifdef WIN32
168 ev->ev_func = (void (__cdecl *)(int))func; /* even at W1 !!! */
169 #else /* WIN32 */
170 ev->ev_func = func;
171 #endif /* WIN32 */
172 ev->ev_arg = arg;
173 ev->ev_pid = getpid();
174 ENTER_CRITICAL();
175 ev->ev_link = *evp;
176 *evp = ev;
177 LEAVE_CRITICAL();
178
179 #ifndef WIN32
180 (void) sm_signal(SIGALRM, sm_tick);
181 # if SM_CONF_SETITIMER
182 timersub(&SmEventQueue->ev_time, &now, &itime.it_value);
183 itime.it_interval.tv_sec = 0;
184 itime.it_interval.tv_usec = 0;
185 if (itime.it_value.tv_sec < 0)
186 itime.it_value.tv_sec = 0;
187 if (itime.it_value.tv_sec == 0 && itime.it_value.tv_usec == 0)
188 itime.it_value.tv_usec = 1000;
189 (void) setitimer(ITIMER_REAL, &itime, NULL);
190 # else /* SM_CONF_SETITIMER */
191 intvl = SmEventQueue->ev_time - now;
192 (void) alarm((unsigned) (intvl < 1 ? 1 : intvl));
193 # endif /* SM_CONF_SETITIMER */
194 if (wasblocked == 0)
195 (void) sm_releasesignal(SIGALRM);
196 #endif /* ! WIN32 */
197 return ev;
198 }
199 /*
200 ** SM_CLREVENT -- remove an event from the event queue.
201 **
202 ** Parameters:
203 ** ev -- pointer to event to remove.
204 **
205 ** Returns:
206 ** none.
207 **
208 ** Side Effects:
209 ** arranges for event ev to not happen.
210 */
211
212 void
sm_clrevent(ev)213 sm_clrevent(ev)
214 register SM_EVENT *ev;
215 {
216 register SM_EVENT **evp;
217 #ifndef WIN32
218 int wasblocked;
219 # if SM_CONF_SETITIMER
220 struct itimerval clr;
221 # endif /* SM_CONF_SETITIMER */
222 #endif /* ! WIN32 */
223
224 if (ev == NULL)
225 return;
226
227 /* find the parent event */
228 #ifndef WIN32
229 wasblocked = sm_blocksignal(SIGALRM);
230 #else /* ! WIN32 */
231 alarm_handler = NULL;
232 #endif /* ! WIN32 */
233 for (evp = (SM_EVENT **) (&SmEventQueue);
234 *evp != NULL;
235 evp = &(*evp)->ev_link)
236 {
237 if (*evp == ev)
238 break;
239 }
240
241 /* now remove it */
242 if (*evp != NULL)
243 {
244 ENTER_CRITICAL();
245 *evp = ev->ev_link;
246 ev->ev_link = SmFreeEventList;
247 SmFreeEventList = ev;
248 LEAVE_CRITICAL();
249 }
250
251 /* restore clocks and pick up anything spare */
252 #ifndef WIN32
253 if (wasblocked == 0)
254 (void) sm_releasesignal(SIGALRM);
255 if (SmEventQueue != NULL)
256 (void) kill(getpid(), SIGALRM);
257 else
258 {
259 /* nothing left in event queue, no need for an alarm */
260 # if SM_CONF_SETITIMER
261 clr.it_interval.tv_sec = 0;
262 clr.it_interval.tv_usec = 0;
263 clr.it_value.tv_sec = 0;
264 clr.it_value.tv_usec = 0;
265 (void) setitimer(ITIMER_REAL, &clr, NULL);
266 # else /* SM_CONF_SETITIMER */
267 (void) alarm(0);
268 # endif /* SM_CONF_SETITIMER */
269 }
270 #endif /* ! WIN32 */
271 }
272 /*
273 ** SM_CLEAR_EVENTS -- remove all events from the event queue.
274 **
275 ** Parameters:
276 ** none.
277 **
278 ** Returns:
279 ** none.
280 */
281
282 void
sm_clear_events()283 sm_clear_events()
284 {
285 register SM_EVENT *ev;
286 #if SM_CONF_SETITIMER
287 struct itimerval clr;
288 #endif /* SM_CONF_SETITIMER */
289 #ifndef WIN32
290 int wasblocked;
291 #endif /* ! WIN32 */
292
293 /* nothing will be left in event queue, no need for an alarm */
294 #if SM_CONF_SETITIMER
295 clr.it_interval.tv_sec = 0;
296 clr.it_interval.tv_usec = 0;
297 clr.it_value.tv_sec = 0;
298 clr.it_value.tv_usec = 0;
299 (void) setitimer(ITIMER_REAL, &clr, NULL);
300 #else /* SM_CONF_SETITIMER */
301 (void) alarm(0);
302 #endif /* SM_CONF_SETITIMER */
303
304 if (SmEventQueue == NULL)
305 return;
306
307 #ifndef WIN32
308 wasblocked = sm_blocksignal(SIGALRM);
309 #endif /* ! WIN32 */
310
311 /* find the end of the EventQueue */
312 for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link)
313 continue;
314
315 ENTER_CRITICAL();
316 ev->ev_link = SmFreeEventList;
317 SmFreeEventList = SmEventQueue;
318 SmEventQueue = NULL;
319 LEAVE_CRITICAL();
320
321 #ifndef WIN32
322 /* restore clocks and pick up anything spare */
323 if (wasblocked == 0)
324 (void) sm_releasesignal(SIGALRM);
325 #endif /* ! WIN32 */
326 }
327 /*
328 ** SM_TICK -- take a clock tick
329 **
330 ** Called by the alarm clock. This routine runs events as needed.
331 ** Always called as a signal handler, so we assume that SIGALRM
332 ** has been blocked.
333 **
334 ** Parameters:
335 ** One that is ignored; for compatibility with signal handlers.
336 **
337 ** Returns:
338 ** none.
339 **
340 ** Side Effects:
341 ** calls the next function in EventQueue.
342 **
343 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
344 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
345 ** DOING.
346 */
347
348 /* ARGSUSED */
349 SIGFUNC_DECL
sm_tick(sig)350 sm_tick(sig)
351 int sig;
352 {
353 register SM_EVENT *ev;
354 pid_t mypid;
355 int save_errno = errno;
356 #if SM_CONF_SETITIMER
357 struct itimerval clr;
358 struct timeval now;
359 #else /* SM_CONF_SETITIMER */
360 register time_t now;
361 #endif /* SM_CONF_SETITIMER */
362
363 #if SM_CONF_SETITIMER
364 clr.it_interval.tv_sec = 0;
365 clr.it_interval.tv_usec = 0;
366 clr.it_value.tv_sec = 0;
367 clr.it_value.tv_usec = 0;
368 (void) setitimer(ITIMER_REAL, &clr, NULL);
369 gettimeofday(&now, NULL);
370 #else /* SM_CONF_SETITIMER */
371 (void) alarm(0);
372 now = time(NULL);
373 #endif /* SM_CONF_SETITIMER */
374
375 FIX_SYSV_SIGNAL(sig, sm_tick);
376 errno = save_errno;
377 CHECK_CRITICAL(sig);
378
379 mypid = getpid();
380 while (PendingSignal != 0)
381 {
382 int sigbit = 0;
383 int sig = 0;
384
385 if (bitset(PEND_SIGHUP, PendingSignal))
386 {
387 sigbit = PEND_SIGHUP;
388 sig = SIGHUP;
389 }
390 else if (bitset(PEND_SIGINT, PendingSignal))
391 {
392 sigbit = PEND_SIGINT;
393 sig = SIGINT;
394 }
395 else if (bitset(PEND_SIGTERM, PendingSignal))
396 {
397 sigbit = PEND_SIGTERM;
398 sig = SIGTERM;
399 }
400 else if (bitset(PEND_SIGUSR1, PendingSignal))
401 {
402 sigbit = PEND_SIGUSR1;
403 sig = SIGUSR1;
404 }
405 else
406 {
407 /* If we get here, we are in trouble */
408 abort();
409 }
410 PendingSignal &= ~sigbit;
411 #ifndef WIN32
412 kill(mypid, sig);
413 #endif /* ! WIN32 */
414 }
415
416 #if SM_CONF_SETITIMER
417 gettimeofday(&now, NULL);
418 #else /* SM_CONF_SETITIMER */
419 now = time(NULL);
420 #endif /* SM_CONF_SETITIMER */
421 while ((ev = SmEventQueue) != NULL &&
422 (ev->ev_pid != mypid ||
423 #if SM_CONF_SETITIMER
424 timercmp(&ev->ev_time, &now, <=)
425 #else /* SM_CONF_SETITIMER */
426 ev->ev_time <= now
427 #endif /* SM_CONF_SETITIMER */
428 ))
429 {
430 void (*f)__P((int));
431 int arg;
432 pid_t pid;
433
434 /* process the event on the top of the queue */
435 ev = SmEventQueue;
436 SmEventQueue = SmEventQueue->ev_link;
437
438 /* we must be careful in here because ev_func may not return */
439 f = ev->ev_func;
440 arg = ev->ev_arg;
441 pid = ev->ev_pid;
442 ENTER_CRITICAL();
443 ev->ev_link = SmFreeEventList;
444 SmFreeEventList = ev;
445 LEAVE_CRITICAL();
446 if (pid != getpid())
447 continue;
448 if (SmEventQueue != NULL)
449 {
450 #if SM_CONF_SETITIMER
451 if (timercmp(&SmEventQueue->ev_time, &now, >))
452 {
453 timersub(&SmEventQueue->ev_time, &now,
454 &clr.it_value);
455 clr.it_interval.tv_sec = 0;
456 clr.it_interval.tv_usec = 0;
457 if (clr.it_value.tv_sec < 0)
458 clr.it_value.tv_sec = 0;
459 if (clr.it_value.tv_sec == 0 &&
460 clr.it_value.tv_usec == 0)
461 clr.it_value.tv_usec = 1000;
462 (void) setitimer(ITIMER_REAL, &clr, NULL);
463 }
464 else
465 {
466 clr.it_interval.tv_sec = 0;
467 clr.it_interval.tv_usec = 0;
468 clr.it_value.tv_sec = 3;
469 clr.it_value.tv_usec = 0;
470 (void) setitimer(ITIMER_REAL, &clr, NULL);
471 }
472 #else /* SM_CONF_SETITIMER */
473 if (SmEventQueue->ev_time > now)
474 (void) alarm((unsigned) (SmEventQueue->ev_time
475 - now));
476 else
477 (void) alarm(3);
478 #endif /* SM_CONF_SETITIMER */
479 }
480
481 /* call ev_func */
482 errno = save_errno;
483 (*f)(arg);
484 #if SM_CONF_SETITIMER
485 clr.it_interval.tv_sec = 0;
486 clr.it_interval.tv_usec = 0;
487 clr.it_value.tv_sec = 0;
488 clr.it_value.tv_usec = 0;
489 (void) setitimer(ITIMER_REAL, &clr, NULL);
490 gettimeofday(&now, NULL);
491 #else /* SM_CONF_SETITIMER */
492 (void) alarm(0);
493 now = time(NULL);
494 #endif /* SM_CONF_SETITIMER */
495 }
496 if (SmEventQueue != NULL)
497 {
498 #if SM_CONF_SETITIMER
499 timersub(&SmEventQueue->ev_time, &now, &clr.it_value);
500 clr.it_interval.tv_sec = 0;
501 clr.it_interval.tv_usec = 0;
502 if (clr.it_value.tv_sec < 0)
503 clr.it_value.tv_sec = 0;
504 if (clr.it_value.tv_sec == 0 && clr.it_value.tv_usec == 0)
505 clr.it_value.tv_usec = 1000;
506 (void) setitimer(ITIMER_REAL, &clr, NULL);
507 #else /* SM_CONF_SETITIMER */
508 (void) alarm((unsigned) (SmEventQueue->ev_time - now));
509 #endif /* SM_CONF_SETITIMER */
510 }
511 errno = save_errno;
512 return SIGFUNC_RETURN;
513 }
514 /*
515 ** SLEEP -- a version of sleep that works with this stuff
516 **
517 ** Because Unix sleep uses the alarm facility, I must reimplement
518 ** it here.
519 **
520 ** Parameters:
521 ** intvl -- time to sleep.
522 **
523 ** Returns:
524 ** zero.
525 **
526 ** Side Effects:
527 ** waits for intvl time. However, other events can
528 ** be run during that interval.
529 */
530
531 #ifndef WIN32
532
533 # if !HAVE_NANOSLEEP
534 static void sm_endsleep __P((int));
535 static bool volatile SmSleepDone;
536 # endif /* !HAVE_NANOSLEEP */
537
538 #ifndef SLEEP_T
539 # define SLEEP_T unsigned int
540 #endif /* ! SLEEP_T */
541
542 SLEEP_T
sleep(intvl)543 sleep(intvl)
544 unsigned int intvl;
545 {
546 #if HAVE_NANOSLEEP
547 struct timespec rqtp;
548
549 if (intvl == 0)
550 return (SLEEP_T) 0;
551 rqtp.tv_sec = intvl;
552 rqtp.tv_nsec = 0;
553 nanosleep(&rqtp, NULL);
554 return (SLEEP_T) 0;
555 #else /* HAVE_NANOSLEEP */
556 int was_held;
557 SM_EVENT *ev;
558 #if _FFR_SLEEP_USE_SELECT > 0
559 int r;
560 # if _FFR_SLEEP_USE_SELECT > 0
561 struct timeval sm_io_to;
562 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
563 #endif /* _FFR_SLEEP_USE_SELECT > 0 */
564 #if SM_CONF_SETITIMER
565 struct timeval now, begin, diff;
566 # if _FFR_SLEEP_USE_SELECT > 0
567 struct timeval slpv;
568 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
569 #else /* SM_CONF_SETITIMER */
570 time_t begin, now;
571 #endif /* SM_CONF_SETITIMER */
572
573 if (intvl == 0)
574 return (SLEEP_T) 0;
575 #if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
576 if (intvl > _FFR_MAX_SLEEP_TIME)
577 {
578 syslog(LOG_ERR, "sleep: interval=%u exceeds max value %d",
579 intvl, _FFR_MAX_SLEEP_TIME);
580 # if 0
581 SM_ASSERT(intvl < (unsigned int) INT_MAX);
582 # endif /* 0 */
583 intvl = _FFR_MAX_SLEEP_TIME;
584 }
585 #endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
586 SmSleepDone = false;
587
588 #if SM_CONF_SETITIMER
589 # if _FFR_SLEEP_USE_SELECT > 0
590 slpv.tv_sec = intvl;
591 slpv.tv_usec = 0;
592 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
593 (void) gettimeofday(&now, NULL);
594 begin = now;
595 #else /* SM_CONF_SETITIMER */
596 now = begin = time(NULL);
597 #endif /* SM_CONF_SETITIMER */
598
599 ev = sm_setevent((time_t) intvl, sm_endsleep, 0);
600 if (ev == NULL)
601 {
602 /* COMPLAIN */
603 #if 0
604 syslog(LOG_ERR, "sleep: sm_setevent(%u) failed", intvl);
605 #endif /* 0 */
606 SmSleepDone = true;
607 }
608 was_held = sm_releasesignal(SIGALRM);
609
610 while (!SmSleepDone)
611 {
612 #if SM_CONF_SETITIMER
613 (void) gettimeofday(&now, NULL);
614 timersub(&now, &begin, &diff);
615 if (diff.tv_sec < 0 ||
616 (diff.tv_sec == 0 && diff.tv_usec == 0))
617 break;
618 # if _FFR_SLEEP_USE_SELECT > 0
619 timersub(&slpv, &diff, &sm_io_to);
620 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
621 #else /* SM_CONF_SETITIMER */
622 now = time(NULL);
623
624 /*
625 ** Check whether time expired before signal is released.
626 ** Due to the granularity of time() add 1 to be on the
627 ** safe side.
628 */
629
630 if (!(begin + (time_t) intvl + 1 > now))
631 break;
632 # if _FFR_SLEEP_USE_SELECT > 0
633 sm_io_to.tv_sec = intvl - (now - begin);
634 if (sm_io_to.tv_sec <= 0)
635 sm_io_to.tv_sec = 1;
636 sm_io_to.tv_usec = 0;
637 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
638 #endif /* SM_CONF_SETITIMER */
639 #if _FFR_SLEEP_USE_SELECT > 0
640 if (intvl <= _FFR_SLEEP_USE_SELECT)
641 {
642 r = select(0, NULL, NULL, NULL, &sm_io_to);
643 if (r == 0)
644 break;
645 }
646 else
647 #endif /* _FFR_SLEEP_USE_SELECT > 0 */
648 (void) pause();
649 }
650
651 /* if out of the loop without the event being triggered remove it */
652 if (!SmSleepDone)
653 sm_clrevent(ev);
654 if (was_held > 0)
655 (void) sm_blocksignal(SIGALRM);
656 return (SLEEP_T) 0;
657 #endif /* HAVE_NANOSLEEP */
658 }
659
660 #if !HAVE_NANOSLEEP
661 static void
sm_endsleep(ignore)662 sm_endsleep(ignore)
663 int ignore;
664 {
665 /*
666 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
667 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
668 ** DOING.
669 */
670
671 SmSleepDone = true;
672 }
673 #endif /* !HAVE_NANOSLEEP */
674
675 #endif /* ! WIN32 */
676