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