clock.c revision 120256
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>
15SM_RCSID("@(#)$Id: clock.c,v 1.35.2.10 2003/06/26 16:36:49 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#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
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
60static SM_EVENT	*volatile SmEventQueue;		/* head of event queue */
61static SM_EVENT	*volatile SmFreeEventList;	/* list of free events */
62
63SM_EVENT *
64sm_seteventm(intvl, func, arg)
65	int intvl;
66	void (*func)();
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
86SM_EVENT *
87sm_sigsafe_seteventm(intvl, func, arg)
88	int intvl;
89	void (*func)();
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 /*  SM_CONF_SETITIMER */
98	auto time_t now, nowi;
99#endif /*  SM_CONF_SETITIMER */
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 /* SM_CONF_SETITIMER */
126		if (ev->ev_time >= nowi)
127#endif /* SM_CONF_SETITIMER */
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
195void
196sm_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 /* SM_CONF_SETITIMER */
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
257void
258sm_clear_events()
259{
260	register SM_EVENT *ev;
261#if SM_CONF_SETITIMER
262	struct itimerval clr;
263#endif /* SM_CONF_SETITIMER */
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 */
318SIGFUNC_DECL
319sm_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 /* SM_CONF_SETITIMER */
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 /* SM_CONF_SETITIMER */
386	now = time(NULL);
387#endif /* SM_CONF_SETITIMER */
388	while ((ev = SmEventQueue) != NULL &&
389	       (ev->ev_pid != mypid ||
390#if SM_CONF_SETITIMER
391		timercmp(&ev->ev_time, &now, <=)
392#else /* SM_CONF_SETITIMER */
393		ev->ev_time <= now
394#endif /* SM_CONF_SETITIMER */
395		))
396	{
397		void (*f)();
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
500static void	sm_endsleep __P((void));
501static bool	volatile SmSleepDone;
502# endif /* !HAVE_NANOSLEEP */
503
504#ifndef SLEEP_T
505# define SLEEP_T	unsigned int
506#endif /* ! SLEEP_T */
507
508SLEEP_T
509sleep(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#endif /* _FFR_SLEEP_USE_SELECT > 0 */
527#if SM_CONF_SETITIMER
528	struct timeval now, begin, diff;
529# if _FFR_SLEEP_USE_SELECT > 0
530	struct timeval sm_io_to, slpv;
531# endif /* _FFR_SLEEP_USE_SELECT > 0 */
532#else /*  SM_CONF_SETITIMER */
533	time_t begin, now;
534#endif /*  SM_CONF_SETITIMER */
535
536	if (intvl == 0)
537		return (SLEEP_T) 0;
538#if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
539	if (intvl > _FFR_MAX_SLEEP_TIME)
540	{
541		syslog(LOG_ERR, "sleep: interval=%u exceeds max value %d",
542			intvl, _FFR_MAX_SLEEP_TIME);
543# if 0
544		SM_ASSERT(intvl < (unsigned int) INT_MAX);
545# endif /* 0 */
546		intvl = _FFR_MAX_SLEEP_TIME;
547	}
548#endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
549	SmSleepDone = false;
550
551#if SM_CONF_SETITIMER
552# if _FFR_SLEEP_USE_SELECT > 0
553	slpv.tv_sec = intvl;
554	slpv.tv_usec = 0;
555# endif /* _FFR_SLEEP_USE_SELECT > 0 */
556	(void) gettimeofday(&now, NULL);
557	begin = now;
558#else /*  SM_CONF_SETITIMER */
559	now = begin = time(NULL);
560#endif /*  SM_CONF_SETITIMER */
561
562	ev = sm_setevent((time_t) intvl, sm_endsleep, 0);
563	if (ev == NULL)
564	{
565		/* COMPLAIN */
566#if 0
567		syslog(LOG_ERR, "sleep: sm_setevent(%u) failed", intvl);
568#endif /* 0 */
569		SmSleepDone = true;
570	}
571	was_held = sm_releasesignal(SIGALRM);
572
573	while (!SmSleepDone)
574	{
575#if SM_CONF_SETITIMER
576		(void) gettimeofday(&now, NULL);
577		timersub(&now, &begin, &diff);
578		if (diff.tv_sec < 0 ||
579		    (diff.tv_sec == 0 && diff.tv_usec == 0))
580			break;
581# if _FFR_SLEEP_USE_SELECT > 0
582		timersub(&slpv, &diff, &sm_io_to);
583# endif /* _FFR_SLEEP_USE_SELECT > 0 */
584#else /* SM_CONF_SETITIMER */
585		now = time(NULL);
586
587		/*
588		**  Check whether time expired before signal is released.
589		**  Due to the granularity of time() add 1 to be on the
590		**  safe side.
591		*/
592
593		if (!(begin + (time_t) intvl + 1 > now))
594			break;
595# if _FFR_SLEEP_USE_SELECT > 0
596		sm_io_to.tv_sec = intvl - (now - begin);
597		if (sm_io_to.tv_sec <= 0)
598			sm_io_to.tv_sec = 1;
599		sm_io_to.utv_sec = 0;
600# endif /* _FFR_SLEEP_USE_SELECT > 0 */
601#endif /* SM_CONF_SETITIMER */
602#if _FFR_SLEEP_USE_SELECT > 0
603		if (intvl <= _FFR_SLEEP_USE_SELECT)
604		{
605			r = select(0, NULL, NULL, NULL, &sm_io_to);
606			if (r == 0)
607				break;
608		}
609		else
610#endif /* _FFR_SLEEP_USE_SELECT > 0 */
611		(void) pause();
612	}
613
614	/* if out of the loop without the event being triggered remove it */
615	if (!SmSleepDone)
616		sm_clrevent(ev);
617	if (was_held > 0)
618		(void) sm_blocksignal(SIGALRM);
619	return (SLEEP_T) 0;
620#endif /* HAVE_NANOSLEEP */
621}
622
623#if !HAVE_NANOSLEEP
624static void
625sm_endsleep()
626{
627	/*
628	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
629	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
630	**	DOING.
631	*/
632
633	SmSleepDone = true;
634}
635#endif /* !HAVE_NANOSLEEP */
636
637