xref: /original-bsd/usr.sbin/sendmail/src/clock.c (revision 28301386)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted
7  * provided that the above copyright notice and this paragraph are
8  * duplicated in all such forms and that any documentation,
9  * advertising materials, and other materials related to such
10  * distribution and use acknowledge that the software was developed
11  * by the University of California, Berkeley.  The name of the
12  * University may not be used to endorse or promote products derived
13  * from this software without specific prior written permission.
14  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17  */
18 
19 #ifndef lint
20 static char sccsid[] = "@(#)clock.c	5.6 (Berkeley) 06/30/88";
21 #endif /* not lint */
22 
23 # include "sendmail.h"
24 # include <signal.h>
25 
26 /*
27 **  SETEVENT -- set an event to happen at a specific time.
28 **
29 **	Events are stored in a sorted list for fast processing.
30 **	An event only applies to the process that set it.
31 **
32 **	Parameters:
33 **		intvl -- intvl until next event occurs.
34 **		func -- function to call on event.
35 **		arg -- argument to func on event.
36 **
37 **	Returns:
38 **		none.
39 **
40 **	Side Effects:
41 **		none.
42 */
43 
44 EVENT *
45 setevent(intvl, func, arg)
46 	time_t intvl;
47 	int (*func)();
48 	int arg;
49 {
50 	register EVENT **evp;
51 	register EVENT *ev;
52 	auto time_t now;
53 	extern tick();
54 
55 # ifdef DEBUG
56 	if (intvl <= 0)
57 	{
58 		syserr("setevent: intvl=%ld\n", intvl);
59 		return (NULL);
60 	}
61 # endif DEBUG
62 
63 	(void) time(&now);
64 
65 	/* search event queue for correct position */
66 	for (evp = &EventQueue; (ev = *evp) != NULL; evp = &ev->ev_link)
67 	{
68 		if (ev->ev_time >= now + intvl)
69 			break;
70 	}
71 
72 	/* insert new event */
73 	ev = (EVENT *) xalloc(sizeof *ev);
74 	ev->ev_time = now + intvl;
75 	ev->ev_func = func;
76 	ev->ev_arg = arg;
77 	ev->ev_pid = getpid();
78 	ev->ev_link = *evp;
79 	*evp = ev;
80 
81 # ifdef DEBUG
82 	if (tTd(5, 5))
83 		printf("setevent: intvl=%ld, for=%ld, func=%x, arg=%d, ev=%x\n",
84 			intvl, now + intvl, func, arg, ev);
85 # endif DEBUG
86 
87 	tick();
88 	return (ev);
89 }
90 /*
91 **  CLREVENT -- remove an event from the event queue.
92 **
93 **	Parameters:
94 **		ev -- pointer to event to remove.
95 **
96 **	Returns:
97 **		none.
98 **
99 **	Side Effects:
100 **		arranges for event ev to not happen.
101 */
102 
103 clrevent(ev)
104 	register EVENT *ev;
105 {
106 	register EVENT **evp;
107 
108 # ifdef DEBUG
109 	if (tTd(5, 5))
110 		printf("clrevent: ev=%x\n", ev);
111 # endif DEBUG
112 	if (ev == NULL)
113 		return;
114 
115 	/* find the parent event */
116 	(void) signal(SIGALRM, SIG_IGN);
117 	for (evp = &EventQueue; *evp != NULL; evp = &(*evp)->ev_link)
118 	{
119 		if (*evp == ev)
120 			break;
121 	}
122 
123 	/* now remove it */
124 	if (*evp != NULL)
125 	{
126 		*evp = ev->ev_link;
127 		free((char *) ev);
128 	}
129 
130 	/* restore clocks and pick up anything spare */
131 	tick();
132 }
133 /*
134 **  TICK -- take a clock tick
135 **
136 **	Called by the alarm clock.  This routine runs events as needed.
137 **
138 **	Parameters:
139 **		none.
140 **
141 **	Returns:
142 **		none.
143 **
144 **	Side Effects:
145 **		calls the next function in EventQueue.
146 */
147 
148 tick()
149 {
150 	register time_t now;
151 	register EVENT *ev;
152 	int mypid = getpid();
153 
154 	(void) signal(SIGALRM, SIG_IGN);
155 	(void) alarm(0);
156 	now = curtime();
157 
158 # ifdef DEBUG
159 	if (tTd(5, 4))
160 		printf("tick: now=%ld\n", now);
161 # endif DEBUG
162 
163 	while ((ev = EventQueue) != NULL &&
164 	       (ev->ev_time <= now || ev->ev_pid != mypid))
165 	{
166 		int (*f)();
167 		int arg;
168 		int pid;
169 
170 		/* process the event on the top of the queue */
171 		ev = EventQueue;
172 		EventQueue = EventQueue->ev_link;
173 # ifdef DEBUG
174 		if (tTd(5, 6))
175 			printf("tick: ev=%x, func=%x, arg=%d, pid=%d\n", ev,
176 				ev->ev_func, ev->ev_arg, ev->ev_pid);
177 # endif DEBUG
178 
179 		/* we must be careful in here because ev_func may not return */
180 		(void) signal(SIGALRM, tick);
181 #ifdef SIGVTALRM
182 		/* reset 4.2bsd signal mask to allow future alarms */
183 		(void) sigsetmask(sigblock(0) & ~sigmask(SIGALRM));
184 #endif SIGVTALRM
185 
186 		f = ev->ev_func;
187 		arg = ev->ev_arg;
188 		pid = ev->ev_pid;
189 		free((char *) ev);
190 		if (pid != getpid())
191 			continue;
192 		if (EventQueue != NULL)
193 		{
194 			if (EventQueue->ev_time > now)
195 				(void) alarm((unsigned) (EventQueue->ev_time - now));
196 			else
197 				(void) alarm(3);
198 		}
199 		(*f)(arg);
200 		(void) alarm(0);
201 		now = curtime();
202 	}
203 	(void) signal(SIGALRM, tick);
204 	if (EventQueue != NULL)
205 		(void) alarm((unsigned) (EventQueue->ev_time - now));
206 }
207 /*
208 **  SLEEP -- a version of sleep that works with this stuff
209 **
210 **	Because sleep uses the alarm facility, I must reimplement
211 **	it here.
212 **
213 **	Parameters:
214 **		intvl -- time to sleep.
215 **
216 **	Returns:
217 **		none.
218 **
219 **	Side Effects:
220 **		waits for intvl time.  However, other events can
221 **		be run during that interval.
222 */
223 
224 static bool	SleepDone;
225 
226 sleep(intvl)
227 	unsigned int intvl;
228 {
229 	extern endsleep();
230 
231 	if (intvl == 0)
232 		return;
233 	SleepDone = FALSE;
234 	(void) setevent((time_t) intvl, endsleep, 0);
235 	while (!SleepDone)
236 		pause();
237 }
238 
239 static
240 endsleep()
241 {
242 	SleepDone = TRUE;
243 }
244