xref: /original-bsd/usr.bin/mail/sigretro.c (revision 0eaa7944)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)sigretro.c	5.1 (Berkeley) 06/06/85";
9 #endif not lint
10 
11 #include <signal.h>
12 #include <errno.h>
13 #include <setjmp.h>
14 #include "sigretro.h"
15 
16 /*
17  * Retrofit new signal interface to old signal primitives.
18  * Supported routines:
19  *	sigsys(sig, func)
20  *	sigset(sig, func)
21  *	sighold(sig)
22  *	sigrelse(sig)
23  *	sigignore(sig)
24  *	sigpause(sig)
25  * Also,
26  *	sigchild()
27  *		to set all held signals to ignored signals in the
28  *		child process after fork(2)
29  */
30 
31 typedef	int	(*sigtype)();
32 
33 sigtype	sigdisp(), sighold(), sigignore();
34 
35 /*
36  * The following helps us keep the extended signal semantics together.
37  * We remember for each signal the address of the function we're
38  * supposed to call.  s_func is SIG_DFL / SIG_IGN if appropriate.
39  */
40 struct sigtable {
41 	sigtype	s_func;			/* What to call */
42 	int	s_flag;			/* Signal flags; see below */
43 } sigtable[NSIG + 1];
44 
45 /*
46  * Signal flag values.
47  */
48 #define	SHELD		1		/* Signal is being held */
49 #define	SDEFER		2		/* Signal occured while held */
50 #define	SSET		4		/* s_func is believable */
51 #define	SPAUSE		8		/* are pausing, waiting for sig */
52 
53 jmp_buf	_pause;				/* For doing sigpause() */
54 
55 /*
56  * Approximate sigsys() system call
57  * This is almost useless since one only calls sigsys()
58  * in the child of a vfork().  If you have vfork(), you have new signals
59  * anyway.  The real sigsys() does all the stuff needed to support
60  * the real sigset() library.  We don't bother here, assuming that
61  * you are either ignoring or defaulting a signal in the child.
62  */
63 sigtype
64 sigsys(sig, func)
65 	sigtype func;
66 {
67 	sigtype old;
68 
69 	old = sigdisp(sig);
70 	signal(sig, func);
71 	return(old);
72 }
73 
74 /*
75  * Set the (permanent) disposition of a signal.
76  * If the signal is subsequently (or even now) held,
77  * the action you set here can be enabled using sigrelse().
78  */
79 sigtype
80 sigset(sig, func)
81 	sigtype func;
82 {
83 	sigtype old;
84 	int _sigtramp();
85 	extern int errno;
86 
87 	if (sig < 1 || sig > NSIG) {
88 		errno = EINVAL;
89 		return(BADSIG);
90 	}
91 	old = sigdisp(sig);
92 	/*
93 	 * Does anyone actually call sigset with SIG_HOLD!?
94 	 */
95 	if (func == SIG_HOLD) {
96 		sighold(sig);
97 		return(old);
98 	}
99 	sigtable[sig].s_flag |= SSET;
100 	sigtable[sig].s_func = func;
101 	if (func == SIG_DFL) {
102 		/*
103 		 * If signal has been held, must retain
104 		 * the catch so that we can note occurrance
105 		 * of signal.
106 		 */
107 		if ((sigtable[sig].s_flag & SHELD) == 0)
108 			signal(sig, SIG_DFL);
109 		else
110 			signal(sig, _sigtramp);
111 		return(old);
112 	}
113 	if (func == SIG_IGN) {
114 		/*
115 		 * Clear pending signal
116 		 */
117 		signal(sig, SIG_IGN);
118 		sigtable[sig].s_flag &= ~SDEFER;
119 		return(old);
120 	}
121 	signal(sig, _sigtramp);
122 	return(old);
123 }
124 
125 /*
126  * Hold a signal.
127  * This CAN be tricky if the signal's disposition is SIG_DFL.
128  * In that case, we still catch the signal so we can note it
129  * happened and do something crazy later.
130  */
131 sigtype
132 sighold(sig)
133 {
134 	sigtype old;
135 	extern int errno;
136 
137 	if (sig < 1 || sig > NSIG) {
138 		errno = EINVAL;
139 		return(BADSIG);
140 	}
141 	old = sigdisp(sig);
142 	if (sigtable[sig].s_flag & SHELD)
143 		return(old);
144 	/*
145 	 * When the default action is required, we have to
146 	 * set up to catch the signal to note signal's occurrance.
147 	 */
148 	if (old == SIG_DFL) {
149 		sigtable[sig].s_flag |= SSET;
150 		signal(sig, _sigtramp);
151 	}
152 	sigtable[sig].s_flag |= SHELD;
153 	return(old);
154 }
155 
156 /*
157  * Release a signal
158  * If the signal occurred while we had it held, cause the signal.
159  */
160 sigtype
161 sigrelse(sig)
162 {
163 	sigtype old;
164 	extern int errno;
165 	int _sigtramp();
166 
167 	if (sig < 1 || sig > NSIG) {
168 		errno = EINVAL;
169 		return(BADSIG);
170 	}
171 	old = sigdisp(sig);
172 	if ((sigtable[sig].s_flag & SHELD) == 0)
173 		return(old);
174 	sigtable[sig].s_flag &= ~SHELD;
175 	if (sigtable[sig].s_flag & SDEFER)
176 		_sigtramp(sig);
177 	/*
178 	 * If disposition was the default, then we can unset the
179 	 * catch to _sigtramp() and let the system do the work.
180 	 */
181 	if (sigtable[sig].s_func == SIG_DFL)
182 		signal(sig, SIG_DFL);
183 	return(old);
184 }
185 
186 /*
187  * Ignore a signal.
188  */
189 sigtype
190 sigignore(sig)
191 {
192 
193 	return(sigset(sig, SIG_IGN));
194 }
195 
196 /*
197  * Pause, waiting for sig to occur.
198  * We assume LUSER called us with the signal held.
199  * When we got the signal, mark the signal as having
200  * occurred.  It will actually cause something when
201  * the signal is released.
202  *
203  * This is probably useless without job control anyway.
204  */
205 sigpause(sig)
206 {
207 	extern int errno;
208 
209 	if (sig < 1 || sig > NSIG) {
210 		errno = EINVAL;
211 		return;
212 	}
213 	sigtable[sig].s_flag |= SHELD|SPAUSE;
214 	if (setjmp(_pause) == 0)
215 		pause();
216 	sigtable[sig].s_flag &= ~SPAUSE;
217 	sigtable[sig].s_flag |= SDEFER;
218 }
219 
220 /*
221  * In the child process after fork(2), set the disposition of all held
222  * signals to SIG_IGN.  This is a new procedure not in the real sigset()
223  * package, provided for retrofitting purposes.
224  */
225 sigchild()
226 {
227 	register int i;
228 
229 	for (i = 1; i <= NSIG; i++)
230 		if (sigtable[i].s_flag & SHELD)
231 			signal(i, SIG_IGN);
232 }
233 
234 /*
235  * Return the current disposition of a signal
236  * If we have not set this signal before, we have to
237  * ask the system
238  */
239 sigtype
240 sigdisp(sig)
241 {
242 	extern int errno;
243 	sigtype old;
244 
245 	if (sig < 1 || sig > NSIG) {
246 		errno = EINVAL;
247 		return(BADSIG);
248 	}
249 	/*
250 	 * If we have no knowledge of this signal,
251 	 * ask the system, then save the result for later.
252 	 */
253 	if ((sigtable[sig].s_flag & SSET) == 0) {
254 		old = signal(sig, SIG_IGN);
255 		sigtable[sig].s_func = old;
256 		sigtable[sig].s_flag |= SSET;
257 		signal(sig, old);
258 		return(old);
259 	}
260 	/*
261 	 * If we have set this signal before, then sigset()
262 	 * will have been careful to leave something meaningful
263 	 * in s_func.
264 	 */
265 	return(sigtable[sig].s_func);
266 }
267 
268 /*
269  * The following routine gets called for any signal
270  * that is to be trapped to a user function.
271  */
272 _sigtramp(sig)
273 {
274 	extern int errno;
275 	sigtype old;
276 
277 	if (sig < 1 || sig > NSIG) {
278 		errno = EINVAL;
279 		return;
280 	}
281 
282 top:
283 	old = signal(sig, SIG_IGN);
284 	/*
285 	 * If signal being paused on, wakeup sigpause()
286 	 */
287 	if (sigtable[sig].s_flag & SPAUSE)
288 		longjmp(_pause, 1);
289 	/*
290 	 * If signal being held, mark its table entry
291 	 * so we can trigger it when signal released.
292 	 * Then just return.
293 	 */
294 	if (sigtable[sig].s_flag & SHELD) {
295 		sigtable[sig].s_flag |= SDEFER;
296 		signal(sig, _sigtramp);
297 		return;
298 	}
299 	/*
300 	 * If the signal is being ignored, just return.
301 	 * This would make SIGCONT more normal, but of course
302 	 * any system with SIGCONT also has the new signal pkg, so...
303 	 */
304 	if (sigtable[sig].s_func == SIG_IGN)
305 		return;
306 	/*
307 	 * If the signal is SIG_DFL, then we probably got here
308 	 * by holding the signal, having it happen, then releasing
309 	 * the signal.  I wonder if a process is allowed to send
310 	 * a signal to itself?
311 	 */
312 	if (sigtable[sig].s_func == SIG_DFL) {
313 		signal(sig, SIG_DFL);
314 		kill(getpid(), sig);
315 		/* Will we get back here? */
316 		return;
317 	}
318 	/*
319 	 * Looks like we should just cause the signal...
320 	 * We hold the signal for the duration of the user's
321 	 * code with the signal re-enabled.  If the signal
322 	 * happens again while in user code, we will recursively
323 	 * trap here and mark that we had another occurance
324 	 * and return to the user's trap code.  When we return
325 	 * from there, we can cause the signal again.
326 	 */
327 	sigtable[sig].s_flag &= ~SDEFER;
328 	sigtable[sig].s_flag |= SHELD;
329 	signal(sig, _sigtramp);
330 	(*sigtable[sig].s_func)(sig);
331 	/*
332 	 * If the signal re-occurred while in the user's routine,
333 	 * just go try it again...
334 	 */
335 	sigtable[sig].s_flag &= ~SHELD;
336 	if (sigtable[sig].s_flag & SDEFER)
337 		goto top;
338 }
339