aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-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
14 files changed, 329 insertions, 168 deletions
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:$?