Initial import from FreeBSD RELENG_4:
[games.git] / contrib / sendmail / libsm / clock.c
1 /*
2  * Copyright (c) 1998-2003 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.35.2.3 2003/03/03 19:57:40 ca Exp $")
16 #include <unistd.h>
17 #include <time.h>
18 #include <errno.h>
19 #if SM_CONF_SETITIMER
20 # include <sys/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
28 #ifndef sigmask
29 # define sigmask(s)     (1 << ((s) - 1))
30 #endif /* ! sigmask */
31
32 static void     sm_endsleep __P((void));
33
34
35 /*
36 **  SM_SETEVENTM -- set an event to happen at a specific time in milliseconds.
37 **
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).
42 **
43 **      Parameters:
44 **              intvl -- interval until next event occurs (milliseconds).
45 **              func -- function to call on event.
46 **              arg -- argument to func on event.
47 **
48 **      Returns:
49 **              On success returns the SM_EVENT entry created.
50 **              On failure returns NULL.
51 **
52 **      Side Effects:
53 **              none.
54 */
55
56 static SM_EVENT *volatile SmEventQueue;         /* head of event queue */
57 static SM_EVENT *volatile SmFreeEventList;      /* list of free events */
58
59 SM_EVENT *
60 sm_seteventm(intvl, func, arg)
61         int intvl;
62         void (*func)();
63         int arg;
64 {
65         ENTER_CRITICAL();
66         if (SmFreeEventList == NULL)
67         {
68                 SmFreeEventList = (SM_EVENT *) sm_pmalloc_x(sizeof *SmFreeEventList);
69                 SmFreeEventList->ev_link = NULL;
70         }
71         LEAVE_CRITICAL();
72
73         return sm_sigsafe_seteventm(intvl, func, arg);
74 }
75
76 /*
77 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
78 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
79 **              DOING.
80 */
81
82 SM_EVENT *
83 sm_sigsafe_seteventm(intvl, func, arg)
84         int intvl;
85         void (*func)();
86         int arg;
87 {
88         register SM_EVENT **evp;
89         register SM_EVENT *ev;
90 #if SM_CONF_SETITIMER
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 */
96         int wasblocked;
97
98         /* negative times are not allowed */
99         if (intvl <= 0)
100                 return NULL;
101
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);
107         nowi = now;
108         timeradd(&now, &ival, &nowi);
109 #else /*  SM_CONF_SETITIMER */
110         now = time(NULL);
111         nowi = now + (time_t)(intvl / 1000);
112 #endif /*  SM_CONF_SETITIMER */
113
114         /* search event queue for correct position */
115         for (evp = (SM_EVENT **) (&SmEventQueue);
116              (ev = *evp) != NULL;
117              evp = &ev->ev_link)
118         {
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 */
124                         break;
125         }
126
127         ENTER_CRITICAL();
128         if (SmFreeEventList == NULL)
129         {
130                 /*
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
135                 **  SmFreeEventList.
136                 */
137
138                 LEAVE_CRITICAL();
139                 return NULL;
140         }
141         else
142         {
143                 ev = SmFreeEventList;
144                 SmFreeEventList = ev->ev_link;
145         }
146         LEAVE_CRITICAL();
147
148         /* insert new event */
149         ev->ev_time = nowi;
150         ev->ev_func = func;
151         ev->ev_arg = arg;
152         ev->ev_pid = getpid();
153         ENTER_CRITICAL();
154         ev->ev_link = *evp;
155         *evp = ev;
156         LEAVE_CRITICAL();
157
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 */
172         if (wasblocked == 0)
173                 (void) sm_releasesignal(SIGALRM);
174         return ev;
175 }
176 /*
177 **  SM_CLREVENT -- remove an event from the event queue.
178 **
179 **      Parameters:
180 **              ev -- pointer to event to remove.
181 **
182 **      Returns:
183 **              none.
184 **
185 **      Side Effects:
186 **              arranges for event ev to not happen.
187 */
188
189 void
190 sm_clrevent(ev)
191         register SM_EVENT *ev;
192 {
193         register SM_EVENT **evp;
194         int wasblocked;
195 # if SM_CONF_SETITIMER
196         struct itimerval clr;
197 # endif /* SM_CONF_SETITIMER */
198
199         if (ev == NULL)
200                 return;
201
202         /* find the parent event */
203         wasblocked = sm_blocksignal(SIGALRM);
204         for (evp = (SM_EVENT **) (&SmEventQueue);
205              *evp != NULL;
206              evp = &(*evp)->ev_link)
207         {
208                 if (*evp == ev)
209                         break;
210         }
211
212         /* now remove it */
213         if (*evp != NULL)
214         {
215                 ENTER_CRITICAL();
216                 *evp = ev->ev_link;
217                 ev->ev_link = SmFreeEventList;
218                 SmFreeEventList = ev;
219                 LEAVE_CRITICAL();
220         }
221
222         /* restore clocks and pick up anything spare */
223         if (wasblocked == 0)
224                 (void) sm_releasesignal(SIGALRM);
225         if (SmEventQueue != NULL)
226                 (void) kill(getpid(), SIGALRM);
227         else
228         {
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 */
237                 (void) alarm(0);
238 # endif /* SM_CONF_SETITIMER */
239         }
240 }
241 /*
242 **  SM_CLEAR_EVENTS -- remove all events from the event queue.
243 **
244 **      Parameters:
245 **              none.
246 **
247 **      Returns:
248 **              none.
249 */
250
251 void
252 sm_clear_events()
253 {
254         register SM_EVENT *ev;
255 #if SM_CONF_SETITIMER
256         struct itimerval clr;
257 #endif /* SM_CONF_SETITIMER */
258         int wasblocked;
259
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 */
268         (void) alarm(0);
269 #endif /* SM_CONF_SETITIMER */
270
271         if (SmEventQueue == NULL)
272                 return;
273
274         wasblocked = sm_blocksignal(SIGALRM);
275
276         /* find the end of the EventQueue */
277         for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link)
278                 continue;
279
280         ENTER_CRITICAL();
281         ev->ev_link = SmFreeEventList;
282         SmFreeEventList = SmEventQueue;
283         SmEventQueue = NULL;
284         LEAVE_CRITICAL();
285
286         /* restore clocks and pick up anything spare */
287         if (wasblocked == 0)
288                 (void) sm_releasesignal(SIGALRM);
289 }
290 /*
291 **  SM_TICK -- take a clock tick
292 **
293 **      Called by the alarm clock.  This routine runs events as needed.
294 **      Always called as a signal handler, so we assume that SIGALRM
295 **      has been blocked.
296 **
297 **      Parameters:
298 **              One that is ignored; for compatibility with signal handlers.
299 **
300 **      Returns:
301 **              none.
302 **
303 **      Side Effects:
304 **              calls the next function in EventQueue.
305 **
306 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
307 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
308 **              DOING.
309 */
310
311 /* ARGSUSED */
312 SIGFUNC_DECL
313 sm_tick(sig)
314         int sig;
315 {
316         register SM_EVENT *ev;
317         pid_t mypid;
318         int save_errno = errno;
319 #if SM_CONF_SETITIMER
320         struct itimerval clr;
321         struct timeval now;
322 #else /* SM_CONF_SETITIMER */
323         register time_t now;
324 #endif /* SM_CONF_SETITIMER */
325
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 */
334         (void) alarm(0);
335         now = time(NULL);
336 #endif /* SM_CONF_SETITIMER */
337
338         FIX_SYSV_SIGNAL(sig, sm_tick);
339         errno = save_errno;
340         CHECK_CRITICAL(sig);
341
342         mypid = getpid();
343         while (PendingSignal != 0)
344         {
345                 int sigbit = 0;
346                 int sig = 0;
347
348                 if (bitset(PEND_SIGHUP, PendingSignal))
349                 {
350                         sigbit = PEND_SIGHUP;
351                         sig = SIGHUP;
352                 }
353                 else if (bitset(PEND_SIGINT, PendingSignal))
354                 {
355                         sigbit = PEND_SIGINT;
356                         sig = SIGINT;
357                 }
358                 else if (bitset(PEND_SIGTERM, PendingSignal))
359                 {
360                         sigbit = PEND_SIGTERM;
361                         sig = SIGTERM;
362                 }
363                 else if (bitset(PEND_SIGUSR1, PendingSignal))
364                 {
365                         sigbit = PEND_SIGUSR1;
366                         sig = SIGUSR1;
367                 }
368                 else
369                 {
370                         /* If we get here, we are in trouble */
371                         abort();
372                 }
373                 PendingSignal &= ~sigbit;
374                 kill(mypid, sig);
375         }
376
377 #if SM_CONF_SETITIMER
378         gettimeofday(&now, NULL);
379 #else /* SM_CONF_SETITIMER */
380         now = time(NULL);
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 */
387                 ev->ev_time <= now
388 #endif /* SM_CONF_SETITIMER */
389                 ))
390         {
391                 void (*f)();
392                 int arg;
393                 pid_t pid;
394
395                 /* process the event on the top of the queue */
396                 ev = SmEventQueue;
397                 SmEventQueue = SmEventQueue->ev_link;
398
399                 /* we must be careful in here because ev_func may not return */
400                 f = ev->ev_func;
401                 arg = ev->ev_arg;
402                 pid = ev->ev_pid;
403                 ENTER_CRITICAL();
404                 ev->ev_link = SmFreeEventList;
405                 SmFreeEventList = ev;
406                 LEAVE_CRITICAL();
407                 if (pid != getpid())
408                         continue;
409                 if (SmEventQueue != NULL)
410                 {
411 #if SM_CONF_SETITIMER
412                         if (timercmp(&SmEventQueue->ev_time, &now, >))
413                         {
414                                 timersub(&SmEventQueue->ev_time, &now,
415                                          &clr.it_value);
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);
424                         }
425                         else
426                         {
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);
432                         }
433 #else /* SM_CONF_SETITIMER */
434                         if (SmEventQueue->ev_time > now)
435                                 (void) alarm((unsigned) (SmEventQueue->ev_time
436                                                          - now));
437                         else
438                                 (void) alarm(3);
439 #endif /* SM_CONF_SETITIMER */
440                 }
441
442                 /* call ev_func */
443                 errno = save_errno;
444                 (*f)(arg);
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 */
453                 (void) alarm(0);
454                 now = time(NULL);
455 #endif /* SM_CONF_SETITIMER */
456         }
457         if (SmEventQueue != NULL)
458         {
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 */
471         }
472         errno = save_errno;
473         return SIGFUNC_RETURN;
474 }
475 /*
476 **  SLEEP -- a version of sleep that works with this stuff
477 **
478 **      Because Unix sleep uses the alarm facility, I must reimplement
479 **      it here.
480 **
481 **      Parameters:
482 **              intvl -- time to sleep.
483 **
484 **      Returns:
485 **              zero.
486 **
487 **      Side Effects:
488 **              waits for intvl time.  However, other events can
489 **              be run during that interval.
490 */
491
492
493 static bool     volatile SmSleepDone;
494
495 #ifndef SLEEP_T
496 # define SLEEP_T        unsigned int
497 #endif /* ! SLEEP_T */
498
499 SLEEP_T
500 sleep(intvl)
501         unsigned int intvl;
502 {
503         int was_held;
504
505         if (intvl == 0)
506                 return (SLEEP_T) 0;
507         SmSleepDone = false;
508         (void) sm_setevent((time_t) intvl, sm_endsleep, 0);
509         was_held = sm_releasesignal(SIGALRM);
510         while (!SmSleepDone)
511                 (void) pause();
512         if (was_held > 0)
513                 (void) sm_blocksignal(SIGALRM);
514         return (SLEEP_T) 0;
515 }
516
517 static void
518 sm_endsleep()
519 {
520         /*
521         **  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
522         **      ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
523         **      DOING.
524         */
525
526         SmSleepDone = true;
527 }
528