ash: finally installed it as /bin/sh on my machine.
some breakage noticed, the most dire is mishandled ^C. fixing it. function old new delta blocking_wait_with_raise_on_sig - 40 +40 waitforjob 85 100 +15 setsignal 280 278 -2 evalvar 1376 1374 -2 waitcmd 186 182 -4 dowait 350 316 -34 redirect 1231 1185 -46 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/5 up/down: 55/-88) Total: -33 bytes
This commit is contained in:
parent
0c68a874e7
commit
f8535ccd65
198
shell/ash.c
198
shell/ash.c
@ -201,14 +201,13 @@ struct globals_misc {
|
|||||||
/*
|
/*
|
||||||
* Sigmode records the current value of the signal handlers for the various
|
* Sigmode records the current value of the signal handlers for the various
|
||||||
* modes. A value of zero means that the current handler is not known.
|
* modes. A value of zero means that the current handler is not known.
|
||||||
* S_HARD_IGN indicates that the signal was ignored on entry to the shell,
|
* S_HARD_IGN indicates that the signal was ignored on entry to the shell.
|
||||||
*/
|
*/
|
||||||
char sigmode[NSIG - 1];
|
char sigmode[NSIG - 1];
|
||||||
#define S_DFL 1 /* default signal handling (SIG_DFL) */
|
#define S_DFL 1 /* default signal handling (SIG_DFL) */
|
||||||
#define S_CATCH 2 /* signal is caught */
|
#define S_CATCH 2 /* signal is caught */
|
||||||
#define S_IGN 3 /* signal is ignored (SIG_IGN) */
|
#define S_IGN 3 /* signal is ignored (SIG_IGN) */
|
||||||
#define S_HARD_IGN 4 /* signal is ignored permenantly */
|
#define S_HARD_IGN 4 /* signal is ignored permenantly */
|
||||||
#define S_RESET 5 /* temporary - to reset a hard ignored sig */
|
|
||||||
|
|
||||||
/* indicates specified signal received */
|
/* indicates specified signal received */
|
||||||
char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
|
char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
|
||||||
@ -368,7 +367,7 @@ force_int_on(void)
|
|||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ignore a signal. Only one usage site - in forkchild()
|
* Ignore a signal. Avoids unnecessary system calls.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ignoresig(int signo)
|
ignoresig(int signo)
|
||||||
@ -3295,81 +3294,90 @@ static void setjobctl(int);
|
|||||||
static void
|
static void
|
||||||
setsignal(int signo)
|
setsignal(int signo)
|
||||||
{
|
{
|
||||||
int action;
|
char *t;
|
||||||
char *t, tsig;
|
char cur_act, new_act;
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
|
|
||||||
t = trap[signo];
|
t = trap[signo];
|
||||||
action = S_IGN;
|
new_act = S_DFL;
|
||||||
if (t == NULL)
|
if (t != NULL) { /* trap for this sig is set */
|
||||||
action = S_DFL;
|
new_act = S_CATCH;
|
||||||
else if (*t != '\0')
|
if (t[0] == '\0') /* trap is "": ignore this sig */
|
||||||
action = S_CATCH;
|
new_act = S_IGN;
|
||||||
if (rootshell && action == S_DFL) {
|
}
|
||||||
|
|
||||||
|
if (rootshell && new_act == S_DFL) {
|
||||||
switch (signo) {
|
switch (signo) {
|
||||||
case SIGINT:
|
case SIGINT:
|
||||||
if (iflag || minusc || sflag == 0)
|
if (iflag || minusc || sflag == 0)
|
||||||
action = S_CATCH;
|
new_act = S_CATCH;
|
||||||
break;
|
break;
|
||||||
case SIGQUIT:
|
case SIGQUIT:
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if (debug)
|
if (debug)
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
/* FALLTHROUGH */
|
/* man bash:
|
||||||
|
* "In all cases, bash ignores SIGQUIT. Non-builtin
|
||||||
|
* commands run by bash have signal handlers
|
||||||
|
* set to the values inherited by the shell
|
||||||
|
* from its parent". */
|
||||||
|
new_act = S_IGN;
|
||||||
|
break;
|
||||||
case SIGTERM:
|
case SIGTERM:
|
||||||
if (iflag)
|
if (iflag)
|
||||||
action = S_IGN;
|
new_act = S_IGN;
|
||||||
break;
|
break;
|
||||||
#if JOBS
|
#if JOBS
|
||||||
case SIGTSTP:
|
case SIGTSTP:
|
||||||
case SIGTTOU:
|
case SIGTTOU:
|
||||||
if (mflag)
|
if (mflag)
|
||||||
action = S_IGN;
|
new_act = S_IGN;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//TODO: if !rootshell, we reset SIGQUIT to DFL,
|
||||||
|
//whereas we have to restore it to what shell got on entry
|
||||||
|
//from the parent. See comment above
|
||||||
|
|
||||||
t = &sigmode[signo - 1];
|
t = &sigmode[signo - 1];
|
||||||
tsig = *t;
|
cur_act = *t;
|
||||||
if (tsig == 0) {
|
if (cur_act == 0) {
|
||||||
/*
|
/* current setting is not yet known */
|
||||||
* current setting unknown
|
if (sigaction(signo, NULL, &act)) {
|
||||||
*/
|
/* pretend it worked; maybe we should give a warning,
|
||||||
if (sigaction(signo, NULL, &act) == -1) {
|
* but other shells don't. We don't alter sigmode,
|
||||||
/*
|
* so we retry every time.
|
||||||
* Pretend it worked; maybe we should give a warning
|
* btw, in Linux it never fails. --vda */
|
||||||
* here, but other shells don't. We don't alter
|
|
||||||
* sigmode, so that we retry every time.
|
|
||||||
*/
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tsig = S_RESET; /* force to be set */
|
|
||||||
if (act.sa_handler == SIG_IGN) {
|
if (act.sa_handler == SIG_IGN) {
|
||||||
tsig = S_HARD_IGN;
|
cur_act = S_HARD_IGN;
|
||||||
if (mflag
|
if (mflag
|
||||||
&& (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
|
&& (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
|
||||||
) {
|
) {
|
||||||
tsig = S_IGN; /* don't hard ignore these */
|
cur_act = S_IGN; /* don't hard ignore these */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tsig == S_HARD_IGN || tsig == action)
|
if (cur_act == S_HARD_IGN || cur_act == new_act)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
act.sa_handler = SIG_DFL;
|
act.sa_handler = SIG_DFL;
|
||||||
switch (action) {
|
switch (new_act) {
|
||||||
case S_CATCH:
|
case S_CATCH:
|
||||||
act.sa_handler = onsig;
|
act.sa_handler = onsig;
|
||||||
|
act.sa_flags = 0; /* matters only if !DFL and !IGN */
|
||||||
|
sigfillset(&act.sa_mask); /* ditto */
|
||||||
break;
|
break;
|
||||||
case S_IGN:
|
case S_IGN:
|
||||||
act.sa_handler = SIG_IGN;
|
act.sa_handler = SIG_IGN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*t = action;
|
|
||||||
act.sa_flags = 0;
|
|
||||||
sigfillset(&act.sa_mask);
|
|
||||||
sigaction_set(signo, &act);
|
sigaction_set(signo, &act);
|
||||||
|
|
||||||
|
*t = new_act;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* mode flags for set_curjob */
|
/* mode flags for set_curjob */
|
||||||
@ -3790,15 +3798,9 @@ dowait(int wait_flags, struct job *job)
|
|||||||
pid = waitpid(-1, &status,
|
pid = waitpid(-1, &status,
|
||||||
(doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
|
(doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
|
||||||
TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
|
TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
|
||||||
|
if (pid <= 0)
|
||||||
if (pid <= 0) {
|
|
||||||
/* If we were doing blocking wait and (probably) got EINTR,
|
|
||||||
* check for pending sigs received while waiting.
|
|
||||||
* (NB: can be moved into callers if needed) */
|
|
||||||
if (wait_flags == DOWAIT_BLOCK && pendingsig)
|
|
||||||
raise_exception(EXSIG);
|
|
||||||
return pid;
|
return pid;
|
||||||
}
|
|
||||||
INT_OFF;
|
INT_OFF;
|
||||||
thisjob = NULL;
|
thisjob = NULL;
|
||||||
for (jp = curjob; jp; jp = jp->prev_job) {
|
for (jp = curjob; jp; jp = jp->prev_job) {
|
||||||
@ -3870,6 +3872,15 @@ dowait(int wait_flags, struct job *job)
|
|||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
blocking_wait_with_raise_on_sig(struct job *job)
|
||||||
|
{
|
||||||
|
pid_t pid = dowait(DOWAIT_BLOCK, job);
|
||||||
|
if (pid <= 0 && pendingsig)
|
||||||
|
raise_exception(EXSIG);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
#if JOBS
|
#if JOBS
|
||||||
static void
|
static void
|
||||||
showjob(FILE *out, struct job *jp, int mode)
|
showjob(FILE *out, struct job *jp, int mode)
|
||||||
@ -3949,7 +3960,7 @@ showjobs(FILE *out, int mode)
|
|||||||
|
|
||||||
TRACE(("showjobs(%x) called\n", mode));
|
TRACE(("showjobs(%x) called\n", mode));
|
||||||
|
|
||||||
/* If not even one job changed, there is nothing to do */
|
/* Handle all finished jobs */
|
||||||
while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
|
while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -4041,7 +4052,14 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
|
|||||||
jp->waited = 1;
|
jp->waited = 1;
|
||||||
jp = jp->prev_job;
|
jp = jp->prev_job;
|
||||||
}
|
}
|
||||||
dowait(DOWAIT_BLOCK, NULL);
|
/* man bash:
|
||||||
|
* "When bash is waiting for an asynchronous command via
|
||||||
|
* the wait builtin, the reception of a signal for which a trap
|
||||||
|
* has been set will cause the wait builtin to return immediately
|
||||||
|
* with an exit status greater than 128, immediately after which
|
||||||
|
* the trap is executed."
|
||||||
|
* Do we do it that way? */
|
||||||
|
blocking_wait_with_raise_on_sig(NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4061,11 +4079,10 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
|
|||||||
job = getjob(*argv, 0);
|
job = getjob(*argv, 0);
|
||||||
/* loop until process terminated or stopped */
|
/* loop until process terminated or stopped */
|
||||||
while (job->state == JOBRUNNING)
|
while (job->state == JOBRUNNING)
|
||||||
dowait(DOWAIT_BLOCK, NULL);
|
blocking_wait_with_raise_on_sig(NULL);
|
||||||
job->waited = 1;
|
job->waited = 1;
|
||||||
retval = getstatus(job);
|
retval = getstatus(job);
|
||||||
repeat:
|
repeat: ;
|
||||||
;
|
|
||||||
} while (*++argv);
|
} while (*++argv);
|
||||||
|
|
||||||
ret:
|
ret:
|
||||||
@ -4492,6 +4509,10 @@ forkchild(struct job *jp, /*union node *n,*/ int mode)
|
|||||||
oldlvl = shlvl;
|
oldlvl = shlvl;
|
||||||
shlvl++;
|
shlvl++;
|
||||||
|
|
||||||
|
/* man bash: "Non-builtin commands run by bash have signal handlers
|
||||||
|
* set to the values inherited by the shell from its parent".
|
||||||
|
* Do we do it correctly? */
|
||||||
|
|
||||||
closescript();
|
closescript();
|
||||||
clear_traps();
|
clear_traps();
|
||||||
#if JOBS
|
#if JOBS
|
||||||
@ -4504,8 +4525,8 @@ forkchild(struct job *jp, /*union node *n,*/ int mode)
|
|||||||
pgrp = getpid();
|
pgrp = getpid();
|
||||||
else
|
else
|
||||||
pgrp = jp->ps[0].pid;
|
pgrp = jp->ps[0].pid;
|
||||||
/* This can fail because we are doing it in the parent also */
|
/* this can fail because we are doing it in the parent also */
|
||||||
(void)setpgid(0, pgrp);
|
setpgid(0, pgrp);
|
||||||
if (mode == FORK_FG)
|
if (mode == FORK_FG)
|
||||||
xtcsetpgrp(ttyfd, pgrp);
|
xtcsetpgrp(ttyfd, pgrp);
|
||||||
setsignal(SIGTSTP);
|
setsignal(SIGTSTP);
|
||||||
@ -4513,6 +4534,8 @@ forkchild(struct job *jp, /*union node *n,*/ int mode)
|
|||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
if (mode == FORK_BG) {
|
if (mode == FORK_BG) {
|
||||||
|
/* man bash: "When job control is not in effect,
|
||||||
|
* asynchronous commands ignore SIGINT and SIGQUIT" */
|
||||||
ignoresig(SIGINT);
|
ignoresig(SIGINT);
|
||||||
ignoresig(SIGQUIT);
|
ignoresig(SIGQUIT);
|
||||||
if (jp->nprocs == 0) {
|
if (jp->nprocs == 0) {
|
||||||
@ -4521,10 +4544,18 @@ forkchild(struct job *jp, /*union node *n,*/ int mode)
|
|||||||
ash_msg_and_raise_error("can't open %s", bb_dev_null);
|
ash_msg_and_raise_error("can't open %s", bb_dev_null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!oldlvl && iflag) {
|
if (!oldlvl) {
|
||||||
setsignal(SIGINT);
|
if (iflag) { /* why if iflag only? */
|
||||||
|
setsignal(SIGINT);
|
||||||
|
setsignal(SIGTERM);
|
||||||
|
}
|
||||||
|
/* man bash:
|
||||||
|
* "In all cases, bash ignores SIGQUIT. Non-builtin
|
||||||
|
* commands run by bash have signal handlers
|
||||||
|
* set to the values inherited by the shell
|
||||||
|
* from its parent".
|
||||||
|
* Take care of the second rule: */
|
||||||
setsignal(SIGQUIT);
|
setsignal(SIGQUIT);
|
||||||
setsignal(SIGTERM);
|
|
||||||
}
|
}
|
||||||
for (jp = curjob; jp; jp = jp->prev_job)
|
for (jp = curjob; jp; jp = jp->prev_job)
|
||||||
freejob(jp);
|
freejob(jp);
|
||||||
@ -4596,12 +4627,12 @@ forkshell(struct job *jp, union node *n, int mode)
|
|||||||
/*
|
/*
|
||||||
* Wait for job to finish.
|
* Wait for job to finish.
|
||||||
*
|
*
|
||||||
* Under job control we have the problem that while a child process is
|
* Under job control we have the problem that while a child process
|
||||||
* running interrupts generated by the user are sent to the child but not
|
* is running interrupts generated by the user are sent to the child
|
||||||
* to the shell. This means that an infinite loop started by an inter-
|
* but not to the shell. This means that an infinite loop started by
|
||||||
* active user may be hard to kill. With job control turned off, an
|
* an interactive user may be hard to kill. With job control turned off,
|
||||||
* interactive user may place an interactive program inside a loop. If
|
* an interactive user may place an interactive program inside a loop.
|
||||||
* the interactive program catches interrupts, the user doesn't want
|
* If the interactive program catches interrupts, the user doesn't want
|
||||||
* these interrupts to also abort the loop. The approach we take here
|
* these interrupts to also abort the loop. The approach we take here
|
||||||
* is to have the shell ignore interrupt signals while waiting for a
|
* is to have the shell ignore interrupt signals while waiting for a
|
||||||
* foreground process to terminate, and then send itself an interrupt
|
* foreground process to terminate, and then send itself an interrupt
|
||||||
@ -4619,9 +4650,44 @@ waitforjob(struct job *jp)
|
|||||||
int st;
|
int st;
|
||||||
|
|
||||||
TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
|
TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
|
||||||
|
|
||||||
|
INT_OFF;
|
||||||
while (jp->state == JOBRUNNING) {
|
while (jp->state == JOBRUNNING) {
|
||||||
|
/* In non-interactive shells, we _can_ get
|
||||||
|
* a keyboard signal here and be EINTRed,
|
||||||
|
* but we just loop back, waiting for command to complete.
|
||||||
|
*
|
||||||
|
* man bash:
|
||||||
|
* "If bash is waiting for a command to complete and receives
|
||||||
|
* a signal for which a trap has been set, the trap
|
||||||
|
* will not be executed until the command completes."
|
||||||
|
*
|
||||||
|
* Reality is that even if trap is not set, bash
|
||||||
|
* will not act on the signal until command completes.
|
||||||
|
* Try this. sleep5intoff.c:
|
||||||
|
* #include <signal.h>
|
||||||
|
* #include <unistd.h>
|
||||||
|
* int main() {
|
||||||
|
* sigset_t set;
|
||||||
|
* sigemptyset(&set);
|
||||||
|
* sigaddset(&set, SIGINT);
|
||||||
|
* sigaddset(&set, SIGQUIT);
|
||||||
|
* sigprocmask(SIG_BLOCK, &set, NULL);
|
||||||
|
* sleep(5);
|
||||||
|
* return 0;
|
||||||
|
* }
|
||||||
|
* $ bash -c './sleep5intoff; echo hi'
|
||||||
|
* ^C^C^C^C <--- pressing ^C once a second
|
||||||
|
* $ _
|
||||||
|
* TODO: we do not execute "echo hi" as bash does:
|
||||||
|
* $ bash -c './sleep5intoff; echo hi'
|
||||||
|
* ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
|
||||||
|
* $ _
|
||||||
|
*/
|
||||||
dowait(DOWAIT_BLOCK, jp);
|
dowait(DOWAIT_BLOCK, jp);
|
||||||
}
|
}
|
||||||
|
INT_ON;
|
||||||
|
|
||||||
st = getstatus(jp);
|
st = getstatus(jp);
|
||||||
#if JOBS
|
#if JOBS
|
||||||
if (jp->jobctl) {
|
if (jp->jobctl) {
|
||||||
@ -4757,12 +4823,10 @@ openhere(union node *redir)
|
|||||||
if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
|
if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
|
||||||
/* child */
|
/* child */
|
||||||
close(pip[0]);
|
close(pip[0]);
|
||||||
signal(SIGINT, SIG_IGN);
|
ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
|
||||||
signal(SIGQUIT, SIG_IGN);
|
ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
|
||||||
signal(SIGHUP, SIG_IGN);
|
ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
|
||||||
#ifdef SIGTSTP
|
ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
|
||||||
signal(SIGTSTP, SIG_IGN);
|
|
||||||
#endif
|
|
||||||
signal(SIGPIPE, SIG_DFL);
|
signal(SIGPIPE, SIG_DFL);
|
||||||
if (redir->type == NHERE)
|
if (redir->type == NHERE)
|
||||||
full_write(pip[1], redir->nhere.doc->narg.text, len);
|
full_write(pip[1], redir->nhere.doc->narg.text, len);
|
||||||
|
Loading…
Reference in New Issue
Block a user