date and time created 87/02/15 16:03:36 by lepreau
[unix-history] / usr / src / usr.bin / mail / sigretro.c
CommitLineData
761330fe
DF
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
ac348c3c 7#ifndef lint
2fd8b883 8static char *sccsid = "@(#)sigretro.c 5.2 (Berkeley) %G%";
761330fe 9#endif not lint
ac348c3c
SL
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
31typedef int (*sigtype)();
32
33sigtype 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 */
40struct 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
53jmp_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 */
63sigtype
64sigsys(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 */
79sigtype
80sigset(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 */
131sigtype
132sighold(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 */
160sigtype
161sigrelse(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 */
189sigtype
190sigignore(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 */
205sigpause(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 */
225sigchild()
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 */
239sigtype
240sigdisp(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
282top:
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}