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