aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--findutils/xargs.c135
1 files changed, 117 insertions, 18 deletions
diff --git a/findutils/xargs.c b/findutils/xargs.c
index b4e3db02c..77e01ef6c 100644
--- a/findutils/xargs.c
+++ b/findutils/xargs.c
@@ -59,6 +59,11 @@
59//config: depends on XARGS 59//config: depends on XARGS
60//config: help 60//config: help
61//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
62 67
63//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs)) 68//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs))
64 69
@@ -99,38 +104,121 @@ struct globals {
99#endif 104#endif
100 const char *eof_str; 105 const char *eof_str;
101 int idx; 106 int idx;
107#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
108 int running_procs;
109 int max_procs;
110#endif
111 smalluint xargs_exitcode;
102} FIX_ALIASING; 112} FIX_ALIASING;
103#define G (*(struct globals*)bb_common_bufsiz1) 113#define G (*(struct globals*)bb_common_bufsiz1)
104#define INIT_G() do { \ 114#define INIT_G() do { \
105 setup_common_bufsiz(); \ 115 setup_common_bufsiz(); \
106 G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \ 116 G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \
107 G.idx = 0; \ 117 G.idx = 0; \
118 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \
119 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \
120 G.xargs_exitcode = 0; \
108 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \ 121 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \
109 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \ 122 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \
110} while (0) 123} while (0)
111 124
112 125
126/*
127 * Returns 0 if xargs should continue (but may set G.xargs_exitcode to 123).
128 * Else sets G.xargs_exitcode to error code and returns nonzero.
129 *
130 * If G.max_procs == 0, performs final waitpid() loop for all children.
131 */
113static int xargs_exec(void) 132static int xargs_exec(void)
114{ 133{
115 int status; 134 int status;
116 135
136#if !ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
117 status = spawn_and_wait(G.args); 137 status = spawn_and_wait(G.args);
138#else
139 if (G.max_procs == 1) {
140 status = spawn_and_wait(G.args);
141 } else {
142 pid_t pid;
143 int wstat;
144 again:
145 if (G.running_procs >= G.max_procs)
146 pid = safe_waitpid(-1, &wstat, 0);
147 else
148 pid = wait_any_nohang(&wstat);
149 if (pid > 0) {
150 /* We may have children we don't know about:
151 * sh -c 'sleep 1 & exec xargs ...'
152 * Do not make G.running_procs go negative.
153 */
154 if (G.running_procs != 0)
155 G.running_procs--;
156 status = WIFSIGNALED(wstat)
157 ? 0x180 + WTERMSIG(wstat)
158 : WEXITSTATUS(wstat);
159 if (status > 0 && status < 255) {
160 /* See below why 123 does not abort */
161 G.xargs_exitcode = 123;
162 status = 0;
163 }
164 if (status == 0)
165 goto again; /* maybe we have more children? */
166 /* else: "bad" status, will bail out */
167 } else if (G.max_procs != 0) {
168 /* Not in final waitpid() loop,
169 * and G.running_procs < G.max_procs: start more procs
170 */
171 status = spawn(G.args);
172 /* here "status" actually holds pid, or -1 */
173 if (status > 0) {
174 G.running_procs++;
175 status = 0;
176 }
177 /* else: status == -1 (failed to fork or exec) */
178 } else {
179 /* final waitpid() loop: must be ECHILD "no more children" */
180 status = 0;
181 }
182 }
183#endif
184 /* Manpage:
185 * """xargs exits with the following status:
186 * 0 if it succeeds
187 * 123 if any invocation of the command exited with status 1-125
188 * 124 if the command exited with status 255
189 * ("""If any invocation of the command exits with a status of 255,
190 * xargs will stop immediately without reading any further input.
191 * An error message is issued on stderr when this happens.""")
192 * 125 if the command is killed by a signal
193 * 126 if the command cannot be run
194 * 127 if the command is not found
195 * 1 if some other error occurred."""
196 */
118 if (status < 0) { 197 if (status < 0) {
119 bb_simple_perror_msg(G.args[0]); 198 bb_simple_perror_msg(G.args[0]);
120 return errno == ENOENT ? 127 : 126; 199 status = (errno == ENOENT) ? 127 : 126;
121 }
122 if (status == 255) {
123 bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
124 return 124;
125 } 200 }
126 if (status >= 0x180) { 201 else if (status >= 0x180) {
127 bb_error_msg("'%s' terminated by signal %d", 202 bb_error_msg("'%s' terminated by signal %d",
128 G.args[0], status - 0x180); 203 G.args[0], status - 0x180);
129 return 125; 204 status = 125;
130 } 205 }
131 if (status) 206 else if (status != 0) {
132 return 123; 207 if (status == 255) {
133 return 0; 208 bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
209 return 124;
210 }
211 /* "123 if any invocation of the command exited with status 1-125"
212 * This implies that nonzero exit code is remembered,
213 * but does not cause xargs to stop: we return 0.
214 */
215 G.xargs_exitcode = 123;
216 status = 0;
217 }
218
219 if (status != 0)
220 G.xargs_exitcode = status;
221 return status;
134} 222}
135 223
136/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space. 224/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space.
@@ -436,6 +524,9 @@ static int xargs_ask_confirmation(void)
436//usage: IF_FEATURE_XARGS_SUPPORT_REPL_STR( 524//usage: IF_FEATURE_XARGS_SUPPORT_REPL_STR(
437//usage: "\n -I STR Replace STR within PROG ARGS with input line" 525//usage: "\n -I STR Replace STR within PROG ARGS with input line"
438//usage: ) 526//usage: )
527//usage: IF_FEATURE_XARGS_SUPPORT_PARALLEL(
528//usage: "\n -P N Run up to N PROGs in parallel"
529//usage: )
439//usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT( 530//usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT(
440//usage: "\n -x Exit if size is exceeded" 531//usage: "\n -x Exit if size is exceeded"
441//usage: ) 532//usage: )
@@ -473,14 +564,14 @@ enum {
473 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \ 564 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
474 IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \ 565 IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \
475 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \ 566 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \
476 IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::") 567 IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::") \
568 IF_FEATURE_XARGS_SUPPORT_PARALLEL( "P:+")
477 569
478int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 570int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
479int xargs_main(int argc UNUSED_PARAM, char **argv) 571int xargs_main(int argc UNUSED_PARAM, char **argv)
480{ 572{
481 int initial_idx; 573 int initial_idx;
482 int i; 574 int i;
483 int child_error = 0;
484 char *max_args; 575 char *max_args;
485 char *max_chars; 576 char *max_chars;
486 char *buf; 577 char *buf;
@@ -500,8 +591,14 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
500 "no-run-if-empty\0" No_argument "r", 591 "no-run-if-empty\0" No_argument "r",
501 &max_args, &max_chars, &G.eof_str, &G.eof_str 592 &max_args, &max_chars, &G.eof_str, &G.eof_str
502 IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str) 593 IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str)
594 IF_FEATURE_XARGS_SUPPORT_PARALLEL(, &G.max_procs)
503 ); 595 );
504 596
597#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
598 if (G.max_procs <= 0) /* -P0 means "run lots of them" */
599 G.max_procs = 100; /* let's not go crazy high */
600#endif
601
505 /* -E ""? You may wonder why not just omit -E? 602 /* -E ""? You may wonder why not just omit -E?
506 * This is used for portability: 603 * This is used for portability:
507 * old xargs was using "_" as default for -E / -e */ 604 * old xargs was using "_" as default for -E / -e */
@@ -620,11 +717,8 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
620 } 717 }
621 718
622 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { 719 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
623 child_error = xargs_exec(); 720 if (xargs_exec() != 0)
624 } 721 break; /* G.xargs_exitcode is set by xargs_exec() */
625
626 if (child_error > 0 && child_error != 123) {
627 break;
628 } 722 }
629 723
630 overlapping_strcpy(buf, rem); 724 overlapping_strcpy(buf, rem);
@@ -635,7 +729,12 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
635 free(buf); 729 free(buf);
636 } 730 }
637 731
638 return child_error; 732#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
733 G.max_procs = 0;
734 xargs_exec(); /* final waitpid() loop */
735#endif
736
737 return G.xargs_exitcode;
639} 738}
640 739
641 740