1/*
2 * Copyright (c) 1998-2004 Proofpoint, 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.48 2013-11-22 20:51:42 ca Exp $")
16#include <unistd.h>
17#include <time.h>
18#include <errno.h>
19#if SM_CONF_SETITIMER
20# include <sm/time.h>
21#endif
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
30#if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
31# include <syslog.h>
32#endif
33
34#ifndef sigmask
35# define sigmask(s)	(1 << ((s) - 1))
36#endif
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)__P((int));
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)__P((int));
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
98	auto time_t now, nowi;
99#endif
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
126		if (ev->ev_time >= nowi)
127#endif
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
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
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
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
386	now = time(NULL);
387#endif
388	while ((ev = SmEventQueue) != NULL &&
389	       (ev->ev_pid != mypid ||
390#if SM_CONF_SETITIMER
391		timercmp(&ev->ev_time, &now, <=)
392#else
393		ev->ev_time <= now
394#endif
395		))
396	{
397		void (*f)__P((int));
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((int));
501static bool	volatile SmSleepDone;
502# endif
503
504#ifndef SLEEP_T
505# define SLEEP_T	unsigned int
506#endif
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# if _FFR_SLEEP_USE_SELECT > 0
527	struct timeval sm_io_to;
528# endif
529#endif /* _FFR_SLEEP_USE_SELECT > 0 */
530#if SM_CONF_SETITIMER
531	struct timeval now, begin, diff;
532# if _FFR_SLEEP_USE_SELECT > 0
533	struct timeval slpv;
534# endif
535#else /*  SM_CONF_SETITIMER */
536	time_t begin, now;
537#endif /*  SM_CONF_SETITIMER */
538
539	if (intvl == 0)
540		return (SLEEP_T) 0;
541#if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
542	if (intvl > _FFR_MAX_SLEEP_TIME)
543	{
544		syslog(LOG_ERR, "sleep: interval=%u exceeds max value %d",
545			intvl, _FFR_MAX_SLEEP_TIME);
546# if 0
547		SM_ASSERT(intvl < (unsigned int) INT_MAX);
548# endif
549		intvl = _FFR_MAX_SLEEP_TIME;
550	}
551#endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
552	SmSleepDone = false;
553
554#if SM_CONF_SETITIMER
555# if _FFR_SLEEP_USE_SELECT > 0
556	slpv.tv_sec = intvl;
557	slpv.tv_usec = 0;
558# endif
559	(void) gettimeofday(&now, NULL);
560	begin = now;
561#else /*  SM_CONF_SETITIMER */
562	now = begin = time(NULL);
563#endif /*  SM_CONF_SETITIMER */
564
565	ev = sm_setevent((time_t) intvl, sm_endsleep, 0);
566	if (ev == NULL)
567	{
568		/* COMPLAIN */
569#if 0
570		syslog(LOG_ERR, "sleep: sm_setevent(%u) failed", intvl);
571#endif
572		SmSleepDone = true;
573	}
574	was_held = sm_releasesignal(SIGALRM);
575
576	while (!SmSleepDone)
577	{
578#if SM_CONF_SETITIMER
579		(void) gettimeofday(&now, NULL);
580		timersub(&now, &begin, &diff);
581		if (diff.tv_sec < 0 ||
582		    (diff.tv_sec == 0 && diff.tv_usec == 0))
583			break;
584# if _FFR_SLEEP_USE_SELECT > 0
585		timersub(&slpv, &diff, &sm_io_to);
586# endif
587#else /* SM_CONF_SETITIMER */
588		now = time(NULL);
589
590		/*
591		**  Check whether time expired before signal is released.
592		**  Due to the granularity of time() add 1 to be on the
593		**  safe side.
594		*/
595
596		if (!(begin + (time_t) intvl + 1 > now))
597			break;
598# if _FFR_SLEEP_USE_SELECT > 0
599		sm_io_to.tv_sec = intvl - (now - begin);
600		if (sm_io_to.tv_sec <= 0)
601			sm_io_to.tv_sec = 1;
602		sm_io_to.tv_usec = 0;
603# endif /* _FFR_SLEEP_USE_SELECT > 0 */
604#endif /* SM_CONF_SETITIMER */
605#if _FFR_SLEEP_USE_SELECT > 0
606		if (intvl <= _FFR_SLEEP_USE_SELECT)
607		{
608			r = select(0, NULL, NULL, NULL, &sm_io_to);
609			if (r == 0)
610				break;
611		}
612		else
613#endif /* _FFR_SLEEP_USE_SELECT > 0 */
614		(void) pause();
615	}
616
617	/* if out of the loop without the event being triggered remove it */
618	if (!SmSleepDone)
619		sm_clrevent(ev);
620	if (was_held > 0)
621		(void) sm_blocksignal(SIGALRM);
622	return (SLEEP_T) 0;
623#endif /* HAVE_NANOSLEEP */
624}
625
626#if !HAVE_NANOSLEEP
627static void
628sm_endsleep(ignore)
629	int ignore;
630{
631	/*
632	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
633	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
634	**	DOING.
635	*/
636
637	SmSleepDone = true;
638}
639#endif /* !HAVE_NANOSLEEP */
640
641