trap.c revision 1.1.1.2 1 /*
2 * signal handling
3 */
4
5 /* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */
6 #define FROM_TRAP_C
7 #include "sh.h"
8
9 /* Table is indexed by signal number
10 *
11 * The script siglist.sh generates siglist.out, which is a sorted, complete
12 * list of signals
13 */
14 Trap sigtraps[SIGNALS+1] = {
15 { SIGEXIT_, "EXIT", "Signal 0" },
16 #include "siglist.out" /* generated by siglist.sh */
17 { SIGERR_, "ERR", "Error handler" },
18 };
19
20 static struct sigaction Sigact_ign, Sigact_trap;
21
22 void
23 inittraps()
24 {
25 #ifdef HAVE_SYS_SIGLIST
26 # ifndef SYS_SIGLIST_DECLARED
27 extern char *sys_siglist[];
28 # endif
29 int i;
30
31 /* Use system description, if available, for unknown signals... */
32 for (i = 0; i < NSIG; i++)
33 if (!sigtraps[i].name && sys_siglist[i] && sys_siglist[i][0])
34 sigtraps[i].mess = sys_siglist[i];
35 #endif /* HAVE_SYS_SIGLIST */
36
37 sigemptyset(&Sigact_ign.sa_mask);
38 Sigact_ign.sa_flags = KSH_SA_FLAGS;
39 Sigact_ign.sa_handler = SIG_IGN;
40 Sigact_trap = Sigact_ign;
41 Sigact_trap.sa_handler = trapsig;
42
43 sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
44 sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
45 sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */
46 sigtraps[SIGHUP].flags |= TF_FATAL;
47 sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
48
49 /* these are always caught so we can clean up any temproary files. */
50 setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
51 setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
52 setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
53 setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
54 }
55
56 #ifdef KSH
57 static RETSIGTYPE alarm_catcher ARGS((int sig));
58
59 void
60 alarm_init()
61 {
62 sigtraps[SIGALRM].flags |= TF_SHELL_USES;
63 setsig(&sigtraps[SIGALRM], alarm_catcher,
64 SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
65 }
66
67 static RETSIGTYPE
68 alarm_catcher(sig)
69 int sig;
70 {
71 if (ksh_tmout_state == TMOUT_READING) {
72 int left = alarm(0);
73
74 if (left == 0) {
75 ksh_tmout_state = TMOUT_LEAVING;
76 intrsig = 1;
77 } else
78 alarm(left);
79 }
80 return RETSIGVAL;
81 }
82 #endif /* KSH */
83
84 Trap *
85 gettrap(name, igncase)
86 const char *name;
87 int igncase;
88 {
89 int i;
90 register Trap *p;
91
92 if (digit(*name)) {
93 int n;
94
95 if (getn(name, &n) && 0 <= n && n < SIGNALS)
96 return &sigtraps[n];
97 return NULL;
98 }
99 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
100 if (p->name && (igncase ? strcasecmp(p->name, name) == 0
101 : strcmp(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 /* Note: trapstr is fully parsed before anything is executed, thus
242 * no problem with afree(p->trap) in settrap() while still in use.
243 */
244 command(trapstr);
245 exstat = oexstat;
246 if (i == SIGEXIT_ || i == SIGERR_) {
247 if (p->flags & TF_CHANGED)
248 /* don't clear TF_CHANGED */
249 afree(trapstr, APERM);
250 else
251 p->trap = trapstr;
252 p->flags |= old_changed;
253 }
254 }
255
256 /* clear pending traps and reset user's trap handlers; used after fork(2) */
257 void
258 cleartraps()
259 {
260 int i;
261 Trap *p;
262
263 trap = 0;
264 intrsig = 0;
265 fatal_trap = 0;
266 for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) {
267 p->set = 0;
268 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
269 settrap(p, (char *) 0);
270 }
271 }
272
273 /* restore signals just before an exec(2) */
274 void
275 restoresigs()
276 {
277 int i;
278 Trap *p;
279
280 for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++)
281 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
282 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
283 SS_RESTORE_CURR|SS_FORCE);
284 }
285
286 void
287 settrap(p, s)
288 Trap *p;
289 char *s;
290 {
291 handler_t f;
292
293 if (p->trap)
294 afree(p->trap, APERM);
295 p->trap = str_save(s, APERM); /* handles s == 0 */
296 p->flags |= TF_CHANGED;
297 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
298
299 p->flags |= TF_USER_SET;
300 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
301 f = trapsig;
302 else if (p->flags & TF_SHELL_USES) {
303 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
304 /* do what user wants at exec time */
305 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
306 if (f == SIG_IGN)
307 p->flags |= TF_EXEC_IGN;
308 else
309 p->flags |= TF_EXEC_DFL;
310 }
311 /* assumes handler already set to what shell wants it
312 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
313 */
314 return;
315 }
316
317 /* todo: should we let user know signal is ignored? how? */
318 setsig(p, f, SS_RESTORE_CURR|SS_USER);
319 }
320
321 /* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
322 * kill shell (unless user catches it and exits)
323 */
324 int
325 block_pipe()
326 {
327 int restore_dfl = 0;
328 Trap *p = &sigtraps[SIGPIPE];
329
330 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
331 setsig(p, SIG_IGN, SS_RESTORE_CURR);
332 if (p->flags & TF_ORIG_DFL)
333 restore_dfl = 1;
334 } else if (p->cursig == SIG_DFL) {
335 setsig(p, SIG_IGN, SS_RESTORE_CURR);
336 restore_dfl = 1; /* restore to SIG_DFL */
337 }
338 return restore_dfl;
339 }
340
341 /* Called by c_print() to undo whatever block_pipe() did */
342 void
343 restore_pipe(restore_dfl)
344 int restore_dfl;
345 {
346 if (restore_dfl)
347 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
348 }
349
350 /* Set action for a signal. Action may not be set if original
351 * action was SIG_IGN, depending on the value of flags and
352 * FTALKING.
353 */
354 int
355 setsig(p, f, flags)
356 Trap *p;
357 handler_t f;
358 int flags;
359 {
360 struct sigaction sigact;
361
362 if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
363 return 1;
364
365 /* First time setting this signal? If so, get and note the current
366 * setting.
367 */
368 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
369 sigaction(p->signal, &Sigact_ign, &sigact);
370 p->flags |= sigact.sa_handler == SIG_IGN ?
371 TF_ORIG_IGN : TF_ORIG_DFL;
372 p->cursig = SIG_IGN;
373 }
374
375 /* Generally, an ignored signal stays ignored, except if
376 * - the user of an interactive shell wants to change it
377 * - the shell wants for force a change
378 */
379 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE)
380 && (!(flags & SS_USER) || !Flag(FTALKING)))
381 return 0;
382
383 setexecsig(p, flags & SS_RESTORE_MASK);
384
385 /* This is here 'cause there should be a way of clearing shtraps, but
386 * don't know if this is a sane way of doing it. At the moment,
387 * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH).
388 */
389 if (!(flags & SS_USER))
390 p->shtrap = (handler_t) 0;
391 if (flags & SS_SHTRAP) {
392 p->shtrap = f;
393 f = trapsig;
394 }
395
396 if (p->cursig != f) {
397 p->cursig = f;
398 sigemptyset(&sigact.sa_mask);
399 sigact.sa_flags = KSH_SA_FLAGS;
400 sigact.sa_handler = f;
401 sigaction(p->signal, &sigact, (struct sigaction *) 0);
402 }
403
404 return 1;
405 }
406
407 /* control what signal is set to before an exec() */
408 void
409 setexecsig(p, restore)
410 Trap *p;
411 int restore;
412 {
413 /* XXX debugging */
414 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
415 internal_errorf(1, "setexecsig: unset signal %d(%s)",
416 p->signal, p->name);
417
418 /* restore original value for exec'd kids */
419 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
420 switch (restore & SS_RESTORE_MASK) {
421 case SS_RESTORE_CURR: /* leave things as they currently are */
422 break;
423 case SS_RESTORE_ORIG:
424 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
425 break;
426 case SS_RESTORE_DFL:
427 p->flags |= TF_EXEC_DFL;
428 break;
429 case SS_RESTORE_IGN:
430 p->flags |= TF_EXEC_IGN;
431 break;
432 }
433 }
434