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:
Denis Vlasenko 2008-12-03 10:36:26 +00:00
parent 0c68a874e7
commit f8535ccd65

View File

@ -201,14 +201,13 @@ struct globals_misc {
/*
* 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.
* 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];
#define S_DFL 1 /* default signal handling (SIG_DFL) */
#define S_CATCH 2 /* signal is caught */
#define S_IGN 3 /* signal is ignored (SIG_IGN) */
#define S_HARD_IGN 4 /* signal is ignored permenantly */
#define S_RESET 5 /* temporary - to reset a hard ignored sig */
/* indicates specified signal received */
char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
@ -368,7 +367,7 @@ force_int_on(void)
} while (0)
/*
* Ignore a signal. Only one usage site - in forkchild()
* Ignore a signal. Avoids unnecessary system calls.
*/
static void
ignoresig(int signo)
@ -3295,81 +3294,90 @@ static void setjobctl(int);
static void
setsignal(int signo)
{
int action;
char *t, tsig;
char *t;
char cur_act, new_act;
struct sigaction act;
t = trap[signo];
action = S_IGN;
if (t == NULL)
action = S_DFL;
else if (*t != '\0')
action = S_CATCH;
if (rootshell && action == S_DFL) {
new_act = S_DFL;
if (t != NULL) { /* trap for this sig is set */
new_act = S_CATCH;
if (t[0] == '\0') /* trap is "": ignore this sig */
new_act = S_IGN;
}
if (rootshell && new_act == S_DFL) {
switch (signo) {
case SIGINT:
if (iflag || minusc || sflag == 0)
action = S_CATCH;
new_act = S_CATCH;
break;
case SIGQUIT:
#if DEBUG
if (debug)
break;
#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:
if (iflag)
action = S_IGN;
new_act = S_IGN;
break;
#if JOBS
case SIGTSTP:
case SIGTTOU:
if (mflag)
action = S_IGN;
new_act = S_IGN;
break;
#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];
tsig = *t;
if (tsig == 0) {
/*
* current setting unknown
*/
if (sigaction(signo, NULL, &act) == -1) {
/*
* Pretend it worked; maybe we should give a warning
* here, but other shells don't. We don't alter
* sigmode, so that we retry every time.
*/
cur_act = *t;
if (cur_act == 0) {
/* current setting is not yet known */
if (sigaction(signo, NULL, &act)) {
/* pretend it worked; maybe we should give a warning,
* but other shells don't. We don't alter sigmode,
* so we retry every time.
* btw, in Linux it never fails. --vda */
return;
}
tsig = S_RESET; /* force to be set */
if (act.sa_handler == SIG_IGN) {
tsig = S_HARD_IGN;
cur_act = S_HARD_IGN;
if (mflag
&& (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;
act.sa_handler = SIG_DFL;
switch (action) {
switch (new_act) {
case S_CATCH:
act.sa_handler = onsig;
act.sa_flags = 0; /* matters only if !DFL and !IGN */
sigfillset(&act.sa_mask); /* ditto */
break;
case S_IGN:
act.sa_handler = SIG_IGN;
break;
}
*t = action;
act.sa_flags = 0;
sigfillset(&act.sa_mask);
sigaction_set(signo, &act);
*t = new_act;
}
/* mode flags for set_curjob */
@ -3790,15 +3798,9 @@ dowait(int wait_flags, struct job *job)
pid = waitpid(-1, &status,
(doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
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);
if (pid <= 0)
return pid;
}
INT_OFF;
thisjob = NULL;
for (jp = curjob; jp; jp = jp->prev_job) {
@ -3870,6 +3872,15 @@ dowait(int wait_flags, struct job *job)
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
static void
showjob(FILE *out, struct job *jp, int mode)
@ -3949,7 +3960,7 @@ showjobs(FILE *out, int 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)
continue;
@ -4041,7 +4052,14 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
jp->waited = 1;
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);
/* loop until process terminated or stopped */
while (job->state == JOBRUNNING)
dowait(DOWAIT_BLOCK, NULL);
blocking_wait_with_raise_on_sig(NULL);
job->waited = 1;
retval = getstatus(job);
repeat:
;
repeat: ;
} while (*++argv);
ret:
@ -4492,6 +4509,10 @@ forkchild(struct job *jp, /*union node *n,*/ int mode)
oldlvl = 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();
clear_traps();
#if JOBS
@ -4504,8 +4525,8 @@ forkchild(struct job *jp, /*union node *n,*/ int mode)
pgrp = getpid();
else
pgrp = jp->ps[0].pid;
/* This can fail because we are doing it in the parent also */
(void)setpgid(0, pgrp);
/* this can fail because we are doing it in the parent also */
setpgid(0, pgrp);
if (mode == FORK_FG)
xtcsetpgrp(ttyfd, pgrp);
setsignal(SIGTSTP);
@ -4513,6 +4534,8 @@ forkchild(struct job *jp, /*union node *n,*/ int mode)
} else
#endif
if (mode == FORK_BG) {
/* man bash: "When job control is not in effect,
* asynchronous commands ignore SIGINT and SIGQUIT" */
ignoresig(SIGINT);
ignoresig(SIGQUIT);
if (jp->nprocs == 0) {
@ -4521,11 +4544,19 @@ forkchild(struct job *jp, /*union node *n,*/ int mode)
ash_msg_and_raise_error("can't open %s", bb_dev_null);
}
}
if (!oldlvl && iflag) {
if (!oldlvl) {
if (iflag) { /* why if iflag only? */
setsignal(SIGINT);
setsignal(SIGQUIT);
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);
}
for (jp = curjob; jp; jp = jp->prev_job)
freejob(jp);
jobless = 0;
@ -4596,12 +4627,12 @@ forkshell(struct job *jp, union node *n, int mode)
/*
* Wait for job to finish.
*
* Under job control we have the problem that while a child process is
* running interrupts generated by the user are sent to the child but not
* to the shell. This means that an infinite loop started by an inter-
* active user may be hard to kill. With job control turned off, an
* interactive user may place an interactive program inside a loop. If
* the interactive program catches interrupts, the user doesn't want
* Under job control we have the problem that while a child process
* is running interrupts generated by the user are sent to the child
* but not to the shell. This means that an infinite loop started by
* an interactive user may be hard to kill. With job control turned off,
* an interactive user may place an interactive program inside a loop.
* If the interactive program catches interrupts, the user doesn't want
* these interrupts to also abort the loop. The approach we take here
* is to have the shell ignore interrupt signals while waiting for a
* foreground process to terminate, and then send itself an interrupt
@ -4619,9 +4650,44 @@ waitforjob(struct job *jp)
int st;
TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
INT_OFF;
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);
}
INT_ON;
st = getstatus(jp);
#if JOBS
if (jp->jobctl) {
@ -4757,12 +4823,10 @@ openhere(union node *redir)
if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
/* child */
close(pip[0]);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGHUP, SIG_IGN);
#ifdef SIGTSTP
signal(SIGTSTP, SIG_IGN);
#endif
ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
signal(SIGPIPE, SIG_DFL);
if (redir->type == NHERE)
full_write(pip[1], redir->nhere.doc->narg.text, len);