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