Commit | Line | Data |
---|---|---|
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 | 8 | static 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 | ||
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 | } |