trap.c revision 1.4 1 /* $NetBSD: trap.c,v 1.4 2001/09/16 16:34:23 wiz Exp $ */
2
3 /*
4 * signal handling
5 */
6
7 /* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */
8 #define FROM_TRAP_C
9 #include "sh.h"
10
11 /* Table is indexed by signal number
12 *
13 * The script siglist.sh generates siglist.out, which is a sorted, complete
14 * list of signals
15 */
16 Trap sigtraps[SIGNALS+1] = {
17 { SIGEXIT_, "EXIT", "Signal 0" },
18 #include "siglist.out" /* generated by siglist.sh */
19 { SIGERR_, "ERR", "Error handler" },
20 };
21
22 static struct sigaction Sigact_ign, Sigact_trap;
23
24 void
25 inittraps()
26 {
27 #ifdef HAVE_SYS_SIGLIST
28 # ifndef SYS_SIGLIST_DECLARED
29 extern char *sys_siglist[];
30 # endif
31 int i;
32
33 /* Use system description, if available, for unknown signals... */
34 for (i = 0; i < NSIG; i++)
35 if (!sigtraps[i].name && sys_siglist[i] && sys_siglist[i][0])
36 sigtraps[i].mess = sys_siglist[i];
37 #endif /* HAVE_SYS_SIGLIST */
38
39 sigemptyset(&Sigact_ign.sa_mask);
40 Sigact_ign.sa_flags = KSH_SA_FLAGS;
41 Sigact_ign.sa_handler = SIG_IGN;
42 Sigact_trap = Sigact_ign;
43 Sigact_trap.sa_handler = trapsig;
44
45 sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
46 sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
47 sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */
48 sigtraps[SIGHUP].flags |= TF_FATAL;
49 sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
50
51 /* these are always caught so we can clean up any temproary files. */
52 setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
53 setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
54 setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
55 setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
56 }
57
58 #ifdef KSH
59 static RETSIGTYPE alarm_catcher ARGS((int sig));
60
61 void
62 alarm_init()
63 {
64 sigtraps[SIGALRM].flags |= TF_SHELL_USES;
65 setsig(&sigtraps[SIGALRM], alarm_catcher,
66 SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
67 }
68
69 static RETSIGTYPE
70 alarm_catcher(sig)
71 int sig;
72 {
73 if (ksh_tmout_state == TMOUT_READING) {
74 int left = alarm(0);
75
76 if (left == 0) {
77 ksh_tmout_state = TMOUT_LEAVING;
78 intrsig = 1;
79 } else
80 alarm(left);
81 }
82 return RETSIGVAL;
83 }
84 #endif /* KSH */
85
86 Trap *
87 gettrap(name, igncase)
88 const char *name;
89 int igncase;
90 {
91 int i;
92 register Trap *p;
93
94 if (digit(*name)) {
95 int n;
96
97 if (getn(name, &n) && 0 <= n && n < SIGNALS)
98 return &sigtraps[n];
99 return NULL;
100 }
101 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
102 if (p->name && (igncase ? strcasecmp(p->name, name) == 0
103 : strcmp(p->name, name) == 0))
104 return p;
105 return NULL;
106 }
107
108 /*
109 * trap signal handler
110 */
111 RETSIGTYPE
112 trapsig(i)
113 int i;
114 {
115 Trap *p = &sigtraps[i];
116
117 trap = p->set = 1;
118 if (p->flags & TF_DFL_INTR)
119 intrsig = 1;
120 if ((p->flags & TF_FATAL) && !p->trap) {
121 fatal_trap = 1;
122 intrsig = 1;
123 }
124 if (p->shtrap)
125 (*p->shtrap)(i);
126 #ifdef V7_SIGNALS
127 if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */
128 sigaction(i, &Sigact_trap, (struct sigaction *) 0);
129 #endif /* V7_SIGNALS */
130 return RETSIGVAL;
131 }
132
133 /* called when we want to allow the user to ^C out of something - won't
134 * work if user has trapped SIGINT.
135 */
136 void
137 intrcheck()
138 {
139 if (intrsig)
140 runtraps(TF_DFL_INTR|TF_FATAL);
141 }
142
143 /* called after EINTR to check if a signal with normally causes process
144 * termination has been received.
145 */
146 int
147 fatal_trap_check()
148 {
149 int i;
150 Trap *p;
151
152 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
153 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
154 if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
155 /* return value is used as an exit code */
156 return 128 + p->signal;
157 return 0;
158 }
159
160 /* Returns the signal number of any pending traps: ie, a signal which has
161 * occurred for which a trap has been set or for which the TF_DFL_INTR flag
162 * is set.
163 */
164 int
165 trap_pending()
166 {
167 int i;
168 Trap *p;
169
170 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
171 if (p->set && ((p->trap && p->trap[0])
172 || ((p->flags & (TF_DFL_INTR|TF_FATAL))
173 && !p->trap)))
174 return p->signal;
175 return 0;
176 }
177
178 /*
179 * run any pending traps. If intr is set, only run traps that
180 * can interrupt commands.
181 */
182 void
183 runtraps(flag)
184 int flag;
185 {
186 int i;
187 register Trap *p;
188
189 #ifdef KSH
190 if (ksh_tmout_state == TMOUT_LEAVING) {
191 ksh_tmout_state = TMOUT_EXECUTING;
192 warningf(FALSE, "timed out waiting for input");
193 unwind(LEXIT);
194 } else
195 /* XXX: this means the alarm will have no effect if a trap
196 * is caught after the alarm() was started...not good.
197 */
198 ksh_tmout_state = TMOUT_EXECUTING;
199 #endif /* KSH */
200 if (!flag)
201 trap = 0;
202 if (flag & TF_DFL_INTR)
203 intrsig = 0;
204 if (flag & TF_FATAL)
205 fatal_trap = 0;
206 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
207 if (p->set && (!flag
208 || ((p->flags & flag) && p->trap == (char *) 0)))
209 runtrap(p);
210 }
211
212 void
213 runtrap(p)
214 Trap *p;
215 {
216 int i = p->signal;
217 char *trapstr = p->trap;
218 int oexstat;
219 int UNINITIALIZED(old_changed);
220
221 p->set = 0;
222 if (trapstr == (char *) 0) { /* SIG_DFL */
223 if (p->flags & TF_FATAL) {
224 /* eg, SIGHUP */
225 exstat = 128 + i;
226 unwind(LLEAVE);
227 }
228 if (p->flags & TF_DFL_INTR) {
229 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
230 exstat = 128 + i;
231 unwind(LINTR);
232 }
233 return;
234 }
235 if (trapstr[0] == '\0') /* SIG_IGN */
236 return;
237 if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */
238 old_changed = p->flags & TF_CHANGED;
239 p->flags &= ~TF_CHANGED;
240 p->trap = (char *) 0;
241 }
242 oexstat = exstat;
243 /* Note: trapstr is fully parsed before anything is executed, thus
244 * no problem with afree(p->trap) in settrap() while still in use.
245 */
246 command(trapstr);
247 exstat = oexstat;
248 if (i == SIGEXIT_ || i == SIGERR_) {
249 if (p->flags & TF_CHANGED)
250 /* don't clear TF_CHANGED */
251 afree(trapstr, APERM);
252 else
253 p->trap = trapstr;
254 p->flags |= old_changed;
255 }
256 }
257
258 /* clear pending traps and reset user's trap handlers; used after fork(2) */
259 void
260 cleartraps()
261 {
262 int i;
263 Trap *p;
264
265 trap = 0;
266 intrsig = 0;
267 fatal_trap = 0;
268 for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) {
269 p->set = 0;
270 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
271 settrap(p, (char *) 0);
272 }
273 }
274
275 /* restore signals just before an exec(2) */
276 void
277 restoresigs()
278 {
279 int i;
280 Trap *p;
281
282 for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++)
283 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
284 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
285 SS_RESTORE_CURR|SS_FORCE);
286 }
287
288 void
289 settrap(p, s)
290 Trap *p;
291 char *s;
292 {
293 handler_t f;
294
295 if (p->trap)
296 afree(p->trap, APERM);
297 p->trap = str_save(s, APERM); /* handles s == 0 */
298 p->flags |= TF_CHANGED;
299 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
300
301 p->flags |= TF_USER_SET;
302 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
303 f = trapsig;
304 else if (p->flags & TF_SHELL_USES) {
305 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
306 /* do what user wants at exec time */
307 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
308 if (f == SIG_IGN)
309 p->flags |= TF_EXEC_IGN;
310 else
311 p->flags |= TF_EXEC_DFL;
312 }
313 /* assumes handler already set to what shell wants it
314 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
315 */
316 return;
317 }
318
319 /* todo: should we let user know signal is ignored? how? */
320 setsig(p, f, SS_RESTORE_CURR|SS_USER);
321 }
322
323 /* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
324 * kill shell (unless user catches it and exits)
325 */
326 int
327 block_pipe()
328 {
329 int restore_dfl = 0;
330 Trap *p = &sigtraps[SIGPIPE];
331
332 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
333 setsig(p, SIG_IGN, SS_RESTORE_CURR);
334 if (p->flags & TF_ORIG_DFL)
335 restore_dfl = 1;
336 } else if (p->cursig == SIG_DFL) {
337 setsig(p, SIG_IGN, SS_RESTORE_CURR);
338 restore_dfl = 1; /* restore to SIG_DFL */
339 }
340 return restore_dfl;
341 }
342
343 /* Called by c_print() to undo whatever block_pipe() did */
344 void
345 restore_pipe(restore_dfl)
346 int restore_dfl;
347 {
348 if (restore_dfl)
349 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
350 }
351
352 /* Set action for a signal. Action may not be set if original
353 * action was SIG_IGN, depending on the value of flags and
354 * FTALKING.
355 */
356 int
357 setsig(p, f, flags)
358 Trap *p;
359 handler_t f;
360 int flags;
361 {
362 struct sigaction sigact;
363
364 if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
365 return 1;
366
367 /* First time setting this signal? If so, get and note the current
368 * setting.
369 */
370 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
371 sigaction(p->signal, &Sigact_ign, &sigact);
372 p->flags |= sigact.sa_handler == SIG_IGN ?
373 TF_ORIG_IGN : TF_ORIG_DFL;
374 p->cursig = SIG_IGN;
375 }
376
377 /* Generally, an ignored signal stays ignored, except if
378 * - the user of an interactive shell wants to change it
379 * - the shell wants for force a change
380 */
381 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE)
382 && (!(flags & SS_USER) || !Flag(FTALKING)))
383 return 0;
384
385 setexecsig(p, flags & SS_RESTORE_MASK);
386
387 /* This is here 'cause there should be a way of clearing shtraps, but
388 * don't know if this is a sane way of doing it. At the moment,
389 * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH).
390 */
391 if (!(flags & SS_USER))
392 p->shtrap = (handler_t) 0;
393 if (flags & SS_SHTRAP) {
394 p->shtrap = f;
395 f = trapsig;
396 }
397
398 if (p->cursig != f) {
399 p->cursig = f;
400 sigemptyset(&sigact.sa_mask);
401 sigact.sa_flags = KSH_SA_FLAGS;
402 sigact.sa_handler = f;
403 sigaction(p->signal, &sigact, (struct sigaction *) 0);
404 }
405
406 return 1;
407 }
408
409 /* control what signal is set to before an exec() */
410 void
411 setexecsig(p, restore)
412 Trap *p;
413 int restore;
414 {
415 /* XXX debugging */
416 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
417 internal_errorf(1, "setexecsig: unset signal %d(%s)",
418 p->signal, p->name);
419
420 /* restore original value for exec'd kids */
421 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
422 switch (restore & SS_RESTORE_MASK) {
423 case SS_RESTORE_CURR: /* leave things as they currently are */
424 break;
425 case SS_RESTORE_ORIG:
426 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
427 break;
428 case SS_RESTORE_DFL:
429 p->flags |= TF_EXEC_DFL;
430 break;
431 case SS_RESTORE_IGN:
432 p->flags |= TF_EXEC_IGN;
433 break;
434 }
435 }
436