xargs: optional support for -P NUM. Closes 9511
Based on patch by Johannes Schindelin <johannes.schindelin@gmx.de> function old new delta xargs_exec - 294 +294 packed_usage 31757 31772 +15 xargs_main 787 719 -68 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/1 up/down: 309/-68) Total: 241 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
b270be3fb3
commit
14551b7036
@ -59,6 +59,11 @@
|
|||||||
//config: depends on XARGS
|
//config: depends on XARGS
|
||||||
//config: help
|
//config: help
|
||||||
//config: Support -I STR and -i[STR] options.
|
//config: Support -I STR and -i[STR] options.
|
||||||
|
//config:
|
||||||
|
//config:config FEATURE_XARGS_SUPPORT_PARALLEL
|
||||||
|
//config: bool "Enable -P N: processes to run in parallel"
|
||||||
|
//config: default y
|
||||||
|
//config: depends on XARGS
|
||||||
|
|
||||||
//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs))
|
//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs))
|
||||||
|
|
||||||
@ -99,38 +104,121 @@ struct globals {
|
|||||||
#endif
|
#endif
|
||||||
const char *eof_str;
|
const char *eof_str;
|
||||||
int idx;
|
int idx;
|
||||||
|
#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
|
||||||
|
int running_procs;
|
||||||
|
int max_procs;
|
||||||
|
#endif
|
||||||
|
smalluint xargs_exitcode;
|
||||||
} FIX_ALIASING;
|
} FIX_ALIASING;
|
||||||
#define G (*(struct globals*)bb_common_bufsiz1)
|
#define G (*(struct globals*)bb_common_bufsiz1)
|
||||||
#define INIT_G() do { \
|
#define INIT_G() do { \
|
||||||
setup_common_bufsiz(); \
|
setup_common_bufsiz(); \
|
||||||
G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \
|
G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \
|
||||||
G.idx = 0; \
|
G.idx = 0; \
|
||||||
|
IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \
|
||||||
|
IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \
|
||||||
|
G.xargs_exitcode = 0; \
|
||||||
IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \
|
IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \
|
||||||
IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \
|
IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns 0 if xargs should continue (but may set G.xargs_exitcode to 123).
|
||||||
|
* Else sets G.xargs_exitcode to error code and returns nonzero.
|
||||||
|
*
|
||||||
|
* If G.max_procs == 0, performs final waitpid() loop for all children.
|
||||||
|
*/
|
||||||
static int xargs_exec(void)
|
static int xargs_exec(void)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
#if !ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
|
||||||
status = spawn_and_wait(G.args);
|
status = spawn_and_wait(G.args);
|
||||||
|
#else
|
||||||
|
if (G.max_procs == 1) {
|
||||||
|
status = spawn_and_wait(G.args);
|
||||||
|
} else {
|
||||||
|
pid_t pid;
|
||||||
|
int wstat;
|
||||||
|
again:
|
||||||
|
if (G.running_procs >= G.max_procs)
|
||||||
|
pid = safe_waitpid(-1, &wstat, 0);
|
||||||
|
else
|
||||||
|
pid = wait_any_nohang(&wstat);
|
||||||
|
if (pid > 0) {
|
||||||
|
/* We may have children we don't know about:
|
||||||
|
* sh -c 'sleep 1 & exec xargs ...'
|
||||||
|
* Do not make G.running_procs go negative.
|
||||||
|
*/
|
||||||
|
if (G.running_procs != 0)
|
||||||
|
G.running_procs--;
|
||||||
|
status = WIFSIGNALED(wstat)
|
||||||
|
? 0x180 + WTERMSIG(wstat)
|
||||||
|
: WEXITSTATUS(wstat);
|
||||||
|
if (status > 0 && status < 255) {
|
||||||
|
/* See below why 123 does not abort */
|
||||||
|
G.xargs_exitcode = 123;
|
||||||
|
status = 0;
|
||||||
|
}
|
||||||
|
if (status == 0)
|
||||||
|
goto again; /* maybe we have more children? */
|
||||||
|
/* else: "bad" status, will bail out */
|
||||||
|
} else if (G.max_procs != 0) {
|
||||||
|
/* Not in final waitpid() loop,
|
||||||
|
* and G.running_procs < G.max_procs: start more procs
|
||||||
|
*/
|
||||||
|
status = spawn(G.args);
|
||||||
|
/* here "status" actually holds pid, or -1 */
|
||||||
|
if (status > 0) {
|
||||||
|
G.running_procs++;
|
||||||
|
status = 0;
|
||||||
|
}
|
||||||
|
/* else: status == -1 (failed to fork or exec) */
|
||||||
|
} else {
|
||||||
|
/* final waitpid() loop: must be ECHILD "no more children" */
|
||||||
|
status = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* Manpage:
|
||||||
|
* """xargs exits with the following status:
|
||||||
|
* 0 if it succeeds
|
||||||
|
* 123 if any invocation of the command exited with status 1-125
|
||||||
|
* 124 if the command exited with status 255
|
||||||
|
* ("""If any invocation of the command exits with a status of 255,
|
||||||
|
* xargs will stop immediately without reading any further input.
|
||||||
|
* An error message is issued on stderr when this happens.""")
|
||||||
|
* 125 if the command is killed by a signal
|
||||||
|
* 126 if the command cannot be run
|
||||||
|
* 127 if the command is not found
|
||||||
|
* 1 if some other error occurred."""
|
||||||
|
*/
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
bb_simple_perror_msg(G.args[0]);
|
bb_simple_perror_msg(G.args[0]);
|
||||||
return errno == ENOENT ? 127 : 126;
|
status = (errno == ENOENT) ? 127 : 126;
|
||||||
}
|
}
|
||||||
if (status == 255) {
|
else if (status >= 0x180) {
|
||||||
bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
|
|
||||||
return 124;
|
|
||||||
}
|
|
||||||
if (status >= 0x180) {
|
|
||||||
bb_error_msg("'%s' terminated by signal %d",
|
bb_error_msg("'%s' terminated by signal %d",
|
||||||
G.args[0], status - 0x180);
|
G.args[0], status - 0x180);
|
||||||
return 125;
|
status = 125;
|
||||||
}
|
}
|
||||||
if (status)
|
else if (status != 0) {
|
||||||
return 123;
|
if (status == 255) {
|
||||||
return 0;
|
bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
|
||||||
|
return 124;
|
||||||
|
}
|
||||||
|
/* "123 if any invocation of the command exited with status 1-125"
|
||||||
|
* This implies that nonzero exit code is remembered,
|
||||||
|
* but does not cause xargs to stop: we return 0.
|
||||||
|
*/
|
||||||
|
G.xargs_exitcode = 123;
|
||||||
|
status = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != 0)
|
||||||
|
G.xargs_exitcode = status;
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space.
|
/* 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)
|
|||||||
//usage: IF_FEATURE_XARGS_SUPPORT_REPL_STR(
|
//usage: IF_FEATURE_XARGS_SUPPORT_REPL_STR(
|
||||||
//usage: "\n -I STR Replace STR within PROG ARGS with input line"
|
//usage: "\n -I STR Replace STR within PROG ARGS with input line"
|
||||||
//usage: )
|
//usage: )
|
||||||
|
//usage: IF_FEATURE_XARGS_SUPPORT_PARALLEL(
|
||||||
|
//usage: "\n -P N Run up to N PROGs in parallel"
|
||||||
|
//usage: )
|
||||||
//usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT(
|
//usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT(
|
||||||
//usage: "\n -x Exit if size is exceeded"
|
//usage: "\n -x Exit if size is exceeded"
|
||||||
//usage: )
|
//usage: )
|
||||||
@ -473,14 +564,14 @@ enum {
|
|||||||
IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
|
IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
|
||||||
IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \
|
IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \
|
||||||
IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \
|
IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \
|
||||||
IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::")
|
IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::") \
|
||||||
|
IF_FEATURE_XARGS_SUPPORT_PARALLEL( "P:+")
|
||||||
|
|
||||||
int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
||||||
int xargs_main(int argc UNUSED_PARAM, char **argv)
|
int xargs_main(int argc UNUSED_PARAM, char **argv)
|
||||||
{
|
{
|
||||||
int initial_idx;
|
int initial_idx;
|
||||||
int i;
|
int i;
|
||||||
int child_error = 0;
|
|
||||||
char *max_args;
|
char *max_args;
|
||||||
char *max_chars;
|
char *max_chars;
|
||||||
char *buf;
|
char *buf;
|
||||||
@ -500,8 +591,14 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
"no-run-if-empty\0" No_argument "r",
|
"no-run-if-empty\0" No_argument "r",
|
||||||
&max_args, &max_chars, &G.eof_str, &G.eof_str
|
&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_REPL_STR(, &G.repl_str, &G.repl_str)
|
||||||
|
IF_FEATURE_XARGS_SUPPORT_PARALLEL(, &G.max_procs)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
|
||||||
|
if (G.max_procs <= 0) /* -P0 means "run lots of them" */
|
||||||
|
G.max_procs = 100; /* let's not go crazy high */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* -E ""? You may wonder why not just omit -E?
|
/* -E ""? You may wonder why not just omit -E?
|
||||||
* This is used for portability:
|
* This is used for portability:
|
||||||
* old xargs was using "_" as default for -E / -e */
|
* old xargs was using "_" as default for -E / -e */
|
||||||
@ -620,11 +717,8 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
|
if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
|
||||||
child_error = xargs_exec();
|
if (xargs_exec() != 0)
|
||||||
}
|
break; /* G.xargs_exitcode is set by xargs_exec() */
|
||||||
|
|
||||||
if (child_error > 0 && child_error != 123) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
overlapping_strcpy(buf, rem);
|
overlapping_strcpy(buf, rem);
|
||||||
@ -635,7 +729,12 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
return child_error;
|
#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
|
||||||
|
G.max_procs = 0;
|
||||||
|
xargs_exec(); /* final waitpid() loop */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return G.xargs_exitcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user