clock.c revision 90792
1/*
2 * Copyright (c) 1998-2001 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>
15SM_RCSID("@(#)$Id: clock.c,v 1.34 2001/11/05 18:33:20 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
32static 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
56static SM_EVENT	*volatile SmEventQueue;		/* head of event queue */
57static SM_EVENT	*volatile SmFreeEventList;	/* list of free events */
58
59SM_EVENT *
60sm_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
82SM_EVENT *
83sm_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 && itime.it_value.tv_usec == 0)
164		itime.it_value.tv_usec = 1000;
165	(void) setitimer(ITIMER_REAL, &itime, NULL);
166# else /* SM_CONF_SETITIMER */
167	intvl = SmEventQueue->ev_time - now;
168	(void) alarm((unsigned) intvl < 1 ? 1 : intvl);
169# endif /* SM_CONF_SETITIMER */
170	if (wasblocked == 0)
171		(void) sm_releasesignal(SIGALRM);
172	return ev;
173}
174/*
175**  SM_CLREVENT -- remove an event from the event queue.
176**
177**	Parameters:
178**		ev -- pointer to event to remove.
179**
180**	Returns:
181**		none.
182**
183**	Side Effects:
184**		arranges for event ev to not happen.
185*/
186
187void
188sm_clrevent(ev)
189	register SM_EVENT *ev;
190{
191	register SM_EVENT **evp;
192	int wasblocked;
193# if SM_CONF_SETITIMER
194	struct itimerval clr;
195# endif /* SM_CONF_SETITIMER */
196
197	if (ev == NULL)
198		return;
199
200	/* find the parent event */
201	wasblocked = sm_blocksignal(SIGALRM);
202	for (evp = (SM_EVENT **) (&SmEventQueue);
203	     *evp != NULL;
204	     evp = &(*evp)->ev_link)
205	{
206		if (*evp == ev)
207			break;
208	}
209
210	/* now remove it */
211	if (*evp != NULL)
212	{
213		ENTER_CRITICAL();
214		*evp = ev->ev_link;
215		ev->ev_link = SmFreeEventList;
216		SmFreeEventList = ev;
217		LEAVE_CRITICAL();
218	}
219
220	/* restore clocks and pick up anything spare */
221	if (wasblocked == 0)
222		(void) sm_releasesignal(SIGALRM);
223	if (SmEventQueue != NULL)
224		(void) kill(getpid(), SIGALRM);
225	else
226	{
227		/* nothing left in event queue, no need for an alarm */
228# if SM_CONF_SETITIMER
229		clr.it_interval.tv_sec = 0;
230		clr.it_interval.tv_usec = 0;
231		clr.it_value.tv_sec = 0;
232		clr.it_value.tv_usec = 0;
233		(void) setitimer(ITIMER_REAL, &clr, NULL);
234# else /* SM_CONF_SETITIMER */
235		(void) alarm(0);
236# endif /* SM_CONF_SETITIMER */
237	}
238}
239/*
240**  SM_CLEAR_EVENTS -- remove all events from the event queue.
241**
242**	Parameters:
243**		none.
244**
245**	Returns:
246**		none.
247*/
248
249void
250sm_clear_events()
251{
252	register SM_EVENT *ev;
253#if SM_CONF_SETITIMER
254	struct itimerval clr;
255#endif /* SM_CONF_SETITIMER */
256	int wasblocked;
257
258	if (SmEventQueue == NULL)
259		return;
260
261	/* nothing will be left in event queue, no need for an alarm */
262#if SM_CONF_SETITIMER
263	clr.it_interval.tv_sec = 0;
264	clr.it_interval.tv_usec = 0;
265	clr.it_value.tv_sec = 0;
266	clr.it_value.tv_usec = 0;
267	(void) setitimer(ITIMER_REAL, &clr, NULL);
268#else /* SM_CONF_SETITIMER */
269	(void) alarm(0);
270#endif /* SM_CONF_SETITIMER */
271	wasblocked = sm_blocksignal(SIGALRM);
272
273	/* find the end of the EventQueue */
274	for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link)
275		continue;
276
277	ENTER_CRITICAL();
278	ev->ev_link = SmFreeEventList;
279	SmFreeEventList = SmEventQueue;
280	SmEventQueue = NULL;
281	LEAVE_CRITICAL();
282
283	/* restore clocks and pick up anything spare */
284	if (wasblocked == 0)
285		(void) sm_releasesignal(SIGALRM);
286}
287/*
288**  SM_TICK -- take a clock tick
289**
290**	Called by the alarm clock.  This routine runs events as needed.
291**	Always called as a signal handler, so we assume that SIGALRM
292**	has been blocked.
293**
294**	Parameters:
295**		One that is ignored; for compatibility with signal handlers.
296**
297**	Returns:
298**		none.
299**
300**	Side Effects:
301**		calls the next function in EventQueue.
302**
303**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
304**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
305**		DOING.
306*/
307
308/* ARGSUSED */
309SIGFUNC_DECL
310sm_tick(sig)
311	int sig;
312{
313	register SM_EVENT *ev;
314	pid_t mypid;
315	int save_errno = errno;
316#if SM_CONF_SETITIMER
317	struct itimerval clr;
318	struct timeval now;
319#else /* SM_CONF_SETITIMER */
320	register time_t now;
321#endif /* SM_CONF_SETITIMER */
322
323#if SM_CONF_SETITIMER
324	clr.it_interval.tv_sec = 0;
325	clr.it_interval.tv_usec = 0;
326	clr.it_value.tv_sec = 0;
327	clr.it_value.tv_usec = 0;
328	(void) setitimer(ITIMER_REAL, &clr, NULL);
329	gettimeofday(&now, NULL);
330#else /* SM_CONF_SETITIMER */
331	(void) alarm(0);
332	now = time(NULL);
333#endif /* SM_CONF_SETITIMER */
334
335	FIX_SYSV_SIGNAL(sig, sm_tick);
336	errno = save_errno;
337	CHECK_CRITICAL(sig);
338
339	mypid = getpid();
340	while (PendingSignal != 0)
341	{
342		int sigbit = 0;
343		int sig = 0;
344
345		if (bitset(PEND_SIGHUP, PendingSignal))
346		{
347			sigbit = PEND_SIGHUP;
348			sig = SIGHUP;
349		}
350		else if (bitset(PEND_SIGINT, PendingSignal))
351		{
352			sigbit = PEND_SIGINT;
353			sig = SIGINT;
354		}
355		else if (bitset(PEND_SIGTERM, PendingSignal))
356		{
357			sigbit = PEND_SIGTERM;
358			sig = SIGTERM;
359		}
360		else if (bitset(PEND_SIGUSR1, PendingSignal))
361		{
362			sigbit = PEND_SIGUSR1;
363			sig = SIGUSR1;
364		}
365		else
366		{
367			/* If we get here, we are in trouble */
368			abort();
369		}
370		PendingSignal &= ~sigbit;
371		kill(mypid, sig);
372	}
373
374#if SM_CONF_SETITIMER
375	gettimeofday(&now, NULL);
376#else /* SM_CONF_SETITIMER */
377	now = time(NULL);
378#endif /* SM_CONF_SETITIMER */
379	while ((ev = SmEventQueue) != NULL &&
380	       (ev->ev_pid != mypid ||
381#if SM_CONF_SETITIMER
382		timercmp(&ev->ev_time, &now, <=)
383#else /* SM_CONF_SETITIMER */
384		ev->ev_time <= now
385#endif /* SM_CONF_SETITIMER */
386		))
387	{
388		void (*f)();
389		int arg;
390		pid_t pid;
391
392		/* process the event on the top of the queue */
393		ev = SmEventQueue;
394		SmEventQueue = SmEventQueue->ev_link;
395
396		/* we must be careful in here because ev_func may not return */
397		f = ev->ev_func;
398		arg = ev->ev_arg;
399		pid = ev->ev_pid;
400		ENTER_CRITICAL();
401		ev->ev_link = SmFreeEventList;
402		SmFreeEventList = ev;
403		LEAVE_CRITICAL();
404		if (pid != getpid())
405			continue;
406		if (SmEventQueue != NULL)
407		{
408#if SM_CONF_SETITIMER
409			if (timercmp(&SmEventQueue->ev_time, &now, >))
410			{
411				timersub(&SmEventQueue->ev_time, &now,
412					 &clr.it_value);
413				clr.it_interval.tv_sec = 0;
414				clr.it_interval.tv_usec = 0;
415				(void) setitimer(ITIMER_REAL, &clr, NULL);
416			}
417			else
418			{
419				clr.it_interval.tv_sec = 0;
420				clr.it_interval.tv_usec = 0;
421				clr.it_value.tv_sec = 3;
422				clr.it_value.tv_usec = 0;
423				(void) setitimer(ITIMER_REAL, &clr, NULL);
424			}
425#else /* SM_CONF_SETITIMER */
426			if (SmEventQueue->ev_time > now)
427				(void) alarm((unsigned) (SmEventQueue->ev_time
428							 - now));
429			else
430				(void) alarm(3);
431#endif /* SM_CONF_SETITIMER */
432		}
433
434		/* call ev_func */
435		errno = save_errno;
436		(*f)(arg);
437#if SM_CONF_SETITIMER
438		clr.it_interval.tv_sec = 0;
439		clr.it_interval.tv_usec = 0;
440		clr.it_value.tv_sec = 0;
441		clr.it_value.tv_usec = 0;
442		(void) setitimer(ITIMER_REAL, &clr, NULL);
443		gettimeofday(&now, NULL);
444#else /* SM_CONF_SETITIMER */
445		(void) alarm(0);
446		now = time(NULL);
447#endif /* SM_CONF_SETITIMER */
448	}
449	if (SmEventQueue != NULL)
450	{
451#if SM_CONF_SETITIMER
452		timersub(&SmEventQueue->ev_time, &now, &clr.it_value);
453		clr.it_interval.tv_sec = 0;
454		clr.it_interval.tv_usec = 0;
455		(void) setitimer(ITIMER_REAL, &clr, NULL);
456#else /* SM_CONF_SETITIMER */
457		(void) alarm((unsigned) (SmEventQueue->ev_time - now));
458#endif /* SM_CONF_SETITIMER */
459	}
460	errno = save_errno;
461	return SIGFUNC_RETURN;
462}
463/*
464**  SLEEP -- a version of sleep that works with this stuff
465**
466**	Because Unix sleep uses the alarm facility, I must reimplement
467**	it here.
468**
469**	Parameters:
470**		intvl -- time to sleep.
471**
472**	Returns:
473**		zero.
474**
475**	Side Effects:
476**		waits for intvl time.  However, other events can
477**		be run during that interval.
478*/
479
480
481static bool	volatile SmSleepDone;
482
483#ifndef SLEEP_T
484# define SLEEP_T	unsigned int
485#endif /* ! SLEEP_T */
486
487SLEEP_T
488sleep(intvl)
489	unsigned int intvl;
490{
491	int was_held;
492
493	if (intvl == 0)
494		return (SLEEP_T) 0;
495	SmSleepDone = false;
496	(void) sm_setevent((time_t) intvl, sm_endsleep, 0);
497	was_held = sm_releasesignal(SIGALRM);
498	while (!SmSleepDone)
499		(void) pause();
500	if (was_held > 0)
501		(void) sm_blocksignal(SIGALRM);
502	return (SLEEP_T) 0;
503}
504
505static void
506sm_endsleep()
507{
508	/*
509	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
510	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
511	**	DOING.
512	*/
513
514	SmSleepDone = true;
515}
516
517