aboutsummaryrefslogtreecommitdiff
path: root/findutils/xargs.c
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2017-09-27 10:08:12 +0100
committerRon Yorston <rmy@pobox.com>2017-09-27 10:11:19 +0100
commitd9383e984da8de72e61e5094a3cf6404c5707ddc (patch)
treedd42825854fc42aea40d4f7a95548d53721d1733 /findutils/xargs.c
parent166b3e4e82799f87d3b002c7177891111eff079e (diff)
parent0c4dbd481aedb5d22c1048e7f7eb547a3b5e50a5 (diff)
downloadbusybox-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.c176
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 */
116static int xargs_exec(void) 140static 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)
144static void store_param(char *s) 237static 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
493int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 594int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
494int xargs_main(int argc, char **argv) 595int 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