a34b05d2e6
This opens an OpenBSD-mirabile (aka MirBSD) repository. ### MirBSD is: # Copyright (c) 1982-2003 by Thorsten "mirabile" Glaser <x86@ePost.de> # Copyright © 1968-2003 The authors of And contributors to UNIX®, the # C Language, BSD/Berkeley Unix; 386BSD, NetBSD 1.1 and OpenBSD. # # Anyone who obtained a copy of this work is hereby permitted to freely use, # distribute, modify, merge, sublicence, give away or sell it as long as the # authors are given due credit and the following notice is retained: # # This work is provided "as is", with no explicit or implicit warranty what- # soever. Use it only at your own risk. In no event may an author or contri- # butor be held liable for any damage, directly or indirectly, that origina- # ted through or is caused by creation or modification of this work. MirBSD is my private tree. MirBSD does not differ very much from OpenBSD and intentionally tracks OpenBSD. That's why it _is_ OpenBSD, just not the official one. It's like with DarrenBSD. At time of this writing, no advertising for MirBSD must be done, because the advertising clause has not yet been sorted out. http://templeofhate.com/tglaser/MirBSD/index.php
452 lines
10 KiB
C
452 lines
10 KiB
C
/* $OpenBSD: trap.c,v 1.13 2003/02/28 09:45:09 jmc Exp $ */
|
|
|
|
/*
|
|
* signal handling
|
|
*/
|
|
|
|
/* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */
|
|
#define FROM_TRAP_C
|
|
#include "sh.h"
|
|
|
|
/* Table is indexed by signal number
|
|
*
|
|
* The script siglist.sh generates siglist.out, which is a sorted, complete
|
|
* list of signals
|
|
*/
|
|
Trap sigtraps[SIGNALS+1] = {
|
|
{ SIGEXIT_, "EXIT", "Signal 0" },
|
|
#include "siglist.out" /* generated by siglist.sh */
|
|
{ SIGERR_, "ERR", "Error handler" },
|
|
};
|
|
|
|
static struct sigaction Sigact_ign, Sigact_trap;
|
|
|
|
void
|
|
inittraps()
|
|
{
|
|
#ifdef HAVE_SYS_SIGLIST
|
|
# ifndef SYS_SIGLIST_DECLARED
|
|
extern char *sys_siglist[];
|
|
# endif
|
|
int i;
|
|
|
|
/* Use system description, if available, for unknown signals... */
|
|
for (i = 0; i < NSIG; i++)
|
|
if (!sigtraps[i].name && sys_siglist[i] && sys_siglist[i][0])
|
|
sigtraps[i].mess = sys_siglist[i];
|
|
#endif /* HAVE_SYS_SIGLIST */
|
|
|
|
sigemptyset(&Sigact_ign.sa_mask);
|
|
Sigact_ign.sa_flags = KSH_SA_FLAGS;
|
|
Sigact_ign.sa_handler = SIG_IGN;
|
|
Sigact_trap = Sigact_ign;
|
|
Sigact_trap.sa_handler = trapsig;
|
|
|
|
sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
|
|
sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
|
|
sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */
|
|
sigtraps[SIGHUP].flags |= TF_FATAL;
|
|
sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
|
|
|
|
/* these are always caught so we can clean up any temporary files. */
|
|
setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
|
|
setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
|
|
setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
|
|
setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
|
|
}
|
|
|
|
#ifdef KSH
|
|
static RETSIGTYPE alarm_catcher ARGS((int sig));
|
|
|
|
void
|
|
alarm_init()
|
|
{
|
|
sigtraps[SIGALRM].flags |= TF_SHELL_USES;
|
|
setsig(&sigtraps[SIGALRM], alarm_catcher,
|
|
SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
|
|
}
|
|
|
|
static RETSIGTYPE
|
|
alarm_catcher(sig)
|
|
int sig;
|
|
{
|
|
int errno_ = errno;
|
|
|
|
if (ksh_tmout_state == TMOUT_READING) {
|
|
int left = alarm(0);
|
|
|
|
if (left == 0) {
|
|
ksh_tmout_state = TMOUT_LEAVING;
|
|
intrsig = 1;
|
|
} else
|
|
alarm(left);
|
|
}
|
|
errno = errno_;
|
|
return RETSIGVAL;
|
|
}
|
|
#endif /* KSH */
|
|
|
|
Trap *
|
|
gettrap(name, igncase)
|
|
const char *name;
|
|
int igncase;
|
|
{
|
|
int i;
|
|
register Trap *p;
|
|
|
|
if (digit(*name)) {
|
|
int n;
|
|
|
|
if (getn(name, &n) && 0 <= n && n < SIGNALS)
|
|
return &sigtraps[n];
|
|
return NULL;
|
|
}
|
|
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
|
|
if (p->name) {
|
|
if (igncase) {
|
|
if (p->name && (!strcasecmp(p->name, name) ||
|
|
(strlen(name) > 3 && !strncasecmp("SIG",
|
|
p->name, 3) &&
|
|
!strcasecmp(p->name, name + 3))))
|
|
return p;
|
|
} else {
|
|
if (p->name && (!strcmp(p->name, name) ||
|
|
(strlen(name) > 3 && !strncmp("SIG",
|
|
p->name, 3) && !strcmp(p->name, name + 3))))
|
|
return p;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* trap signal handler
|
|
*/
|
|
RETSIGTYPE
|
|
trapsig(i)
|
|
int i;
|
|
{
|
|
Trap *p = &sigtraps[i];
|
|
int errno_ = errno;
|
|
|
|
trap = p->set = 1;
|
|
if (p->flags & TF_DFL_INTR)
|
|
intrsig = 1;
|
|
if ((p->flags & TF_FATAL) && !p->trap) {
|
|
fatal_trap = 1;
|
|
intrsig = 1;
|
|
}
|
|
if (p->shtrap)
|
|
(*p->shtrap)(i);
|
|
#ifdef V7_SIGNALS
|
|
if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */
|
|
sigaction(i, &Sigact_trap, (struct sigaction *) 0);
|
|
#endif /* V7_SIGNALS */
|
|
errno = errno_;
|
|
return RETSIGVAL;
|
|
}
|
|
|
|
/* called when we want to allow the user to ^C out of something - won't
|
|
* work if user has trapped SIGINT.
|
|
*/
|
|
void
|
|
intrcheck()
|
|
{
|
|
if (intrsig)
|
|
runtraps(TF_DFL_INTR|TF_FATAL);
|
|
}
|
|
|
|
/* called after EINTR to check if a signal with normally causes process
|
|
* termination has been received.
|
|
*/
|
|
int
|
|
fatal_trap_check()
|
|
{
|
|
int i;
|
|
Trap *p;
|
|
|
|
/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
|
|
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
|
|
if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
|
|
/* return value is used as an exit code */
|
|
return 128 + p->signal;
|
|
return 0;
|
|
}
|
|
|
|
/* Returns the signal number of any pending traps: ie, a signal which has
|
|
* occurred for which a trap has been set or for which the TF_DFL_INTR flag
|
|
* is set.
|
|
*/
|
|
int
|
|
trap_pending()
|
|
{
|
|
int i;
|
|
Trap *p;
|
|
|
|
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
|
|
if (p->set && ((p->trap && p->trap[0])
|
|
|| ((p->flags & (TF_DFL_INTR|TF_FATAL))
|
|
&& !p->trap)))
|
|
return p->signal;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* run any pending traps. If intr is set, only run traps that
|
|
* can interrupt commands.
|
|
*/
|
|
void
|
|
runtraps(flag)
|
|
int flag;
|
|
{
|
|
int i;
|
|
register Trap *p;
|
|
|
|
#ifdef KSH
|
|
if (ksh_tmout_state == TMOUT_LEAVING) {
|
|
ksh_tmout_state = TMOUT_EXECUTING;
|
|
warningf(FALSE, "timed out waiting for input");
|
|
unwind(LEXIT);
|
|
} else
|
|
/* XXX: this means the alarm will have no effect if a trap
|
|
* is caught after the alarm() was started...not good.
|
|
*/
|
|
ksh_tmout_state = TMOUT_EXECUTING;
|
|
#endif /* KSH */
|
|
if (!flag)
|
|
trap = 0;
|
|
if (flag & TF_DFL_INTR)
|
|
intrsig = 0;
|
|
if (flag & TF_FATAL)
|
|
fatal_trap = 0;
|
|
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
|
|
if (p->set && (!flag
|
|
|| ((p->flags & flag) && p->trap == (char *) 0)))
|
|
runtrap(p);
|
|
}
|
|
|
|
void
|
|
runtrap(p)
|
|
Trap *p;
|
|
{
|
|
int i = p->signal;
|
|
char *trapstr = p->trap;
|
|
int oexstat;
|
|
int UNINITIALIZED(old_changed);
|
|
|
|
p->set = 0;
|
|
if (trapstr == (char *) 0) { /* SIG_DFL */
|
|
if (p->flags & TF_FATAL) {
|
|
/* eg, SIGHUP */
|
|
exstat = 128 + i;
|
|
unwind(LLEAVE);
|
|
}
|
|
if (p->flags & TF_DFL_INTR) {
|
|
/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
|
|
exstat = 128 + i;
|
|
unwind(LINTR);
|
|
}
|
|
return;
|
|
}
|
|
if (trapstr[0] == '\0') /* SIG_IGN */
|
|
return;
|
|
if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */
|
|
old_changed = p->flags & TF_CHANGED;
|
|
p->flags &= ~TF_CHANGED;
|
|
p->trap = (char *) 0;
|
|
}
|
|
oexstat = exstat;
|
|
/* Note: trapstr is fully parsed before anything is executed, thus
|
|
* no problem with afree(p->trap) in settrap() while still in use.
|
|
*/
|
|
command(trapstr);
|
|
exstat = oexstat;
|
|
if (i == SIGEXIT_ || i == SIGERR_) {
|
|
if (p->flags & TF_CHANGED)
|
|
/* don't clear TF_CHANGED */
|
|
afree(trapstr, APERM);
|
|
else
|
|
p->trap = trapstr;
|
|
p->flags |= old_changed;
|
|
}
|
|
}
|
|
|
|
/* clear pending traps and reset user's trap handlers; used after fork(2) */
|
|
void
|
|
cleartraps()
|
|
{
|
|
int i;
|
|
Trap *p;
|
|
|
|
trap = 0;
|
|
intrsig = 0;
|
|
fatal_trap = 0;
|
|
for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) {
|
|
p->set = 0;
|
|
if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
|
|
settrap(p, (char *) 0);
|
|
}
|
|
}
|
|
|
|
/* restore signals just before an exec(2) */
|
|
void
|
|
restoresigs()
|
|
{
|
|
int i;
|
|
Trap *p;
|
|
|
|
for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++)
|
|
if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
|
|
setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
|
|
SS_RESTORE_CURR|SS_FORCE);
|
|
}
|
|
|
|
void
|
|
settrap(p, s)
|
|
Trap *p;
|
|
char *s;
|
|
{
|
|
handler_t f;
|
|
|
|
if (p->trap)
|
|
afree(p->trap, APERM);
|
|
p->trap = str_save(s, APERM); /* handles s == 0 */
|
|
p->flags |= TF_CHANGED;
|
|
f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
|
|
|
|
p->flags |= TF_USER_SET;
|
|
if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
|
|
f = trapsig;
|
|
else if (p->flags & TF_SHELL_USES) {
|
|
if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
|
|
/* do what user wants at exec time */
|
|
p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
|
|
if (f == SIG_IGN)
|
|
p->flags |= TF_EXEC_IGN;
|
|
else
|
|
p->flags |= TF_EXEC_DFL;
|
|
}
|
|
/* assumes handler already set to what shell wants it
|
|
* (normally trapsig, but could be j_sigchld() or SIG_IGN)
|
|
*/
|
|
return;
|
|
}
|
|
|
|
/* todo: should we let user know signal is ignored? how? */
|
|
setsig(p, f, SS_RESTORE_CURR|SS_USER);
|
|
}
|
|
|
|
/* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
|
|
* kill shell (unless user catches it and exits)
|
|
*/
|
|
int
|
|
block_pipe()
|
|
{
|
|
int restore_dfl = 0;
|
|
Trap *p = &sigtraps[SIGPIPE];
|
|
|
|
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
|
|
setsig(p, SIG_IGN, SS_RESTORE_CURR);
|
|
if (p->flags & TF_ORIG_DFL)
|
|
restore_dfl = 1;
|
|
} else if (p->cursig == SIG_DFL) {
|
|
setsig(p, SIG_IGN, SS_RESTORE_CURR);
|
|
restore_dfl = 1; /* restore to SIG_DFL */
|
|
}
|
|
return restore_dfl;
|
|
}
|
|
|
|
/* Called by c_print() to undo whatever block_pipe() did */
|
|
void
|
|
restore_pipe(restore_dfl)
|
|
int restore_dfl;
|
|
{
|
|
if (restore_dfl)
|
|
setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
|
|
}
|
|
|
|
/* Set action for a signal. Action may not be set if original
|
|
* action was SIG_IGN, depending on the value of flags and
|
|
* FTALKING.
|
|
*/
|
|
int
|
|
setsig(p, f, flags)
|
|
Trap *p;
|
|
handler_t f;
|
|
int flags;
|
|
{
|
|
struct sigaction sigact;
|
|
|
|
if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
|
|
return 1;
|
|
|
|
/* First time setting this signal? If so, get and note the current
|
|
* setting.
|
|
*/
|
|
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
|
|
sigaction(p->signal, &Sigact_ign, &sigact);
|
|
p->flags |= sigact.sa_handler == SIG_IGN ?
|
|
TF_ORIG_IGN : TF_ORIG_DFL;
|
|
p->cursig = SIG_IGN;
|
|
}
|
|
|
|
/* Generally, an ignored signal stays ignored, except if
|
|
* - the user of an interactive shell wants to change it
|
|
* - the shell wants for force a change
|
|
*/
|
|
if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE)
|
|
&& (!(flags & SS_USER) || !Flag(FTALKING)))
|
|
return 0;
|
|
|
|
setexecsig(p, flags & SS_RESTORE_MASK);
|
|
|
|
/* This is here 'cause there should be a way of clearing shtraps, but
|
|
* don't know if this is a sane way of doing it. At the moment,
|
|
* all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH).
|
|
*/
|
|
if (!(flags & SS_USER))
|
|
p->shtrap = (handler_t) 0;
|
|
if (flags & SS_SHTRAP) {
|
|
p->shtrap = f;
|
|
f = trapsig;
|
|
}
|
|
|
|
if (p->cursig != f) {
|
|
p->cursig = f;
|
|
sigemptyset(&sigact.sa_mask);
|
|
sigact.sa_flags = KSH_SA_FLAGS;
|
|
sigact.sa_handler = f;
|
|
sigaction(p->signal, &sigact, (struct sigaction *) 0);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* control what signal is set to before an exec() */
|
|
void
|
|
setexecsig(p, restore)
|
|
Trap *p;
|
|
int restore;
|
|
{
|
|
/* XXX debugging */
|
|
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
|
|
internal_errorf(1, "setexecsig: unset signal %d(%s)",
|
|
p->signal, p->name);
|
|
|
|
/* restore original value for exec'd kids */
|
|
p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
|
|
switch (restore & SS_RESTORE_MASK) {
|
|
case SS_RESTORE_CURR: /* leave things as they currently are */
|
|
break;
|
|
case SS_RESTORE_ORIG:
|
|
p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
|
|
break;
|
|
case SS_RESTORE_DFL:
|
|
p->flags |= TF_EXEC_DFL;
|
|
break;
|
|
case SS_RESTORE_IGN:
|
|
p->flags |= TF_EXEC_IGN;
|
|
break;
|
|
}
|
|
}
|