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 /shell | |
| 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>
Diffstat (limited to 'shell')
| -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; |
