diff options
-rw-r--r-- | console-tools/loadfont.c | 6 | ||||
-rw-r--r-- | include/libbb.h | 2 | ||||
-rw-r--r-- | libbb/common_bufsiz.c | 9 | ||||
-rw-r--r-- | libbb/executable.c | 2 | ||||
-rw-r--r-- | libbb/run_shell.c | 29 | ||||
-rw-r--r-- | loginutils/README | 2 | ||||
-rw-r--r-- | loginutils/login.c | 2 | ||||
-rw-r--r-- | loginutils/su.c | 33 | ||||
-rw-r--r-- | loginutils/sulogin.c | 2 | ||||
-rw-r--r-- | shell/ash.c | 34 | ||||
-rw-r--r-- | shell/ash_test/ash-misc/group_in_braces.right | 5 | ||||
-rwxr-xr-x | shell/ash_test/ash-misc/group_in_braces.tests | 11 | ||||
-rw-r--r-- | shell/ash_test/ash-misc/wait4.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-misc/wait4.tests | 2 | ||||
-rw-r--r-- | shell/ash_test/ash-misc/wait5.right | 2 | ||||
-rwxr-xr-x | shell/ash_test/ash-misc/wait5.tests | 5 | ||||
-rw-r--r-- | shell/hush.c | 411 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/group_in_braces.right | 5 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/group_in_braces.tests | 11 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/wait4.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/wait4.tests | 2 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/wait5.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/wait5.tests | 5 | ||||
-rwxr-xr-x | testsuite/cpio.tests | 24 |
24 files changed, 412 insertions, 196 deletions
diff --git a/console-tools/loadfont.c b/console-tools/loadfont.c index 032506d6d..58073e0dc 100644 --- a/console-tools/loadfont.c +++ b/console-tools/loadfont.c | |||
@@ -319,8 +319,10 @@ int loadfont_main(int argc UNUSED_PARAM, char **argv) | |||
319 | * We used to look at the length of the input file | 319 | * We used to look at the length of the input file |
320 | * with stat(); now that we accept compressed files, | 320 | * with stat(); now that we accept compressed files, |
321 | * just read the entire file. | 321 | * just read the entire file. |
322 | * Len was 32k, but latarcyrheb-sun32.psfu is 34377 bytes | ||
323 | * (it has largish Unicode map). | ||
322 | */ | 324 | */ |
323 | len = 32*1024; // can't be larger | 325 | len = 128*1024; |
324 | buffer = xmalloc_read(STDIN_FILENO, &len); | 326 | buffer = xmalloc_read(STDIN_FILENO, &len); |
325 | // xmalloc_open_zipped_read_close(filename, &len); | 327 | // xmalloc_open_zipped_read_close(filename, &len); |
326 | if (!buffer) | 328 | if (!buffer) |
@@ -405,7 +407,7 @@ int setfont_main(int argc UNUSED_PARAM, char **argv) | |||
405 | } | 407 | } |
406 | } | 408 | } |
407 | // load font | 409 | // load font |
408 | len = 32*1024; // can't be larger | 410 | len = 128*1024; |
409 | buffer = xmalloc_open_zipped_read_close(*argv, &len); | 411 | buffer = xmalloc_open_zipped_read_close(*argv, &len); |
410 | if (!buffer) | 412 | if (!buffer) |
411 | bb_simple_perror_msg_and_die(*argv); | 413 | bb_simple_perror_msg_and_die(*argv); |
diff --git a/include/libbb.h b/include/libbb.h index 30a0e00ba..91702fa6e 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -1360,7 +1360,7 @@ char *bb_simplify_abs_path_inplace(char *path) FAST_FUNC; | |||
1360 | #endif | 1360 | #endif |
1361 | extern void bb_do_delay(int seconds) FAST_FUNC; | 1361 | extern void bb_do_delay(int seconds) FAST_FUNC; |
1362 | extern void change_identity(const struct passwd *pw) FAST_FUNC; | 1362 | extern void change_identity(const struct passwd *pw) FAST_FUNC; |
1363 | extern void run_shell(const char *shell, int loginshell, const char *command, const char **additional_args) NORETURN FAST_FUNC; | 1363 | extern void run_shell(const char *shell, int loginshell, const char **args) NORETURN FAST_FUNC; |
1364 | 1364 | ||
1365 | /* Returns $SHELL, getpwuid(getuid())->pw_shell, or DEFAULT_SHELL. | 1365 | /* Returns $SHELL, getpwuid(getuid())->pw_shell, or DEFAULT_SHELL. |
1366 | * Note that getpwuid result might need xstrdup'ing | 1366 | * Note that getpwuid result might need xstrdup'ing |
diff --git a/libbb/common_bufsiz.c b/libbb/common_bufsiz.c index 1a3585169..2847eb57d 100644 --- a/libbb/common_bufsiz.c +++ b/libbb/common_bufsiz.c | |||
@@ -54,6 +54,15 @@ char bb_common_bufsiz1[COMMON_BUFSIZE] ALIGNED(sizeof(long long)); | |||
54 | #else | 54 | #else |
55 | 55 | ||
56 | # ifndef setup_common_bufsiz | 56 | # ifndef setup_common_bufsiz |
57 | /* For now, this is never used: | ||
58 | * scripts/generate_BUFSIZ.sh never generates "malloced" bufsiz1: | ||
59 | * enum { COMMON_BUFSIZE = 1024 }; | ||
60 | * extern char *const bb_common_bufsiz1; | ||
61 | * void setup_common_bufsiz(void); | ||
62 | * This has proved to be worse than the approach of defining | ||
63 | * larger bb_common_bufsiz1[] array. | ||
64 | */ | ||
65 | |||
57 | /* | 66 | /* |
58 | * It is not defined as a dummy macro. | 67 | * It is not defined as a dummy macro. |
59 | * It means we have to provide this function. | 68 | * It means we have to provide this function. |
diff --git a/libbb/executable.c b/libbb/executable.c index 308c525a3..5f0ff8c6e 100644 --- a/libbb/executable.c +++ b/libbb/executable.c | |||
@@ -111,5 +111,5 @@ void FAST_FUNC exec_prog_or_SHELL(char **argv) | |||
111 | if (argv[0]) { | 111 | if (argv[0]) { |
112 | BB_EXECVP_or_die(argv); | 112 | BB_EXECVP_or_die(argv); |
113 | } | 113 | } |
114 | run_shell(getenv("SHELL"), /*login:*/ 1, NULL, NULL); | 114 | run_shell(getenv("SHELL"), /*login:*/ 1, NULL); |
115 | } | 115 | } |
diff --git a/libbb/run_shell.c b/libbb/run_shell.c index 4d92c3caa..b6b9360e8 100644 --- a/libbb/run_shell.c +++ b/libbb/run_shell.c | |||
@@ -50,19 +50,17 @@ void FAST_FUNC set_current_security_context(security_context_t sid) | |||
50 | #endif | 50 | #endif |
51 | 51 | ||
52 | /* Run SHELL, or DEFAULT_SHELL if SHELL is "" or NULL. | 52 | /* Run SHELL, or DEFAULT_SHELL if SHELL is "" or NULL. |
53 | * If COMMAND is nonzero, pass it to the shell with the -c option. | 53 | * If ADDITIONAL_ARGS is not NULL, pass them to the shell. |
54 | * If ADDITIONAL_ARGS is nonzero, pass it to the shell as more | 54 | */ |
55 | * arguments. */ | 55 | void FAST_FUNC run_shell(const char *shell, int loginshell, const char **additional_args) |
56 | void FAST_FUNC run_shell(const char *shell, int loginshell, const char *command, const char **additional_args) | ||
57 | { | 56 | { |
58 | const char **args; | 57 | const char **args; |
59 | int argno; | ||
60 | int additional_args_cnt = 0; | ||
61 | 58 | ||
62 | for (args = additional_args; args && *args; args++) | 59 | args = additional_args; |
63 | additional_args_cnt++; | 60 | while (args && *args) |
61 | args++; | ||
64 | 62 | ||
65 | args = xmalloc(sizeof(char*) * (4 + additional_args_cnt)); | 63 | args = xmalloc(sizeof(char*) * (2 + (args - additional_args))); |
66 | 64 | ||
67 | if (!shell || !shell[0]) | 65 | if (!shell || !shell[0]) |
68 | shell = DEFAULT_SHELL; | 66 | shell = DEFAULT_SHELL; |
@@ -70,16 +68,13 @@ void FAST_FUNC run_shell(const char *shell, int loginshell, const char *command, | |||
70 | args[0] = bb_get_last_path_component_nostrip(shell); | 68 | args[0] = bb_get_last_path_component_nostrip(shell); |
71 | if (loginshell) | 69 | if (loginshell) |
72 | args[0] = xasprintf("-%s", args[0]); | 70 | args[0] = xasprintf("-%s", args[0]); |
73 | argno = 1; | 71 | args[1] = NULL; |
74 | if (command) { | ||
75 | args[argno++] = "-c"; | ||
76 | args[argno++] = command; | ||
77 | } | ||
78 | if (additional_args) { | 72 | if (additional_args) { |
79 | for (; *additional_args; ++additional_args) | 73 | int cnt = 1; |
80 | args[argno++] = *additional_args; | 74 | for (;;) |
75 | if ((args[cnt++] = *additional_args++) == NULL) | ||
76 | break; | ||
81 | } | 77 | } |
82 | args[argno] = NULL; | ||
83 | 78 | ||
84 | #if ENABLE_SELINUX | 79 | #if ENABLE_SELINUX |
85 | if (current_sid) | 80 | if (current_sid) |
diff --git a/loginutils/README b/loginutils/README index ce8851097..847b371b3 100644 --- a/loginutils/README +++ b/loginutils/README | |||
@@ -23,7 +23,7 @@ Getty should establish a new session and process group, and ensure | |||
23 | that tty is a ctty. | 23 | that tty is a ctty. |
24 | 24 | ||
25 | ??? Should getty ensure that other processes which might have opened | 25 | ??? Should getty ensure that other processes which might have opened |
26 | fds to this tty be dusconnected? agetty has a -R option which makes | 26 | fds to this tty be disconnected? agetty has a -R option which makes |
27 | agetty call vhangup() after tty is opened. (Then agetty opens it again, | 27 | agetty call vhangup() after tty is opened. (Then agetty opens it again, |
28 | since it probably vhangup'ed its own fd too). | 28 | since it probably vhangup'ed its own fd too). |
29 | 29 | ||
diff --git a/loginutils/login.c b/loginutils/login.c index 94b6c45db..52abc1886 100644 --- a/loginutils/login.c +++ b/loginutils/login.c | |||
@@ -618,7 +618,7 @@ int login_main(int argc UNUSED_PARAM, char **argv) | |||
618 | signal(SIGINT, SIG_DFL); | 618 | signal(SIGINT, SIG_DFL); |
619 | 619 | ||
620 | /* Exec login shell with no additional parameters */ | 620 | /* Exec login shell with no additional parameters */ |
621 | run_shell(pw->pw_shell, 1, NULL, NULL); | 621 | run_shell(pw->pw_shell, 1, NULL); |
622 | 622 | ||
623 | /* return EXIT_FAILURE; - not reached */ | 623 | /* return EXIT_FAILURE; - not reached */ |
624 | } | 624 | } |
diff --git a/loginutils/su.c b/loginutils/su.c index 3c0e8c100..24ffbde86 100644 --- a/loginutils/su.c +++ b/loginutils/su.c | |||
@@ -31,10 +31,10 @@ | |||
31 | //kbuild:lib-$(CONFIG_SU) += su.o | 31 | //kbuild:lib-$(CONFIG_SU) += su.o |
32 | 32 | ||
33 | //usage:#define su_trivial_usage | 33 | //usage:#define su_trivial_usage |
34 | //usage: "[OPTIONS] [-] [USER]" | 34 | //usage: "[-lmp] [-] [-s SH] [USER [SCRIPT ARGS / -c 'CMD' ARG0 ARGS]]" |
35 | //usage:#define su_full_usage "\n\n" | 35 | //usage:#define su_full_usage "\n\n" |
36 | //usage: "Run shell under USER (by default, root)\n" | 36 | //usage: "Run shell under USER (by default, root)\n" |
37 | //usage: "\n -,-l Clear environment, run shell as login shell" | 37 | //usage: "\n -,-l Clear environment, go to home dir, run shell as login shell" |
38 | //usage: "\n -p,-m Do not set new $HOME, $SHELL, $USER, $LOGNAME" | 38 | //usage: "\n -p,-m Do not set new $HOME, $SHELL, $USER, $LOGNAME" |
39 | //usage: "\n -c CMD Command to pass to 'sh -c'" | 39 | //usage: "\n -c CMD Command to pass to 'sh -c'" |
40 | //usage: "\n -s SH Shell to use instead of user's default" | 40 | //usage: "\n -s SH Shell to use instead of user's default" |
@@ -81,8 +81,12 @@ int su_main(int argc UNUSED_PARAM, char **argv) | |||
81 | #endif | 81 | #endif |
82 | const char *old_user; | 82 | const char *old_user; |
83 | 83 | ||
84 | /* Note: we don't use "'+': stop at first non-option" idiom here. | ||
85 | * For su, "SCRIPT ARGS" or "-c CMD ARGS" do not stop option parsing: | ||
86 | * ARGS starting with dash will be treated as su options, | ||
87 | * not passed to shell. (Tested on util-linux 2.28). | ||
88 | */ | ||
84 | flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell); | 89 | flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell); |
85 | //argc -= optind; | ||
86 | argv += optind; | 90 | argv += optind; |
87 | 91 | ||
88 | if (argv[0] && LONE_DASH(argv[0])) { | 92 | if (argv[0] && LONE_DASH(argv[0])) { |
@@ -162,8 +166,29 @@ int su_main(int argc UNUSED_PARAM, char **argv) | |||
162 | pw); | 166 | pw); |
163 | IF_SELINUX(set_current_security_context(NULL);) | 167 | IF_SELINUX(set_current_security_context(NULL);) |
164 | 168 | ||
169 | if (opt_command) { | ||
170 | *--argv = opt_command; | ||
171 | *--argv = (char*)"-c"; | ||
172 | } | ||
173 | |||
174 | /* A nasty ioctl exists which can stuff data into input queue: | ||
175 | * #include <sys/ioctl.h> | ||
176 | * int main() { | ||
177 | * const char *msg = "echo $UID\n"; | ||
178 | * while (*msg) ioctl(0, TIOCSTI, *msg++); | ||
179 | * return 0; | ||
180 | * } | ||
181 | * With "su USER -c EXPLOIT" run by root, exploit can make root shell | ||
182 | * read as input and execute arbitrary command. | ||
183 | * It's debatable whether we need to protect against this | ||
184 | * (root may hesitate to run unknown scripts interactively). | ||
185 | * | ||
186 | * Some versions of su run -c CMD in a different session: | ||
187 | * ioctl(TIOCSTI) works only on the controlling tty. | ||
188 | */ | ||
189 | |||
165 | /* Never returns */ | 190 | /* Never returns */ |
166 | run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)argv); | 191 | run_shell(opt_shell, flags & SU_OPT_l, (const char**)argv); |
167 | 192 | ||
168 | /* return EXIT_FAILURE; - not reached */ | 193 | /* return EXIT_FAILURE; - not reached */ |
169 | } | 194 | } |
diff --git a/loginutils/sulogin.c b/loginutils/sulogin.c index 6befea933..2e32e2bbd 100644 --- a/loginutils/sulogin.c +++ b/loginutils/sulogin.c | |||
@@ -89,5 +89,5 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv) | |||
89 | shell = pwd->pw_shell; | 89 | shell = pwd->pw_shell; |
90 | 90 | ||
91 | /* Exec login shell with no additional parameters. Never returns. */ | 91 | /* Exec login shell with no additional parameters. Never returns. */ |
92 | run_shell(shell, 1, NULL, NULL); | 92 | run_shell(shell, 1, NULL); |
93 | } | 93 | } |
diff --git a/shell/ash.c b/shell/ash.c index 29eb44263..d0ccfe982 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #define DEBUG_TIME 0 | 46 | #define DEBUG_TIME 0 |
47 | #define DEBUG_PID 1 | 47 | #define DEBUG_PID 1 |
48 | #define DEBUG_SIG 1 | 48 | #define DEBUG_SIG 1 |
49 | #define DEBUG_INTONOFF 0 | ||
49 | 50 | ||
50 | #define PROFILE 0 | 51 | #define PROFILE 0 |
51 | 52 | ||
@@ -512,10 +513,18 @@ static void exitshell(void) NORETURN; | |||
512 | * much more efficient and portable. (But hacking the kernel is so much | 513 | * much more efficient and portable. (But hacking the kernel is so much |
513 | * more fun than worrying about efficiency and portability. :-)) | 514 | * more fun than worrying about efficiency and portability. :-)) |
514 | */ | 515 | */ |
515 | #define INT_OFF do { \ | 516 | #if DEBUG_INTONOFF |
517 | # define INT_OFF do { \ | ||
518 | TRACE(("%s:%d INT_OFF(%d)\n", __func__, __LINE__, suppress_int)); \ | ||
516 | suppress_int++; \ | 519 | suppress_int++; \ |
517 | barrier(); \ | 520 | barrier(); \ |
518 | } while (0) | 521 | } while (0) |
522 | #else | ||
523 | # define INT_OFF do { \ | ||
524 | suppress_int++; \ | ||
525 | barrier(); \ | ||
526 | } while (0) | ||
527 | #endif | ||
519 | 528 | ||
520 | /* | 529 | /* |
521 | * Called to raise an exception. Since C doesn't include exceptions, we | 530 | * Called to raise an exception. Since C doesn't include exceptions, we |
@@ -583,7 +592,14 @@ int_on(void) | |||
583 | raise_interrupt(); | 592 | raise_interrupt(); |
584 | } | 593 | } |
585 | } | 594 | } |
586 | #define INT_ON int_on() | 595 | #if DEBUG_INTONOFF |
596 | # define INT_ON do { \ | ||
597 | TRACE(("%s:%d INT_ON(%d)\n", __func__, __LINE__, suppress_int-1)); \ | ||
598 | int_on(); \ | ||
599 | } while (0) | ||
600 | #else | ||
601 | # define INT_ON int_on() | ||
602 | #endif | ||
587 | static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void | 603 | static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void |
588 | force_int_on(void) | 604 | force_int_on(void) |
589 | { | 605 | { |
@@ -4225,6 +4241,8 @@ wait_block_or_sig(int *status) | |||
4225 | int pid; | 4241 | int pid; |
4226 | 4242 | ||
4227 | do { | 4243 | do { |
4244 | sigset_t mask; | ||
4245 | |||
4228 | /* Poll all children for changes in their state */ | 4246 | /* Poll all children for changes in their state */ |
4229 | got_sigchld = 0; | 4247 | got_sigchld = 0; |
4230 | /* if job control is active, accept stopped processes too */ | 4248 | /* if job control is active, accept stopped processes too */ |
@@ -4233,14 +4251,13 @@ wait_block_or_sig(int *status) | |||
4233 | break; /* Error (e.g. EINTR, ECHILD) or pid */ | 4251 | break; /* Error (e.g. EINTR, ECHILD) or pid */ |
4234 | 4252 | ||
4235 | /* Children exist, but none are ready. Sleep until interesting signal */ | 4253 | /* Children exist, but none are ready. Sleep until interesting signal */ |
4236 | #if 0 /* dash does this */ | 4254 | #if 1 |
4237 | sigset_t mask; | ||
4238 | sigfillset(&mask); | 4255 | sigfillset(&mask); |
4239 | sigprocmask(SIG_SETMASK, &mask, &mask); | 4256 | sigprocmask(SIG_SETMASK, &mask, &mask); |
4240 | while (!got_sigchld && !pending_sig) | 4257 | while (!got_sigchld && !pending_sig) |
4241 | sigsuspend(&mask); | 4258 | sigsuspend(&mask); |
4242 | sigprocmask(SIG_SETMASK, &mask, NULL); | 4259 | sigprocmask(SIG_SETMASK, &mask, NULL); |
4243 | #else | 4260 | #else /* unsafe: a signal can set pending_sig after check, but before pause() */ |
4244 | while (!got_sigchld && !pending_sig) | 4261 | while (!got_sigchld && !pending_sig) |
4245 | pause(); | 4262 | pause(); |
4246 | #endif | 4263 | #endif |
@@ -4280,9 +4297,9 @@ dowait(int block, struct job *job) | |||
4280 | * either enter a sleeping waitpid() (BUG), or need to busy-loop. | 4297 | * either enter a sleeping waitpid() (BUG), or need to busy-loop. |
4281 | * | 4298 | * |
4282 | * Because of this, we run inside INT_OFF, but use a special routine | 4299 | * Because of this, we run inside INT_OFF, but use a special routine |
4283 | * which combines waitpid() and pause(). | 4300 | * which combines waitpid() and sigsuspend(). |
4284 | * This is the reason why we need to have a handler for SIGCHLD: | 4301 | * This is the reason why we need to have a handler for SIGCHLD: |
4285 | * SIG_DFL handler does not wake pause(). | 4302 | * SIG_DFL handler does not wake sigsuspend(). |
4286 | */ | 4303 | */ |
4287 | INT_OFF; | 4304 | INT_OFF; |
4288 | if (block == DOWAIT_BLOCK_OR_SIG) { | 4305 | if (block == DOWAIT_BLOCK_OR_SIG) { |
@@ -9542,7 +9559,7 @@ mklocal(char *name) | |||
9542 | /* else: | 9559 | /* else: |
9543 | * it's a duplicate "local VAR" declaration, do nothing | 9560 | * it's a duplicate "local VAR" declaration, do nothing |
9544 | */ | 9561 | */ |
9545 | return; | 9562 | goto ret; |
9546 | } | 9563 | } |
9547 | lvp = lvp->next; | 9564 | lvp = lvp->next; |
9548 | } | 9565 | } |
@@ -9581,6 +9598,7 @@ mklocal(char *name) | |||
9581 | lvp->vp = vp; | 9598 | lvp->vp = vp; |
9582 | lvp->next = localvars; | 9599 | lvp->next = localvars; |
9583 | localvars = lvp; | 9600 | localvars = lvp; |
9601 | ret: | ||
9584 | INT_ON; | 9602 | INT_ON; |
9585 | } | 9603 | } |
9586 | 9604 | ||
diff --git a/shell/ash_test/ash-misc/group_in_braces.right b/shell/ash_test/ash-misc/group_in_braces.right new file mode 100644 index 000000000..a7064499b --- /dev/null +++ b/shell/ash_test/ash-misc/group_in_braces.right | |||
@@ -0,0 +1,5 @@ | |||
1 | Zero:0 | ||
2 | Zero:0 | ||
3 | Zero:0 | ||
4 | Zero:0 | ||
5 | Zero:0 | ||
diff --git a/shell/ash_test/ash-misc/group_in_braces.tests b/shell/ash_test/ash-misc/group_in_braces.tests new file mode 100755 index 000000000..f6571c35d --- /dev/null +++ b/shell/ash_test/ash-misc/group_in_braces.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | # Test cases where { cmd } does not require semicolon after "cmd" | ||
2 | (exit 2); { { true; } } | ||
3 | echo Zero:$? | ||
4 | (exit 2); {(true)} | ||
5 | echo Zero:$? | ||
6 | (exit 2); { true | { true; } } | ||
7 | echo Zero:$? | ||
8 | (exit 2); { while false; do :; done } | ||
9 | echo Zero:$? | ||
10 | (exit 2); { case a in b) ;; esac } | ||
11 | echo Zero:$? | ||
diff --git a/shell/ash_test/ash-misc/wait4.right b/shell/ash_test/ash-misc/wait4.right new file mode 100644 index 000000000..f7987db32 --- /dev/null +++ b/shell/ash_test/ash-misc/wait4.right | |||
@@ -0,0 +1 @@ | |||
Three:3 | |||
diff --git a/shell/ash_test/ash-misc/wait4.tests b/shell/ash_test/ash-misc/wait4.tests new file mode 100755 index 000000000..cc34059ac --- /dev/null +++ b/shell/ash_test/ash-misc/wait4.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | sleep 1 | (sleep 1;exit 3) & wait %1 | ||
2 | echo Three:$? | ||
diff --git a/shell/ash_test/ash-misc/wait5.right b/shell/ash_test/ash-misc/wait5.right new file mode 100644 index 000000000..82c9d5696 --- /dev/null +++ b/shell/ash_test/ash-misc/wait5.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Zero:0 | ||
2 | Three:3 | ||
diff --git a/shell/ash_test/ash-misc/wait5.tests b/shell/ash_test/ash-misc/wait5.tests new file mode 100755 index 000000000..1b4762d89 --- /dev/null +++ b/shell/ash_test/ash-misc/wait5.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | sleep 0 | (sleep 0;exit 3) & | ||
2 | sleep 1 | ||
3 | echo Zero:$? | ||
4 | wait %1 | ||
5 | echo Three:$? | ||
diff --git a/shell/hush.c b/shell/hush.c index 7c2f157b8..2f07f4ac1 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -45,7 +45,6 @@ | |||
45 | * tilde expansion | 45 | * tilde expansion |
46 | * aliases | 46 | * aliases |
47 | * kill %jobspec | 47 | * kill %jobspec |
48 | * wait %jobspec | ||
49 | * follow IFS rules more precisely, including update semantics | 48 | * follow IFS rules more precisely, including update semantics |
50 | * builtins mandated by standards we don't support: | 49 | * builtins mandated by standards we don't support: |
51 | * [un]alias, command, fc, getopts, newgrp, readonly, times | 50 | * [un]alias, command, fc, getopts, newgrp, readonly, times |
@@ -470,11 +469,7 @@ typedef struct in_str { | |||
470 | int peek_buf[2]; | 469 | int peek_buf[2]; |
471 | int last_char; | 470 | int last_char; |
472 | FILE *file; | 471 | FILE *file; |
473 | int (*get) (struct in_str *) FAST_FUNC; | ||
474 | int (*peek) (struct in_str *) FAST_FUNC; | ||
475 | } in_str; | 472 | } in_str; |
476 | #define i_getch(input) ((input)->get(input)) | ||
477 | #define i_peek(input) ((input)->peek(input)) | ||
478 | 473 | ||
479 | /* The descrip member of this structure is only used to make | 474 | /* The descrip member of this structure is only used to make |
480 | * debugging output pretty */ | 475 | * debugging output pretty */ |
@@ -589,10 +584,10 @@ struct pipe { | |||
589 | IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */ | 584 | IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */ |
590 | }; | 585 | }; |
591 | typedef enum pipe_style { | 586 | typedef enum pipe_style { |
592 | PIPE_SEQ = 1, | 587 | PIPE_SEQ = 0, |
593 | PIPE_AND = 2, | 588 | PIPE_AND = 1, |
594 | PIPE_OR = 3, | 589 | PIPE_OR = 2, |
595 | PIPE_BG = 4, | 590 | PIPE_BG = 3, |
596 | } pipe_style; | 591 | } pipe_style; |
597 | /* Is there anything in this pipe at all? */ | 592 | /* Is there anything in this pipe at all? */ |
598 | #define IS_NULL_PIPE(pi) \ | 593 | #define IS_NULL_PIPE(pi) \ |
@@ -2260,10 +2255,23 @@ static inline int fgetc_interactive(struct in_str *i) | |||
2260 | } | 2255 | } |
2261 | #endif /* INTERACTIVE */ | 2256 | #endif /* INTERACTIVE */ |
2262 | 2257 | ||
2263 | static int FAST_FUNC file_get(struct in_str *i) | 2258 | static int i_getch(struct in_str *i) |
2264 | { | 2259 | { |
2265 | int ch; | 2260 | int ch; |
2266 | 2261 | ||
2262 | if (!i->file) { | ||
2263 | /* string-based in_str */ | ||
2264 | ch = (unsigned char)*i->p; | ||
2265 | if (ch != '\0') { | ||
2266 | i->p++; | ||
2267 | i->last_char = ch; | ||
2268 | return ch; | ||
2269 | } | ||
2270 | return EOF; | ||
2271 | } | ||
2272 | |||
2273 | /* FILE-based in_str */ | ||
2274 | |||
2267 | #if ENABLE_FEATURE_EDITING | 2275 | #if ENABLE_FEATURE_EDITING |
2268 | /* This can be stdin, check line editing char[] buffer */ | 2276 | /* This can be stdin, check line editing char[] buffer */ |
2269 | if (i->p && *i->p != '\0') { | 2277 | if (i->p && *i->p != '\0') { |
@@ -2289,10 +2297,18 @@ static int FAST_FUNC file_get(struct in_str *i) | |||
2289 | return ch; | 2297 | return ch; |
2290 | } | 2298 | } |
2291 | 2299 | ||
2292 | static int FAST_FUNC file_peek(struct in_str *i) | 2300 | static int i_peek(struct in_str *i) |
2293 | { | 2301 | { |
2294 | int ch; | 2302 | int ch; |
2295 | 2303 | ||
2304 | if (!i->file) { | ||
2305 | /* string-based in_str */ | ||
2306 | /* Doesn't report EOF on NUL. None of the callers care. */ | ||
2307 | return (unsigned char)*i->p; | ||
2308 | } | ||
2309 | |||
2310 | /* FILE-based in_str */ | ||
2311 | |||
2296 | #if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE | 2312 | #if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE |
2297 | /* This can be stdin, check line editing char[] buffer */ | 2313 | /* This can be stdin, check line editing char[] buffer */ |
2298 | if (i->p && *i->p != '\0') | 2314 | if (i->p && *i->p != '\0') |
@@ -2319,23 +2335,6 @@ static int FAST_FUNC file_peek(struct in_str *i) | |||
2319 | return ch; | 2335 | return ch; |
2320 | } | 2336 | } |
2321 | 2337 | ||
2322 | static int FAST_FUNC static_get(struct in_str *i) | ||
2323 | { | ||
2324 | int ch = (unsigned char)*i->p; | ||
2325 | if (ch != '\0') { | ||
2326 | i->p++; | ||
2327 | i->last_char = ch; | ||
2328 | return ch; | ||
2329 | } | ||
2330 | return EOF; | ||
2331 | } | ||
2332 | |||
2333 | static int FAST_FUNC static_peek(struct in_str *i) | ||
2334 | { | ||
2335 | /* Doesn't report EOF on NUL. None of the callers care. */ | ||
2336 | return (unsigned char)*i->p; | ||
2337 | } | ||
2338 | |||
2339 | /* Only ever called if i_peek() was called, and did not return EOF. | 2338 | /* Only ever called if i_peek() was called, and did not return EOF. |
2340 | * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL, | 2339 | * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL, |
2341 | * not end-of-line. Therefore we never need to read a new editing line here. | 2340 | * not end-of-line. Therefore we never need to read a new editing line here. |
@@ -2371,8 +2370,6 @@ static int i_peek2(struct in_str *i) | |||
2371 | static void setup_file_in_str(struct in_str *i, FILE *f) | 2370 | static void setup_file_in_str(struct in_str *i, FILE *f) |
2372 | { | 2371 | { |
2373 | memset(i, 0, sizeof(*i)); | 2372 | memset(i, 0, sizeof(*i)); |
2374 | i->get = file_get; | ||
2375 | i->peek = file_peek; | ||
2376 | /* i->promptmode = 0; - PS1 (memset did it) */ | 2373 | /* i->promptmode = 0; - PS1 (memset did it) */ |
2377 | i->file = f; | 2374 | i->file = f; |
2378 | /* i->p = NULL; */ | 2375 | /* i->p = NULL; */ |
@@ -2381,9 +2378,8 @@ static void setup_file_in_str(struct in_str *i, FILE *f) | |||
2381 | static void setup_string_in_str(struct in_str *i, const char *s) | 2378 | static void setup_string_in_str(struct in_str *i, const char *s) |
2382 | { | 2379 | { |
2383 | memset(i, 0, sizeof(*i)); | 2380 | memset(i, 0, sizeof(*i)); |
2384 | i->get = static_get; | ||
2385 | i->peek = static_peek; | ||
2386 | /* i->promptmode = 0; - PS1 (memset did it) */ | 2381 | /* i->promptmode = 0; - PS1 (memset did it) */ |
2382 | /*i->file = NULL */; | ||
2387 | i->p = s; | 2383 | i->p = s; |
2388 | } | 2384 | } |
2389 | 2385 | ||
@@ -3140,7 +3136,6 @@ static struct pipe *new_pipe(void) | |||
3140 | { | 3136 | { |
3141 | struct pipe *pi; | 3137 | struct pipe *pi; |
3142 | pi = xzalloc(sizeof(struct pipe)); | 3138 | pi = xzalloc(sizeof(struct pipe)); |
3143 | /*pi->followup = 0; - deliberately invalid value */ | ||
3144 | /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */ | 3139 | /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */ |
3145 | return pi; | 3140 | return pi; |
3146 | } | 3141 | } |
@@ -3915,12 +3910,17 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
3915 | command->cmd_type = CMD_SUBSHELL; | 3910 | command->cmd_type = CMD_SUBSHELL; |
3916 | } else { | 3911 | } else { |
3917 | /* bash does not allow "{echo...", requires whitespace */ | 3912 | /* bash does not allow "{echo...", requires whitespace */ |
3918 | ch = i_getch(input); | 3913 | ch = i_peek(input); |
3919 | if (ch != ' ' && ch != '\t' && ch != '\n') { | 3914 | if (ch != ' ' && ch != '\t' && ch != '\n' |
3915 | && ch != '(' /* but "{(..." is allowed (without whitespace) */ | ||
3916 | ) { | ||
3920 | syntax_error_unexpected_ch(ch); | 3917 | syntax_error_unexpected_ch(ch); |
3921 | return 1; | 3918 | return 1; |
3922 | } | 3919 | } |
3923 | nommu_addchr(&ctx->as_string, ch); | 3920 | if (ch != '(') { |
3921 | ch = i_getch(input); | ||
3922 | nommu_addchr(&ctx->as_string, ch); | ||
3923 | } | ||
3924 | } | 3924 | } |
3925 | 3925 | ||
3926 | { | 3926 | { |
@@ -4528,12 +4528,14 @@ static struct pipe *parse_stream(char **pstring, | |||
4528 | syntax_error_unterm_str("here document"); | 4528 | syntax_error_unterm_str("here document"); |
4529 | goto parse_error; | 4529 | goto parse_error; |
4530 | } | 4530 | } |
4531 | /* end_trigger == '}' case errors out earlier, | ||
4532 | * checking only ')' */ | ||
4533 | if (end_trigger == ')') { | 4531 | if (end_trigger == ')') { |
4534 | syntax_error_unterm_ch('('); | 4532 | syntax_error_unterm_ch('('); |
4535 | goto parse_error; | 4533 | goto parse_error; |
4536 | } | 4534 | } |
4535 | if (end_trigger == '}') { | ||
4536 | syntax_error_unterm_ch('{'); | ||
4537 | goto parse_error; | ||
4538 | } | ||
4537 | 4539 | ||
4538 | if (done_word(&dest, &ctx)) { | 4540 | if (done_word(&dest, &ctx)) { |
4539 | goto parse_error; | 4541 | goto parse_error; |
@@ -4575,6 +4577,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4575 | || dest.has_quoted_part /* ""{... - non-special */ | 4577 | || dest.has_quoted_part /* ""{... - non-special */ |
4576 | || (next != ';' /* }; - special */ | 4578 | || (next != ';' /* }; - special */ |
4577 | && next != ')' /* }) - special */ | 4579 | && next != ')' /* }) - special */ |
4580 | && next != '(' /* {( - special */ | ||
4578 | && next != '&' /* }& and }&& ... - special */ | 4581 | && next != '&' /* }& and }&& ... - special */ |
4579 | && next != '|' /* }|| ... - special */ | 4582 | && next != '|' /* }|| ... - special */ |
4580 | && !strchr(defifs, next) /* {word - non-special */ | 4583 | && !strchr(defifs, next) /* {word - non-special */ |
@@ -4657,17 +4660,31 @@ static struct pipe *parse_stream(char **pstring, | |||
4657 | * Pathological example: { ""}; } should exec "}" cmd | 4660 | * Pathological example: { ""}; } should exec "}" cmd |
4658 | */ | 4661 | */ |
4659 | if (ch == '}') { | 4662 | if (ch == '}') { |
4660 | if (!IS_NULL_CMD(ctx.command) /* cmd } */ | 4663 | if (dest.length != 0 /* word} */ |
4661 | || dest.length != 0 /* word} */ | ||
4662 | || dest.has_quoted_part /* ""} */ | 4664 | || dest.has_quoted_part /* ""} */ |
4663 | ) { | 4665 | ) { |
4664 | goto ordinary_char; | 4666 | goto ordinary_char; |
4665 | } | 4667 | } |
4668 | if (!IS_NULL_CMD(ctx.command)) { /* cmd } */ | ||
4669 | /* Generally, there should be semicolon: "cmd; }" | ||
4670 | * However, bash allows to omit it if "cmd" is | ||
4671 | * a group. Examples: | ||
4672 | * { { echo 1; } } | ||
4673 | * {(echo 1)} | ||
4674 | * { echo 0 >&2 | { echo 1; } } | ||
4675 | * { while false; do :; done } | ||
4676 | * { case a in b) ;; esac } | ||
4677 | */ | ||
4678 | if (ctx.command->group) | ||
4679 | goto term_group; | ||
4680 | goto ordinary_char; | ||
4681 | } | ||
4666 | if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */ | 4682 | if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */ |
4683 | /* Can't be an end of {cmd}, skip the check */ | ||
4667 | goto skip_end_trigger; | 4684 | goto skip_end_trigger; |
4668 | /* else: } does terminate a group */ | 4685 | /* else: } does terminate a group */ |
4669 | } | 4686 | } |
4670 | 4687 | term_group: | |
4671 | if (end_trigger && end_trigger == ch | 4688 | if (end_trigger && end_trigger == ch |
4672 | && (ch != ';' || heredoc_cnt == 0) | 4689 | && (ch != ';' || heredoc_cnt == 0) |
4673 | #if ENABLE_HUSH_CASE | 4690 | #if ENABLE_HUSH_CASE |
@@ -4987,8 +5004,8 @@ static struct pipe *parse_stream(char **pstring, | |||
4987 | * Run it from interactive shell, watch pmap `pidof hush`. | 5004 | * Run it from interactive shell, watch pmap `pidof hush`. |
4988 | * while if false; then false; fi; do break; fi | 5005 | * while if false; then false; fi; do break; fi |
4989 | * Samples to catch leaks at execution: | 5006 | * Samples to catch leaks at execution: |
4990 | * while if (true | {true;}); then echo ok; fi; do break; done | 5007 | * while if (true | { true;}); then echo ok; fi; do break; done |
4991 | * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done | 5008 | * while if (true | { true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done |
4992 | */ | 5009 | */ |
4993 | pctx = &ctx; | 5010 | pctx = &ctx; |
4994 | do { | 5011 | do { |
@@ -6962,12 +6979,12 @@ static const char *get_cmdtext(struct pipe *pi) | |||
6962 | * On subsequent bg argv is trashed, but we won't use it */ | 6979 | * On subsequent bg argv is trashed, but we won't use it */ |
6963 | if (pi->cmdtext) | 6980 | if (pi->cmdtext) |
6964 | return pi->cmdtext; | 6981 | return pi->cmdtext; |
6982 | |||
6965 | argv = pi->cmds[0].argv; | 6983 | argv = pi->cmds[0].argv; |
6966 | if (!argv || !argv[0]) { | 6984 | if (!argv) { |
6967 | pi->cmdtext = xzalloc(1); | 6985 | pi->cmdtext = xzalloc(1); |
6968 | return pi->cmdtext; | 6986 | return pi->cmdtext; |
6969 | } | 6987 | } |
6970 | |||
6971 | len = 0; | 6988 | len = 0; |
6972 | do { | 6989 | do { |
6973 | len += strlen(*argv) + 1; | 6990 | len += strlen(*argv) + 1; |
@@ -6976,9 +6993,7 @@ static const char *get_cmdtext(struct pipe *pi) | |||
6976 | pi->cmdtext = p; | 6993 | pi->cmdtext = p; |
6977 | argv = pi->cmds[0].argv; | 6994 | argv = pi->cmds[0].argv; |
6978 | do { | 6995 | do { |
6979 | len = strlen(*argv); | 6996 | p = stpcpy(p, *argv); |
6980 | memcpy(p, *argv, len); | ||
6981 | p += len; | ||
6982 | *p++ = ' '; | 6997 | *p++ = ' '; |
6983 | } while (*++argv); | 6998 | } while (*++argv); |
6984 | p[-1] = '\0'; | 6999 | p[-1] = '\0'; |
@@ -7043,6 +7058,27 @@ static void delete_finished_bg_job(struct pipe *pi) | |||
7043 | } | 7058 | } |
7044 | #endif /* JOB */ | 7059 | #endif /* JOB */ |
7045 | 7060 | ||
7061 | static int job_exited_or_stopped(struct pipe *pi) | ||
7062 | { | ||
7063 | int rcode, i; | ||
7064 | |||
7065 | if (pi->alive_cmds != pi->stopped_cmds) | ||
7066 | return -1; | ||
7067 | |||
7068 | /* All processes in fg pipe have exited or stopped */ | ||
7069 | rcode = 0; | ||
7070 | i = pi->num_cmds; | ||
7071 | while (--i >= 0) { | ||
7072 | rcode = pi->cmds[i].cmd_exitcode; | ||
7073 | /* usually last process gives overall exitstatus, | ||
7074 | * but with "set -o pipefail", last *failed* process does */ | ||
7075 | if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0) | ||
7076 | break; | ||
7077 | } | ||
7078 | IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) | ||
7079 | return rcode; | ||
7080 | } | ||
7081 | |||
7046 | static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | 7082 | static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) |
7047 | { | 7083 | { |
7048 | #if ENABLE_HUSH_JOB | 7084 | #if ENABLE_HUSH_JOB |
@@ -7066,7 +7102,10 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
7066 | /* Were we asked to wait for a fg pipe? */ | 7102 | /* Were we asked to wait for a fg pipe? */ |
7067 | if (fg_pipe) { | 7103 | if (fg_pipe) { |
7068 | i = fg_pipe->num_cmds; | 7104 | i = fg_pipe->num_cmds; |
7105 | |||
7069 | while (--i >= 0) { | 7106 | while (--i >= 0) { |
7107 | int rcode; | ||
7108 | |||
7070 | debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid); | 7109 | debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid); |
7071 | if (fg_pipe->cmds[i].pid != childpid) | 7110 | if (fg_pipe->cmds[i].pid != childpid) |
7072 | continue; | 7111 | continue; |
@@ -7095,18 +7134,8 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
7095 | } | 7134 | } |
7096 | debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n", | 7135 | debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n", |
7097 | fg_pipe->alive_cmds, fg_pipe->stopped_cmds); | 7136 | fg_pipe->alive_cmds, fg_pipe->stopped_cmds); |
7098 | if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) { | 7137 | rcode = job_exited_or_stopped(fg_pipe); |
7099 | /* All processes in fg pipe have exited or stopped */ | 7138 | if (rcode >= 0) { |
7100 | int rcode = 0; | ||
7101 | i = fg_pipe->num_cmds; | ||
7102 | while (--i >= 0) { | ||
7103 | rcode = fg_pipe->cmds[i].cmd_exitcode; | ||
7104 | /* usually last process gives overall exitstatus, | ||
7105 | * but with "set -o pipefail", last *failed* process does */ | ||
7106 | if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0) | ||
7107 | break; | ||
7108 | } | ||
7109 | IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) | ||
7110 | /* Note: *non-interactive* bash does not continue if all processes in fg pipe | 7139 | /* Note: *non-interactive* bash does not continue if all processes in fg pipe |
7111 | * are stopped. Testcase: "cat | cat" in a script (not on command line!) | 7140 | * are stopped. Testcase: "cat | cat" in a script (not on command line!) |
7112 | * and "killall -STOP cat" */ | 7141 | * and "killall -STOP cat" */ |
@@ -7163,9 +7192,18 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
7163 | 7192 | ||
7164 | /* Check to see if any processes have exited -- if they have, | 7193 | /* Check to see if any processes have exited -- if they have, |
7165 | * figure out why and see if a job has completed. | 7194 | * figure out why and see if a job has completed. |
7166 | * Alternatively (fg_pipe == NULL, waitfor_pid != 0), | 7195 | * |
7167 | * wait for a specific pid to complete, return exitcode+1 | 7196 | * If non-NULL fg_pipe: wait for its completion or stop. |
7168 | * (this allows to distinguish zero as "no children exited" result). | 7197 | * Return its exitcode or zero if stopped. |
7198 | * | ||
7199 | * Alternatively (fg_pipe == NULL, waitfor_pid != 0): | ||
7200 | * waitpid(WNOHANG), if waitfor_pid exits or stops, return exitcode+1, | ||
7201 | * else return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for) | ||
7202 | * or 0 if no children changed status. | ||
7203 | * | ||
7204 | * Alternatively (fg_pipe == NULL, waitfor_pid == 0), | ||
7205 | * return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for) | ||
7206 | * or 0 if no children changed status. | ||
7169 | */ | 7207 | */ |
7170 | static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) | 7208 | static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) |
7171 | { | 7209 | { |
@@ -7234,9 +7272,13 @@ static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) | |||
7234 | break; | 7272 | break; |
7235 | } | 7273 | } |
7236 | if (childpid == waitfor_pid) { | 7274 | if (childpid == waitfor_pid) { |
7275 | debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status); | ||
7237 | rcode = WEXITSTATUS(status); | 7276 | rcode = WEXITSTATUS(status); |
7238 | if (WIFSIGNALED(status)) | 7277 | if (WIFSIGNALED(status)) |
7239 | rcode = 128 + WTERMSIG(status); | 7278 | rcode = 128 + WTERMSIG(status); |
7279 | if (WIFSTOPPED(status)) | ||
7280 | /* bash: "cmd & wait $!" and cmd stops: $? = 128 + stopsig */ | ||
7281 | rcode = 128 + WSTOPSIG(status); | ||
7240 | rcode++; | 7282 | rcode++; |
7241 | break; /* "wait PID" called us, give it exitcode+1 */ | 7283 | break; /* "wait PID" called us, give it exitcode+1 */ |
7242 | } | 7284 | } |
@@ -7757,6 +7799,8 @@ static int run_list(struct pipe *pi) | |||
7757 | 7799 | ||
7758 | /* Go through list of pipes, (maybe) executing them. */ | 7800 | /* Go through list of pipes, (maybe) executing them. */ |
7759 | for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { | 7801 | for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { |
7802 | int r; | ||
7803 | |||
7760 | if (G.flag_SIGINT) | 7804 | if (G.flag_SIGINT) |
7761 | break; | 7805 | break; |
7762 | if (G_flag_return_in_progress == 1) | 7806 | if (G_flag_return_in_progress == 1) |
@@ -7854,12 +7898,14 @@ static int run_list(struct pipe *pi) | |||
7854 | #endif | 7898 | #endif |
7855 | #if ENABLE_HUSH_CASE | 7899 | #if ENABLE_HUSH_CASE |
7856 | if (rword == RES_CASE) { | 7900 | if (rword == RES_CASE) { |
7901 | debug_printf_exec("CASE cond_code:%d\n", cond_code); | ||
7857 | case_word = expand_strvec_to_string(pi->cmds->argv); | 7902 | case_word = expand_strvec_to_string(pi->cmds->argv); |
7858 | continue; | 7903 | continue; |
7859 | } | 7904 | } |
7860 | if (rword == RES_MATCH) { | 7905 | if (rword == RES_MATCH) { |
7861 | char **argv; | 7906 | char **argv; |
7862 | 7907 | ||
7908 | debug_printf_exec("MATCH cond_code:%d\n", cond_code); | ||
7863 | if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */ | 7909 | if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */ |
7864 | break; | 7910 | break; |
7865 | /* all prev words didn't match, does this one match? */ | 7911 | /* all prev words didn't match, does this one match? */ |
@@ -7870,8 +7916,8 @@ static int run_list(struct pipe *pi) | |||
7870 | cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); | 7916 | cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); |
7871 | free(pattern); | 7917 | free(pattern); |
7872 | if (cond_code == 0) { /* match! we will execute this branch */ | 7918 | if (cond_code == 0) { /* match! we will execute this branch */ |
7873 | free(case_word); /* make future "word)" stop */ | 7919 | free(case_word); |
7874 | case_word = NULL; | 7920 | case_word = NULL; /* make future "word)" stop */ |
7875 | break; | 7921 | break; |
7876 | } | 7922 | } |
7877 | argv++; | 7923 | argv++; |
@@ -7879,9 +7925,17 @@ static int run_list(struct pipe *pi) | |||
7879 | continue; | 7925 | continue; |
7880 | } | 7926 | } |
7881 | if (rword == RES_CASE_BODY) { /* inside of a case branch */ | 7927 | if (rword == RES_CASE_BODY) { /* inside of a case branch */ |
7928 | debug_printf_exec("CASE_BODY cond_code:%d\n", cond_code); | ||
7882 | if (cond_code != 0) | 7929 | if (cond_code != 0) |
7883 | continue; /* not matched yet, skip this pipe */ | 7930 | continue; /* not matched yet, skip this pipe */ |
7884 | } | 7931 | } |
7932 | if (rword == RES_ESAC) { | ||
7933 | debug_printf_exec("ESAC cond_code:%d\n", cond_code); | ||
7934 | if (case_word) { | ||
7935 | /* "case" did not match anything: still set $? (to 0) */ | ||
7936 | G.last_exitcode = rcode = EXIT_SUCCESS; | ||
7937 | } | ||
7938 | } | ||
7885 | #endif | 7939 | #endif |
7886 | /* Just pressing <enter> in shell should check for jobs. | 7940 | /* Just pressing <enter> in shell should check for jobs. |
7887 | * OTOH, in non-interactive shell this is useless | 7941 | * OTOH, in non-interactive shell this is useless |
@@ -7897,74 +7951,72 @@ static int run_list(struct pipe *pi) | |||
7897 | * after run_pipe to collect any background children, | 7951 | * after run_pipe to collect any background children, |
7898 | * even if list execution is to be stopped. */ | 7952 | * even if list execution is to be stopped. */ |
7899 | debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds); | 7953 | debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds); |
7900 | { | ||
7901 | int r; | ||
7902 | #if ENABLE_HUSH_LOOPS | 7954 | #if ENABLE_HUSH_LOOPS |
7903 | G.flag_break_continue = 0; | 7955 | G.flag_break_continue = 0; |
7904 | #endif | ||
7905 | rcode = r = run_pipe(pi); /* NB: rcode is a smallint */ | ||
7906 | if (r != -1) { | ||
7907 | /* We ran a builtin, function, or group. | ||
7908 | * rcode is already known | ||
7909 | * and we don't need to wait for anything. */ | ||
7910 | G.last_exitcode = rcode; | ||
7911 | debug_printf_exec(": builtin/func exitcode %d\n", rcode); | ||
7912 | check_and_run_traps(); | ||
7913 | #if ENABLE_HUSH_LOOPS | ||
7914 | /* Was it "break" or "continue"? */ | ||
7915 | if (G.flag_break_continue) { | ||
7916 | smallint fbc = G.flag_break_continue; | ||
7917 | /* We might fall into outer *loop*, | ||
7918 | * don't want to break it too */ | ||
7919 | if (loop_top) { | ||
7920 | G.depth_break_continue--; | ||
7921 | if (G.depth_break_continue == 0) | ||
7922 | G.flag_break_continue = 0; | ||
7923 | /* else: e.g. "continue 2" should *break* once, *then* continue */ | ||
7924 | } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */ | ||
7925 | if (G.depth_break_continue != 0 || fbc == BC_BREAK) { | ||
7926 | checkjobs(NULL, 0 /*(no pid to wait for)*/); | ||
7927 | break; | ||
7928 | } | ||
7929 | /* "continue": simulate end of loop */ | ||
7930 | rword = RES_DONE; | ||
7931 | continue; | ||
7932 | } | ||
7933 | #endif | 7956 | #endif |
7934 | if (G_flag_return_in_progress == 1) { | 7957 | rcode = r = run_pipe(pi); /* NB: rcode is a smalluint, r is int */ |
7958 | if (r != -1) { | ||
7959 | /* We ran a builtin, function, or group. | ||
7960 | * rcode is already known | ||
7961 | * and we don't need to wait for anything. */ | ||
7962 | debug_printf_exec(": builtin/func exitcode %d\n", rcode); | ||
7963 | G.last_exitcode = rcode; | ||
7964 | check_and_run_traps(); | ||
7965 | #if ENABLE_HUSH_LOOPS | ||
7966 | /* Was it "break" or "continue"? */ | ||
7967 | if (G.flag_break_continue) { | ||
7968 | smallint fbc = G.flag_break_continue; | ||
7969 | /* We might fall into outer *loop*, | ||
7970 | * don't want to break it too */ | ||
7971 | if (loop_top) { | ||
7972 | G.depth_break_continue--; | ||
7973 | if (G.depth_break_continue == 0) | ||
7974 | G.flag_break_continue = 0; | ||
7975 | /* else: e.g. "continue 2" should *break* once, *then* continue */ | ||
7976 | } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */ | ||
7977 | if (G.depth_break_continue != 0 || fbc == BC_BREAK) { | ||
7935 | checkjobs(NULL, 0 /*(no pid to wait for)*/); | 7978 | checkjobs(NULL, 0 /*(no pid to wait for)*/); |
7936 | break; | 7979 | break; |
7937 | } | 7980 | } |
7938 | } else if (pi->followup == PIPE_BG) { | 7981 | /* "continue": simulate end of loop */ |
7939 | /* What does bash do with attempts to background builtins? */ | 7982 | rword = RES_DONE; |
7940 | /* even bash 3.2 doesn't do that well with nested bg: | 7983 | continue; |
7941 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | 7984 | } |
7942 | * I'm NOT treating inner &'s as jobs */ | ||
7943 | check_and_run_traps(); | ||
7944 | #if ENABLE_HUSH_JOB | ||
7945 | if (G.run_list_level == 1) | ||
7946 | insert_bg_job(pi); | ||
7947 | #endif | 7985 | #endif |
7948 | /* Last command's pid goes to $! */ | 7986 | if (G_flag_return_in_progress == 1) { |
7949 | G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; | 7987 | checkjobs(NULL, 0 /*(no pid to wait for)*/); |
7950 | G.last_exitcode = rcode = EXIT_SUCCESS; | 7988 | break; |
7951 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); | 7989 | } |
7952 | } else { | 7990 | } else if (pi->followup == PIPE_BG) { |
7991 | /* What does bash do with attempts to background builtins? */ | ||
7992 | /* even bash 3.2 doesn't do that well with nested bg: | ||
7993 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | ||
7994 | * I'm NOT treating inner &'s as jobs */ | ||
7953 | #if ENABLE_HUSH_JOB | 7995 | #if ENABLE_HUSH_JOB |
7954 | if (G.run_list_level == 1 && G_interactive_fd) { | 7996 | if (G.run_list_level == 1) |
7955 | /* Waits for completion, then fg's main shell */ | 7997 | insert_bg_job(pi); |
7956 | rcode = checkjobs_and_fg_shell(pi); | 7998 | #endif |
7957 | debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode); | 7999 | /* Last command's pid goes to $! */ |
7958 | check_and_run_traps(); | 8000 | G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; |
7959 | } else | 8001 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); |
7960 | #endif | 8002 | /* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */ |
7961 | { /* This one just waits for completion */ | 8003 | rcode = EXIT_SUCCESS; |
7962 | rcode = checkjobs(pi, 0 /*(no pid to wait for)*/); | 8004 | goto check_traps; |
7963 | debug_printf_exec(": checkjobs exitcode %d\n", rcode); | 8005 | } else { |
7964 | check_and_run_traps(); | 8006 | #if ENABLE_HUSH_JOB |
7965 | } | 8007 | if (G.run_list_level == 1 && G_interactive_fd) { |
7966 | G.last_exitcode = rcode; | 8008 | /* Waits for completion, then fg's main shell */ |
8009 | rcode = checkjobs_and_fg_shell(pi); | ||
8010 | debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode); | ||
8011 | goto check_traps; | ||
7967 | } | 8012 | } |
8013 | #endif | ||
8014 | /* This one just waits for completion */ | ||
8015 | rcode = checkjobs(pi, 0 /*(no pid to wait for)*/); | ||
8016 | debug_printf_exec(": checkjobs exitcode %d\n", rcode); | ||
8017 | check_traps: | ||
8018 | G.last_exitcode = rcode; | ||
8019 | check_and_run_traps(); | ||
7968 | } | 8020 | } |
7969 | 8021 | ||
7970 | /* Analyze how result affects subsequent commands */ | 8022 | /* Analyze how result affects subsequent commands */ |
@@ -8088,11 +8140,11 @@ static void install_fatal_sighandlers(void) | |||
8088 | 8140 | ||
8089 | /* We will restore tty pgrp on these signals */ | 8141 | /* We will restore tty pgrp on these signals */ |
8090 | mask = 0 | 8142 | mask = 0 |
8091 | + (1 << SIGILL ) * HUSH_DEBUG | 8143 | /*+ (1 << SIGILL ) * HUSH_DEBUG*/ |
8092 | + (1 << SIGFPE ) * HUSH_DEBUG | 8144 | /*+ (1 << SIGFPE ) * HUSH_DEBUG*/ |
8093 | + (1 << SIGBUS ) * HUSH_DEBUG | 8145 | + (1 << SIGBUS ) * HUSH_DEBUG |
8094 | + (1 << SIGSEGV) * HUSH_DEBUG | 8146 | + (1 << SIGSEGV) * HUSH_DEBUG |
8095 | + (1 << SIGTRAP) * HUSH_DEBUG | 8147 | /*+ (1 << SIGTRAP) * HUSH_DEBUG*/ |
8096 | + (1 << SIGABRT) | 8148 | + (1 << SIGABRT) |
8097 | /* bash 3.2 seems to handle these just like 'fatal' ones */ | 8149 | /* bash 3.2 seems to handle these just like 'fatal' ones */ |
8098 | + (1 << SIGPIPE) | 8150 | + (1 << SIGPIPE) |
@@ -9194,10 +9246,28 @@ static int FAST_FUNC builtin_type(char **argv) | |||
9194 | } | 9246 | } |
9195 | 9247 | ||
9196 | #if ENABLE_HUSH_JOB | 9248 | #if ENABLE_HUSH_JOB |
9249 | static struct pipe *parse_jobspec(const char *str) | ||
9250 | { | ||
9251 | struct pipe *pi; | ||
9252 | int jobnum; | ||
9253 | |||
9254 | if (sscanf(str, "%%%d", &jobnum) != 1) { | ||
9255 | bb_error_msg("bad argument '%s'", str); | ||
9256 | return NULL; | ||
9257 | } | ||
9258 | for (pi = G.job_list; pi; pi = pi->next) { | ||
9259 | if (pi->jobid == jobnum) { | ||
9260 | return pi; | ||
9261 | } | ||
9262 | } | ||
9263 | bb_error_msg("%d: no such job", jobnum); | ||
9264 | return NULL; | ||
9265 | } | ||
9266 | |||
9197 | /* built-in 'fg' and 'bg' handler */ | 9267 | /* built-in 'fg' and 'bg' handler */ |
9198 | static int FAST_FUNC builtin_fg_bg(char **argv) | 9268 | static int FAST_FUNC builtin_fg_bg(char **argv) |
9199 | { | 9269 | { |
9200 | int i, jobnum; | 9270 | int i; |
9201 | struct pipe *pi; | 9271 | struct pipe *pi; |
9202 | 9272 | ||
9203 | if (!G_interactive_fd) | 9273 | if (!G_interactive_fd) |
@@ -9213,17 +9283,10 @@ static int FAST_FUNC builtin_fg_bg(char **argv) | |||
9213 | bb_error_msg("%s: no current job", argv[0]); | 9283 | bb_error_msg("%s: no current job", argv[0]); |
9214 | return EXIT_FAILURE; | 9284 | return EXIT_FAILURE; |
9215 | } | 9285 | } |
9216 | if (sscanf(argv[1], "%%%d", &jobnum) != 1) { | 9286 | |
9217 | bb_error_msg("%s: bad argument '%s'", argv[0], argv[1]); | 9287 | pi = parse_jobspec(argv[1]); |
9288 | if (!pi) | ||
9218 | return EXIT_FAILURE; | 9289 | return EXIT_FAILURE; |
9219 | } | ||
9220 | for (pi = G.job_list; pi; pi = pi->next) { | ||
9221 | if (pi->jobid == jobnum) { | ||
9222 | goto found; | ||
9223 | } | ||
9224 | } | ||
9225 | bb_error_msg("%s: %d: no such job", argv[0], jobnum); | ||
9226 | return EXIT_FAILURE; | ||
9227 | found: | 9290 | found: |
9228 | /* TODO: bash prints a string representation | 9291 | /* TODO: bash prints a string representation |
9229 | * of job being foregrounded (like "sleep 1 | cat") */ | 9292 | * of job being foregrounded (like "sleep 1 | cat") */ |
@@ -9286,6 +9349,7 @@ static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM) | |||
9286 | struct pipe *job; | 9349 | struct pipe *job; |
9287 | const char *status_string; | 9350 | const char *status_string; |
9288 | 9351 | ||
9352 | checkjobs(NULL, 0 /*(no pid to wait for)*/); | ||
9289 | for (job = G.job_list; job; job = job->next) { | 9353 | for (job = G.job_list; job; job = job->next) { |
9290 | if (job->alive_cmds == job->stopped_cmds) | 9354 | if (job->alive_cmds == job->stopped_cmds) |
9291 | status_string = "Stopped"; | 9355 | status_string = "Stopped"; |
@@ -9438,12 +9502,18 @@ static int FAST_FUNC builtin_umask(char **argv) | |||
9438 | } | 9502 | } |
9439 | 9503 | ||
9440 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ | 9504 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ |
9441 | static int wait_for_child_or_signal(pid_t waitfor_pid) | 9505 | #if !ENABLE_HUSH_JOB |
9506 | # define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid) | ||
9507 | #endif | ||
9508 | static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid) | ||
9442 | { | 9509 | { |
9443 | int ret = 0; | 9510 | int ret = 0; |
9444 | for (;;) { | 9511 | for (;;) { |
9445 | int sig; | 9512 | int sig; |
9446 | sigset_t oldset, allsigs; | 9513 | sigset_t oldset; |
9514 | |||
9515 | if (!sigisemptyset(&G.pending_set)) | ||
9516 | goto check_sig; | ||
9447 | 9517 | ||
9448 | /* waitpid is not interruptible by SA_RESTARTed | 9518 | /* waitpid is not interruptible by SA_RESTARTed |
9449 | * signals which we use. Thus, this ugly dance: | 9519 | * signals which we use. Thus, this ugly dance: |
@@ -9452,43 +9522,51 @@ static int wait_for_child_or_signal(pid_t waitfor_pid) | |||
9452 | /* Make sure possible SIGCHLD is stored in kernel's | 9522 | /* Make sure possible SIGCHLD is stored in kernel's |
9453 | * pending signal mask before we call waitpid. | 9523 | * pending signal mask before we call waitpid. |
9454 | * Or else we may race with SIGCHLD, lose it, | 9524 | * Or else we may race with SIGCHLD, lose it, |
9455 | * and get stuck in sigwaitinfo... | 9525 | * and get stuck in sigsuspend... |
9456 | */ | 9526 | */ |
9457 | sigfillset(&allsigs); | 9527 | sigfillset(&oldset); /* block all signals, remember old set */ |
9458 | sigprocmask(SIG_SETMASK, &allsigs, &oldset); | 9528 | sigprocmask(SIG_SETMASK, &oldset, &oldset); |
9459 | 9529 | ||
9460 | if (!sigisemptyset(&G.pending_set)) { | 9530 | if (!sigisemptyset(&G.pending_set)) { |
9461 | /* Crap! we raced with some signal! */ | 9531 | /* Crap! we raced with some signal! */ |
9462 | // sig = 0; | ||
9463 | goto restore; | 9532 | goto restore; |
9464 | } | 9533 | } |
9465 | 9534 | ||
9466 | /*errno = 0; - checkjobs does this */ | 9535 | /*errno = 0; - checkjobs does this */ |
9536 | /* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */ | ||
9467 | ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */ | 9537 | ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */ |
9538 | debug_printf_exec("checkjobs:%d\n", ret); | ||
9539 | #if ENABLE_HUSH_JOB | ||
9540 | if (waitfor_pipe) { | ||
9541 | int rcode = job_exited_or_stopped(waitfor_pipe); | ||
9542 | debug_printf_exec("job_exited_or_stopped:%d\n", rcode); | ||
9543 | if (rcode >= 0) { | ||
9544 | ret = rcode; | ||
9545 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
9546 | break; | ||
9547 | } | ||
9548 | } | ||
9549 | #endif | ||
9468 | /* if ECHILD, there are no children (ret is -1 or 0) */ | 9550 | /* if ECHILD, there are no children (ret is -1 or 0) */ |
9469 | /* if ret == 0, no children changed state */ | 9551 | /* if ret == 0, no children changed state */ |
9470 | /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */ | 9552 | /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */ |
9471 | if (errno == ECHILD || ret--) { | 9553 | if (errno == ECHILD || ret) { |
9472 | if (ret < 0) /* if ECHILD, may need to fix */ | 9554 | ret--; |
9555 | if (ret < 0) /* if ECHILD, may need to fix "ret" */ | ||
9473 | ret = 0; | 9556 | ret = 0; |
9474 | sigprocmask(SIG_SETMASK, &oldset, NULL); | 9557 | sigprocmask(SIG_SETMASK, &oldset, NULL); |
9475 | break; | 9558 | break; |
9476 | } | 9559 | } |
9477 | |||
9478 | /* Wait for SIGCHLD or any other signal */ | 9560 | /* Wait for SIGCHLD or any other signal */ |
9479 | //sig = sigwaitinfo(&allsigs, NULL); | ||
9480 | /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */ | 9561 | /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */ |
9481 | /* Note: sigsuspend invokes signal handler */ | 9562 | /* Note: sigsuspend invokes signal handler */ |
9482 | sigsuspend(&oldset); | 9563 | sigsuspend(&oldset); |
9483 | restore: | 9564 | restore: |
9484 | sigprocmask(SIG_SETMASK, &oldset, NULL); | 9565 | sigprocmask(SIG_SETMASK, &oldset, NULL); |
9485 | 9566 | check_sig: | |
9486 | /* So, did we get a signal? */ | 9567 | /* So, did we get a signal? */ |
9487 | //if (sig > 0) | ||
9488 | // raise(sig); /* run handler */ | ||
9489 | sig = check_and_run_traps(); | 9568 | sig = check_and_run_traps(); |
9490 | if (sig /*&& sig != SIGCHLD - always true */) { | 9569 | if (sig /*&& sig != SIGCHLD - always true */) { |
9491 | /* see note 2 */ | ||
9492 | ret = 128 + sig; | 9570 | ret = 128 + sig; |
9493 | break; | 9571 | break; |
9494 | } | 9572 | } |
@@ -9520,18 +9598,30 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
9520 | * ^C <-- after ~4 sec from keyboard | 9598 | * ^C <-- after ~4 sec from keyboard |
9521 | * $ | 9599 | * $ |
9522 | */ | 9600 | */ |
9523 | return wait_for_child_or_signal(0 /*(no pid to wait for)*/); | 9601 | return wait_for_child_or_signal(NULL, 0 /*(no job and no pid to wait for)*/); |
9524 | } | 9602 | } |
9525 | 9603 | ||
9526 | /* TODO: support "wait %jobspec" */ | ||
9527 | do { | 9604 | do { |
9528 | pid_t pid = bb_strtou(*argv, NULL, 10); | 9605 | pid_t pid = bb_strtou(*argv, NULL, 10); |
9529 | if (errno || pid <= 0) { | 9606 | if (errno || pid <= 0) { |
9607 | #if ENABLE_HUSH_JOB | ||
9608 | if (argv[0][0] == '%') { | ||
9609 | struct pipe *wait_pipe; | ||
9610 | wait_pipe = parse_jobspec(*argv); | ||
9611 | if (wait_pipe) { | ||
9612 | ret = job_exited_or_stopped(wait_pipe); | ||
9613 | if (ret < 0) | ||
9614 | ret = wait_for_child_or_signal(wait_pipe, 0); | ||
9615 | continue; | ||
9616 | } | ||
9617 | } | ||
9618 | #endif | ||
9530 | /* mimic bash message */ | 9619 | /* mimic bash message */ |
9531 | bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); | 9620 | bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); |
9532 | ret = EXIT_FAILURE; | 9621 | ret = EXIT_FAILURE; |
9533 | continue; /* bash checks all argv[] */ | 9622 | continue; /* bash checks all argv[] */ |
9534 | } | 9623 | } |
9624 | |||
9535 | /* Do we have such child? */ | 9625 | /* Do we have such child? */ |
9536 | ret = waitpid(pid, &status, WNOHANG); | 9626 | ret = waitpid(pid, &status, WNOHANG); |
9537 | if (ret < 0) { | 9627 | if (ret < 0) { |
@@ -9541,12 +9631,13 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
9541 | /* "wait $!" but last bg task has already exited. Try: | 9631 | /* "wait $!" but last bg task has already exited. Try: |
9542 | * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $? | 9632 | * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $? |
9543 | * In bash it prints exitcode 0, then 3. | 9633 | * In bash it prints exitcode 0, then 3. |
9634 | * In dash, it is 127. | ||
9544 | */ | 9635 | */ |
9545 | ret = 0; /* FIXME */ | 9636 | /* ret = G.last_bg_pid_exitstatus - FIXME */ |
9546 | continue; | 9637 | } else { |
9638 | /* Example: "wait 1". mimic bash message */ | ||
9639 | bb_error_msg("wait: pid %d is not a child of this shell", (int)pid); | ||
9547 | } | 9640 | } |
9548 | /* Example: "wait 1". mimic bash message */ | ||
9549 | bb_error_msg("wait: pid %d is not a child of this shell", (int)pid); | ||
9550 | } else { | 9641 | } else { |
9551 | /* ??? */ | 9642 | /* ??? */ |
9552 | bb_perror_msg("wait %s", *argv); | 9643 | bb_perror_msg("wait %s", *argv); |
@@ -9556,7 +9647,7 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
9556 | } | 9647 | } |
9557 | if (ret == 0) { | 9648 | if (ret == 0) { |
9558 | /* Yes, and it still runs */ | 9649 | /* Yes, and it still runs */ |
9559 | ret = wait_for_child_or_signal(pid); | 9650 | ret = wait_for_child_or_signal(NULL, pid); |
9560 | } else { | 9651 | } else { |
9561 | /* Yes, and it just exited */ | 9652 | /* Yes, and it just exited */ |
9562 | process_wait_result(NULL, pid, status); | 9653 | process_wait_result(NULL, pid, status); |
diff --git a/shell/hush_test/hush-misc/group_in_braces.right b/shell/hush_test/hush-misc/group_in_braces.right new file mode 100644 index 000000000..a7064499b --- /dev/null +++ b/shell/hush_test/hush-misc/group_in_braces.right | |||
@@ -0,0 +1,5 @@ | |||
1 | Zero:0 | ||
2 | Zero:0 | ||
3 | Zero:0 | ||
4 | Zero:0 | ||
5 | Zero:0 | ||
diff --git a/shell/hush_test/hush-misc/group_in_braces.tests b/shell/hush_test/hush-misc/group_in_braces.tests new file mode 100755 index 000000000..f6571c35d --- /dev/null +++ b/shell/hush_test/hush-misc/group_in_braces.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | # Test cases where { cmd } does not require semicolon after "cmd" | ||
2 | (exit 2); { { true; } } | ||
3 | echo Zero:$? | ||
4 | (exit 2); {(true)} | ||
5 | echo Zero:$? | ||
6 | (exit 2); { true | { true; } } | ||
7 | echo Zero:$? | ||
8 | (exit 2); { while false; do :; done } | ||
9 | echo Zero:$? | ||
10 | (exit 2); { case a in b) ;; esac } | ||
11 | echo Zero:$? | ||
diff --git a/shell/hush_test/hush-misc/wait4.right b/shell/hush_test/hush-misc/wait4.right new file mode 100644 index 000000000..f7987db32 --- /dev/null +++ b/shell/hush_test/hush-misc/wait4.right | |||
@@ -0,0 +1 @@ | |||
Three:3 | |||
diff --git a/shell/hush_test/hush-misc/wait4.tests b/shell/hush_test/hush-misc/wait4.tests new file mode 100755 index 000000000..cc34059ac --- /dev/null +++ b/shell/hush_test/hush-misc/wait4.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | sleep 1 | (sleep 1;exit 3) & wait %1 | ||
2 | echo Three:$? | ||
diff --git a/shell/hush_test/hush-misc/wait5.right b/shell/hush_test/hush-misc/wait5.right new file mode 100644 index 000000000..82c9d5696 --- /dev/null +++ b/shell/hush_test/hush-misc/wait5.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Zero:0 | ||
2 | Three:3 | ||
diff --git a/shell/hush_test/hush-misc/wait5.tests b/shell/hush_test/hush-misc/wait5.tests new file mode 100755 index 000000000..1b4762d89 --- /dev/null +++ b/shell/hush_test/hush-misc/wait5.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | sleep 0 | (sleep 0;exit 3) & | ||
2 | sleep 1 | ||
3 | echo Zero:$? | ||
4 | wait %1 | ||
5 | echo Three:$? | ||
diff --git a/testsuite/cpio.tests b/testsuite/cpio.tests index 4cd441a60..d44c95b10 100755 --- a/testsuite/cpio.tests +++ b/testsuite/cpio.tests | |||
@@ -127,6 +127,30 @@ testing "cpio extracts in existing directory" \ | |||
127 | " "" "" | 127 | " "" "" |
128 | SKIP= | 128 | SKIP= |
129 | 129 | ||
130 | testing "cpio uses by default uid/gid" \ | ||
131 | "echo $0 | cpio -o -H newc | cpio -tv 2>&1 | tail -n +2 | awk ' { print \$2 } '; echo \$?" \ | ||
132 | "\ | ||
133 | $user/$group | ||
134 | 0 | ||
135 | " "" "" | ||
136 | SKIP= | ||
137 | |||
138 | testing "cpio -R with create" \ | ||
139 | "echo $0 | cpio -o -H newc -R 1234:5678 | cpio -tv 2>&1 | tail -n +2 | awk ' { print \$2 } '; echo \$?" \ | ||
140 | "\ | ||
141 | 1234/5678 | ||
142 | 0 | ||
143 | " "" "" | ||
144 | SKIP= | ||
145 | |||
146 | testing "cpio -R with extract" \ | ||
147 | "echo $0 | cpio -o -H newc | cpio -tv -R 8765:4321 2>&1 | tail -n +2 | awk ' { print \$2 } '; echo \$?" \ | ||
148 | "\ | ||
149 | 8765/4321 | ||
150 | 0 | ||
151 | " "" "" | ||
152 | SKIP= | ||
153 | |||
130 | # Clean up | 154 | # Clean up |
131 | rm -rf cpio.testdir cpio.testdir2 2>/dev/null | 155 | rm -rf cpio.testdir cpio.testdir2 2>/dev/null |
132 | 156 | ||