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