diff options
-rw-r--r-- | findutils/xargs.c | 135 |
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 | */ | ||
113 | static int xargs_exec(void) | 132 | static 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 | ||
478 | int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 570 | int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
479 | int xargs_main(int argc UNUSED_PARAM, char **argv) | 571 | int 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 | ||