1 /* $OpenBSD: trap.c,v 1.32 2018/03/15 16:51:29 anton Exp $ */
14 Trap sigtraps
[NSIG
+ 1];
16 static struct sigaction Sigact_ign
, Sigact_trap
;
23 /* Populate sigtraps based on sys_signame and sys_siglist. */
24 for (i
= 0; i
<= NSIG
; i
++) {
25 sigtraps
[i
].signal
= i
;
27 sigtraps
[i
].name
= "ERR";
28 sigtraps
[i
].mess
= "Error handler";
30 sigtraps
[i
].name
= sys_signame
[i
];
31 sigtraps
[i
].mess
= sys_siglist
[i
];
34 sigtraps
[SIGEXIT_
].name
= "EXIT"; /* our name for signal 0 */
36 sigemptyset(&Sigact_ign
.sa_mask
);
37 Sigact_ign
.sa_flags
= 0; /* interruptible */
38 Sigact_ign
.sa_handler
= SIG_IGN
;
39 Sigact_trap
= Sigact_ign
;
40 Sigact_trap
.sa_handler
= trapsig
;
42 sigtraps
[SIGINT
].flags
|= TF_DFL_INTR
| TF_TTY_INTR
;
43 sigtraps
[SIGQUIT
].flags
|= TF_DFL_INTR
| TF_TTY_INTR
;
44 sigtraps
[SIGTERM
].flags
|= TF_DFL_INTR
;/* not fatal for interactive */
45 sigtraps
[SIGHUP
].flags
|= TF_FATAL
;
46 sigtraps
[SIGCHLD
].flags
|= TF_SHELL_USES
;
48 /* these are always caught so we can clean up any temporary files. */
49 setsig(&sigtraps
[SIGINT
], trapsig
, SS_RESTORE_ORIG
);
50 setsig(&sigtraps
[SIGQUIT
], trapsig
, SS_RESTORE_ORIG
);
51 setsig(&sigtraps
[SIGTERM
], trapsig
, SS_RESTORE_ORIG
);
52 setsig(&sigtraps
[SIGHUP
], trapsig
, SS_RESTORE_ORIG
);
55 static void alarm_catcher(int sig
);
60 sigtraps
[SIGALRM
].flags
|= TF_SHELL_USES
;
61 setsig(&sigtraps
[SIGALRM
], alarm_catcher
,
62 SS_RESTORE_ORIG
|SS_FORCE
|SS_SHTRAP
);
66 alarm_catcher(int sig
)
70 if (ksh_tmout_state
== TMOUT_READING
) {
74 ksh_tmout_state
= TMOUT_LEAVING
;
83 gettrap(const char *name
, int igncase
)
91 if (getn(name
, &n
) && 0 <= n
&& n
< NSIG
)
95 for (p
= sigtraps
, i
= NSIG
+1; --i
>= 0; p
++)
98 if (p
->name
&& (!strcasecmp(p
->name
, name
) ||
99 (strlen(name
) > 3 && !strncasecmp("SIG",
101 !strcasecmp(p
->name
, name
+ 3))))
104 if (p
->name
&& (!strcmp(p
->name
, name
) ||
105 (strlen(name
) > 3 && !strncmp("SIG",
106 p
->name
, 3) && !strcmp(p
->name
, name
+ 3))))
114 * trap signal handler
119 Trap
*p
= &sigtraps
[i
];
123 if (p
->flags
& TF_DFL_INTR
)
125 if ((p
->flags
& TF_FATAL
) && !p
->trap
) {
134 /* called when we want to allow the user to ^C out of something - won't
135 * work if user has trapped SIGINT.
141 runtraps(TF_DFL_INTR
|TF_FATAL
);
144 /* called after EINTR to check if a signal with normally causes process
145 * termination has been received.
148 fatal_trap_check(void)
153 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
154 for (p
= sigtraps
, i
= NSIG
+1; --i
>= 0; p
++)
155 if (p
->set
&& (p
->flags
& (TF_DFL_INTR
|TF_FATAL
)))
156 /* return value is used as an exit code */
157 return 128 + p
->signal
;
161 /* Returns the signal number of any pending traps: ie, a signal which has
162 * occurred for which a trap has been set or for which the TF_DFL_INTR flag
171 for (p
= sigtraps
, i
= NSIG
+1; --i
>= 0; p
++)
172 if (p
->set
&& ((p
->trap
&& p
->trap
[0]) ||
173 ((p
->flags
& (TF_DFL_INTR
|TF_FATAL
)) && !p
->trap
)))
179 * run any pending traps. If intr is set, only run traps that
180 * can interrupt commands.
188 if (ksh_tmout_state
== TMOUT_LEAVING
) {
189 ksh_tmout_state
= TMOUT_EXECUTING
;
190 warningf(false, "timed out waiting for input");
193 /* XXX: this means the alarm will have no effect if a trap
194 * is caught after the alarm() was started...not good.
196 ksh_tmout_state
= TMOUT_EXECUTING
;
199 if (flag
& TF_DFL_INTR
)
203 for (p
= sigtraps
, i
= NSIG
+1; --i
>= 0; p
++)
204 if (p
->set
&& (!flag
||
205 ((p
->flags
& flag
) && p
->trap
== NULL
)))
213 char *trapstr
= p
->trap
;
218 if (trapstr
== NULL
) { /* SIG_DFL */
219 if (p
->flags
& TF_FATAL
) {
224 if (p
->flags
& TF_DFL_INTR
) {
225 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
231 if (trapstr
[0] == '\0') /* SIG_IGN */
233 if (i
== SIGEXIT_
|| i
== SIGERR_
) { /* avoid recursion on these */
234 old_changed
= p
->flags
& TF_CHANGED
;
235 p
->flags
&= ~TF_CHANGED
;
239 /* Note: trapstr is fully parsed before anything is executed, thus
240 * no problem with afree(p->trap) in settrap() while still in use.
242 command(trapstr
, current_lineno
);
244 if (i
== SIGEXIT_
|| i
== SIGERR_
) {
245 if (p
->flags
& TF_CHANGED
)
246 /* don't clear TF_CHANGED */
247 afree(trapstr
, APERM
);
250 p
->flags
|= old_changed
;
254 /* clear pending traps and reset user's trap handlers; used after fork(2) */
264 for (i
= NSIG
+1, p
= sigtraps
; --i
>= 0; p
++) {
266 if ((p
->flags
& TF_USER_SET
) && (p
->trap
&& p
->trap
[0]))
271 /* restore signals just before an exec(2) */
278 for (i
= NSIG
+1, p
= sigtraps
; --i
>= 0; p
++)
279 if (p
->flags
& (TF_EXEC_IGN
|TF_EXEC_DFL
))
280 setsig(p
, (p
->flags
& TF_EXEC_IGN
) ? SIG_IGN
: SIG_DFL
,
281 SS_RESTORE_CURR
|SS_FORCE
);
285 settrap(Trap
*p
, char *s
)
289 afree(p
->trap
, APERM
);
290 p
->trap
= str_save(s
, APERM
); /* handles s == 0 */
291 p
->flags
|= TF_CHANGED
;
292 f
= !s
? SIG_DFL
: s
[0] ? trapsig
: SIG_IGN
;
294 p
->flags
|= TF_USER_SET
;
295 if ((p
->flags
& (TF_DFL_INTR
|TF_FATAL
)) && f
== SIG_DFL
)
297 else if (p
->flags
& TF_SHELL_USES
) {
298 if (!(p
->flags
& TF_ORIG_IGN
) || Flag(FTALKING
)) {
299 /* do what user wants at exec time */
300 p
->flags
&= ~(TF_EXEC_IGN
|TF_EXEC_DFL
);
302 p
->flags
|= TF_EXEC_IGN
;
304 p
->flags
|= TF_EXEC_DFL
;
307 /* assumes handler already set to what shell wants it
308 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
313 /* todo: should we let user know signal is ignored? how? */
314 setsig(p
, f
, SS_RESTORE_CURR
|SS_USER
);
317 /* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
318 * kill shell (unless user catches it and exits)
324 Trap
*p
= &sigtraps
[SIGPIPE
];
326 if (!(p
->flags
& (TF_ORIG_IGN
|TF_ORIG_DFL
))) {
327 setsig(p
, SIG_IGN
, SS_RESTORE_CURR
);
328 if (p
->flags
& TF_ORIG_DFL
)
330 } else if (p
->cursig
== SIG_DFL
) {
331 setsig(p
, SIG_IGN
, SS_RESTORE_CURR
);
332 restore_dfl
= 1; /* restore to SIG_DFL */
337 /* Called by c_print() to undo whatever block_pipe() did */
339 restore_pipe(int restore_dfl
)
342 setsig(&sigtraps
[SIGPIPE
], SIG_DFL
, SS_RESTORE_CURR
);
345 /* Set action for a signal. Action may not be set if original
346 * action was SIG_IGN, depending on the value of flags and
350 setsig(Trap
*p
, sig_t f
, int flags
)
352 struct sigaction sigact
;
354 if (p
->signal
== SIGEXIT_
|| p
->signal
== SIGERR_
)
357 /* First time setting this signal? If so, get and note the current
360 if (!(p
->flags
& (TF_ORIG_IGN
|TF_ORIG_DFL
))) {
361 sigaction(p
->signal
, &Sigact_ign
, &sigact
);
362 p
->flags
|= sigact
.sa_handler
== SIG_IGN
?
363 TF_ORIG_IGN
: TF_ORIG_DFL
;
367 /* Generally, an ignored signal stays ignored, except if
368 * - the user of an interactive shell wants to change it
369 * - the shell wants for force a change
371 if ((p
->flags
& TF_ORIG_IGN
) && !(flags
& SS_FORCE
) &&
372 (!(flags
& SS_USER
) || !Flag(FTALKING
)))
375 setexecsig(p
, flags
& SS_RESTORE_MASK
);
377 /* This is here 'cause there should be a way of clearing shtraps, but
378 * don't know if this is a sane way of doing it. At the moment,
379 * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH).
381 if (!(flags
& SS_USER
))
383 if (flags
& SS_SHTRAP
) {
388 if (p
->cursig
!= f
) {
390 sigemptyset(&sigact
.sa_mask
);
391 sigact
.sa_flags
= 0 /* interruptible */;
392 sigact
.sa_handler
= f
;
393 sigaction(p
->signal
, &sigact
, NULL
);
399 /* control what signal is set to before an exec() */
401 setexecsig(Trap
*p
, int restore
)
404 if (!(p
->flags
& (TF_ORIG_IGN
|TF_ORIG_DFL
)))
405 internal_errorf("%s: unset signal %d(%s)",
406 __func__
, p
->signal
, p
->name
);
408 /* restore original value for exec'd kids */
409 p
->flags
&= ~(TF_EXEC_IGN
|TF_EXEC_DFL
);
410 switch (restore
& SS_RESTORE_MASK
) {
411 case SS_RESTORE_CURR
: /* leave things as they currently are */
413 case SS_RESTORE_ORIG
:
414 p
->flags
|= p
->flags
& TF_ORIG_IGN
? TF_EXEC_IGN
: TF_EXEC_DFL
;
417 p
->flags
|= TF_EXEC_DFL
;
420 p
->flags
|= TF_EXEC_IGN
;