Commit | Line | Data |
---|---|---|
761330fe DF |
1 | /* |
2 | * Copyright (c) 1980 Regents of the University of California. | |
0c5f72fb KB |
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. | |
761330fe DF |
11 | */ |
12 | ||
0c5f72fb KB |
13 | #ifdef notdef |
14 | static char sccsid[] = "@(#)sigretro.c 5.3 (Berkeley) %G%"; | |
15 | #endif /* notdef */ | |
ac348c3c SL |
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 | } |