1 /* $OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $ */
9 Trap sigtraps
[NSIG
+ 1];
11 static struct sigaction Sigact_ign
, Sigact_trap
;
18 /* Populate sigtraps based on sys_signame and sys_siglist. */
19 for (i
= 0; i
<= NSIG
; i
++) {
20 sigtraps
[i
].signal
= i
;
22 sigtraps
[i
].name
= "ERR";
23 sigtraps
[i
].mess
= "Error handler";
25 sigtraps
[i
].name
= sys_signame
[i
];
26 sigtraps
[i
].mess
= sys_siglist
[i
];
29 sigtraps
[SIGEXIT_
].name
= "EXIT"; /* our name for signal 0 */
31 sigemptyset(&Sigact_ign
.sa_mask
);
32 Sigact_ign
.sa_flags
= 0; /* interruptible */
33 Sigact_ign
.sa_handler
= SIG_IGN
;
34 Sigact_trap
= Sigact_ign
;
35 Sigact_trap
.sa_handler
= trapsig
;
37 sigtraps
[SIGINT
].flags
|= TF_DFL_INTR
| TF_TTY_INTR
;
38 sigtraps
[SIGQUIT
].flags
|= TF_DFL_INTR
| TF_TTY_INTR
;
39 sigtraps
[SIGTERM
].flags
|= TF_DFL_INTR
;/* not fatal for interactive */
40 sigtraps
[SIGHUP
].flags
|= TF_FATAL
;
41 sigtraps
[SIGCHLD
].flags
|= TF_SHELL_USES
;
43 /* these are always caught so we can clean up any temporary files. */
44 setsig(&sigtraps
[SIGINT
], trapsig
, SS_RESTORE_ORIG
);
45 setsig(&sigtraps
[SIGQUIT
], trapsig
, SS_RESTORE_ORIG
);
46 setsig(&sigtraps
[SIGTERM
], trapsig
, SS_RESTORE_ORIG
);
47 setsig(&sigtraps
[SIGHUP
], trapsig
, SS_RESTORE_ORIG
);
50 static void alarm_catcher(int sig
);
55 sigtraps
[SIGALRM
].flags
|= TF_SHELL_USES
;
56 setsig(&sigtraps
[SIGALRM
], alarm_catcher
,
57 SS_RESTORE_ORIG
|SS_FORCE
|SS_SHTRAP
);
62 alarm_catcher(int sig
)
66 if (ksh_tmout_state
== TMOUT_READING
) {
70 ksh_tmout_state
= TMOUT_LEAVING
;
79 gettrap(const char *name
, int igncase
)
87 if (getn(name
, &n
) && 0 <= n
&& n
< NSIG
)
91 for (p
= sigtraps
, i
= NSIG
+1; --i
>= 0; p
++)
94 if (p
->name
&& (!strcasecmp(p
->name
, name
) ||
95 (strlen(name
) > 3 && !strncasecmp("SIG",
97 !strcasecmp(p
->name
, name
+ 3))))
100 if (p
->name
&& (!strcmp(p
->name
, name
) ||
101 (strlen(name
) > 3 && !strncmp("SIG",
102 p
->name
, 3) && !strcmp(p
->name
, name
+ 3))))
110 * trap signal handler
115 Trap
*p
= &sigtraps
[i
];
119 if (p
->flags
& TF_DFL_INTR
)
121 if ((p
->flags
& TF_FATAL
) && !p
->trap
) {
130 /* called when we want to allow the user to ^C out of something - won't
131 * work if user has trapped SIGINT.
137 runtraps(TF_DFL_INTR
|TF_FATAL
);
140 /* called after EINTR to check if a signal with normally causes process
141 * termination has been received.
144 fatal_trap_check(void)
149 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
150 for (p
= sigtraps
, i
= NSIG
+1; --i
>= 0; p
++)
151 if (p
->set
&& (p
->flags
& (TF_DFL_INTR
|TF_FATAL
)))
152 /* return value is used as an exit code */
153 return 128 + p
->signal
;
157 /* Returns the signal number of any pending traps: ie, a signal which has
158 * occurred for which a trap has been set or for which the TF_DFL_INTR flag
167 for (p
= sigtraps
, i
= NSIG
+1; --i
>= 0; p
++)
168 if (p
->set
&& ((p
->trap
&& p
->trap
[0]) ||
169 ((p
->flags
& (TF_DFL_INTR
|TF_FATAL
)) && !p
->trap
)))
175 * run any pending traps. If intr is set, only run traps that
176 * can interrupt commands.
184 if (ksh_tmout_state
== TMOUT_LEAVING
) {
185 ksh_tmout_state
= TMOUT_EXECUTING
;
186 warningf(false, "timed out waiting for input");
189 /* XXX: this means the alarm will have no effect if a trap
190 * is caught after the alarm() was started...not good.
192 ksh_tmout_state
= TMOUT_EXECUTING
;
195 if (flag
& TF_DFL_INTR
)
199 for (p
= sigtraps
, i
= NSIG
+1; --i
>= 0; p
++)
200 if (p
->set
&& (!flag
||
201 ((p
->flags
& flag
) && p
->trap
== (char *) 0)))
209 char *trapstr
= p
->trap
;
214 if (trapstr
== (char *) 0) { /* SIG_DFL */
215 if (p
->flags
& TF_FATAL
) {
220 if (p
->flags
& TF_DFL_INTR
) {
221 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
227 if (trapstr
[0] == '\0') /* SIG_IGN */
229 if (i
== SIGEXIT_
|| i
== SIGERR_
) { /* avoid recursion on these */
230 old_changed
= p
->flags
& TF_CHANGED
;
231 p
->flags
&= ~TF_CHANGED
;
232 p
->trap
= (char *) 0;
235 /* Note: trapstr is fully parsed before anything is executed, thus
236 * no problem with afree(p->trap) in settrap() while still in use.
238 command(trapstr
, current_lineno
);
240 if (i
== SIGEXIT_
|| i
== SIGERR_
) {
241 if (p
->flags
& TF_CHANGED
)
242 /* don't clear TF_CHANGED */
243 afree(trapstr
, APERM
);
246 p
->flags
|= old_changed
;
250 /* clear pending traps and reset user's trap handlers; used after fork(2) */
260 for (i
= NSIG
+1, p
= sigtraps
; --i
>= 0; p
++) {
262 if ((p
->flags
& TF_USER_SET
) && (p
->trap
&& p
->trap
[0]))
263 settrap(p
, (char *) 0);
267 /* restore signals just before an exec(2) */
274 for (i
= NSIG
+1, p
= sigtraps
; --i
>= 0; p
++)
275 if (p
->flags
& (TF_EXEC_IGN
|TF_EXEC_DFL
))
276 setsig(p
, (p
->flags
& TF_EXEC_IGN
) ? SIG_IGN
: SIG_DFL
,
277 SS_RESTORE_CURR
|SS_FORCE
);
281 settrap(Trap
*p
, char *s
)
286 afree(p
->trap
, APERM
);
287 p
->trap
= str_save(s
, APERM
); /* handles s == 0 */
288 p
->flags
|= TF_CHANGED
;
289 f
= !s
? SIG_DFL
: s
[0] ? trapsig
: SIG_IGN
;
291 p
->flags
|= TF_USER_SET
;
292 if ((p
->flags
& (TF_DFL_INTR
|TF_FATAL
)) && f
== SIG_DFL
)
294 else if (p
->flags
& TF_SHELL_USES
) {
295 if (!(p
->flags
& TF_ORIG_IGN
) || Flag(FTALKING
)) {
296 /* do what user wants at exec time */
297 p
->flags
&= ~(TF_EXEC_IGN
|TF_EXEC_DFL
);
299 p
->flags
|= TF_EXEC_IGN
;
301 p
->flags
|= TF_EXEC_DFL
;
304 /* assumes handler already set to what shell wants it
305 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
310 /* todo: should we let user know signal is ignored? how? */
311 setsig(p
, f
, SS_RESTORE_CURR
|SS_USER
);
314 /* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
315 * kill shell (unless user catches it and exits)
321 Trap
*p
= &sigtraps
[SIGPIPE
];
323 if (!(p
->flags
& (TF_ORIG_IGN
|TF_ORIG_DFL
))) {
324 setsig(p
, SIG_IGN
, SS_RESTORE_CURR
);
325 if (p
->flags
& TF_ORIG_DFL
)
327 } else if (p
->cursig
== SIG_DFL
) {
328 setsig(p
, SIG_IGN
, SS_RESTORE_CURR
);
329 restore_dfl
= 1; /* restore to SIG_DFL */
334 /* Called by c_print() to undo whatever block_pipe() did */
336 restore_pipe(int restore_dfl
)
339 setsig(&sigtraps
[SIGPIPE
], SIG_DFL
, SS_RESTORE_CURR
);
342 /* Set action for a signal. Action may not be set if original
343 * action was SIG_IGN, depending on the value of flags and
347 setsig(Trap
*p
, sig_t f
, int flags
)
349 struct sigaction sigact
;
351 if (p
->signal
== SIGEXIT_
|| p
->signal
== SIGERR_
)
354 /* First time setting this signal? If so, get and note the current
357 if (!(p
->flags
& (TF_ORIG_IGN
|TF_ORIG_DFL
))) {
358 sigaction(p
->signal
, &Sigact_ign
, &sigact
);
359 p
->flags
|= sigact
.sa_handler
== SIG_IGN
?
360 TF_ORIG_IGN
: TF_ORIG_DFL
;
364 /* Generally, an ignored signal stays ignored, except if
365 * - the user of an interactive shell wants to change it
366 * - the shell wants for force a change
368 if ((p
->flags
& TF_ORIG_IGN
) && !(flags
& SS_FORCE
) &&
369 (!(flags
& SS_USER
) || !Flag(FTALKING
)))
372 setexecsig(p
, flags
& SS_RESTORE_MASK
);
374 /* This is here 'cause there should be a way of clearing shtraps, but
375 * don't know if this is a sane way of doing it. At the moment,
376 * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH).
378 if (!(flags
& SS_USER
))
380 if (flags
& SS_SHTRAP
) {
385 if (p
->cursig
!= f
) {
387 sigemptyset(&sigact
.sa_mask
);
388 sigact
.sa_flags
= 0 /* interruptible */;
389 sigact
.sa_handler
= f
;
390 sigaction(p
->signal
, &sigact
, (struct sigaction
*) 0);
396 /* control what signal is set to before an exec() */
398 setexecsig(Trap
*p
, int restore
)
401 if (!(p
->flags
& (TF_ORIG_IGN
|TF_ORIG_DFL
)))
402 internal_errorf(1, "setexecsig: unset signal %d(%s)",
405 /* restore original value for exec'd kids */
406 p
->flags
&= ~(TF_EXEC_IGN
|TF_EXEC_DFL
);
407 switch (restore
& SS_RESTORE_MASK
) {
408 case SS_RESTORE_CURR
: /* leave things as they currently are */
410 case SS_RESTORE_ORIG
:
411 p
->flags
|= p
->flags
& TF_ORIG_IGN
? TF_EXEC_IGN
: TF_EXEC_DFL
;
414 p
->flags
|= TF_EXEC_DFL
;
417 p
->flags
|= TF_EXEC_IGN
;