clock.c revision 95154
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.1.1.2 2002/04/10 03:04:55 gshapiro 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)
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
189void
190sm_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
251void
252sm_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	if (SmEventQueue == NULL)
261		return;
262
263	/* nothing will be left in event queue, no need for an alarm */
264#if SM_CONF_SETITIMER
265	clr.it_interval.tv_sec = 0;
266	clr.it_interval.tv_usec = 0;
267	clr.it_value.tv_sec = 0;
268	clr.it_value.tv_usec = 0;
269	(void) setitimer(ITIMER_REAL, &clr, NULL);
270#else /* SM_CONF_SETITIMER */
271	(void) alarm(0);
272#endif /* SM_CONF_SETITIMER */
273	wasblocked = sm_blocksignal(SIGALRM);
274
275	/* find the end of the EventQueue */
276	for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link)
277		continue;
278
279	ENTER_CRITICAL();
280	ev->ev_link = SmFreeEventList;
281	SmFreeEventList = SmEventQueue;
282	SmEventQueue = NULL;
283	LEAVE_CRITICAL();
284
285	/* restore clocks and pick up anything spare */
286	if (wasblocked == 0)
287		(void) sm_releasesignal(SIGALRM);
288}
289/*
290**  SM_TICK -- take a clock tick
291**
292**	Called by the alarm clock.  This routine runs events as needed.
293**	Always called as a signal handler, so we assume that SIGALRM
294**	has been blocked.
295**
296**	Parameters:
297**		One that is ignored; for compatibility with signal handlers.
298**
299**	Returns:
300**		none.
301**
302**	Side Effects:
303**		calls the next function in EventQueue.
304**
305**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
306**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
307**		DOING.
308*/
309
310/* ARGSUSED */
311SIGFUNC_DECL
312sm_tick(sig)
313	int sig;
314{
315	register SM_EVENT *ev;
316	pid_t mypid;
317	int save_errno = errno;
318#if SM_CONF_SETITIMER
319	struct itimerval clr;
320	struct timeval now;
321#else /* SM_CONF_SETITIMER */
322	register time_t now;
323#endif /* SM_CONF_SETITIMER */
324
325#if SM_CONF_SETITIMER
326	clr.it_interval.tv_sec = 0;
327	clr.it_interval.tv_usec = 0;
328	clr.it_value.tv_sec = 0;
329	clr.it_value.tv_usec = 0;
330	(void) setitimer(ITIMER_REAL, &clr, NULL);
331	gettimeofday(&now, NULL);
332#else /* SM_CONF_SETITIMER */
333	(void) alarm(0);
334	now = time(NULL);
335#endif /* SM_CONF_SETITIMER */
336
337	FIX_SYSV_SIGNAL(sig, sm_tick);
338	errno = save_errno;
339	CHECK_CRITICAL(sig);
340
341	mypid = getpid();
342	while (PendingSignal != 0)
343	{
344		int sigbit = 0;
345		int sig = 0;
346
347		if (bitset(PEND_SIGHUP, PendingSignal))
348		{
349			sigbit = PEND_SIGHUP;
350			sig = SIGHUP;
351		}
352		else if (bitset(PEND_SIGINT, PendingSignal))
353		{
354			sigbit = PEND_SIGINT;
355			sig = SIGINT;
356		}
357		else if (bitset(PEND_SIGTERM, PendingSignal))
358		{
359			sigbit = PEND_SIGTERM;
360			sig = SIGTERM;
361		}
362		else if (bitset(PEND_SIGUSR1, PendingSignal))
363		{
364			sigbit = PEND_SIGUSR1;
365			sig = SIGUSR1;
366		}
367		else
368		{
369			/* If we get here, we are in trouble */
370			abort();
371		}
372		PendingSignal &= ~sigbit;
373		kill(mypid, sig);
374	}
375
376#if SM_CONF_SETITIMER
377	gettimeofday(&now, NULL);
378#else /* SM_CONF_SETITIMER */
379	now = time(NULL);
380#endif /* SM_CONF_SETITIMER */
381	while ((ev = SmEventQueue) != NULL &&
382	       (ev->ev_pid != mypid ||
383#if SM_CONF_SETITIMER
384		timercmp(&ev->ev_time, &now, <=)
385#else /* SM_CONF_SETITIMER */
386		ev->ev_time <= now
387#endif /* SM_CONF_SETITIMER */
388		))
389	{
390		void (*f)();
391		int arg;
392		pid_t pid;
393
394		/* process the event on the top of the queue */
395		ev = SmEventQueue;
396		SmEventQueue = SmEventQueue->ev_link;
397
398		/* we must be careful in here because ev_func may not return */
399		f = ev->ev_func;
400		arg = ev->ev_arg;
401		pid = ev->ev_pid;
402		ENTER_CRITICAL();
403		ev->ev_link = SmFreeEventList;
404		SmFreeEventList = ev;
405		LEAVE_CRITICAL();
406		if (pid != getpid())
407			continue;
408		if (SmEventQueue != NULL)
409		{
410#if SM_CONF_SETITIMER
411			if (timercmp(&SmEventQueue->ev_time, &now, >))
412			{
413				timersub(&SmEventQueue->ev_time, &now,
414					 &clr.it_value);
415				clr.it_interval.tv_sec = 0;
416				clr.it_interval.tv_usec = 0;
417				if (clr.it_value.tv_sec < 0)
418					clr.it_value.tv_sec = 0;
419				if (clr.it_value.tv_sec == 0 &&
420				    clr.it_value.tv_usec == 0)
421					clr.it_value.tv_usec = 1000;
422				(void) setitimer(ITIMER_REAL, &clr, NULL);
423			}
424			else
425			{
426				clr.it_interval.tv_sec = 0;
427				clr.it_interval.tv_usec = 0;
428				clr.it_value.tv_sec = 3;
429				clr.it_value.tv_usec = 0;
430				(void) setitimer(ITIMER_REAL, &clr, NULL);
431			}
432#else /* SM_CONF_SETITIMER */
433			if (SmEventQueue->ev_time > now)
434				(void) alarm((unsigned) (SmEventQueue->ev_time
435							 - now));
436			else
437				(void) alarm(3);
438#endif /* SM_CONF_SETITIMER */
439		}
440
441		/* call ev_func */
442		errno = save_errno;
443		(*f)(arg);
444#if SM_CONF_SETITIMER
445		clr.it_interval.tv_sec = 0;
446		clr.it_interval.tv_usec = 0;
447		clr.it_value.tv_sec = 0;
448		clr.it_value.tv_usec = 0;
449		(void) setitimer(ITIMER_REAL, &clr, NULL);
450		gettimeofday(&now, NULL);
451#else /* SM_CONF_SETITIMER */
452		(void) alarm(0);
453		now = time(NULL);
454#endif /* SM_CONF_SETITIMER */
455	}
456	if (SmEventQueue != NULL)
457	{
458#if SM_CONF_SETITIMER
459		timersub(&SmEventQueue->ev_time, &now, &clr.it_value);
460		clr.it_interval.tv_sec = 0;
461		clr.it_interval.tv_usec = 0;
462		if (clr.it_value.tv_sec < 0)
463			clr.it_value.tv_sec = 0;
464		if (clr.it_value.tv_sec == 0 && clr.it_value.tv_usec == 0)
465			clr.it_value.tv_usec = 1000;
466		(void) setitimer(ITIMER_REAL, &clr, NULL);
467#else /* SM_CONF_SETITIMER */
468		(void) alarm((unsigned) (SmEventQueue->ev_time - now));
469#endif /* SM_CONF_SETITIMER */
470	}
471	errno = save_errno;
472	return SIGFUNC_RETURN;
473}
474/*
475**  SLEEP -- a version of sleep that works with this stuff
476**
477**	Because Unix sleep uses the alarm facility, I must reimplement
478**	it here.
479**
480**	Parameters:
481**		intvl -- time to sleep.
482**
483**	Returns:
484**		zero.
485**
486**	Side Effects:
487**		waits for intvl time.  However, other events can
488**		be run during that interval.
489*/
490
491
492static bool	volatile SmSleepDone;
493
494#ifndef SLEEP_T
495# define SLEEP_T	unsigned int
496#endif /* ! SLEEP_T */
497
498SLEEP_T
499sleep(intvl)
500	unsigned int intvl;
501{
502	int was_held;
503
504	if (intvl == 0)
505		return (SLEEP_T) 0;
506	SmSleepDone = false;
507	(void) sm_setevent((time_t) intvl, sm_endsleep, 0);
508	was_held = sm_releasesignal(SIGALRM);
509	while (!SmSleepDone)
510		(void) pause();
511	if (was_held > 0)
512		(void) sm_blocksignal(SIGALRM);
513	return (SLEEP_T) 0;
514}
515
516static void
517sm_endsleep()
518{
519	/*
520	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
521	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
522	**	DOING.
523	*/
524
525	SmSleepDone = true;
526}
527
528