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