diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-14 19:58:46 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-14 19:58:46 +0200 |
commit | 2ed74e25d354e6958dc86a21aa32c2dacb809bf0 (patch) | |
tree | 4fd9ec08db321fa8c771dc2b0c7de4efb1324d83 | |
parent | 0c5657e9119eb52263e83e9b55394a8f43f4e928 (diff) | |
download | busybox-w32-2ed74e25d354e6958dc86a21aa32c2dacb809bf0.tar.gz busybox-w32-2ed74e25d354e6958dc86a21aa32c2dacb809bf0.tar.bz2 busybox-w32-2ed74e25d354e6958dc86a21aa32c2dacb809bf0.zip |
hush: make "wait %1" work even if the job is dead
Example script:
sleep 1 | (sleep 1;exit 3) &
sleep 2
echo Zero:$?
wait %1
echo Three:$?
function old new delta
clean_up_last_dead_job - 24 +24
process_wait_result 426 447 +21
builtin_wait 285 293 +8
insert_job_into_table 264 269 +5
builtin_jobs 68 73 +5
remove_job_from_table 59 57 -2
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 4/1 up/down: 63/-2) Total: 61 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/hush.c | 91 |
1 files changed, 52 insertions, 39 deletions
diff --git a/shell/hush.c b/shell/hush.c index af5c26090..b76351fde 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -7237,11 +7237,42 @@ static const char *get_cmdtext(struct pipe *pi) | |||
7237 | return pi->cmdtext; | 7237 | return pi->cmdtext; |
7238 | } | 7238 | } |
7239 | 7239 | ||
7240 | static void remove_job_from_table(struct pipe *pi) | ||
7241 | { | ||
7242 | struct pipe *prev_pipe; | ||
7243 | |||
7244 | if (pi == G.job_list) { | ||
7245 | G.job_list = pi->next; | ||
7246 | } else { | ||
7247 | prev_pipe = G.job_list; | ||
7248 | while (prev_pipe->next != pi) | ||
7249 | prev_pipe = prev_pipe->next; | ||
7250 | prev_pipe->next = pi->next; | ||
7251 | } | ||
7252 | G.last_jobid = 0; | ||
7253 | if (G.job_list) | ||
7254 | G.last_jobid = G.job_list->jobid; | ||
7255 | } | ||
7256 | |||
7257 | static void delete_finished_job(struct pipe *pi) | ||
7258 | { | ||
7259 | remove_job_from_table(pi); | ||
7260 | free_pipe(pi); | ||
7261 | } | ||
7262 | |||
7263 | static void clean_up_last_dead_job(void) | ||
7264 | { | ||
7265 | if (G.job_list && !G.job_list->alive_cmds) | ||
7266 | delete_finished_job(G.job_list); | ||
7267 | } | ||
7268 | |||
7240 | static void insert_job_into_table(struct pipe *pi) | 7269 | static void insert_job_into_table(struct pipe *pi) |
7241 | { | 7270 | { |
7242 | struct pipe *job, **jobp; | 7271 | struct pipe *job, **jobp; |
7243 | int i; | 7272 | int i; |
7244 | 7273 | ||
7274 | clean_up_last_dead_job(); | ||
7275 | |||
7245 | /* Find the end of the list, and find next job ID to use */ | 7276 | /* Find the end of the list, and find next job ID to use */ |
7246 | i = 0; | 7277 | i = 0; |
7247 | jobp = &G.job_list; | 7278 | jobp = &G.job_list; |
@@ -7267,30 +7298,6 @@ static void insert_job_into_table(struct pipe *pi) | |||
7267 | printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext); | 7298 | printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext); |
7268 | G.last_jobid = job->jobid; | 7299 | G.last_jobid = job->jobid; |
7269 | } | 7300 | } |
7270 | |||
7271 | static void remove_job_from_table(struct pipe *pi) | ||
7272 | { | ||
7273 | struct pipe *prev_pipe; | ||
7274 | |||
7275 | if (pi == G.job_list) { | ||
7276 | G.job_list = pi->next; | ||
7277 | } else { | ||
7278 | prev_pipe = G.job_list; | ||
7279 | while (prev_pipe->next != pi) | ||
7280 | prev_pipe = prev_pipe->next; | ||
7281 | prev_pipe->next = pi->next; | ||
7282 | } | ||
7283 | if (G.job_list) | ||
7284 | G.last_jobid = G.job_list->jobid; | ||
7285 | else | ||
7286 | G.last_jobid = 0; | ||
7287 | } | ||
7288 | |||
7289 | static void delete_finished_job(struct pipe *pi) | ||
7290 | { | ||
7291 | remove_job_from_table(pi); | ||
7292 | free_pipe(pi); | ||
7293 | } | ||
7294 | #endif /* JOB */ | 7301 | #endif /* JOB */ |
7295 | 7302 | ||
7296 | static int job_exited_or_stopped(struct pipe *pi) | 7303 | static int job_exited_or_stopped(struct pipe *pi) |
@@ -7415,14 +7422,22 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
7415 | pi->cmds[i].pid = 0; | 7422 | pi->cmds[i].pid = 0; |
7416 | pi->alive_cmds--; | 7423 | pi->alive_cmds--; |
7417 | if (!pi->alive_cmds) { | 7424 | if (!pi->alive_cmds) { |
7418 | if (G_interactive_fd) | 7425 | if (G_interactive_fd) { |
7419 | printf(JOB_STATUS_FORMAT, pi->jobid, | 7426 | printf(JOB_STATUS_FORMAT, pi->jobid, |
7420 | "Done", pi->cmdtext); | 7427 | "Done", pi->cmdtext); |
7421 | delete_finished_job(pi); | 7428 | delete_finished_job(pi); |
7422 | //bash deletes finished jobs from job table only in interactive mode, after "jobs" cmd, | 7429 | } else { |
7423 | //or if pid of a new process matches one of the old ones | 7430 | /* |
7424 | //(see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source). | 7431 | * bash deletes finished jobs from job table only in interactive mode, |
7425 | //Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash. | 7432 | * after "jobs" cmd, or if pid of a new process matches one of the old ones |
7433 | * (see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source). | ||
7434 | * Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash. | ||
7435 | * We only retain one "dead" job, if it's the single job on the list. | ||
7436 | * This covers most of real-world scenarios where this is useful. | ||
7437 | */ | ||
7438 | if (pi != G.job_list) | ||
7439 | delete_finished_job(pi); | ||
7440 | } | ||
7426 | } | 7441 | } |
7427 | } else { | 7442 | } else { |
7428 | /* child stopped */ | 7443 | /* child stopped */ |
@@ -9696,6 +9711,9 @@ static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM) | |||
9696 | 9711 | ||
9697 | printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); | 9712 | printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); |
9698 | } | 9713 | } |
9714 | |||
9715 | clean_up_last_dead_job(); | ||
9716 | |||
9699 | return EXIT_SUCCESS; | 9717 | return EXIT_SUCCESS; |
9700 | } | 9718 | } |
9701 | 9719 | ||
@@ -9939,17 +9957,12 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
9939 | wait_pipe = parse_jobspec(*argv); | 9957 | wait_pipe = parse_jobspec(*argv); |
9940 | if (wait_pipe) { | 9958 | if (wait_pipe) { |
9941 | ret = job_exited_or_stopped(wait_pipe); | 9959 | ret = job_exited_or_stopped(wait_pipe); |
9942 | if (ret < 0) | 9960 | if (ret < 0) { |
9943 | ret = wait_for_child_or_signal(wait_pipe, 0); | 9961 | ret = wait_for_child_or_signal(wait_pipe, 0); |
9944 | //bash immediately deletes finished jobs from job table only in interactive mode, | 9962 | } else { |
9945 | //we _always_ delete them at once. If we'd start keeping some dead jobs, this | 9963 | /* waiting on "last dead job" removes it */ |
9946 | //(and more) would be necessary to avoid accumulating dead jobs: | 9964 | clean_up_last_dead_job(); |
9947 | # if 0 | ||
9948 | else { | ||
9949 | if (!wait_pipe->alive_cmds) | ||
9950 | delete_finished_job(wait_pipe); | ||
9951 | } | 9965 | } |
9952 | # endif | ||
9953 | } | 9966 | } |
9954 | /* else: parse_jobspec() already emitted error msg */ | 9967 | /* else: parse_jobspec() already emitted error msg */ |
9955 | continue; | 9968 | continue; |