aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--console-tools/loadfont.c6
-rw-r--r--include/libbb.h2
-rw-r--r--libbb/common_bufsiz.c9
-rw-r--r--libbb/executable.c2
-rw-r--r--libbb/run_shell.c29
-rw-r--r--loginutils/README2
-rw-r--r--loginutils/login.c2
-rw-r--r--loginutils/su.c33
-rw-r--r--loginutils/sulogin.c2
-rw-r--r--shell/ash.c34
-rw-r--r--shell/ash_test/ash-misc/group_in_braces.right5
-rwxr-xr-xshell/ash_test/ash-misc/group_in_braces.tests11
-rw-r--r--shell/ash_test/ash-misc/wait4.right1
-rwxr-xr-xshell/ash_test/ash-misc/wait4.tests2
-rw-r--r--shell/ash_test/ash-misc/wait5.right2
-rwxr-xr-xshell/ash_test/ash-misc/wait5.tests5
-rw-r--r--shell/hush.c411
-rw-r--r--shell/hush_test/hush-misc/group_in_braces.right5
-rwxr-xr-xshell/hush_test/hush-misc/group_in_braces.tests11
-rw-r--r--shell/hush_test/hush-misc/wait4.right1
-rwxr-xr-xshell/hush_test/hush-misc/wait4.tests2
-rw-r--r--shell/hush_test/hush-misc/wait5.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait5.tests5
-rwxr-xr-xtestsuite/cpio.tests24
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
1361extern void bb_do_delay(int seconds) FAST_FUNC; 1361extern void bb_do_delay(int seconds) FAST_FUNC;
1362extern void change_identity(const struct passwd *pw) FAST_FUNC; 1362extern void change_identity(const struct passwd *pw) FAST_FUNC;
1363extern void run_shell(const char *shell, int loginshell, const char *command, const char **additional_args) NORETURN FAST_FUNC; 1363extern 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. */ 55void FAST_FUNC run_shell(const char *shell, int loginshell, const char **additional_args)
56void 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
23that tty is a ctty. 23that 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
26fds to this tty be dusconnected? agetty has a -R option which makes 26fds to this tty be disconnected? agetty has a -R option which makes
27agetty call vhangup() after tty is opened. (Then agetty opens it again, 27agetty call vhangup() after tty is opened. (Then agetty opens it again,
28since it probably vhangup'ed its own fd too). 28since 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
587static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void 603static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
588force_int_on(void) 604force_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 @@
1Zero:0
2Zero:0
3Zero:0
4Zero:0
5Zero: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; } }
3echo Zero:$?
4(exit 2); {(true)}
5echo Zero:$?
6(exit 2); { true | { true; } }
7echo Zero:$?
8(exit 2); { while false; do :; done }
9echo Zero:$?
10(exit 2); { case a in b) ;; esac }
11echo 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 @@
1sleep 1 | (sleep 1;exit 3) & wait %1
2echo 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 @@
1Zero:0
2Three: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 @@
1sleep 0 | (sleep 0;exit 3) &
2sleep 1
3echo Zero:$?
4wait %1
5echo 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};
591typedef enum pipe_style { 586typedef 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
2263static int FAST_FUNC file_get(struct in_str *i) 2258static 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
2292static int FAST_FUNC file_peek(struct in_str *i) 2300static 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
2322static 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
2333static 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)
2371static void setup_file_in_str(struct in_str *i, FILE *f) 2370static 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)
2381static void setup_string_in_str(struct in_str *i, const char *s) 2378static 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
7061static 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
7046static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) 7082static 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 */
7170static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) 7208static 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
9249static 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 */
9198static int FAST_FUNC builtin_fg_bg(char **argv) 9268static 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 */
9441static 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
9508static 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 @@
1Zero:0
2Zero:0
3Zero:0
4Zero:0
5Zero: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; } }
3echo Zero:$?
4(exit 2); {(true)}
5echo Zero:$?
6(exit 2); { true | { true; } }
7echo Zero:$?
8(exit 2); { while false; do :; done }
9echo Zero:$?
10(exit 2); { case a in b) ;; esac }
11echo 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 @@
1sleep 1 | (sleep 1;exit 3) & wait %1
2echo 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 @@
1Zero:0
2Three: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 @@
1sleep 0 | (sleep 0;exit 3) &
2sleep 1
3echo Zero:$?
4wait %1
5echo 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" "" ""
128SKIP= 128SKIP=
129 129
130testing "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
1340
135" "" ""
136SKIP=
137
138testing "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"\
1411234/5678
1420
143" "" ""
144SKIP=
145
146testing "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"\
1498765/4321
1500
151" "" ""
152SKIP=
153
130# Clean up 154# Clean up
131rm -rf cpio.testdir cpio.testdir2 2>/dev/null 155rm -rf cpio.testdir cpio.testdir2 2>/dev/null
132 156