diff options
author | Ron Yorston <rmy@pobox.com> | 2016-11-02 10:19:18 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2016-11-02 10:19:18 +0000 |
commit | e45ff573dc9707b1ea71f490ef16cd9d08feaaf2 (patch) | |
tree | 09980b02b9c8efc8d772c7379abdcd4eef4ddad8 /shell | |
parent | 7bc3efa4f0c7608723d301bda5ee006a0f6141fe (diff) | |
parent | 2e6af549715f5d7b4c2ab204e46c8b8f6f057045 (diff) | |
download | busybox-w32-e45ff573dc9707b1ea71f490ef16cd9d08feaaf2.tar.gz busybox-w32-e45ff573dc9707b1ea71f490ef16cd9d08feaaf2.tar.bz2 busybox-w32-e45ff573dc9707b1ea71f490ef16cd9d08feaaf2.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
-rw-r--r-- | shell/ash.c | 122 | ||||
-rw-r--r-- | shell/ash_test/ash-vars/readonly1.right | 2 | ||||
-rwxr-xr-x | shell/ash_test/ash-vars/readonly1.tests | 7 | ||||
-rw-r--r-- | shell/hush.c | 418 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/wait1.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/wait1.tests | 3 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/wait2.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/wait2.tests | 4 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/wait3.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/wait3.tests | 3 |
10 files changed, 348 insertions, 217 deletions
diff --git a/shell/ash.c b/shell/ash.c index dd320355e..a709c3602 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -362,8 +362,7 @@ struct globals_misc { | |||
362 | volatile int suppress_int; /* counter */ | 362 | volatile int suppress_int; /* counter */ |
363 | volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ | 363 | volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ |
364 | volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ | 364 | volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ |
365 | /* last pending signal */ | 365 | volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ |
366 | volatile /*sig_atomic_t*/ smallint pending_sig; | ||
367 | smallint exception_type; /* kind of exception (0..5) */ | 366 | smallint exception_type; /* kind of exception (0..5) */ |
368 | /* exceptions */ | 367 | /* exceptions */ |
369 | #define EXINT 0 /* SIGINT received */ | 368 | #define EXINT 0 /* SIGINT received */ |
@@ -915,13 +914,8 @@ trace_vprintf(const char *fmt, va_list va) | |||
915 | { | 914 | { |
916 | if (debug != 1) | 915 | if (debug != 1) |
917 | return; | 916 | return; |
918 | if (DEBUG_TIME) | ||
919 | fprintf(tracefile, "%u ", (int) time(NULL)); | ||
920 | if (DEBUG_PID) | ||
921 | fprintf(tracefile, "[%u] ", (int) getpid()); | ||
922 | if (DEBUG_SIG) | ||
923 | fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int); | ||
924 | vfprintf(tracefile, fmt, va); | 917 | vfprintf(tracefile, fmt, va); |
918 | fprintf(tracefile, "\n"); | ||
925 | } | 919 | } |
926 | 920 | ||
927 | static void | 921 | static void |
@@ -1314,11 +1308,10 @@ ash_vmsg_and_raise(int cond, const char *msg, va_list ap) | |||
1314 | { | 1308 | { |
1315 | #if DEBUG | 1309 | #if DEBUG |
1316 | if (msg) { | 1310 | if (msg) { |
1317 | TRACE(("ash_vmsg_and_raise(%d, \"", cond)); | 1311 | TRACE(("ash_vmsg_and_raise(%d):", cond)); |
1318 | TRACEV((msg, ap)); | 1312 | TRACEV((msg, ap)); |
1319 | TRACE(("\") pid=%d\n", getpid())); | ||
1320 | } else | 1313 | } else |
1321 | TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid())); | 1314 | TRACE(("ash_vmsg_and_raise(%d):NULL\n", cond)); |
1322 | if (msg) | 1315 | if (msg) |
1323 | #endif | 1316 | #endif |
1324 | ash_vmsg(msg, ap); | 1317 | ash_vmsg(msg, ap); |
@@ -2250,6 +2243,7 @@ setvareq(char *s, int flags) | |||
2250 | if (flags & VNOSAVE) | 2243 | if (flags & VNOSAVE) |
2251 | free(s); | 2244 | free(s); |
2252 | n = vp->var_text; | 2245 | n = vp->var_text; |
2246 | exitstatus = 1; | ||
2253 | ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n); | 2247 | ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n); |
2254 | } | 2248 | } |
2255 | 2249 | ||
@@ -3715,11 +3709,6 @@ setsignal(int signo) | |||
3715 | #define CUR_RUNNING 1 | 3709 | #define CUR_RUNNING 1 |
3716 | #define CUR_STOPPED 0 | 3710 | #define CUR_STOPPED 0 |
3717 | 3711 | ||
3718 | /* mode flags for dowait */ | ||
3719 | #define DOWAIT_NONBLOCK 0 | ||
3720 | #define DOWAIT_BLOCK 1 | ||
3721 | #define DOWAIT_BLOCK_OR_SIG 2 | ||
3722 | |||
3723 | #if JOBS | 3712 | #if JOBS |
3724 | /* pgrp of shell on invocation */ | 3713 | /* pgrp of shell on invocation */ |
3725 | static int initialpgrp; //references:2 | 3714 | static int initialpgrp; //references:2 |
@@ -4231,24 +4220,30 @@ waitpid_child(int *status, int wait_flags) | |||
4231 | #else | 4220 | #else |
4232 | 4221 | ||
4233 | static int | 4222 | static int |
4234 | wait_block_or_sig(int *status, int wait_flags) | 4223 | wait_block_or_sig(int *status) |
4235 | { | 4224 | { |
4236 | sigset_t mask; | ||
4237 | int pid; | 4225 | int pid; |
4238 | 4226 | ||
4239 | do { | 4227 | do { |
4240 | /* Poll all children for changes in their state */ | 4228 | /* Poll all children for changes in their state */ |
4241 | got_sigchld = 0; | 4229 | got_sigchld = 0; |
4242 | pid = waitpid(-1, status, wait_flags | WNOHANG); | 4230 | /* if job control is active, accept stopped processes too */ |
4231 | pid = waitpid(-1, status, doing_jobctl ? (WNOHANG|WUNTRACED) : WNOHANG); | ||
4243 | if (pid != 0) | 4232 | if (pid != 0) |
4244 | break; /* Error (e.g. EINTR) or pid */ | 4233 | break; /* Error (e.g. EINTR, ECHILD) or pid */ |
4245 | 4234 | ||
4246 | /* No child is ready. Sleep until interesting signal is received */ | 4235 | /* Children exist, but none are ready. Sleep until interesting signal */ |
4236 | #if 0 /* dash does this */ | ||
4237 | sigset_t mask; | ||
4247 | sigfillset(&mask); | 4238 | sigfillset(&mask); |
4248 | sigprocmask(SIG_SETMASK, &mask, &mask); | 4239 | sigprocmask(SIG_SETMASK, &mask, &mask); |
4249 | while (!got_sigchld && !pending_sig) | 4240 | while (!got_sigchld && !pending_sig) |
4250 | sigsuspend(&mask); | 4241 | sigsuspend(&mask); |
4251 | sigprocmask(SIG_SETMASK, &mask, NULL); | 4242 | sigprocmask(SIG_SETMASK, &mask, NULL); |
4243 | #else | ||
4244 | while (!got_sigchld && !pending_sig) | ||
4245 | pause(); | ||
4246 | #endif | ||
4252 | 4247 | ||
4253 | /* If it was SIGCHLD, poll children again */ | 4248 | /* If it was SIGCHLD, poll children again */ |
4254 | } while (got_sigchld); | 4249 | } while (got_sigchld); |
@@ -4257,11 +4252,13 @@ wait_block_or_sig(int *status, int wait_flags) | |||
4257 | } | 4252 | } |
4258 | #endif | 4253 | #endif |
4259 | 4254 | ||
4255 | #define DOWAIT_NONBLOCK 0 | ||
4256 | #define DOWAIT_BLOCK 1 | ||
4257 | #define DOWAIT_BLOCK_OR_SIG 2 | ||
4260 | 4258 | ||
4261 | static int | 4259 | static int |
4262 | dowait(int block, struct job *job) | 4260 | dowait(int block, struct job *job) |
4263 | { | 4261 | { |
4264 | int wait_flags; | ||
4265 | int pid; | 4262 | int pid; |
4266 | int status; | 4263 | int status; |
4267 | struct job *jp; | 4264 | struct job *jp; |
@@ -4269,13 +4266,6 @@ dowait(int block, struct job *job) | |||
4269 | 4266 | ||
4270 | TRACE(("dowait(0x%x) called\n", block)); | 4267 | TRACE(("dowait(0x%x) called\n", block)); |
4271 | 4268 | ||
4272 | wait_flags = 0; | ||
4273 | if (block == DOWAIT_NONBLOCK) | ||
4274 | wait_flags = WNOHANG; | ||
4275 | /* If job control is compiled in, we accept stopped processes too. */ | ||
4276 | if (doing_jobctl) | ||
4277 | wait_flags |= WUNTRACED; | ||
4278 | |||
4279 | /* It's wrong to call waitpid() outside of INT_OFF region: | 4269 | /* It's wrong to call waitpid() outside of INT_OFF region: |
4280 | * signal can arrive just after syscall return and handler can | 4270 | * signal can arrive just after syscall return and handler can |
4281 | * longjmp away, losing stop/exit notification processing. | 4271 | * longjmp away, losing stop/exit notification processing. |
@@ -4286,17 +4276,27 @@ dowait(int block, struct job *job) | |||
4286 | * in INT_OFF region: "wait" needs to wait for any running job | 4276 | * in INT_OFF region: "wait" needs to wait for any running job |
4287 | * to change state, but should exit on any trap too. | 4277 | * to change state, but should exit on any trap too. |
4288 | * In INT_OFF region, a signal just before syscall entry can set | 4278 | * In INT_OFF region, a signal just before syscall entry can set |
4289 | * pending_sig valiables, but we can't check them, and we would | 4279 | * pending_sig variables, but we can't check them, and we would |
4290 | * either enter a sleeping waitpid() (BUG), or need to busy-loop. | 4280 | * either enter a sleeping waitpid() (BUG), or need to busy-loop. |
4281 | * | ||
4291 | * Because of this, we run inside INT_OFF, but use a special routine | 4282 | * Because of this, we run inside INT_OFF, but use a special routine |
4292 | * which combines waitpid() and sigsuspend(). | 4283 | * which combines waitpid() and pause(). |
4284 | * This is the reason why we need to have a handler for SIGCHLD: | ||
4285 | * SIG_DFL handler does not wake pause(). | ||
4293 | */ | 4286 | */ |
4294 | INT_OFF; | 4287 | INT_OFF; |
4295 | if (block == DOWAIT_BLOCK_OR_SIG) | 4288 | if (block == DOWAIT_BLOCK_OR_SIG) { |
4296 | pid = wait_block_or_sig(&status, wait_flags); | 4289 | pid = wait_block_or_sig(&status); |
4297 | else | 4290 | } else { |
4298 | /* NB: _not_ safe_waitpid, we need to detect EINTR. */ | 4291 | int wait_flags = 0; |
4292 | if (block == DOWAIT_NONBLOCK) | ||
4293 | wait_flags = WNOHANG; | ||
4294 | /* if job control is active, accept stopped processes too */ | ||
4295 | if (doing_jobctl) | ||
4296 | wait_flags |= WUNTRACED; | ||
4297 | /* NB: _not_ safe_waitpid, we need to detect EINTR */ | ||
4299 | pid = waitpid(-1, &status, wait_flags); | 4298 | pid = waitpid(-1, &status, wait_flags); |
4299 | } | ||
4300 | TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", | 4300 | TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", |
4301 | pid, status, errno, strerror(errno))); | 4301 | pid, status, errno, strerror(errno))); |
4302 | if (pid <= 0) | 4302 | if (pid <= 0) |
@@ -7410,6 +7410,21 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
7410 | 7410 | ||
7411 | if (fflag) | 7411 | if (fflag) |
7412 | goto nometa; | 7412 | goto nometa; |
7413 | |||
7414 | /* Avoid glob() (and thus, stat() et al) for words like "echo" */ | ||
7415 | p = str->text; | ||
7416 | while (*p) { | ||
7417 | if (*p == '*') | ||
7418 | goto need_glob; | ||
7419 | if (*p == '?') | ||
7420 | goto need_glob; | ||
7421 | if (*p == '[') | ||
7422 | goto need_glob; | ||
7423 | p++; | ||
7424 | } | ||
7425 | goto nometa; | ||
7426 | |||
7427 | need_glob: | ||
7413 | INT_OFF; | 7428 | INT_OFF; |
7414 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); | 7429 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); |
7415 | // GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match | 7430 | // GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match |
@@ -10064,7 +10079,7 @@ evalcommand(union node *cmd, int flags) | |||
10064 | if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) { | 10079 | if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) { |
10065 | if (exception_type == EXERROR && spclbltin <= 0) { | 10080 | if (exception_type == EXERROR && spclbltin <= 0) { |
10066 | FORCE_INT_ON; | 10081 | FORCE_INT_ON; |
10067 | break; | 10082 | goto readstatus; |
10068 | } | 10083 | } |
10069 | raise: | 10084 | raise: |
10070 | longjmp(exception_handler->loc, 1); | 10085 | longjmp(exception_handler->loc, 1); |
@@ -10584,6 +10599,9 @@ popfile(void) | |||
10584 | { | 10599 | { |
10585 | struct parsefile *pf = g_parsefile; | 10600 | struct parsefile *pf = g_parsefile; |
10586 | 10601 | ||
10602 | if (pf == &basepf) | ||
10603 | return; | ||
10604 | |||
10587 | INT_OFF; | 10605 | INT_OFF; |
10588 | if (pf->pf_fd >= 0) | 10606 | if (pf->pf_fd >= 0) |
10589 | close(pf->pf_fd); | 10607 | close(pf->pf_fd); |
@@ -12747,6 +12765,10 @@ expandstr(const char *ps) | |||
12747 | static int | 12765 | static int |
12748 | evalstring(char *s, int flags) | 12766 | evalstring(char *s, int flags) |
12749 | { | 12767 | { |
12768 | struct jmploc *volatile savehandler; | ||
12769 | struct jmploc jmploc; | ||
12770 | int ex; | ||
12771 | |||
12750 | union node *n; | 12772 | union node *n; |
12751 | struct stackmark smark; | 12773 | struct stackmark smark; |
12752 | int status; | 12774 | int status; |
@@ -12756,6 +12778,19 @@ evalstring(char *s, int flags) | |||
12756 | setstackmark(&smark); | 12778 | setstackmark(&smark); |
12757 | 12779 | ||
12758 | status = 0; | 12780 | status = 0; |
12781 | /* On exception inside execution loop, we must popfile(). | ||
12782 | * Try interactively: | ||
12783 | * readonly a=a | ||
12784 | * command eval "a=b" # throws "is read only" error | ||
12785 | * "command BLTIN" is not supposed to abort (even in non-interactive use). | ||
12786 | * But if we skip popfile(), we hit EOF in eval's string, and exit. | ||
12787 | */ | ||
12788 | savehandler = exception_handler; | ||
12789 | ex = setjmp(jmploc.loc); | ||
12790 | if (ex) | ||
12791 | goto out; | ||
12792 | exception_handler = &jmploc; | ||
12793 | |||
12759 | while ((n = parsecmd(0)) != NODE_EOF) { | 12794 | while ((n = parsecmd(0)) != NODE_EOF) { |
12760 | int i; | 12795 | int i; |
12761 | 12796 | ||
@@ -12766,10 +12801,15 @@ evalstring(char *s, int flags) | |||
12766 | if (evalskip) | 12801 | if (evalskip) |
12767 | break; | 12802 | break; |
12768 | } | 12803 | } |
12804 | out: | ||
12769 | popstackmark(&smark); | 12805 | popstackmark(&smark); |
12770 | popfile(); | 12806 | popfile(); |
12771 | stunalloc(s); | 12807 | stunalloc(s); |
12772 | 12808 | ||
12809 | exception_handler = savehandler; | ||
12810 | if (ex) | ||
12811 | longjmp(exception_handler->loc, ex); | ||
12812 | |||
12773 | return status; | 12813 | return status; |
12774 | } | 12814 | } |
12775 | 12815 | ||
@@ -13982,11 +14022,6 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
13982 | goto state4; | 14022 | goto state4; |
13983 | } | 14023 | } |
13984 | exception_handler = &jmploc; | 14024 | exception_handler = &jmploc; |
13985 | #if DEBUG | ||
13986 | opentrace(); | ||
13987 | TRACE(("Shell args: ")); | ||
13988 | trace_puts_args(argv); | ||
13989 | #endif | ||
13990 | rootpid = getpid(); | 14025 | rootpid = getpid(); |
13991 | 14026 | ||
13992 | init(IF_PLATFORM_MINGW32(argc >= 2 && strcmp(argv[1], "-X") == 0)); | 14027 | init(IF_PLATFORM_MINGW32(argc >= 2 && strcmp(argv[1], "-X") == 0)); |
@@ -14003,6 +14038,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
14003 | } | 14038 | } |
14004 | #endif | 14039 | #endif |
14005 | procargs(argv); | 14040 | procargs(argv); |
14041 | #if DEBUG | ||
14042 | TRACE(("Shell args: ")); | ||
14043 | trace_puts_args(argv); | ||
14044 | #endif | ||
14045 | |||
14006 | #if ENABLE_PLATFORM_MINGW32 | 14046 | #if ENABLE_PLATFORM_MINGW32 |
14007 | if ( noconsole ) { | 14047 | if ( noconsole ) { |
14008 | DWORD dummy; | 14048 | DWORD dummy; |
diff --git a/shell/ash_test/ash-vars/readonly1.right b/shell/ash_test/ash-vars/readonly1.right new file mode 100644 index 000000000..2b363e325 --- /dev/null +++ b/shell/ash_test/ash-vars/readonly1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | One:1 | ||
2 | One:1 | ||
diff --git a/shell/ash_test/ash-vars/readonly1.tests b/shell/ash_test/ash-vars/readonly1.tests new file mode 100755 index 000000000..81b461f5f --- /dev/null +++ b/shell/ash_test/ash-vars/readonly1.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | readonly bla=123 | ||
2 | # Bare "eval bla=123" should abort ("eval" is a special builtin): | ||
3 | (eval bla=123 2>/dev/null; echo BUG) | ||
4 | echo One:$? | ||
5 | # "command BLTIN" disables "special-ness", should not abort: | ||
6 | command eval bla=123 2>/dev/null | ||
7 | echo One:$? | ||
diff --git a/shell/hush.c b/shell/hush.c index c80429d5c..7c2f157b8 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -44,6 +44,8 @@ | |||
44 | * special variables (done: PWD, PPID, RANDOM) | 44 | * special variables (done: PWD, PPID, RANDOM) |
45 | * tilde expansion | 45 | * tilde expansion |
46 | * aliases | 46 | * aliases |
47 | * kill %jobspec | ||
48 | * wait %jobspec | ||
47 | * follow IFS rules more precisely, including update semantics | 49 | * follow IFS rules more precisely, including update semantics |
48 | * builtins mandated by standards we don't support: | 50 | * builtins mandated by standards we don't support: |
49 | * [un]alias, command, fc, getopts, newgrp, readonly, times | 51 | * [un]alias, command, fc, getopts, newgrp, readonly, times |
@@ -7041,16 +7043,134 @@ static void delete_finished_bg_job(struct pipe *pi) | |||
7041 | } | 7043 | } |
7042 | #endif /* JOB */ | 7044 | #endif /* JOB */ |
7043 | 7045 | ||
7044 | /* Check to see if any processes have exited -- if they | 7046 | static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) |
7045 | * have, figure out why and see if a job has completed */ | ||
7046 | static int checkjobs(struct pipe *fg_pipe) | ||
7047 | { | 7047 | { |
7048 | int attributes; | ||
7049 | int status; | ||
7050 | #if ENABLE_HUSH_JOB | 7048 | #if ENABLE_HUSH_JOB |
7051 | struct pipe *pi; | 7049 | struct pipe *pi; |
7052 | #endif | 7050 | #endif |
7053 | pid_t childpid; | 7051 | int i, dead; |
7052 | |||
7053 | dead = WIFEXITED(status) || WIFSIGNALED(status); | ||
7054 | |||
7055 | #if DEBUG_JOBS | ||
7056 | if (WIFSTOPPED(status)) | ||
7057 | debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n", | ||
7058 | childpid, WSTOPSIG(status), WEXITSTATUS(status)); | ||
7059 | if (WIFSIGNALED(status)) | ||
7060 | debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n", | ||
7061 | childpid, WTERMSIG(status), WEXITSTATUS(status)); | ||
7062 | if (WIFEXITED(status)) | ||
7063 | debug_printf_jobs("pid %d exited, exitcode %d\n", | ||
7064 | childpid, WEXITSTATUS(status)); | ||
7065 | #endif | ||
7066 | /* Were we asked to wait for a fg pipe? */ | ||
7067 | if (fg_pipe) { | ||
7068 | i = fg_pipe->num_cmds; | ||
7069 | while (--i >= 0) { | ||
7070 | debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid); | ||
7071 | if (fg_pipe->cmds[i].pid != childpid) | ||
7072 | continue; | ||
7073 | if (dead) { | ||
7074 | int ex; | ||
7075 | fg_pipe->cmds[i].pid = 0; | ||
7076 | fg_pipe->alive_cmds--; | ||
7077 | ex = WEXITSTATUS(status); | ||
7078 | /* bash prints killer signal's name for *last* | ||
7079 | * process in pipe (prints just newline for SIGINT/SIGPIPE). | ||
7080 | * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) | ||
7081 | */ | ||
7082 | if (WIFSIGNALED(status)) { | ||
7083 | int sig = WTERMSIG(status); | ||
7084 | if (i == fg_pipe->num_cmds-1) | ||
7085 | /* TODO: use strsignal() instead for bash compat? but that's bloat... */ | ||
7086 | puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig)); | ||
7087 | /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ | ||
7088 | /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? | ||
7089 | * Maybe we need to use sig | 128? */ | ||
7090 | ex = sig + 128; | ||
7091 | } | ||
7092 | fg_pipe->cmds[i].cmd_exitcode = ex; | ||
7093 | } else { | ||
7094 | fg_pipe->stopped_cmds++; | ||
7095 | } | ||
7096 | debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n", | ||
7097 | fg_pipe->alive_cmds, fg_pipe->stopped_cmds); | ||
7098 | if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) { | ||
7099 | /* All processes in fg pipe have exited or stopped */ | ||
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 | ||
7111 | * are stopped. Testcase: "cat | cat" in a script (not on command line!) | ||
7112 | * and "killall -STOP cat" */ | ||
7113 | if (G_interactive_fd) { | ||
7114 | #if ENABLE_HUSH_JOB | ||
7115 | if (fg_pipe->alive_cmds != 0) | ||
7116 | insert_bg_job(fg_pipe); | ||
7117 | #endif | ||
7118 | return rcode; | ||
7119 | } | ||
7120 | if (fg_pipe->alive_cmds == 0) | ||
7121 | return rcode; | ||
7122 | } | ||
7123 | /* There are still running processes in the fg_pipe */ | ||
7124 | return -1; | ||
7125 | } | ||
7126 | /* It wasnt in fg_pipe, look for process in bg pipes */ | ||
7127 | } | ||
7128 | |||
7129 | #if ENABLE_HUSH_JOB | ||
7130 | /* We were asked to wait for bg or orphaned children */ | ||
7131 | /* No need to remember exitcode in this case */ | ||
7132 | for (pi = G.job_list; pi; pi = pi->next) { | ||
7133 | for (i = 0; i < pi->num_cmds; i++) { | ||
7134 | if (pi->cmds[i].pid == childpid) | ||
7135 | goto found_pi_and_prognum; | ||
7136 | } | ||
7137 | } | ||
7138 | /* Happens when shell is used as init process (init=/bin/sh) */ | ||
7139 | debug_printf("checkjobs: pid %d was not in our list!\n", childpid); | ||
7140 | return -1; /* this wasn't a process from fg_pipe */ | ||
7141 | |||
7142 | found_pi_and_prognum: | ||
7143 | if (dead) { | ||
7144 | /* child exited */ | ||
7145 | pi->cmds[i].pid = 0; | ||
7146 | pi->cmds[i].cmd_exitcode = WEXITSTATUS(status); | ||
7147 | if (WIFSIGNALED(status)) | ||
7148 | pi->cmds[i].cmd_exitcode = 128 + WTERMSIG(status); | ||
7149 | pi->alive_cmds--; | ||
7150 | if (!pi->alive_cmds) { | ||
7151 | if (G_interactive_fd) | ||
7152 | printf(JOB_STATUS_FORMAT, pi->jobid, | ||
7153 | "Done", pi->cmdtext); | ||
7154 | delete_finished_bg_job(pi); | ||
7155 | } | ||
7156 | } else { | ||
7157 | /* child stopped */ | ||
7158 | pi->stopped_cmds++; | ||
7159 | } | ||
7160 | #endif | ||
7161 | return -1; /* this wasn't a process from fg_pipe */ | ||
7162 | } | ||
7163 | |||
7164 | /* Check to see if any processes have exited -- if they have, | ||
7165 | * figure out why and see if a job has completed. | ||
7166 | * Alternatively (fg_pipe == NULL, waitfor_pid != 0), | ||
7167 | * wait for a specific pid to complete, return exitcode+1 | ||
7168 | * (this allows to distinguish zero as "no children exited" result). | ||
7169 | */ | ||
7170 | static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) | ||
7171 | { | ||
7172 | int attributes; | ||
7173 | int status; | ||
7054 | int rcode = 0; | 7174 | int rcode = 0; |
7055 | 7175 | ||
7056 | debug_printf_jobs("checkjobs %p\n", fg_pipe); | 7176 | debug_printf_jobs("checkjobs %p\n", fg_pipe); |
@@ -7087,12 +7207,10 @@ static int checkjobs(struct pipe *fg_pipe) | |||
7087 | * 1 <========== bg pipe is not fully done, but exitcode is already known! | 7207 | * 1 <========== bg pipe is not fully done, but exitcode is already known! |
7088 | * [hush 1.14.0: yes we do it right] | 7208 | * [hush 1.14.0: yes we do it right] |
7089 | */ | 7209 | */ |
7090 | wait_more: | ||
7091 | while (1) { | 7210 | while (1) { |
7092 | int i; | 7211 | pid_t childpid; |
7093 | int dead; | ||
7094 | |||
7095 | #if ENABLE_HUSH_FAST | 7212 | #if ENABLE_HUSH_FAST |
7213 | int i; | ||
7096 | i = G.count_SIGCHLD; | 7214 | i = G.count_SIGCHLD; |
7097 | #endif | 7215 | #endif |
7098 | childpid = waitpid(-1, &status, attributes); | 7216 | childpid = waitpid(-1, &status, attributes); |
@@ -7106,112 +7224,24 @@ static int checkjobs(struct pipe *fg_pipe) | |||
7106 | //bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | 7224 | //bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); |
7107 | } | 7225 | } |
7108 | #endif | 7226 | #endif |
7227 | /* ECHILD (no children), or 0 (no change in children status) */ | ||
7228 | rcode = childpid; | ||
7109 | break; | 7229 | break; |
7110 | } | 7230 | } |
7111 | dead = WIFEXITED(status) || WIFSIGNALED(status); | 7231 | rcode = process_wait_result(fg_pipe, childpid, status); |
7112 | 7232 | if (rcode >= 0) { | |
7113 | #if DEBUG_JOBS | 7233 | /* fg_pipe exited or stopped */ |
7114 | if (WIFSTOPPED(status)) | 7234 | break; |
7115 | debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n", | ||
7116 | childpid, WSTOPSIG(status), WEXITSTATUS(status)); | ||
7117 | if (WIFSIGNALED(status)) | ||
7118 | debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n", | ||
7119 | childpid, WTERMSIG(status), WEXITSTATUS(status)); | ||
7120 | if (WIFEXITED(status)) | ||
7121 | debug_printf_jobs("pid %d exited, exitcode %d\n", | ||
7122 | childpid, WEXITSTATUS(status)); | ||
7123 | #endif | ||
7124 | /* Were we asked to wait for fg pipe? */ | ||
7125 | if (fg_pipe) { | ||
7126 | i = fg_pipe->num_cmds; | ||
7127 | while (--i >= 0) { | ||
7128 | debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid); | ||
7129 | if (fg_pipe->cmds[i].pid != childpid) | ||
7130 | continue; | ||
7131 | if (dead) { | ||
7132 | int ex; | ||
7133 | fg_pipe->cmds[i].pid = 0; | ||
7134 | fg_pipe->alive_cmds--; | ||
7135 | ex = WEXITSTATUS(status); | ||
7136 | /* bash prints killer signal's name for *last* | ||
7137 | * process in pipe (prints just newline for SIGINT/SIGPIPE). | ||
7138 | * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) | ||
7139 | */ | ||
7140 | if (WIFSIGNALED(status)) { | ||
7141 | int sig = WTERMSIG(status); | ||
7142 | if (i == fg_pipe->num_cmds-1) | ||
7143 | /* TODO: use strsignal() instead for bash compat? but that's bloat... */ | ||
7144 | puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig)); | ||
7145 | /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ | ||
7146 | /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? | ||
7147 | * Maybe we need to use sig | 128? */ | ||
7148 | ex = sig + 128; | ||
7149 | } | ||
7150 | fg_pipe->cmds[i].cmd_exitcode = ex; | ||
7151 | } else { | ||
7152 | fg_pipe->stopped_cmds++; | ||
7153 | } | ||
7154 | debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n", | ||
7155 | fg_pipe->alive_cmds, fg_pipe->stopped_cmds); | ||
7156 | if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) { | ||
7157 | /* All processes in fg pipe have exited or stopped */ | ||
7158 | i = fg_pipe->num_cmds; | ||
7159 | while (--i >= 0) { | ||
7160 | rcode = fg_pipe->cmds[i].cmd_exitcode; | ||
7161 | /* usually last process gives overall exitstatus, | ||
7162 | * but with "set -o pipefail", last *failed* process does */ | ||
7163 | if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0) | ||
7164 | break; | ||
7165 | } | ||
7166 | IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) | ||
7167 | /* Note: *non-interactive* bash does not continue if all processes in fg pipe | ||
7168 | * are stopped. Testcase: "cat | cat" in a script (not on command line!) | ||
7169 | * and "killall -STOP cat" */ | ||
7170 | if (G_interactive_fd) { | ||
7171 | #if ENABLE_HUSH_JOB | ||
7172 | if (fg_pipe->alive_cmds != 0) | ||
7173 | insert_bg_job(fg_pipe); | ||
7174 | #endif | ||
7175 | return rcode; | ||
7176 | } | ||
7177 | if (fg_pipe->alive_cmds == 0) | ||
7178 | return rcode; | ||
7179 | } | ||
7180 | /* There are still running processes in the fg pipe */ | ||
7181 | goto wait_more; /* do waitpid again */ | ||
7182 | } | ||
7183 | /* it wasnt fg_pipe, look for process in bg pipes */ | ||
7184 | } | ||
7185 | |||
7186 | #if ENABLE_HUSH_JOB | ||
7187 | /* We asked to wait for bg or orphaned children */ | ||
7188 | /* No need to remember exitcode in this case */ | ||
7189 | for (pi = G.job_list; pi; pi = pi->next) { | ||
7190 | for (i = 0; i < pi->num_cmds; i++) { | ||
7191 | if (pi->cmds[i].pid == childpid) | ||
7192 | goto found_pi_and_prognum; | ||
7193 | } | ||
7194 | } | 7235 | } |
7195 | /* Happens when shell is used as init process (init=/bin/sh) */ | 7236 | if (childpid == waitfor_pid) { |
7196 | debug_printf("checkjobs: pid %d was not in our list!\n", childpid); | 7237 | rcode = WEXITSTATUS(status); |
7197 | continue; /* do waitpid again */ | 7238 | if (WIFSIGNALED(status)) |
7198 | 7239 | rcode = 128 + WTERMSIG(status); | |
7199 | found_pi_and_prognum: | 7240 | rcode++; |
7200 | if (dead) { | 7241 | break; /* "wait PID" called us, give it exitcode+1 */ |
7201 | /* child exited */ | ||
7202 | pi->cmds[i].pid = 0; | ||
7203 | pi->alive_cmds--; | ||
7204 | if (!pi->alive_cmds) { | ||
7205 | if (G_interactive_fd) | ||
7206 | printf(JOB_STATUS_FORMAT, pi->jobid, | ||
7207 | "Done", pi->cmdtext); | ||
7208 | delete_finished_bg_job(pi); | ||
7209 | } | ||
7210 | } else { | ||
7211 | /* child stopped */ | ||
7212 | pi->stopped_cmds++; | ||
7213 | } | 7242 | } |
7214 | #endif | 7243 | /* This wasn't one of our processes, or */ |
7244 | /* fg_pipe still has running processes, do waitpid again */ | ||
7215 | } /* while (waitpid succeeds)... */ | 7245 | } /* while (waitpid succeeds)... */ |
7216 | 7246 | ||
7217 | return rcode; | 7247 | return rcode; |
@@ -7221,7 +7251,7 @@ static int checkjobs(struct pipe *fg_pipe) | |||
7221 | static int checkjobs_and_fg_shell(struct pipe *fg_pipe) | 7251 | static int checkjobs_and_fg_shell(struct pipe *fg_pipe) |
7222 | { | 7252 | { |
7223 | pid_t p; | 7253 | pid_t p; |
7224 | int rcode = checkjobs(fg_pipe); | 7254 | int rcode = checkjobs(fg_pipe, 0 /*(no pid to wait for)*/); |
7225 | if (G_saved_tty_pgrp) { | 7255 | if (G_saved_tty_pgrp) { |
7226 | /* Job finished, move the shell to the foreground */ | 7256 | /* Job finished, move the shell to the foreground */ |
7227 | p = getpgrp(); /* our process group id */ | 7257 | p = getpgrp(); /* our process group id */ |
@@ -7893,7 +7923,7 @@ static int run_list(struct pipe *pi) | |||
7893 | /* else: e.g. "continue 2" should *break* once, *then* continue */ | 7923 | /* else: e.g. "continue 2" should *break* once, *then* continue */ |
7894 | } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */ | 7924 | } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */ |
7895 | if (G.depth_break_continue != 0 || fbc == BC_BREAK) { | 7925 | if (G.depth_break_continue != 0 || fbc == BC_BREAK) { |
7896 | checkjobs(NULL); | 7926 | checkjobs(NULL, 0 /*(no pid to wait for)*/); |
7897 | break; | 7927 | break; |
7898 | } | 7928 | } |
7899 | /* "continue": simulate end of loop */ | 7929 | /* "continue": simulate end of loop */ |
@@ -7902,7 +7932,7 @@ static int run_list(struct pipe *pi) | |||
7902 | } | 7932 | } |
7903 | #endif | 7933 | #endif |
7904 | if (G_flag_return_in_progress == 1) { | 7934 | if (G_flag_return_in_progress == 1) { |
7905 | checkjobs(NULL); | 7935 | checkjobs(NULL, 0 /*(no pid to wait for)*/); |
7906 | break; | 7936 | break; |
7907 | } | 7937 | } |
7908 | } else if (pi->followup == PIPE_BG) { | 7938 | } else if (pi->followup == PIPE_BG) { |
@@ -7929,7 +7959,7 @@ static int run_list(struct pipe *pi) | |||
7929 | } else | 7959 | } else |
7930 | #endif | 7960 | #endif |
7931 | { /* This one just waits for completion */ | 7961 | { /* This one just waits for completion */ |
7932 | rcode = checkjobs(pi); | 7962 | rcode = checkjobs(pi, 0 /*(no pid to wait for)*/); |
7933 | debug_printf_exec(": checkjobs exitcode %d\n", rcode); | 7963 | debug_printf_exec(": checkjobs exitcode %d\n", rcode); |
7934 | check_and_run_traps(); | 7964 | check_and_run_traps(); |
7935 | } | 7965 | } |
@@ -7943,7 +7973,7 @@ static int run_list(struct pipe *pi) | |||
7943 | cond_code = rcode; | 7973 | cond_code = rcode; |
7944 | #endif | 7974 | #endif |
7945 | check_jobs_and_continue: | 7975 | check_jobs_and_continue: |
7946 | checkjobs(NULL); | 7976 | checkjobs(NULL, 0 /*(no pid to wait for)*/); |
7947 | dont_check_jobs_but_continue: ; | 7977 | dont_check_jobs_but_continue: ; |
7948 | #if ENABLE_HUSH_LOOPS | 7978 | #if ENABLE_HUSH_LOOPS |
7949 | /* Beware of "while false; true; do ..."! */ | 7979 | /* Beware of "while false; true; do ..."! */ |
@@ -9408,9 +9438,68 @@ static int FAST_FUNC builtin_umask(char **argv) | |||
9408 | } | 9438 | } |
9409 | 9439 | ||
9410 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ | 9440 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ |
9441 | static int wait_for_child_or_signal(pid_t waitfor_pid) | ||
9442 | { | ||
9443 | int ret = 0; | ||
9444 | for (;;) { | ||
9445 | int sig; | ||
9446 | sigset_t oldset, allsigs; | ||
9447 | |||
9448 | /* waitpid is not interruptible by SA_RESTARTed | ||
9449 | * signals which we use. Thus, this ugly dance: | ||
9450 | */ | ||
9451 | |||
9452 | /* Make sure possible SIGCHLD is stored in kernel's | ||
9453 | * pending signal mask before we call waitpid. | ||
9454 | * Or else we may race with SIGCHLD, lose it, | ||
9455 | * and get stuck in sigwaitinfo... | ||
9456 | */ | ||
9457 | sigfillset(&allsigs); | ||
9458 | sigprocmask(SIG_SETMASK, &allsigs, &oldset); | ||
9459 | |||
9460 | if (!sigisemptyset(&G.pending_set)) { | ||
9461 | /* Crap! we raced with some signal! */ | ||
9462 | // sig = 0; | ||
9463 | goto restore; | ||
9464 | } | ||
9465 | |||
9466 | /*errno = 0; - checkjobs does this */ | ||
9467 | ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */ | ||
9468 | /* if ECHILD, there are no children (ret is -1 or 0) */ | ||
9469 | /* if ret == 0, no children changed state */ | ||
9470 | /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */ | ||
9471 | if (errno == ECHILD || ret--) { | ||
9472 | if (ret < 0) /* if ECHILD, may need to fix */ | ||
9473 | ret = 0; | ||
9474 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
9475 | break; | ||
9476 | } | ||
9477 | |||
9478 | /* 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! */ | ||
9481 | /* Note: sigsuspend invokes signal handler */ | ||
9482 | sigsuspend(&oldset); | ||
9483 | restore: | ||
9484 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
9485 | |||
9486 | /* So, did we get a signal? */ | ||
9487 | //if (sig > 0) | ||
9488 | // raise(sig); /* run handler */ | ||
9489 | sig = check_and_run_traps(); | ||
9490 | if (sig /*&& sig != SIGCHLD - always true */) { | ||
9491 | /* see note 2 */ | ||
9492 | ret = 128 + sig; | ||
9493 | break; | ||
9494 | } | ||
9495 | /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */ | ||
9496 | } | ||
9497 | return ret; | ||
9498 | } | ||
9499 | |||
9411 | static int FAST_FUNC builtin_wait(char **argv) | 9500 | static int FAST_FUNC builtin_wait(char **argv) |
9412 | { | 9501 | { |
9413 | int ret = EXIT_SUCCESS; | 9502 | int ret; |
9414 | int status; | 9503 | int status; |
9415 | 9504 | ||
9416 | argv = skip_dash_dash(argv); | 9505 | argv = skip_dash_dash(argv); |
@@ -9431,74 +9520,51 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
9431 | * ^C <-- after ~4 sec from keyboard | 9520 | * ^C <-- after ~4 sec from keyboard |
9432 | * $ | 9521 | * $ |
9433 | */ | 9522 | */ |
9434 | while (1) { | 9523 | return wait_for_child_or_signal(0 /*(no pid to wait for)*/); |
9435 | int sig; | ||
9436 | sigset_t oldset, allsigs; | ||
9437 | |||
9438 | /* waitpid is not interruptible by SA_RESTARTed | ||
9439 | * signals which we use. Thus, this ugly dance: | ||
9440 | */ | ||
9441 | |||
9442 | /* Make sure possible SIGCHLD is stored in kernel's | ||
9443 | * pending signal mask before we call waitpid. | ||
9444 | * Or else we may race with SIGCHLD, lose it, | ||
9445 | * and get stuck in sigwaitinfo... | ||
9446 | */ | ||
9447 | sigfillset(&allsigs); | ||
9448 | sigprocmask(SIG_SETMASK, &allsigs, &oldset); | ||
9449 | |||
9450 | if (!sigisemptyset(&G.pending_set)) { | ||
9451 | /* Crap! we raced with some signal! */ | ||
9452 | // sig = 0; | ||
9453 | goto restore; | ||
9454 | } | ||
9455 | |||
9456 | checkjobs(NULL); /* waitpid(WNOHANG) inside */ | ||
9457 | if (errno == ECHILD) { | ||
9458 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
9459 | break; | ||
9460 | } | ||
9461 | |||
9462 | /* Wait for SIGCHLD or any other signal */ | ||
9463 | //sig = sigwaitinfo(&allsigs, NULL); | ||
9464 | /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */ | ||
9465 | /* Note: sigsuspend invokes signal handler */ | ||
9466 | sigsuspend(&oldset); | ||
9467 | restore: | ||
9468 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
9469 | |||
9470 | /* So, did we get a signal? */ | ||
9471 | //if (sig > 0) | ||
9472 | // raise(sig); /* run handler */ | ||
9473 | sig = check_and_run_traps(); | ||
9474 | if (sig /*&& sig != SIGCHLD - always true */) { | ||
9475 | /* see note 2 */ | ||
9476 | ret = 128 + sig; | ||
9477 | break; | ||
9478 | } | ||
9479 | /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */ | ||
9480 | } | ||
9481 | return ret; | ||
9482 | } | 9524 | } |
9483 | 9525 | ||
9484 | /* This is probably buggy wrt interruptible-ness */ | 9526 | /* TODO: support "wait %jobspec" */ |
9485 | while (*argv) { | 9527 | do { |
9486 | pid_t pid = bb_strtou(*argv, NULL, 10); | 9528 | pid_t pid = bb_strtou(*argv, NULL, 10); |
9487 | if (errno) { | 9529 | if (errno || pid <= 0) { |
9488 | /* mimic bash message */ | 9530 | /* mimic bash message */ |
9489 | bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); | 9531 | bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); |
9490 | return EXIT_FAILURE; | 9532 | ret = EXIT_FAILURE; |
9533 | continue; /* bash checks all argv[] */ | ||
9534 | } | ||
9535 | /* Do we have such child? */ | ||
9536 | ret = waitpid(pid, &status, WNOHANG); | ||
9537 | if (ret < 0) { | ||
9538 | /* No */ | ||
9539 | if (errno == ECHILD) { | ||
9540 | if (G.last_bg_pid > 0 && pid == G.last_bg_pid) { | ||
9541 | /* "wait $!" but last bg task has already exited. Try: | ||
9542 | * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $? | ||
9543 | * In bash it prints exitcode 0, then 3. | ||
9544 | */ | ||
9545 | ret = 0; /* FIXME */ | ||
9546 | continue; | ||
9547 | } | ||
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 { | ||
9551 | /* ??? */ | ||
9552 | bb_perror_msg("wait %s", *argv); | ||
9553 | } | ||
9554 | ret = 127; | ||
9555 | continue; /* bash checks all argv[] */ | ||
9491 | } | 9556 | } |
9492 | if (waitpid(pid, &status, 0) == pid) { | 9557 | if (ret == 0) { |
9558 | /* Yes, and it still runs */ | ||
9559 | ret = wait_for_child_or_signal(pid); | ||
9560 | } else { | ||
9561 | /* Yes, and it just exited */ | ||
9562 | process_wait_result(NULL, pid, status); | ||
9493 | ret = WEXITSTATUS(status); | 9563 | ret = WEXITSTATUS(status); |
9494 | if (WIFSIGNALED(status)) | 9564 | if (WIFSIGNALED(status)) |
9495 | ret = 128 + WTERMSIG(status); | 9565 | ret = 128 + WTERMSIG(status); |
9496 | } else { | ||
9497 | bb_perror_msg("wait %s", *argv); | ||
9498 | ret = 127; | ||
9499 | } | 9566 | } |
9500 | argv++; | 9567 | } while (*++argv); |
9501 | } | ||
9502 | 9568 | ||
9503 | return ret; | 9569 | return ret; |
9504 | } | 9570 | } |
diff --git a/shell/hush_test/hush-misc/wait1.right b/shell/hush_test/hush-misc/wait1.right new file mode 100644 index 000000000..20f514da0 --- /dev/null +++ b/shell/hush_test/hush-misc/wait1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 0 | ||
2 | [1] Running sleep 2 | ||
diff --git a/shell/hush_test/hush-misc/wait1.tests b/shell/hush_test/hush-misc/wait1.tests new file mode 100755 index 000000000..f9cf6d48c --- /dev/null +++ b/shell/hush_test/hush-misc/wait1.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | sleep 2 & sleep 1 & wait $! | ||
2 | echo $? | ||
3 | jobs | ||
diff --git a/shell/hush_test/hush-misc/wait2.right b/shell/hush_test/hush-misc/wait2.right new file mode 100644 index 000000000..f018c2c98 --- /dev/null +++ b/shell/hush_test/hush-misc/wait2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 0 | ||
2 | [1] Running sleep 3 | ||
diff --git a/shell/hush_test/hush-misc/wait2.tests b/shell/hush_test/hush-misc/wait2.tests new file mode 100755 index 000000000..be20f95a5 --- /dev/null +++ b/shell/hush_test/hush-misc/wait2.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | sleep 3 & sleep 2 & sleep 1 | ||
2 | wait $! | ||
3 | echo $? | ||
4 | jobs | ||
diff --git a/shell/hush_test/hush-misc/wait3.right b/shell/hush_test/hush-misc/wait3.right new file mode 100644 index 000000000..5437b1d73 --- /dev/null +++ b/shell/hush_test/hush-misc/wait3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 3 | ||
2 | [1] Running sleep 2 | ||
diff --git a/shell/hush_test/hush-misc/wait3.tests b/shell/hush_test/hush-misc/wait3.tests new file mode 100755 index 000000000..ac541c3fc --- /dev/null +++ b/shell/hush_test/hush-misc/wait3.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | sleep 2 & (sleep 1;exit 3) & wait $! | ||
2 | echo $? | ||
3 | jobs | ||