diff options
author | Ron Yorston <rmy@pobox.com> | 2017-09-27 10:08:12 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2017-09-27 10:11:19 +0100 |
commit | d9383e984da8de72e61e5094a3cf6404c5707ddc (patch) | |
tree | dd42825854fc42aea40d4f7a95548d53721d1733 /findutils/xargs.c | |
parent | 166b3e4e82799f87d3b002c7177891111eff079e (diff) | |
parent | 0c4dbd481aedb5d22c1048e7f7eb547a3b5e50a5 (diff) | |
download | busybox-w32-d9383e984da8de72e61e5094a3cf6404c5707ddc.tar.gz busybox-w32-d9383e984da8de72e61e5094a3cf6404c5707ddc.tar.bz2 busybox-w32-d9383e984da8de72e61e5094a3cf6404c5707ddc.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'findutils/xargs.c')
-rw-r--r-- | findutils/xargs.c | 176 |
1 files changed, 144 insertions, 32 deletions
diff --git a/findutils/xargs.c b/findutils/xargs.c index 4765085dc..caa89f13e 100644 --- a/findutils/xargs.c +++ b/findutils/xargs.c | |||
@@ -14,7 +14,6 @@ | |||
14 | * xargs is described in the Single Unix Specification v3 at | 14 | * xargs is described in the Single Unix Specification v3 at |
15 | * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html | 15 | * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html |
16 | */ | 16 | */ |
17 | |||
18 | //config:config XARGS | 17 | //config:config XARGS |
19 | //config: bool "xargs (6.7 kb)" | 18 | //config: bool "xargs (6.7 kb)" |
20 | //config: default y | 19 | //config: default y |
@@ -60,6 +59,16 @@ | |||
60 | //config: depends on XARGS | 59 | //config: depends on XARGS |
61 | //config: help | 60 | //config: help |
62 | //config: Support -I STR and -i[STR] options. | 61 | //config: Support -I STR and -i[STR] options. |
62 | //config: | ||
63 | //config:config FEATURE_XARGS_SUPPORT_PARALLEL | ||
64 | //config: bool "Enable -P N: processes to run in parallel" | ||
65 | //config: default y | ||
66 | //config: depends on XARGS | ||
67 | //config: | ||
68 | //config:config FEATURE_XARGS_SUPPORT_ARGS_FILE | ||
69 | //config: bool "Enable -a FILE: use FILE instead of stdin" | ||
70 | //config: default y | ||
71 | //config: depends on XARGS | ||
63 | 72 | ||
64 | //applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs)) | 73 | //applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs)) |
65 | 74 | ||
@@ -103,37 +112,121 @@ struct globals { | |||
103 | #endif | 112 | #endif |
104 | const char *eof_str; | 113 | const char *eof_str; |
105 | int idx; | 114 | int idx; |
115 | #if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL | ||
116 | int running_procs; | ||
117 | int max_procs; | ||
118 | #endif | ||
119 | smalluint xargs_exitcode; | ||
106 | } FIX_ALIASING; | 120 | } FIX_ALIASING; |
107 | #define G (*(struct globals*)bb_common_bufsiz1) | 121 | #define G (*(struct globals*)bb_common_bufsiz1) |
108 | #define INIT_G() do { \ | 122 | #define INIT_G() do { \ |
109 | setup_common_bufsiz(); \ | 123 | setup_common_bufsiz(); \ |
110 | G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \ | 124 | G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \ |
125 | G.idx = 0; \ | ||
126 | IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \ | ||
127 | IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \ | ||
128 | G.xargs_exitcode = 0; \ | ||
111 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \ | 129 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \ |
112 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \ | 130 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \ |
113 | } while (0) | 131 | } while (0) |
114 | 132 | ||
115 | 133 | ||
134 | /* | ||
135 | * Returns 0 if xargs should continue (but may set G.xargs_exitcode to 123). | ||
136 | * Else sets G.xargs_exitcode to error code and returns nonzero. | ||
137 | * | ||
138 | * If G.max_procs == 0, performs final waitpid() loop for all children. | ||
139 | */ | ||
116 | static int xargs_exec(void) | 140 | static int xargs_exec(void) |
117 | { | 141 | { |
118 | int status; | 142 | int status; |
119 | 143 | ||
144 | #if !ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL | ||
120 | status = spawn_and_wait(G.args); | 145 | status = spawn_and_wait(G.args); |
146 | #else | ||
147 | if (G.max_procs == 1) { | ||
148 | status = spawn_and_wait(G.args); | ||
149 | } else { | ||
150 | pid_t pid; | ||
151 | int wstat; | ||
152 | again: | ||
153 | if (G.running_procs >= G.max_procs) | ||
154 | pid = safe_waitpid(-1, &wstat, 0); | ||
155 | else | ||
156 | pid = wait_any_nohang(&wstat); | ||
157 | if (pid > 0) { | ||
158 | /* We may have children we don't know about: | ||
159 | * sh -c 'sleep 1 & exec xargs ...' | ||
160 | * Do not make G.running_procs go negative. | ||
161 | */ | ||
162 | if (G.running_procs != 0) | ||
163 | G.running_procs--; | ||
164 | status = WIFSIGNALED(wstat) | ||
165 | ? 0x180 + WTERMSIG(wstat) | ||
166 | : WEXITSTATUS(wstat); | ||
167 | if (status > 0 && status < 255) { | ||
168 | /* See below why 123 does not abort */ | ||
169 | G.xargs_exitcode = 123; | ||
170 | status = 0; | ||
171 | } | ||
172 | if (status == 0) | ||
173 | goto again; /* maybe we have more children? */ | ||
174 | /* else: "bad" status, will bail out */ | ||
175 | } else if (G.max_procs != 0) { | ||
176 | /* Not in final waitpid() loop, | ||
177 | * and G.running_procs < G.max_procs: start more procs | ||
178 | */ | ||
179 | status = spawn(G.args); | ||
180 | /* here "status" actually holds pid, or -1 */ | ||
181 | if (status > 0) { | ||
182 | G.running_procs++; | ||
183 | status = 0; | ||
184 | } | ||
185 | /* else: status == -1 (failed to fork or exec) */ | ||
186 | } else { | ||
187 | /* final waitpid() loop: must be ECHILD "no more children" */ | ||
188 | status = 0; | ||
189 | } | ||
190 | } | ||
191 | #endif | ||
192 | /* Manpage: | ||
193 | * """xargs exits with the following status: | ||
194 | * 0 if it succeeds | ||
195 | * 123 if any invocation of the command exited with status 1-125 | ||
196 | * 124 if the command exited with status 255 | ||
197 | * ("""If any invocation of the command exits with a status of 255, | ||
198 | * xargs will stop immediately without reading any further input. | ||
199 | * An error message is issued on stderr when this happens.""") | ||
200 | * 125 if the command is killed by a signal | ||
201 | * 126 if the command cannot be run | ||
202 | * 127 if the command is not found | ||
203 | * 1 if some other error occurred.""" | ||
204 | */ | ||
121 | if (status < 0) { | 205 | if (status < 0) { |
122 | bb_simple_perror_msg(G.args[0]); | 206 | bb_simple_perror_msg(G.args[0]); |
123 | return errno == ENOENT ? 127 : 126; | 207 | status = (errno == ENOENT) ? 127 : 126; |
124 | } | ||
125 | if (status == 255) { | ||
126 | bb_error_msg("%s: exited with status 255; aborting", G.args[0]); | ||
127 | return 124; | ||
128 | } | 208 | } |
129 | if (status >= 0x180) { | 209 | else if (status >= 0x180) { |
130 | bb_error_msg("'%s' terminated by signal %d", | 210 | bb_error_msg("'%s' terminated by signal %d", |
131 | G.args[0], status - 0x180); | 211 | G.args[0], status - 0x180); |
132 | return 125; | 212 | status = 125; |
213 | } | ||
214 | else if (status != 0) { | ||
215 | if (status == 255) { | ||
216 | bb_error_msg("%s: exited with status 255; aborting", G.args[0]); | ||
217 | return 124; | ||
218 | } | ||
219 | /* "123 if any invocation of the command exited with status 1-125" | ||
220 | * This implies that nonzero exit code is remembered, | ||
221 | * but does not cause xargs to stop: we return 0. | ||
222 | */ | ||
223 | G.xargs_exitcode = 123; | ||
224 | status = 0; | ||
133 | } | 225 | } |
134 | if (status) | 226 | |
135 | return 123; | 227 | if (status != 0) |
136 | return 0; | 228 | G.xargs_exitcode = status; |
229 | return status; | ||
137 | } | 230 | } |
138 | 231 | ||
139 | /* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space. | 232 | /* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space. |
@@ -144,7 +237,7 @@ static int xargs_exec(void) | |||
144 | static void store_param(char *s) | 237 | static void store_param(char *s) |
145 | { | 238 | { |
146 | /* Grow by 256 elements at once */ | 239 | /* Grow by 256 elements at once */ |
147 | if (!(G.idx & 0xff)) { /* G.idx == N*256 */ | 240 | if (!(G.idx & 0xff)) { /* G.idx == N*256? */ |
148 | /* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */ | 241 | /* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */ |
149 | G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100)); | 242 | G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100)); |
150 | } | 243 | } |
@@ -444,6 +537,9 @@ static int xargs_ask_confirmation(void) | |||
444 | //usage: IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( | 537 | //usage: IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( |
445 | //usage: "\n -0 Input is separated by NUL characters" | 538 | //usage: "\n -0 Input is separated by NUL characters" |
446 | //usage: ) | 539 | //usage: ) |
540 | //usage: IF_FEATURE_XARGS_SUPPORT_ARGS_FILE( | ||
541 | //usage: "\n -a FILE Read from FILE instead of stdin" | ||
542 | //usage: ) | ||
447 | //usage: "\n -t Print the command on stderr before execution" | 543 | //usage: "\n -t Print the command on stderr before execution" |
448 | //usage: "\n -e[STR] STR stops input processing" | 544 | //usage: "\n -e[STR] STR stops input processing" |
449 | //usage: "\n -n N Pass no more than N args to PROG" | 545 | //usage: "\n -n N Pass no more than N args to PROG" |
@@ -451,6 +547,9 @@ static int xargs_ask_confirmation(void) | |||
451 | //usage: IF_FEATURE_XARGS_SUPPORT_REPL_STR( | 547 | //usage: IF_FEATURE_XARGS_SUPPORT_REPL_STR( |
452 | //usage: "\n -I STR Replace STR within PROG ARGS with input line" | 548 | //usage: "\n -I STR Replace STR within PROG ARGS with input line" |
453 | //usage: ) | 549 | //usage: ) |
550 | //usage: IF_FEATURE_XARGS_SUPPORT_PARALLEL( | ||
551 | //usage: "\n -P N Run up to N PROGs in parallel" | ||
552 | //usage: ) | ||
454 | //usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT( | 553 | //usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT( |
455 | //usage: "\n -x Exit if size is exceeded" | 554 | //usage: "\n -x Exit if size is exceeded" |
456 | //usage: ) | 555 | //usage: ) |
@@ -488,13 +587,15 @@ enum { | |||
488 | IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \ | 587 | IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \ |
489 | IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \ | 588 | IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \ |
490 | IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \ | 589 | IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \ |
491 | IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::") | 590 | IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::") \ |
591 | IF_FEATURE_XARGS_SUPPORT_PARALLEL( "P:+") \ | ||
592 | IF_FEATURE_XARGS_SUPPORT_ARGS_FILE( "a:") | ||
492 | 593 | ||
493 | int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 594 | int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
494 | int xargs_main(int argc, char **argv) | 595 | int xargs_main(int argc UNUSED_PARAM, char **argv) |
495 | { | 596 | { |
597 | int initial_idx; | ||
496 | int i; | 598 | int i; |
497 | int child_error = 0; | ||
498 | char *max_args; | 599 | char *max_args; |
499 | char *max_chars; | 600 | char *max_chars; |
500 | char *buf; | 601 | char *buf; |
@@ -507,6 +608,7 @@ int xargs_main(int argc, char **argv) | |||
507 | #else | 608 | #else |
508 | #define read_args process_stdin | 609 | #define read_args process_stdin |
509 | #endif | 610 | #endif |
611 | IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(char *opt_a = NULL;) | ||
510 | 612 | ||
511 | INIT_G(); | 613 | INIT_G(); |
512 | 614 | ||
@@ -514,8 +616,20 @@ int xargs_main(int argc, char **argv) | |||
514 | "no-run-if-empty\0" No_argument "r", | 616 | "no-run-if-empty\0" No_argument "r", |
515 | &max_args, &max_chars, &G.eof_str, &G.eof_str | 617 | &max_args, &max_chars, &G.eof_str, &G.eof_str |
516 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str) | 618 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str) |
619 | IF_FEATURE_XARGS_SUPPORT_PARALLEL(, &G.max_procs) | ||
620 | IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(, &opt_a) | ||
517 | ); | 621 | ); |
518 | 622 | ||
623 | #if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL | ||
624 | if (G.max_procs <= 0) /* -P0 means "run lots of them" */ | ||
625 | G.max_procs = 100; /* let's not go crazy high */ | ||
626 | #endif | ||
627 | |||
628 | #if ENABLE_FEATURE_XARGS_SUPPORT_ARGS_FILE | ||
629 | if (opt_a) | ||
630 | xmove_fd(xopen(opt_a, O_RDONLY), 0); | ||
631 | #endif | ||
632 | |||
519 | /* -E ""? You may wonder why not just omit -E? | 633 | /* -E ""? You may wonder why not just omit -E? |
520 | * This is used for portability: | 634 | * This is used for portability: |
521 | * old xargs was using "_" as default for -E / -e */ | 635 | * old xargs was using "_" as default for -E / -e */ |
@@ -528,11 +642,11 @@ int xargs_main(int argc, char **argv) | |||
528 | } | 642 | } |
529 | 643 | ||
530 | argv += optind; | 644 | argv += optind; |
531 | argc -= optind; | 645 | //argc -= optind; |
532 | if (!argv[0]) { | 646 | if (!argv[0]) { |
533 | /* default behavior is to echo all the filenames */ | 647 | /* default behavior is to echo all the filenames */ |
534 | *--argv = (char*)"echo"; | 648 | *--argv = (char*)"echo"; |
535 | argc++; | 649 | //argc++; |
536 | } | 650 | } |
537 | 651 | ||
538 | /* | 652 | /* |
@@ -587,7 +701,6 @@ int xargs_main(int argc, char **argv) | |||
587 | */ | 701 | */ |
588 | G.args = NULL; | 702 | G.args = NULL; |
589 | G.argv = argv; | 703 | G.argv = argv; |
590 | argc = 0; | ||
591 | read_args = process_stdin_with_replace; | 704 | read_args = process_stdin_with_replace; |
592 | /* Make -I imply -r. GNU findutils seems to do the same: */ | 705 | /* Make -I imply -r. GNU findutils seems to do the same: */ |
593 | /* (otherwise "echo -n | xargs -I% echo %" would SEGV) */ | 706 | /* (otherwise "echo -n | xargs -I% echo %" would SEGV) */ |
@@ -595,30 +708,27 @@ int xargs_main(int argc, char **argv) | |||
595 | } else | 708 | } else |
596 | #endif | 709 | #endif |
597 | { | 710 | { |
598 | /* Allocate pointers for execvp. | 711 | /* Store the command to be executed, part 1. |
599 | * We can statically allocate (argc + n_max_arg + 1) elements | 712 | * We can statically allocate (argc + n_max_arg + 1) elements |
600 | * and do not bother with resizing args[], but on 64-bit machines | 713 | * and do not bother with resizing args[], but on 64-bit machines |
601 | * this results in args[] vector which is ~8 times bigger | 714 | * this results in args[] vector which is ~8 times bigger |
602 | * than n_max_chars! That is, with n_max_chars == 20k, | 715 | * than n_max_chars! That is, with n_max_chars == 20k, |
603 | * args[] will take 160k (!), which will most likely be | 716 | * args[] will take 160k (!), which will most likely be |
604 | * almost entirely unused. | 717 | * almost entirely unused. |
605 | * | ||
606 | * See store_param() for matching 256-step growth logic | ||
607 | */ | 718 | */ |
608 | G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff)); | ||
609 | /* Store the command to be executed, part 1 */ | ||
610 | for (i = 0; argv[i]; i++) | 719 | for (i = 0; argv[i]; i++) |
611 | G.args[i] = argv[i]; | 720 | store_param(argv[i]); |
612 | } | 721 | } |
613 | 722 | ||
723 | initial_idx = G.idx; | ||
614 | while (1) { | 724 | while (1) { |
615 | char *rem; | 725 | char *rem; |
616 | 726 | ||
617 | G.idx = argc; | 727 | G.idx = initial_idx; |
618 | rem = read_args(n_max_chars, n_max_arg, buf); | 728 | rem = read_args(n_max_chars, n_max_arg, buf); |
619 | store_param(NULL); | 729 | store_param(NULL); |
620 | 730 | ||
621 | if (!G.args[argc]) { | 731 | if (!G.args[initial_idx]) { /* not even one ARG was added? */ |
622 | if (*rem != '\0') | 732 | if (*rem != '\0') |
623 | bb_error_msg_and_die("argument line too long"); | 733 | bb_error_msg_and_die("argument line too long"); |
624 | if (opt & OPT_NO_EMPTY) | 734 | if (opt & OPT_NO_EMPTY) |
@@ -638,11 +748,8 @@ int xargs_main(int argc, char **argv) | |||
638 | } | 748 | } |
639 | 749 | ||
640 | if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { | 750 | if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { |
641 | child_error = xargs_exec(); | 751 | if (xargs_exec() != 0) |
642 | } | 752 | break; /* G.xargs_exitcode is set by xargs_exec() */ |
643 | |||
644 | if (child_error > 0 && child_error != 123) { | ||
645 | break; | ||
646 | } | 753 | } |
647 | 754 | ||
648 | overlapping_strcpy(buf, rem); | 755 | overlapping_strcpy(buf, rem); |
@@ -653,7 +760,12 @@ int xargs_main(int argc, char **argv) | |||
653 | free(buf); | 760 | free(buf); |
654 | } | 761 | } |
655 | 762 | ||
656 | return child_error; | 763 | #if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL |
764 | G.max_procs = 0; | ||
765 | xargs_exec(); /* final waitpid() loop */ | ||
766 | #endif | ||
767 | |||
768 | return G.xargs_exitcode; | ||
657 | } | 769 | } |
658 | 770 | ||
659 | 771 | ||