hush: restore redirected stdin
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 1 Nov 2019 13:16:07 +0000 (14:16 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 1 Nov 2019 13:16:07 +0000 (14:16 +0100)
function                                             old     new   delta
restore_redirects                                     52      95     +43
save_fd_on_redirect                                  243     253     +10
hfopen                                                90      99      +9
fgetc_interactive                                    259     261      +2
builtin_type                                         117     115      -2
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 4/1 up/down: 64/-2)              Total: 62 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash_test/ash-redir/redir_stdin1.right [new file with mode: 0644]
shell/ash_test/ash-redir/redir_stdin1.tests [new file with mode: 0755]
shell/hush.c
shell/hush_test/hush-redir/redir_stdin1.right [new file with mode: 0644]
shell/hush_test/hush-redir/redir_stdin1.tests [new file with mode: 0755]

diff --git a/shell/ash_test/ash-redir/redir_stdin1.right b/shell/ash_test/ash-redir/redir_stdin1.right
new file mode 100644 (file)
index 0000000..1c6217e
--- /dev/null
@@ -0,0 +1,3 @@
+#Testing that stdin redirect is restored
+read2
+Ok:0
diff --git a/shell/ash_test/ash-redir/redir_stdin1.tests b/shell/ash_test/ash-redir/redir_stdin1.tests
new file mode 100755 (executable)
index 0000000..f72253f
--- /dev/null
@@ -0,0 +1,7 @@
+#Testing that stdin redirect is restored
+echo read2 | $THIS_SH -c 'read r <redir_stdin1.tests
+echo $r
+read r
+echo $r
+'
+echo Ok:$?
index 96a93587571dc8ec63afc6a576872fd05eafc812..25e5fb906d4f0517152e93106fa6b1ae06f49ed3 100644 (file)
@@ -573,7 +573,6 @@ typedef struct HFILE {
        char *cur;
        char *end;
        struct HFILE *next_hfile;
-       int is_stdin;
        int fd;
        char buf[1024];
 } HFILE;
@@ -973,6 +972,7 @@ struct globals {
        unsigned execute_lineno;
 #endif
        HFILE *HFILE_list;
+       HFILE *HFILE_stdin;
        /* Which signals have non-DFL handler (even with no traps set)?
         * Set at the start to:
         * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
@@ -1603,7 +1603,8 @@ static HFILE *hfopen(const char *name)
        }
 
        fp = xmalloc(sizeof(*fp));
-       fp->is_stdin = (name == NULL);
+       if (name == NULL)
+               G.HFILE_stdin = fp;
        fp->fd = fd;
        fp->cur = fp->end = fp->buf;
        fp->next_hfile = G.HFILE_list;
@@ -2666,7 +2667,7 @@ static int fgetc_interactive(struct in_str *i)
 {
        int ch;
        /* If it's interactive stdin, get new line. */
-       if (G_interactive_fd && i->file->is_stdin) {
+       if (G_interactive_fd && i->file == G.HFILE_stdin) {
                /* Returns first char (or EOF), the rest is in i->p[] */
                ch = get_user_input(i);
                G.promptmode = 1; /* PS2 */
@@ -7605,7 +7606,9 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
                avoid_fd = 9;
 
 #if ENABLE_HUSH_INTERACTIVE
-       if (fd == G_interactive_fd) {
+       if (fd != 0 /* don't trigger for G_interactive_fd == 0 (that's "not interactive" flag) */
+        && fd == G_interactive_fd
+       ) {
                /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */
                G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd);
                debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd);
@@ -7619,7 +7622,7 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
                /* No need to move script fds.
                 * For NOMMU case, it's actively wrong: we'd change ->fd
                 * fields in memory for the parent, but parent's fds
-                * aren't be moved, it would use wrong fd!
+                * aren't moved, it would use wrong fd!
                 * Reproducer: "cmd 3>FILE" in script.
                 * If we would call move_HFILEs_on_redirect(), child would:
                 *  fcntl64(3, F_DUPFD_CLOEXEC, 10)   = 10
@@ -7683,6 +7686,20 @@ static void restore_redirects(struct squirrel *sq)
                }
                free(sq);
        }
+       if (G.HFILE_stdin
+        && G.HFILE_stdin->fd != STDIN_FILENO
+       ) {
+               /* Testcase: interactive "read r <FILE; echo $r; read r; echo $r".
+                * Redirect moves ->fd to e.g. 10,
+                * and it is not restored above (we do not restore script fds
+                * after redirects, we just use new, "moved" fds).
+                * However for stdin, get_user_input() -> read_line_input(),
+                * and read builtin, depend on fd == STDIN_FILENO.
+                */
+               debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd);
+               xmove_fd(G.HFILE_stdin->fd, STDIN_FILENO);
+               G.HFILE_stdin->fd = STDIN_FILENO;
+       }
 
        /* If moved, G_interactive_fd stays on new fd, not restoring it */
 }
@@ -10214,8 +10231,6 @@ int hush_main(int argc, char **argv)
                                G_saved_tty_pgrp = 0;
                        }
                }
-// TODO: track & disallow any attempts of user
-// to (inadvertently) close/redirect G_interactive_fd
        }
        debug_printf("interactive_fd:%d\n", G_interactive_fd);
        if (G_interactive_fd) {
diff --git a/shell/hush_test/hush-redir/redir_stdin1.right b/shell/hush_test/hush-redir/redir_stdin1.right
new file mode 100644 (file)
index 0000000..1c6217e
--- /dev/null
@@ -0,0 +1,3 @@
+#Testing that stdin redirect is restored
+read2
+Ok:0
diff --git a/shell/hush_test/hush-redir/redir_stdin1.tests b/shell/hush_test/hush-redir/redir_stdin1.tests
new file mode 100755 (executable)
index 0000000..f72253f
--- /dev/null
@@ -0,0 +1,7 @@
+#Testing that stdin redirect is restored
+echo read2 | $THIS_SH -c 'read r <redir_stdin1.tests
+echo $r
+read r
+echo $r
+'
+echo Ok:$?