trap.c revision 1.2 1 /* $NetBSD: trap.c,v 1.2 1997/01/12 19:12:22 tls 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][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)
88 const char *name;
89 {
90 int i;
91 register Trap *p;
92
93 if (digit(*name)) {
94 int n;
95
96 if (getn(name, &n) && 0 <= n && n < SIGNALS)
97 return &sigtraps[n];
98 return NULL;
99 }
100 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
101 if (p->name && strcasecmp(p->name, name) == 0)
102 return p;
103 return NULL;
104 }
105
106 /*
107 * trap signal handler
108 */
109 RETSIGTYPE
110 trapsig(i)
111 int i;
112 {
113 Trap *p = &sigtraps[i];
114
115 trap = p->set = 1;
116 if (p->flags & TF_DFL_INTR)
117 intrsig = 1;
118 if ((p->flags & TF_FATAL) && !p->trap) {
119 fatal_trap = 1;
120 intrsig = 1;
121 }
122 if (p->shtrap)
123 (*p->shtrap)(i);
124 #ifdef V7_SIGNALS
125 if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */
126 sigaction(i, &Sigact_trap, (struct sigaction *) 0);
127 #endif /* V7_SIGNALS */
128 return RETSIGVAL;
129 }
130
131 /* called when we want to allow the user to ^C out of something - won't
132 * work if user has trapped SIGINT.
133 */
134 void
135 intrcheck()
136 {
137 if (intrsig)
138 runtraps(TF_DFL_INTR|TF_FATAL);
139 }
140
141 /* called after EINTR to check if a signal with normally causes process
142 * termination has been received.
143 */
144 int
145 fatal_trap_check()
146 {
147 int i;
148 Trap *p;
149
150 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
151 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
152 if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
153 /* return value is used as an exit code */
154 return 128 + p->signal;
155 return 0;
156 }
157
158 /* Returns the signal number of any pending traps: ie, a signal which has
159 * occured for which a trap has been set or for which the TF_DFL_INTR flag
160 * is set.
161 */
162 int
163 trap_pending()
164 {
165 int i;
166 Trap *p;
167
168 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
169 if (p->set && ((p->trap && p->trap[0])
170 || ((p->flags & (TF_DFL_INTR|TF_FATAL))
171 && !p->trap)))
172 return p->signal;
173 return 0;
174 }
175
176 /*
177 * run any pending traps. If intr is set, only run traps that
178 * can interrupt commands.
179 */
180 void
181 runtraps(flag)
182 int flag;
183 {
184 int i;
185 register Trap *p;
186
187 #ifdef KSH
188 if (ksh_tmout_state == TMOUT_LEAVING) {
189 ksh_tmout_state = TMOUT_EXECUTING;
190 warningf(FALSE, "timed out waiting for input");
191 unwind(LEXIT);
192 } else
193 /* XXX: this means the alarm will have no effect if a trap
194 * is caught after the alarm() was started...not good.
195 */
196 ksh_tmout_state = TMOUT_EXECUTING;
197 #endif /* KSH */
198 if (!flag)
199 trap = 0;
200 if (flag & TF_DFL_INTR)
201 intrsig = 0;
202 if (flag & TF_FATAL)
203 fatal_trap = 0;
204 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
205 if (p->set && (!flag
206 || ((p->flags & flag) && p->trap == (char *) 0)))
207 runtrap(p);
208 }
209
210 void
211 runtrap(p)
212 Trap *p;
213 {
214 int i = p->signal;
215 char *trapstr = p->trap;
216 int oexstat;
217 int UNINITIALIZED(old_changed);
218
219 p->set = 0;
220 if (trapstr == (char *) 0) { /* SIG_DFL */
221 if (p->flags & TF_FATAL) {
222 /* eg, SIGHUP */
223 exstat = 128 + i;
224 unwind(LLEAVE);
225 }
226 if (p->flags & TF_DFL_INTR) {
227 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
228 exstat = 128 + i;
229 unwind(LINTR);
230 }
231 return;
232 }
233 if (trapstr[0] == '\0') /* SIG_IGN */
234 return;
235 if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */
236 old_changed = p->flags & TF_CHANGED;
237 p->flags &= ~TF_CHANGED;
238 p->trap = (char *) 0;
239 }
240 oexstat = exstat;
241 command(trapstr);
242 exstat = oexstat;
243 if (i == SIGEXIT_ || i == SIGERR_) {
244 if (p->flags & TF_CHANGED)
245 /* don't clear TF_CHANGED */
246 afree(trapstr, APERM);
247 else
248 p->trap = trapstr;
249 p->flags |= old_changed;
250 }
251 }
252
253 /* clear pending traps and reset user's trap handlers; used after fork(2) */
254 void
255 cleartraps()
256 {
257 int i;
258 Trap *p;
259
260 trap = 0;
261 intrsig = 0;
262 fatal_trap = 0;
263 for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) {
264 p->set = 0;
265 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
266 settrap(p, (char *) 0);
267 }
268 }
269
270 /* restore signals just before an exec(2) */
271 void
272 restoresigs()
273 {
274 int i;
275 Trap *p;
276
277 for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++)
278 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
279 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
280 SS_RESTORE_CURR|SS_FORCE);
281 }
282
283 void
284 settrap(p, s)
285 Trap *p;
286 char *s;
287 {
288 handler_t f;
289
290 if (p->trap)
291 afree(p->trap, APERM);
292 p->trap = str_save(s, APERM); /* handles s == 0 */
293 p->flags |= TF_CHANGED;
294 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
295
296 p->flags |= TF_USER_SET;
297 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
298 f = trapsig;
299 else if (p->flags & TF_SHELL_USES) {
300 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
301 /* do what user wants at exec time */
302 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
303 if (f == SIG_IGN)
304 p->flags |= TF_EXEC_IGN;
305 else
306 p->flags |= TF_EXEC_DFL;
307 }
308 /* assumes handler already set to what shell wants it
309 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
310 */
311 return;
312 }
313
314 /* todo: should we let user know signal is ignored? how? */
315 setsig(p, f, SS_RESTORE_CURR|SS_USER);
316 }
317
318 /* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
319 * kill shell (unless user catches it and exits)
320 */
321 int
322 block_pipe()
323 {
324 int restore_dfl = 0;
325 Trap *p = &sigtraps[SIGPIPE];
326
327 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
328 setsig(p, SIG_IGN, SS_RESTORE_CURR);
329 if (p->flags & TF_ORIG_DFL)
330 restore_dfl = 1;
331 } else if (p->cursig == SIG_DFL) {
332 setsig(p, SIG_IGN, SS_RESTORE_CURR);
333 restore_dfl = 1; /* restore to SIG_DFL */
334 }
335 return restore_dfl;
336 }
337
338 /* Called by c_print() to undo whatever block_pipe() did */
339 void
340 restore_pipe(restore_dfl)
341 int restore_dfl;
342 {
343 if (restore_dfl)
344 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
345 }
346
347 /* Set action for a signal. Action may not be set if original
348 * action was SIG_IGN, depending on the value of flags and
349 * FTALKING.
350 */
351 int
352 setsig(p, f, flags)
353 Trap *p;
354 handler_t f;
355 int flags;
356 {
357 struct sigaction sigact;
358
359 if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
360 return 1;
361
362 /* First time setting this signal? If so, get and note the current
363 * setting.
364 */
365 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
366 sigaction(p->signal, &Sigact_ign, &sigact);
367 p->flags |= sigact.sa_handler == SIG_IGN ?
368 TF_ORIG_IGN : TF_ORIG_DFL;
369 p->cursig = SIG_IGN;
370 }
371
372 /* Generally, an ignored signal stays ignored, except if
373 * - the user of an interactive shell wants to change it
374 * - the shell wants for force a change
375 */
376 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE)
377 && (!(flags & SS_USER) || !Flag(FTALKING)))
378 return 0;
379
380 setexecsig(p, flags & SS_RESTORE_MASK);
381
382 /* This is here 'cause there should be a way of clearing shtraps, but
383 * don't know if this is a sane way of doing it. At the moment,
384 * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH).
385 */
386 if (!(flags & SS_USER))
387 p->shtrap = (handler_t) 0;
388 if (flags & SS_SHTRAP) {
389 p->shtrap = f;
390 f = trapsig;
391 }
392
393 if (p->cursig != f) {
394 p->cursig = f;
395 sigemptyset(&sigact.sa_mask);
396 sigact.sa_flags = KSH_SA_FLAGS;
397 sigact.sa_handler = f;
398 sigaction(p->signal, &sigact, (struct sigaction *) 0);
399 }
400
401 return 1;
402 }
403
404 /* control what signal is set to before an exec() */
405 void
406 setexecsig(p, restore)
407 Trap *p;
408 int restore;
409 {
410 /* XXX debugging */
411 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
412 internal_errorf(1, "setexecsig: unset signal %d(%s)",
413 p->signal, p->name);
414
415 /* restore original value for exec'd kids */
416 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
417 switch (restore & SS_RESTORE_MASK) {
418 case SS_RESTORE_CURR: /* leave things as they currently are */
419 break;
420 case SS_RESTORE_ORIG:
421 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
422 break;
423 case SS_RESTORE_DFL:
424 p->flags |= TF_EXEC_DFL;
425 break;
426 case SS_RESTORE_IGN:
427 p->flags |= TF_EXEC_IGN;
428 break;
429 }
430 }
431