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