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