aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2017-01-11 19:59:03 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2017-01-11 19:59:03 +0100
commit098b713c7b5b22fc60b606be97da431eaedc6639 (patch)
treed1eef52f5412f67ea2991d86fd236386c217151d /shell
parent4c179373e07fbc1d8fc8e53c7096fce9ee4b08b6 (diff)
downloadbusybox-w32-098b713c7b5b22fc60b606be97da431eaedc6639.tar.gz
busybox-w32-098b713c7b5b22fc60b606be97da431eaedc6639.tar.bz2
busybox-w32-098b713c7b5b22fc60b606be97da431eaedc6639.zip
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>
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c83
1 files changed, 79 insertions, 4 deletions
diff --git a/shell/ash.c b/shell/ash.c
index bfdd94047..d8f41327b 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -3579,6 +3579,72 @@ static struct job *curjob; //lots
3579/* number of presumed living untracked jobs */ 3579/* number of presumed living untracked jobs */
3580static int jobless; //4 3580static int jobless; //4
3581 3581
3582#if 0
3583/* Bash has a feature: it restores termios after a successful wait for
3584 * a foreground job which had at least one stopped or sigkilled member.
3585 * The probable rationale is that SIGSTOP and SIGKILL can preclude task from
3586 * properly restoring tty state. Should we do this too?
3587 * A reproducer: ^Z an interactive python:
3588 *
3589 * # python
3590 * Python 2.7.12 (...)
3591 * >>> ^Z
3592 * { python leaves tty in -icanon -echo state. We do survive that... }
3593 * [1]+ Stopped python
3594 * { ...however, next program (python #2) does not survive it well: }
3595 * # python
3596 * Python 2.7.12 (...)
3597 * >>> Traceback (most recent call last):
3598 * { above, I typed "qwerty<CR>", but -echo state is still in effect }
3599 * File "<stdin>", line 1, in <module>
3600 * NameError: name 'qwerty' is not defined
3601 *
3602 * The implementation below is modeled on bash code and seems to work.
3603 * However, I'm not sure we should do this. For one: what if I'd fg
3604 * the stopped python instead? It'll be confused by "restored" tty state.
3605 */
3606static struct termios shell_tty_info;
3607static void
3608get_tty_state(void)
3609{
3610 if (rootshell && ttyfd >= 0)
3611 tcgetattr(ttyfd, &shell_tty_info);
3612}
3613static void
3614set_tty_state(void)
3615{
3616 /* if (rootshell) - caller ensures this */
3617 if (ttyfd >= 0)
3618 tcsetattr(ttyfd, TCSADRAIN, &shell_tty_info);
3619}
3620static int
3621job_signal_status(struct job *jp)
3622{
3623 int status;
3624 unsigned i;
3625 struct procstat *ps = jp->ps;
3626 for (i = 0; i < jp->nprocs; i++) {
3627 status = ps[i].ps_status;
3628 if (WIFSIGNALED(status) || WIFSTOPPED(status))
3629 return status;
3630 }
3631 return 0;
3632}
3633static void
3634restore_tty_if_stopped_or_signaled(struct job *jp)
3635{
3636//TODO: check what happens if we come from waitforjob() in expbackq()
3637 if (rootshell) {
3638 int s = job_signal_status(jp);
3639 if (s) /* WIFSIGNALED(s) || WIFSTOPPED(s) */
3640 set_tty_state();
3641 }
3642}
3643#else
3644# define get_tty_state() ((void)0)
3645# define restore_tty_if_stopped_or_signaled(jp) ((void)0)
3646#endif
3647
3582static void 3648static void
3583set_curjob(struct job *jp, unsigned mode) 3649set_curjob(struct job *jp, unsigned mode)
3584{ 3650{
@@ -3910,8 +3976,10 @@ restartjob(struct job *jp, int mode)
3910 goto out; 3976 goto out;
3911 jp->state = JOBRUNNING; 3977 jp->state = JOBRUNNING;
3912 pgid = jp->ps[0].ps_pid; 3978 pgid = jp->ps[0].ps_pid;
3913 if (mode == FORK_FG) 3979 if (mode == FORK_FG) {
3980 get_tty_state();
3914 xtcsetpgrp(ttyfd, pgid); 3981 xtcsetpgrp(ttyfd, pgid);
3982 }
3915 killpg(pgid, SIGCONT); 3983 killpg(pgid, SIGCONT);
3916 ps = jp->ps; 3984 ps = jp->ps;
3917 i = jp->nprocs; 3985 i = jp->nprocs;
@@ -4445,7 +4513,7 @@ makejob(/*union node *node,*/ int nprocs)
4445 memset(jp, 0, sizeof(*jp)); 4513 memset(jp, 0, sizeof(*jp));
4446#if JOBS 4514#if JOBS
4447 /* jp->jobctl is a bitfield. 4515 /* jp->jobctl is a bitfield.
4448 * "jp->jobctl |= jobctl" likely to give awful code */ 4516 * "jp->jobctl |= doing_jobctl" likely to give awful code */
4449 if (doing_jobctl) 4517 if (doing_jobctl)
4450 jp->jobctl = 1; 4518 jp->jobctl = 1;
4451#endif 4519#endif
@@ -5040,6 +5108,8 @@ waitforjob(struct job *jp)
5040#if JOBS 5108#if JOBS
5041 if (jp->jobctl) { 5109 if (jp->jobctl) {
5042 xtcsetpgrp(ttyfd, rootpid); 5110 xtcsetpgrp(ttyfd, rootpid);
5111 restore_tty_if_stopped_or_signaled(jp);
5112
5043 /* 5113 /*
5044 * This is truly gross. 5114 * This is truly gross.
5045 * If we're doing job control, then we did a TIOCSPGRP which 5115 * If we're doing job control, then we did a TIOCSPGRP which
@@ -8852,13 +8922,15 @@ static int
8852evalsubshell(union node *n, int flags) 8922evalsubshell(union node *n, int flags)
8853{ 8923{
8854 struct job *jp; 8924 struct job *jp;
8855 int backgnd = (n->type == NBACKGND); 8925 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
8856 int status; 8926 int status;
8857 8927
8858 expredir(n->nredir.redirect); 8928 expredir(n->nredir.redirect);
8859 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) 8929 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
8860 goto nofork; 8930 goto nofork;
8861 INT_OFF; 8931 INT_OFF;
8932 if (backgnd == FORK_FG)
8933 get_tty_state();
8862 jp = makejob(/*n,*/ 1); 8934 jp = makejob(/*n,*/ 1);
8863 if (forkshell(jp, n, backgnd) == 0) { 8935 if (forkshell(jp, n, backgnd) == 0) {
8864 /* child */ 8936 /* child */
@@ -8873,7 +8945,7 @@ evalsubshell(union node *n, int flags)
8873 } 8945 }
8874 /* parent */ 8946 /* parent */
8875 status = 0; 8947 status = 0;
8876 if (!backgnd) 8948 if (backgnd == FORK_FG)
8877 status = waitforjob(jp); 8949 status = waitforjob(jp);
8878 INT_ON; 8950 INT_ON;
8879 return status; 8951 return status;
@@ -8965,6 +9037,8 @@ evalpipe(union node *n, int flags)
8965 pipelen++; 9037 pipelen++;
8966 flags |= EV_EXIT; 9038 flags |= EV_EXIT;
8967 INT_OFF; 9039 INT_OFF;
9040 if (n->npipe.pipe_backgnd == 0)
9041 get_tty_state();
8968 jp = makejob(/*n,*/ pipelen); 9042 jp = makejob(/*n,*/ pipelen);
8969 prevfd = -1; 9043 prevfd = -1;
8970 for (lp = n->npipe.cmdlist; lp; lp = lp->next) { 9044 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
@@ -9647,6 +9721,7 @@ evalcommand(union node *cmd, int flags)
9647 if (!(flags & EV_EXIT) || may_have_traps) { 9721 if (!(flags & EV_EXIT) || may_have_traps) {
9648 /* No, forking off a child is necessary */ 9722 /* No, forking off a child is necessary */
9649 INT_OFF; 9723 INT_OFF;
9724 get_tty_state();
9650 jp = makejob(/*cmd,*/ 1); 9725 jp = makejob(/*cmd,*/ 1);
9651 if (forkshell(jp, cmd, FORK_FG) != 0) { 9726 if (forkshell(jp, cmd, FORK_FG) != 0) {
9652 /* parent */ 9727 /* parent */