ash: commented-out possible fix for 7694

bash has a feature: it restores termios after a successful wait for
a foreground job which had at least one stopped or sigkilled member.
The probable rationale is that SIGSTOP and SIGKILL can preclude task from
properly restoring tty state. Should we do this too?
A reproducer: ^Z an interactive python:

$ python
Python 2.7.12 (...)
>>> ^Z
    { python leaves tty in -icanon -echo state. We do survive that... }
 [1]+  Stopped                    python
    { ...however, next program (python no.2) does not survive it well: }
$ python
Python 2.7.12 (...)
>>> Traceback (most recent call last):
    { above, I typed "qwerty<CR>", but -echo state is still in effect }
  File "<stdin>", line 1, in <module>
NameError: name 'qwerty' is not defined

The implementation is modeled on bash code and seems to work.
However, I'm not sure we should do this. For one: what if I'd fg
the stopped python instead? It'll be confused by "restored" tty state.

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-01-11 19:59:03 +01:00
parent 4c179373e0
commit 098b713c7b

View File

@ -3579,6 +3579,72 @@ static struct job *curjob; //lots
/* number of presumed living untracked jobs */
static int jobless; //4
#if 0
/* Bash has a feature: it restores termios after a successful wait for
* a foreground job which had at least one stopped or sigkilled member.
* The probable rationale is that SIGSTOP and SIGKILL can preclude task from
* properly restoring tty state. Should we do this too?
* A reproducer: ^Z an interactive python:
*
* # python
* Python 2.7.12 (...)
* >>> ^Z
* { python leaves tty in -icanon -echo state. We do survive that... }
* [1]+ Stopped python
* { ...however, next program (python #2) does not survive it well: }
* # python
* Python 2.7.12 (...)
* >>> Traceback (most recent call last):
* { above, I typed "qwerty<CR>", but -echo state is still in effect }
* File "<stdin>", line 1, in <module>
* NameError: name 'qwerty' is not defined
*
* The implementation below is modeled on bash code and seems to work.
* However, I'm not sure we should do this. For one: what if I'd fg
* the stopped python instead? It'll be confused by "restored" tty state.
*/
static struct termios shell_tty_info;
static void
get_tty_state(void)
{
if (rootshell && ttyfd >= 0)
tcgetattr(ttyfd, &shell_tty_info);
}
static void
set_tty_state(void)
{
/* if (rootshell) - caller ensures this */
if (ttyfd >= 0)
tcsetattr(ttyfd, TCSADRAIN, &shell_tty_info);
}
static int
job_signal_status(struct job *jp)
{
int status;
unsigned i;
struct procstat *ps = jp->ps;
for (i = 0; i < jp->nprocs; i++) {
status = ps[i].ps_status;
if (WIFSIGNALED(status) || WIFSTOPPED(status))
return status;
}
return 0;
}
static void
restore_tty_if_stopped_or_signaled(struct job *jp)
{
//TODO: check what happens if we come from waitforjob() in expbackq()
if (rootshell) {
int s = job_signal_status(jp);
if (s) /* WIFSIGNALED(s) || WIFSTOPPED(s) */
set_tty_state();
}
}
#else
# define get_tty_state() ((void)0)
# define restore_tty_if_stopped_or_signaled(jp) ((void)0)
#endif
static void
set_curjob(struct job *jp, unsigned mode)
{
@ -3910,8 +3976,10 @@ restartjob(struct job *jp, int mode)
goto out;
jp->state = JOBRUNNING;
pgid = jp->ps[0].ps_pid;
if (mode == FORK_FG)
if (mode == FORK_FG) {
get_tty_state();
xtcsetpgrp(ttyfd, pgid);
}
killpg(pgid, SIGCONT);
ps = jp->ps;
i = jp->nprocs;
@ -4445,7 +4513,7 @@ makejob(/*union node *node,*/ int nprocs)
memset(jp, 0, sizeof(*jp));
#if JOBS
/* jp->jobctl is a bitfield.
* "jp->jobctl |= jobctl" likely to give awful code */
* "jp->jobctl |= doing_jobctl" likely to give awful code */
if (doing_jobctl)
jp->jobctl = 1;
#endif
@ -5040,6 +5108,8 @@ waitforjob(struct job *jp)
#if JOBS
if (jp->jobctl) {
xtcsetpgrp(ttyfd, rootpid);
restore_tty_if_stopped_or_signaled(jp);
/*
* This is truly gross.
* If we're doing job control, then we did a TIOCSPGRP which
@ -8852,13 +8922,15 @@ static int
evalsubshell(union node *n, int flags)
{
struct job *jp;
int backgnd = (n->type == NBACKGND);
int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
int status;
expredir(n->nredir.redirect);
if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
goto nofork;
INT_OFF;
if (backgnd == FORK_FG)
get_tty_state();
jp = makejob(/*n,*/ 1);
if (forkshell(jp, n, backgnd) == 0) {
/* child */
@ -8873,7 +8945,7 @@ evalsubshell(union node *n, int flags)
}
/* parent */
status = 0;
if (!backgnd)
if (backgnd == FORK_FG)
status = waitforjob(jp);
INT_ON;
return status;
@ -8965,6 +9037,8 @@ evalpipe(union node *n, int flags)
pipelen++;
flags |= EV_EXIT;
INT_OFF;
if (n->npipe.pipe_backgnd == 0)
get_tty_state();
jp = makejob(/*n,*/ pipelen);
prevfd = -1;
for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
@ -9647,6 +9721,7 @@ evalcommand(union node *cmd, int flags)
if (!(flags & EV_EXIT) || may_have_traps) {
/* No, forking off a child is necessary */
INT_OFF;
get_tty_state();
jp = makejob(/*cmd,*/ 1);
if (forkshell(jp, cmd, FORK_FG) != 0) {
/* parent */