Otherwise child processes can consume xargs input:

    $ yes /etc/issue | head -n1000 | xargs -i cat | wc -l
    0
    $ yes /etc/issue | head -n1000 | ./busybox xargs -i cat | wc -l
    628

After this patch (comparing busybox to xargs):

    $ yes /etc/issue | head -n1000 | ./busybox xargs cat | wc -l
    2000
    $ yes /etc/issue | head -n1000 | xargs cat | wc -l
    2000

    $ yes /etc/issue | head -n1000 | ./busybox xargs -i cat | wc -l
    0
    $ yes /etc/issue | head -n1000 | xargs -i cat | wc -l
    0

Refs: https://savannah.gnu.org/bugs/?3992
Signed-off-by: Azat Khuzhin <[email protected]>
---
 findutils/xargs.c     | 50 +++++++++++++++++++++++++++++++------------
 testsuite/xargs.tests | 19 ++++++++++++++++
 2 files changed, 55 insertions(+), 14 deletions(-)

diff --git a/findutils/xargs.c b/findutils/xargs.c
index 890c37534..8105a2043 100644
--- a/findutils/xargs.c
+++ b/findutils/xargs.c
@@ -108,11 +108,19 @@ struct globals {
        char **argv;
        const char *repl_str;
        char eol_ch;
+#endif
+#if ENABLE_FEATURE_XARGS_SUPPORT_ARGS_FILE
+       char *opt_a;
 #endif
        const char *eof_str;
        int idx;
        int fd_tty;
+       /* Used to replace stdin for spawned commands, to forbid access to 
xargs stdin */
+       int fd_null;
+       /* Used to restore "stdin" (a.k.a. input file with commands) after 
command spawning */
        int fd_stdin;
+       /* Original stdin of xargs program, that will be used for spawned 
programs in case of -a */
+       int fd_stdin_orig;
 #if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
        int running_procs;
        int max_procs;
@@ -132,6 +140,7 @@ struct globals {
        setup_common_bufsiz(); \
        IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \
        IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \
+       IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(G.opt_a = NULL;) \
        /* Even zero values are set because we are NOEXEC applet */ \
        G.eof_str = NULL; \
        G.idx = 0; \
@@ -189,8 +198,16 @@ static int xargs_exec(void)
 {
        int status;
 
-       if (option_mask32 & OPT_STDIN_TTY)
-               xdup2(G.fd_tty, STDIN_FILENO);
+       int stdin_fd = G.fd_stdin_orig;
+       if (option_mask32 & OPT_STDIN_TTY) {
+               stdin_fd = G.fd_tty;
+       } else {
+#if ENABLE_FEATURE_XARGS_SUPPORT_ARGS_FILE
+               if (!G.opt_a)
+                       stdin_fd = G.fd_null;
+#endif
+       }
+       xdup2(stdin_fd, STDIN_FILENO);
 
 #if !ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
        status = spawn_and_wait(G.args);
@@ -278,8 +295,7 @@ static int xargs_exec(void)
  ret:
        if (status != 0)
                G.xargs_exitcode = status;
-       if (option_mask32 & OPT_STDIN_TTY)
-               xdup2(G.fd_stdin, STDIN_FILENO);
+       xdup2(G.fd_stdin, STDIN_FILENO);
        return status;
 }
 
@@ -624,8 +640,6 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
 #else
 #define read_args process_stdin
 #endif
-       IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(char *opt_a = NULL;)
-
        INIT_G();
 
        opt = getopt32long(argv, OPTION_STR,
@@ -633,7 +647,7 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
                &max_args, &max_chars, &G.eof_str, &G.eof_str
                IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str)
                IF_FEATURE_XARGS_SUPPORT_PARALLEL(, &G.max_procs)
-               IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(, &opt_a)
+               IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(, &G.opt_a)
        );
 
 #if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
@@ -641,11 +655,6 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
                G.max_procs = 100; /* let's not go crazy high */
 #endif
 
-#if ENABLE_FEATURE_XARGS_SUPPORT_ARGS_FILE
-       if (opt_a)
-               xmove_fd(xopen(opt_a, O_RDONLY), 0);
-#endif
-
        /* -E ""? You may wonder why not just omit -E?
         * This is used for portability:
         * old xargs was using "_" as default for -E / -e */
@@ -736,11 +745,24 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
                        store_param(argv[i]);
        }
 
+       /* Preserve original stdin to reopen it for child in case of -a */
+       G.fd_stdin_orig = dup(STDIN_FILENO);
+       close_on_exec_on(G.fd_stdin_orig);
+
+#if ENABLE_FEATURE_XARGS_SUPPORT_ARGS_FILE
+       if (G.opt_a)
+               xmove_fd(xopen(G.opt_a, O_RDONLY), STDIN_FILENO);
+#endif
+
+       G.fd_stdin = dup(STDIN_FILENO);
+       close_on_exec_on(G.fd_stdin);
+
        if (opt & OPT_STDIN_TTY) {
                G.fd_tty = xopen(CURRENT_TTY, O_RDONLY);
                close_on_exec_on(G.fd_tty);
-               G.fd_stdin = dup(STDIN_FILENO);
-               close_on_exec_on(G.fd_stdin);
+       } else {
+               G.fd_null = xopen("/dev/null", O_RDONLY);
+               close_on_exec_on(G.fd_null);
        }
 
        initial_idx = G.idx;
diff --git a/testsuite/xargs.tests b/testsuite/xargs.tests
index c5e7b99e9..77d7e8b2e 100755
--- a/testsuite/xargs.tests
+++ b/testsuite/xargs.tests
@@ -59,6 +59,25 @@ testing "xargs -n2" \
        "1 2\n3 4\n5\n" \
        "" "1 2 3 4 5\n"
 
+# ensure that programs that is run under xargs
+# will not have access to xargs stdin
+optional FEATURE_XARGS_SUPPORT_REPL_STR
+testing "xargs closes stdin" \
+       "xargs -i cat | wc -l" \
+       "0\n" \
+       "" \
+       "$(yes /etc/issue | head -n1000)"
+
+# ensure that cat will get original stdin in case of xargs has -a, so here it
+# will got "yes /etc/issue | head -n10", but due to stripping whitespaces it
+# will got only 9 lines
+optional FEATURE_XARGS_SUPPORT_REPL_STR FEATURE_XARGS_SUPPORT_ARGS_FILE
+testing "xargs closes stdin with -a" \
+       "xargs -a input -i cat | wc -l" \
+       "9\n" \
+       "/etc/issue" \
+       "$(yes /etc/issue | head -n10)"
+
 SKIP=
 
 optional FEATURE_XARGS_SUPPORT_QUOTES FEATURE_XARGS_SUPPORT_REPL_STR
-- 
2.47.1

_______________________________________________
busybox mailing list
[email protected]
https://lists.busybox.net/mailman/listinfo/busybox

Reply via email to