#include "zsh.mdh"
#include "signals.pro"
mod_export int sigtrapped[VSIGCOUNT];
mod_export Eprog siglists[VSIGCOUNT];
mod_export int nsigtrapped;
mod_export int queueing_enabled, queue_front, queue_rear;
mod_export int signal_queue[MAX_QUEUE_SIZE];
mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE];
mod_export int trap_queueing_enabled, trap_queue_front, trap_queue_rear;
mod_export int trap_queue[MAX_QUEUE_SIZE];
#if !defined(POSIX_SIGNALS) && !defined(BSD_SIGNALS)
static sigset_t blocked_set;
#endif
#ifdef POSIX_SIGNALS
# define signal_jmp_buf sigjmp_buf
# define signal_setjmp(b) sigsetjmp((b),1)
# define signal_longjmp(b,n) siglongjmp((b),(n))
#else
# define signal_jmp_buf jmp_buf
# define signal_setjmp(b) setjmp(b)
# define signal_longjmp(b,n) longjmp((b),(n))
#endif
#ifdef NO_SIGNAL_BLOCKING
# define signal_process(sig) signal_ignore(sig)
# define signal_reset(sig) install_handler(sig)
#else
# define signal_process(sig) ;
# define signal_reset(sig) ;
#endif
mod_export void
install_handler(int sig)
{
#ifdef POSIX_SIGNALS
struct sigaction act;
act.sa_handler = (SIGNAL_HANDTYPE) zhandler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
# ifdef SA_INTERRUPT
if (interact)
act.sa_flags |= SA_INTERRUPT;
# endif
sigaction(sig, &act, (struct sigaction *)NULL);
#else
# ifdef BSD_SIGNALS
struct sigvec vec;
vec.sv_handler = (SIGNAL_HANDTYPE) zhandler;
vec.sv_mask = sigmask(sig);
# ifdef SV_INTERRUPT
vec.sv_flags = SV_INTERRUPT;
# endif
sigvec(sig, &vec, (struct sigvec *)NULL);
# else
# ifdef SYSV_SIGNALS
sigset(sig, zhandler);
# else
signal(sig, zhandler);
# endif
# endif
#endif
}
mod_export void
intr(void)
{
if (interact)
install_handler(SIGINT);
}
#if 0
void
nointr(void)
{
if (interact)
signal_ignore(SIGINT);
}
#endif
mod_export void
holdintr(void)
{
if (interact)
signal_block(signal_mask(SIGINT));
}
mod_export void
noholdintr(void)
{
if (interact)
signal_unblock(signal_mask(SIGINT));
}
sigset_t
signal_mask(int sig)
{
sigset_t set;
sigemptyset(&set);
if (sig)
sigaddset(&set, sig);
return set;
}
#ifndef BSD_SIGNALS
sigset_t
signal_block(sigset_t set)
{
sigset_t oset;
#ifdef POSIX_SIGNALS
sigprocmask(SIG_BLOCK, &set, &oset);
#else
# ifdef SYSV_SIGNALS
int i;
oset = blocked_set;
for (i = 1; i <= NSIG; ++i) {
if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
sigaddset(&blocked_set, i);
sighold(i);
}
}
# else
int i;
oset = blocked_set;
for (i = 1; i <= NSIG; ++i) {
if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
sigaddset(&blocked_set, i);
signal_ignore(i);
}
}
# endif
#endif
return oset;
}
#endif
sigset_t
signal_unblock(sigset_t set)
{
sigset_t oset;
#ifdef POSIX_SIGNALS
sigprocmask(SIG_UNBLOCK, &set, &oset);
#else
# ifdef BSD_SIGNALS
sigfillset(&oset);
oset = sigsetmask(oset);
sigsetmask(oset & ~set);
# else
# ifdef SYSV_SIGNALS
int i;
oset = blocked_set;
for (i = 1; i <= NSIG; ++i) {
if (sigismember(&set, i) && sigismember(&blocked_set, i)) {
sigdelset(&blocked_set, i);
sigrelse(i);
}
}
# else
int i;
oset = blocked_set;
for (i = 1; i <= NSIG; ++i) {
if (sigismember(&set, i) && sigismember(&blocked_set, i)) {
sigdelset(&blocked_set, i);
install_handler(i);
}
}
# endif
# endif
#endif
return oset;
}
mod_export sigset_t
signal_setmask(sigset_t set)
{
sigset_t oset;
#ifdef POSIX_SIGNALS
sigprocmask(SIG_SETMASK, &set, &oset);
#else
# ifdef BSD_SIGNALS
oset = sigsetmask(set);
# else
# ifdef SYSV_SIGNALS
int i;
oset = blocked_set;
for (i = 1; i <= NSIG; ++i) {
if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
sigaddset(&blocked_set, i);
sighold(i);
} else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) {
sigdelset(&blocked_set, i);
sigrelse(i);
}
}
# else
int i;
oset = blocked_set;
for (i = 1; i < NSIG; ++i) {
if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
sigaddset(&blocked_set, i);
signal_ignore(i);
} else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) {
sigdelset(&blocked_set, i);
install_handler(i);
}
}
# endif
# endif
#endif
return oset;
}
#if defined(NO_SIGNAL_BLOCKING)
static int suspend_longjmp = 0;
static signal_jmp_buf suspend_jmp_buf;
#endif
int
signal_suspend(UNUSED(int sig))
{
int ret;
#ifdef POSIX_SIGNALS
sigset_t set;
#ifdef BROKEN_POSIX_SIGSUSPEND
sigset_t oset;
#endif
sigemptyset(&set);
#ifdef BROKEN_POSIX_SIGSUSPEND
sigprocmask(SIG_SETMASK, &set, &oset);
pause();
sigprocmask(SIG_SETMASK, &oset, NULL);
#else
ret = sigsuspend(&set);
#endif
#else
# ifdef BSD_SIGNALS
sigset_t set;
sigemptyset(&set);
ret = sigpause(set);
# else
# ifdef SYSV_SIGNALS
ret = sigpause(sig);
# else
if (signal_setjmp(suspend_jmp_buf) == 0) {
suspend_longjmp = 1;
child_unblock();
ret = pause();
}
suspend_longjmp = 0;
# endif
# endif
#endif
return ret;
}
int last_signal;
mod_export RETSIGTYPE
zhandler(int sig)
{
sigset_t newmask, oldmask;
#if defined(NO_SIGNAL_BLOCKING)
int do_jump;
signal_jmp_buf jump_to;
#endif
last_signal = sig;
signal_process(sig);
sigfillset(&newmask);
oldmask = signal_block(newmask);
#if defined(NO_SIGNAL_BLOCKING)
do_jump = suspend_longjmp;
suspend_longjmp = 0;
if (sig == SIGCHLD) {
if (do_jump) {
jump_to = suspend_jmp_buf;
}
}
#endif
if (queueing_enabled) {
int temp_rear = ++queue_rear % MAX_QUEUE_SIZE;
DPUTS(temp_rear == queue_front, "BUG: signal queue full");
if (temp_rear != queue_front) {
queue_rear = temp_rear;
signal_queue[queue_rear] = sig;
signal_mask_queue[queue_rear] = oldmask;
}
signal_reset(sig);
return;
}
signal_setmask(oldmask);
switch (sig) {
case SIGCHLD:
for (;;) {
int old_errno = errno;
int status;
Job jn;
Process pn;
pid_t pid;
pid_t *procsubpid = &cmdoutpid;
int *procsubval = &cmdoutval;
int cont = 0;
struct execstack *es = exstack;
#ifdef HAVE_WAIT3
# ifdef HAVE_GETRUSAGE
struct rusage ru;
pid = wait3((void *)&status, WNOHANG|WUNTRACED, &ru);
# else
pid = wait3((void *)&status, WNOHANG|WUNTRACED, NULL);
# endif
#else
# ifdef HAVE_WAITPID
pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
# else
pid = wait(&status);
# endif
#endif
if (!pid)
break;
for (;;) {
if (pid == *procsubpid) {
*procsubpid = 0;
if (WIFSIGNALED(status))
*procsubval = (0200 | WTERMSIG(status));
else
*procsubval = WEXITSTATUS(status);
get_usage();
cont = 1;
break;
}
if (!es)
break;
procsubpid = &es->cmdoutpid;
procsubval = &es->cmdoutval;
es = es->next;
}
if (cont)
continue;
if (pid == -1) {
if (errno != ECHILD)
zerr("wait failed: %e", errno);
errno = old_errno;
break;
}
if (findproc(pid, &jn, &pn, 0)) {
#if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE)
struct timezone dummy_tz;
gettimeofday(&pn->endtime, &dummy_tz);
pn->status = status;
pn->ti = ru;
#else
update_process(pn, status);
#endif
update_job(jn);
} else if (findproc(pid, &jn, &pn, 1)) {
pn->status = status;
update_job(jn);
} else {
get_usage();
}
}
break;
case SIGHUP:
if (!handletrap(SIGHUP)) {
stopmsg = 1;
zexit(SIGHUP, 1);
}
break;
case SIGINT:
if (!handletrap(SIGINT)) {
if ((isset(PRIVILEGED) || isset(RESTRICTED)) &&
isset(INTERACTIVE) && noerrexit < 0)
zexit(SIGINT, 1);
if (list_pipe || chline || simple_pline) {
breaks = loops;
errflag = 1;
inerrflush();
}
}
break;
#ifdef SIGWINCH
case SIGWINCH:
adjustwinsize(1);
(void) handletrap(SIGWINCH);
break;
#endif
case SIGALRM:
if (!handletrap(SIGALRM)) {
int idle = ttyidlegetfn(NULL);
int tmout = getiparam("TMOUT");
if (idle >= 0 && idle < tmout)
alarm(tmout - idle);
else {
errflag = noerrs = 0;
zwarn("timeout");
stopmsg = 1;
zexit(SIGALRM, 1);
}
}
break;
default:
(void) handletrap(sig);
break;
}
signal_reset(sig);
#if defined(NO_SIGNAL_BLOCKING)
if (do_jump)
signal_longjmp(jump_to, 1);
#endif
}
void
killrunjobs(int from_signal)
{
int i, killed = 0;
if (unset(HUP))
return;
for (i = 1; i <= maxjob; i++)
if ((from_signal || i != thisjob) && (jobtab[i].stat & STAT_LOCKED) &&
!(jobtab[i].stat & STAT_NOPRINT) &&
!(jobtab[i].stat & STAT_STOPPED)) {
if (jobtab[i].gleader != getpid() &&
killpg(jobtab[i].gleader, SIGHUP) != -1)
killed++;
}
if (killed)
zwarn("warning: %d jobs SIGHUPed", killed);
}
int
killjb(Job jn, int sig)
{
Process pn;
int err = 0;
if (jobbing) {
if (jn->stat & STAT_SUPERJOB) {
if (sig == SIGCONT) {
for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
if (killpg(pn->pid, sig) == -1)
if (kill(pn->pid, sig) == -1 && errno != ESRCH)
err = -1;
for (pn = jn->procs; pn->next; pn = pn->next)
if (kill(pn->pid, sig) == -1 && errno != ESRCH)
err = -1;
if (!jobtab[jn->other].procs && pn)
if (kill(pn->pid, sig) == -1 && errno != ESRCH)
err = -1;
return err;
}
if (killpg(jobtab[jn->other].gleader, sig) == -1 && errno != ESRCH)
err = -1;
if (killpg(jn->gleader, sig) == -1 && errno != ESRCH)
err = -1;
return err;
}
else
return killpg(jn->gleader, sig);
}
for (pn = jn->procs; pn; pn = pn->next)
if ((err = kill(pn->pid, sig)) == -1 && errno != ESRCH && sig != 0)
return -1;
return err;
}
struct savetrap {
int sig, flags, local;
void *list;
};
static LinkList savetraps;
static int dontsavetrap;
static void
dosavetrap(int sig, int level)
{
struct savetrap *st;
st = (struct savetrap *)zalloc(sizeof(*st));
st->sig = sig;
st->local = level;
if ((st->flags = sigtrapped[sig]) & ZSIG_FUNC) {
Shfunc shf, newshf = NULL;
if ((shf = (Shfunc)gettrapnode(sig, 1))) {
newshf = (Shfunc) zalloc(sizeof(*newshf));
newshf->node.nam = ztrdup(shf->node.nam);
newshf->node.flags = shf->node.flags;
newshf->funcdef = dupeprog(shf->funcdef, 0);
newshf->filename = ztrdup(shf->filename);
if (shf->node.flags & PM_UNDEFINED)
newshf->funcdef->shf = newshf;
}
#ifdef DEBUG
else dputs("BUG: no function present with function trap flag set.");
#endif
DPUTS(siglists[sig], "BUG: function signal has eval list, too.");
st->list = newshf;
} else if (sigtrapped[sig]) {
st->list = siglists[sig] ? dupeprog(siglists[sig], 0) : NULL;
} else {
DPUTS(siglists[sig], "BUG: siglists not null for untrapped signal");
st->list = NULL;
}
if (!savetraps)
savetraps = znewlinklist();
zinsertlinknode(savetraps, (LinkNode)savetraps, st);
}
mod_export int
settrap(int sig, Eprog l, int flags)
{
if (sig == -1)
return 1;
if (jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN)) {
zerr("can't trap SIG%s in interactive shells", sigs[sig]);
return 1;
}
queue_signals();
unsettrap(sig);
DPUTS((flags & ZSIG_FUNC) && l,
"BUG: trap function has passed eval list, too");
siglists[sig] = l;
if (!(flags & ZSIG_FUNC) && empty_eprog(l)) {
sigtrapped[sig] = ZSIG_IGNORED;
if (sig && sig <= SIGCOUNT &&
#ifdef SIGWINCH
sig != SIGWINCH &&
#endif
sig != SIGCHLD)
signal_ignore(sig);
} else {
nsigtrapped++;
sigtrapped[sig] = ZSIG_TRAPPED;
if (sig && sig <= SIGCOUNT &&
#ifdef SIGWINCH
sig != SIGWINCH &&
#endif
sig != SIGCHLD)
install_handler(sig);
}
sigtrapped[sig] |= (locallevel << ZSIG_SHIFT) | flags;
unqueue_signals();
return 0;
}
void
unsettrap(int sig)
{
HashNode hn;
queue_signals();
hn = removetrap(sig);
if (hn)
shfunctab->freenode(hn);
unqueue_signals();
}
HashNode
removetrap(int sig)
{
int trapped;
if (sig == -1 ||
(jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN)))
return NULL;
queue_signals();
trapped = sigtrapped[sig];
if (!dontsavetrap && (isset(LOCALTRAPS) || sig == SIGEXIT) &&
locallevel &&
(!trapped || locallevel > (sigtrapped[sig] >> ZSIG_SHIFT)))
dosavetrap(sig, locallevel);
if (!trapped) {
unqueue_signals();
return NULL;
}
if (sigtrapped[sig] & ZSIG_TRAPPED)
nsigtrapped--;
sigtrapped[sig] = 0;
if (sig == SIGINT && interact) {
intr();
noholdintr();
} else if (sig == SIGHUP)
install_handler(sig);
else if (sig && sig <= SIGCOUNT &&
#ifdef SIGWINCH
sig != SIGWINCH &&
#endif
sig != SIGCHLD)
signal_default(sig);
if (trapped & ZSIG_FUNC) {
HashNode node = gettrapnode(sig, 1);
if (node)
removehashnode(shfunctab, node->nam);
unqueue_signals();
return node;
} else if (siglists[sig]) {
freeeprog(siglists[sig]);
siglists[sig] = NULL;
}
unqueue_signals();
return NULL;
}
void
starttrapscope(void)
{
if (intrap)
return;
if (sigtrapped[SIGEXIT]) {
locallevel++;
unsettrap(SIGEXIT);
locallevel--;
}
}
void
endtrapscope(void)
{
LinkNode ln;
struct savetrap *st;
int exittr;
void *exitfn = NULL;
if (intrap)
exittr = 0;
else if ((exittr = sigtrapped[SIGEXIT])) {
if (exittr & ZSIG_FUNC) {
exitfn = removehashnode(shfunctab, "TRAPEXIT");
} else {
exitfn = siglists[SIGEXIT];
siglists[SIGEXIT] = NULL;
}
if (sigtrapped[SIGEXIT] & ZSIG_TRAPPED)
nsigtrapped--;
sigtrapped[SIGEXIT] = 0;
}
if (savetraps) {
while ((ln = firstnode(savetraps)) &&
(st = (struct savetrap *) ln->dat) &&
st->local > locallevel) {
int sig = st->sig;
remnode(savetraps, ln);
if (st->flags && (st->list != NULL)) {
dontsavetrap++;
if (st->flags & ZSIG_FUNC)
settrap(sig, NULL, ZSIG_FUNC);
else
settrap(sig, (Eprog) st->list, 0);
dontsavetrap--;
DPUTS((sigtrapped[sig] ^ st->flags) & ZSIG_TRAPPED,
"BUG: settrap didn't restore correct ZSIG_TRAPPED");
if ((sigtrapped[sig] = st->flags) & ZSIG_FUNC)
shfunctab->addnode(shfunctab, ((Shfunc)st->list)->node.nam,
(Shfunc) st->list);
} else if (sigtrapped[sig])
unsettrap(sig);
zfree(st, sizeof(*st));
}
}
if (exittr) {
dotrapargs(SIGEXIT, &exittr, exitfn);
if (exittr & ZSIG_FUNC)
shfunctab->freenode((HashNode)exitfn);
else
freeeprog(exitfn);
}
DPUTS(!locallevel && savetraps && firstnode(savetraps),
"BUG: still saved traps outside all function scope");
}
static int
handletrap(int sig)
{
if (!sigtrapped[sig])
return 0;
if (trap_queueing_enabled)
{
int temp_rear = ++trap_queue_rear % MAX_QUEUE_SIZE;
DPUTS(temp_rear == trap_queue_front, "BUG: trap queue full");
if (temp_rear != trap_queue_front) {
trap_queue_rear = temp_rear;
trap_queue[trap_queue_rear] = sig;
}
return 1;
}
dotrap(sig);
if (sig == SIGALRM)
{
int tmout;
if ((tmout = getiparam("TMOUT")))
alarm(tmout);
}
return 1;
}
void
queue_traps(int wait_cmd)
{
if (!isset(TRAPSASYNC) && !wait_cmd) {
trap_queueing_enabled = 1;
}
}
void
unqueue_traps(void)
{
trap_queueing_enabled = 0;
while (trap_queue_front != trap_queue_rear) {
trap_queue_front = (trap_queue_front + 1) % MAX_QUEUE_SIZE;
(void) handletrap(trap_queue[trap_queue_front]);
}
}
int intrap;
int trapisfunc;
int traplocallevel;
static void
dotrapargs(int sig, int *sigtr, void *sigfn)
{
LinkList args;
char *name, num[4];
int obreaks = breaks;
int oretflag = retflag;
int isfunc;
int traperr, new_trap_state, new_trap_return;
if ((*sigtr & ZSIG_IGNORED) || !sigfn || errflag)
return;
if (intrap) {
switch (sig) {
case SIGEXIT:
case SIGDEBUG:
case SIGZERR:
return;
}
}
intrap++;
*sigtr |= ZSIG_IGNORED;
lexsave();
execsave();
breaks = retflag = 0;
traplocallevel = locallevel;
runhookdef(BEFORETRAPHOOK, NULL);
if (*sigtr & ZSIG_FUNC) {
int osc = sfcontext;
HashNode hn = gettrapnode(sig, 0);
args = znewlinklist();
if (hn) {
name = ztrdup(hn->nam);
} else {
name = (char *) zalloc(5 + strlen(sigs[sig]));
sprintf(name, "TRAP%s", sigs[sig]);
}
zaddlinknode(args, name);
sprintf(num, "%d", sig);
zaddlinknode(args, num);
trap_return = -1;
trap_state = TRAP_STATE_PRIMED;
trapisfunc = isfunc = 1;
sfcontext = SFC_SIGNAL;
doshfunc((Shfunc)sigfn, args, 1);
sfcontext = osc;
freelinklist(args, (FreeFunc) NULL);
zsfree(name);
} else {
trap_return = -2;
trap_state = TRAP_STATE_PRIMED;
trapisfunc = isfunc = 0;
execode((Eprog)sigfn, 1, 0);
}
runhookdef(AFTERTRAPHOOK, NULL);
traperr = errflag;
new_trap_state = trap_state;
new_trap_return = trap_return;
execrestore();
lexrestore();
if (new_trap_state == TRAP_STATE_FORCE_RETURN &&
!(isfunc && new_trap_return == 0)) {
if (isfunc) {
breaks = loops;
errflag = 1;
}
lastval = new_trap_return;
retflag = 1;
} else {
if (traperr && emulation != EMULATE_SH)
lastval = 1;
if (try_tryflag)
errflag = traperr;
breaks += obreaks;
retflag = oretflag;
if (breaks > loops)
breaks = loops;
}
if (zleactive && resetneeded)
zleentry(ZLE_CMD_REFRESH);
if (*sigtr != ZSIG_IGNORED)
*sigtr &= ~ZSIG_IGNORED;
intrap--;
}
void
dotrap(int sig)
{
void *funcprog;
if (sigtrapped[sig] & ZSIG_FUNC) {
HashNode hn = gettrapnode(sig, 0);
if (hn)
funcprog = hn;
else {
#ifdef DEBUG
dputs("BUG: running function trap which has escaped.");
#endif
funcprog = NULL;
}
} else
funcprog = siglists[sig];
if ((sigtrapped[sig] & ZSIG_IGNORED) || !funcprog || errflag)
return;
dotrapargs(sig, sigtrapped+sig, funcprog);
}