diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-31 17:24:49 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-31 17:24:49 +0000 |
commit | 7566bae19715a493f40fd3fae4d69d10089af411 (patch) | |
tree | 90f41b90064a0530004c7df70b4036f2e9ad2859 | |
parent | 70c6e40e478e83020e35a00517feab659ee0e782 (diff) | |
download | busybox-w32-7566bae19715a493f40fd3fae4d69d10089af411.tar.gz busybox-w32-7566bae19715a493f40fd3fae4d69d10089af411.tar.bz2 busybox-w32-7566bae19715a493f40fd3fae4d69d10089af411.zip |
hush: fix wait builtin
function old new delta
builtin_wait 174 275 +101
sigwaitinfo - 48 +48
__GI_sigwaitinfo - 48 +48
check_and_run_traps 133 169 +36
checkjobs 349 380 +31
hush_main 971 991 +20
static.zero_timespec - 8 +8
run_list 2010 2016 +6
file_get 254 260 +6
static.zero_ts 8 - -8
------------------------------------------------------------------------------
(add/remove: 3/1 grow/shrink: 6/0 up/down: 304/-8) Total: 296 bytes
-rw-r--r-- | shell/hush.c | 130 |
1 files changed, 99 insertions, 31 deletions
diff --git a/shell/hush.c b/shell/hush.c index de3b56d21..af6763517 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -491,13 +491,14 @@ struct globals { | |||
491 | unsigned char charmap[256]; | 491 | unsigned char charmap[256]; |
492 | char user_input_buf[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; | 492 | char user_input_buf[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; |
493 | /* Signal and trap handling */ | 493 | /* Signal and trap handling */ |
494 | char **traps; /* char *traps[NSIG] */ | 494 | // unsigned count_SIGCHLD; |
495 | // unsigned handled_SIGCHLD; | ||
495 | /* which signals have non-DFL handler (even with no traps set)? */ | 496 | /* which signals have non-DFL handler (even with no traps set)? */ |
496 | unsigned non_DFL_mask; | 497 | unsigned non_DFL_mask; |
498 | char **traps; /* char *traps[NSIG] */ | ||
497 | sigset_t blocked_set; | 499 | sigset_t blocked_set; |
498 | sigset_t inherited_set; | 500 | sigset_t inherited_set; |
499 | }; | 501 | }; |
500 | |||
501 | #define G (*ptr_to_globals) | 502 | #define G (*ptr_to_globals) |
502 | /* Not #defining name to G.name - this quickly gets unwieldy | 503 | /* Not #defining name to G.name - this quickly gets unwieldy |
503 | * (too many defines). Also, I actually prefer to see when a variable | 504 | * (too many defines). Also, I actually prefer to see when a variable |
@@ -829,12 +830,18 @@ static void free_strings(char **strings) | |||
829 | * POSIX says pending signal mask is cleared in child - no need to clear it. | 830 | * POSIX says pending signal mask is cleared in child - no need to clear it. |
830 | * Restore blocked signal set to one inherited by shell just prior to exec. | 831 | * Restore blocked signal set to one inherited by shell just prior to exec. |
831 | * | 832 | * |
832 | * Note: as a result, we do not use signal handlers much. The only use | 833 | * Note: as a result, we do not use signal handlers much. The only uses |
833 | * is to restore terminal pgrp on exit. | 834 | * are to count SIGCHLDs [disabled - bug somewhere, + bloat] |
835 | * and to restore tty pgrp on signal-induced exit. | ||
834 | * | 836 | * |
835 | * TODO: check/fix wait builtin to be interruptible. | 837 | * TODO: check/fix wait builtin to be interruptible. |
836 | */ | 838 | */ |
837 | 839 | ||
840 | //static void SIGCHLD_handler(int sig UNUSED_PARAM) | ||
841 | //{ | ||
842 | // G.count_SIGCHLD++; | ||
843 | //} | ||
844 | |||
838 | /* called once at shell init */ | 845 | /* called once at shell init */ |
839 | static void init_signal_mask(void) | 846 | static void init_signal_mask(void) |
840 | { | 847 | { |
@@ -863,20 +870,24 @@ static void init_signal_mask(void) | |||
863 | mask >>= 1; | 870 | mask >>= 1; |
864 | sig++; | 871 | sig++; |
865 | } | 872 | } |
873 | sigdelset(&G.blocked_set, SIGCHLD); | ||
866 | sigprocmask(SIG_SETMASK, &G.blocked_set, &G.inherited_set); | 874 | sigprocmask(SIG_SETMASK, &G.blocked_set, &G.inherited_set); |
867 | } | 875 | } |
868 | 876 | ||
869 | static void check_and_run_traps(void) | 877 | static int check_and_run_traps(int sig) |
870 | { | 878 | { |
871 | static const struct timespec zero_ts = { 0, 0 }; | 879 | static const struct timespec zero_timespec = { 0, 0 }; |
872 | smalluint save_rcode; | 880 | smalluint save_rcode; |
873 | int sig; | 881 | int last_sig = 0; |
874 | 882 | ||
883 | if (sig) | ||
884 | goto jump_in; | ||
875 | while (1) { | 885 | while (1) { |
876 | sig = sigtimedwait(&G.blocked_set, NULL, &zero_ts); | 886 | sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec); |
877 | if (sig <= 0) | 887 | if (sig <= 0) |
878 | break; | 888 | break; |
879 | 889 | jump_in: | |
890 | last_sig = sig; | ||
880 | if (G.traps && G.traps[sig]) { | 891 | if (G.traps && G.traps[sig]) { |
881 | if (G.traps[sig][0]) { | 892 | if (G.traps[sig][0]) { |
882 | /* We have user-defined handler */ | 893 | /* We have user-defined handler */ |
@@ -890,7 +901,11 @@ static void check_and_run_traps(void) | |||
890 | } | 901 | } |
891 | /* not a trap: special action */ | 902 | /* not a trap: special action */ |
892 | switch (sig) { | 903 | switch (sig) { |
904 | // case SIGCHLD: | ||
905 | // G.count_SIGCHLD++; | ||
906 | // break; | ||
893 | case SIGINT: | 907 | case SIGINT: |
908 | bb_putchar('\n'); | ||
894 | G.flag_SIGINT = 1; | 909 | G.flag_SIGINT = 1; |
895 | break; | 910 | break; |
896 | //TODO | 911 | //TODO |
@@ -900,6 +915,7 @@ static void check_and_run_traps(void) | |||
900 | break; | 915 | break; |
901 | } | 916 | } |
902 | } | 917 | } |
918 | return last_sig; | ||
903 | } | 919 | } |
904 | 920 | ||
905 | #if ENABLE_HUSH_JOB | 921 | #if ENABLE_HUSH_JOB |
@@ -1209,7 +1225,7 @@ static void get_user_input(struct in_str *i) | |||
1209 | * only after <Enter>. (^C will work) */ | 1225 | * only after <Enter>. (^C will work) */ |
1210 | r = read_line_input(prompt_str, G.user_input_buf, BUFSIZ-1, G.line_input_state); | 1226 | r = read_line_input(prompt_str, G.user_input_buf, BUFSIZ-1, G.line_input_state); |
1211 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ | 1227 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ |
1212 | check_and_run_traps(); | 1228 | check_and_run_traps(0); |
1213 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ | 1229 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ |
1214 | i->eof_flag = (r < 0); | 1230 | i->eof_flag = (r < 0); |
1215 | if (i->eof_flag) { /* EOF/error detected */ | 1231 | if (i->eof_flag) { /* EOF/error detected */ |
@@ -1223,7 +1239,7 @@ static void get_user_input(struct in_str *i) | |||
1223 | fflush(stdout); | 1239 | fflush(stdout); |
1224 | G.user_input_buf[0] = r = fgetc(i->file); | 1240 | G.user_input_buf[0] = r = fgetc(i->file); |
1225 | /*G.user_input_buf[1] = '\0'; - already is and never changed */ | 1241 | /*G.user_input_buf[1] = '\0'; - already is and never changed */ |
1226 | //do we need check_and_run_traps()? (maybe only if stdin) | 1242 | //do we need check_and_run_traps(0)? (maybe only if stdin) |
1227 | } while (G.flag_SIGINT); | 1243 | } while (G.flag_SIGINT); |
1228 | i->eof_flag = (r == EOF); | 1244 | i->eof_flag = (r == EOF); |
1229 | #endif | 1245 | #endif |
@@ -2329,6 +2345,11 @@ static int checkjobs(struct pipe* fg_pipe) | |||
2329 | 2345 | ||
2330 | debug_printf_jobs("checkjobs %p\n", fg_pipe); | 2346 | debug_printf_jobs("checkjobs %p\n", fg_pipe); |
2331 | 2347 | ||
2348 | errno = 0; | ||
2349 | // if (G.handled_SIGCHLD == G.count_SIGCHLD) | ||
2350 | // /* avoid doing syscall, nothing there anyway */ | ||
2351 | // return rcode; | ||
2352 | |||
2332 | attributes = WUNTRACED; | 2353 | attributes = WUNTRACED; |
2333 | if (fg_pipe == NULL) | 2354 | if (fg_pipe == NULL) |
2334 | attributes |= WNOHANG; | 2355 | attributes |= WNOHANG; |
@@ -2346,10 +2367,21 @@ static int checkjobs(struct pipe* fg_pipe) | |||
2346 | // + killall -STOP cat | 2367 | // + killall -STOP cat |
2347 | 2368 | ||
2348 | wait_more: | 2369 | wait_more: |
2349 | // TODO: safe_waitpid? | 2370 | while (1) { |
2350 | while ((childpid = waitpid(-1, &status, attributes)) > 0) { | ||
2351 | int i; | 2371 | int i; |
2352 | const int dead = WIFEXITED(status) || WIFSIGNALED(status); | 2372 | int dead; |
2373 | |||
2374 | // i = G.count_SIGCHLD; | ||
2375 | childpid = waitpid(-1, &status, attributes); | ||
2376 | if (childpid <= 0) { | ||
2377 | if (childpid && errno != ECHILD) | ||
2378 | bb_perror_msg("waitpid"); | ||
2379 | // else /* Until next SIGCHLD, waitpid's are useless */ | ||
2380 | // G.handled_SIGCHLD = i; | ||
2381 | break; | ||
2382 | } | ||
2383 | dead = WIFEXITED(status) || WIFSIGNALED(status); | ||
2384 | |||
2353 | #if DEBUG_JOBS | 2385 | #if DEBUG_JOBS |
2354 | if (WIFSTOPPED(status)) | 2386 | if (WIFSTOPPED(status)) |
2355 | debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n", | 2387 | debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n", |
@@ -2428,10 +2460,6 @@ static int checkjobs(struct pipe* fg_pipe) | |||
2428 | #endif | 2460 | #endif |
2429 | } /* while (waitpid succeeds)... */ | 2461 | } /* while (waitpid succeeds)... */ |
2430 | 2462 | ||
2431 | /* wait found no children or failed */ | ||
2432 | |||
2433 | if (childpid && errno != ECHILD) | ||
2434 | bb_perror_msg("waitpid"); | ||
2435 | return rcode; | 2463 | return rcode; |
2436 | } | 2464 | } |
2437 | 2465 | ||
@@ -3004,7 +3032,7 @@ static int run_list(struct pipe *pi) | |||
3004 | if (r != -1) { | 3032 | if (r != -1) { |
3005 | /* we only ran a builtin: rcode is already known | 3033 | /* we only ran a builtin: rcode is already known |
3006 | * and we don't need to wait for anything. */ | 3034 | * and we don't need to wait for anything. */ |
3007 | check_and_run_traps(); | 3035 | check_and_run_traps(0); |
3008 | #if ENABLE_HUSH_LOOPS | 3036 | #if ENABLE_HUSH_LOOPS |
3009 | /* was it "break" or "continue"? */ | 3037 | /* was it "break" or "continue"? */ |
3010 | if (G.flag_break_continue) { | 3038 | if (G.flag_break_continue) { |
@@ -3029,7 +3057,7 @@ static int run_list(struct pipe *pi) | |||
3029 | /* even bash 3.2 doesn't do that well with nested bg: | 3057 | /* even bash 3.2 doesn't do that well with nested bg: |
3030 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | 3058 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". |
3031 | * I'm NOT treating inner &'s as jobs */ | 3059 | * I'm NOT treating inner &'s as jobs */ |
3032 | check_and_run_traps(); | 3060 | check_and_run_traps(0); |
3033 | #if ENABLE_HUSH_JOB | 3061 | #if ENABLE_HUSH_JOB |
3034 | if (G.run_list_level == 1) | 3062 | if (G.run_list_level == 1) |
3035 | insert_bg_job(pi); | 3063 | insert_bg_job(pi); |
@@ -3040,13 +3068,13 @@ static int run_list(struct pipe *pi) | |||
3040 | if (G.run_list_level == 1 && G.interactive_fd) { | 3068 | if (G.run_list_level == 1 && G.interactive_fd) { |
3041 | /* waits for completion, then fg's main shell */ | 3069 | /* waits for completion, then fg's main shell */ |
3042 | rcode = checkjobs_and_fg_shell(pi); | 3070 | rcode = checkjobs_and_fg_shell(pi); |
3043 | check_and_run_traps(); | 3071 | check_and_run_traps(0); |
3044 | debug_printf_exec(": checkjobs_and_fg_shell returned %d\n", rcode); | 3072 | debug_printf_exec(": checkjobs_and_fg_shell returned %d\n", rcode); |
3045 | } else | 3073 | } else |
3046 | #endif | 3074 | #endif |
3047 | { /* this one just waits for completion */ | 3075 | { /* this one just waits for completion */ |
3048 | rcode = checkjobs(pi); | 3076 | rcode = checkjobs(pi); |
3049 | check_and_run_traps(); | 3077 | check_and_run_traps(0); |
3050 | debug_printf_exec(": checkjobs returned %d\n", rcode); | 3078 | debug_printf_exec(": checkjobs returned %d\n", rcode); |
3051 | } | 3079 | } |
3052 | } | 3080 | } |
@@ -3668,9 +3696,9 @@ static int process_command_subs(o_string *dest, | |||
3668 | } | 3696 | } |
3669 | 3697 | ||
3670 | debug_printf("done reading from pipe, pclose()ing\n"); | 3698 | debug_printf("done reading from pipe, pclose()ing\n"); |
3671 | /* This is the step that wait()s for the child. Should be pretty | 3699 | /* This is the step that waits for the child. Should be pretty |
3672 | * safe, since we just read an EOF from its stdout. We could try | 3700 | * safe, since we just read an EOF from its stdout. We could try |
3673 | * to do better, by using wait(), and keeping track of background jobs | 3701 | * to do better, by using waitpid, and keeping track of background jobs |
3674 | * at the same time. That would be a lot of work, and contrary | 3702 | * at the same time. That would be a lot of work, and contrary |
3675 | * to the KISS philosophy of this program. */ | 3703 | * to the KISS philosophy of this program. */ |
3676 | retcode = fclose(p); | 3704 | retcode = fclose(p); |
@@ -4586,7 +4614,7 @@ int hush_main(int argc, char **argv) | |||
4586 | // to (inadvertently) close/redirect it | 4614 | // to (inadvertently) close/redirect it |
4587 | } | 4615 | } |
4588 | } | 4616 | } |
4589 | init_signal_mask(); | 4617 | init_signal_mask(); /* note: ensures SIGCHLD is not masked */ |
4590 | debug_printf("G.interactive_fd=%d\n", G.interactive_fd); | 4618 | debug_printf("G.interactive_fd=%d\n", G.interactive_fd); |
4591 | if (G.interactive_fd) { | 4619 | if (G.interactive_fd) { |
4592 | fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC); | 4620 | fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC); |
@@ -4617,8 +4645,12 @@ int hush_main(int argc, char **argv) | |||
4617 | fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC); | 4645 | fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC); |
4618 | } | 4646 | } |
4619 | } | 4647 | } |
4620 | init_signal_mask(); | 4648 | init_signal_mask(); /* note: ensures SIGCHLD is not masked */ |
4621 | #endif | 4649 | #endif |
4650 | /* POSIX allows shell to re-enable SIGCHLD | ||
4651 | * even if it was SIG_IGN on entry */ | ||
4652 | // G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ | ||
4653 | signal(SIGCHLD, SIG_DFL); // SIGCHLD_handler); | ||
4622 | 4654 | ||
4623 | #if ENABLE_HUSH_INTERACTIVE && !ENABLE_FEATURE_SH_EXTRA_QUIET | 4655 | #if ENABLE_HUSH_INTERACTIVE && !ENABLE_FEATURE_SH_EXTRA_QUIET |
4624 | if (G.interactive_fd) { | 4656 | if (G.interactive_fd) { |
@@ -5164,12 +5196,48 @@ static int builtin_unset(char **argv) | |||
5164 | static int builtin_wait(char **argv) | 5196 | static int builtin_wait(char **argv) |
5165 | { | 5197 | { |
5166 | int ret = EXIT_SUCCESS; | 5198 | int ret = EXIT_SUCCESS; |
5167 | int status; | 5199 | int status, sig; |
5168 | 5200 | ||
5169 | if (*++argv == NULL) | 5201 | if (*++argv == NULL) { |
5170 | /* don't care about exit status */ | 5202 | /* Don't care about wait results */ |
5171 | wait(NULL); | 5203 | /* Note 1: must wait until there are no more children */ |
5204 | /* Note 2: must be interruptible */ | ||
5205 | /* Examples: | ||
5206 | * $ sleep 3 & sleep 6 & wait | ||
5207 | * [1] 30934 sleep 3 | ||
5208 | * [2] 30935 sleep 6 | ||
5209 | * [1] Done sleep 3 | ||
5210 | * [2] Done sleep 6 | ||
5211 | * $ sleep 3 & sleep 6 & wait | ||
5212 | * [1] 30936 sleep 3 | ||
5213 | * [2] 30937 sleep 6 | ||
5214 | * [1] Done sleep 3 | ||
5215 | * ^C <-- after ~4 sec from keyboard | ||
5216 | * $ | ||
5217 | */ | ||
5218 | sigaddset(&G.blocked_set, SIGCHLD); | ||
5219 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
5220 | while (1) { | ||
5221 | checkjobs(NULL); | ||
5222 | if (errno == ECHILD) | ||
5223 | break; | ||
5224 | /* Wait for SIGCHLD or any other signal of interest */ | ||
5225 | /* sigtimedwait with infinite timeout: */ | ||
5226 | sig = sigwaitinfo(&G.blocked_set, NULL); | ||
5227 | if (sig > 0) { | ||
5228 | sig = check_and_run_traps(sig); | ||
5229 | if (sig && sig != SIGCHLD) { /* see note 2 */ | ||
5230 | ret = 128 + sig; | ||
5231 | break; | ||
5232 | } | ||
5233 | } | ||
5234 | } | ||
5235 | sigdelset(&G.blocked_set, SIGCHLD); | ||
5236 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
5237 | return ret; | ||
5238 | } | ||
5172 | 5239 | ||
5240 | /* This is probably buggy wrt interruptible-ness */ | ||
5173 | while (*argv) { | 5241 | while (*argv) { |
5174 | pid_t pid = bb_strtou(*argv, NULL, 10); | 5242 | pid_t pid = bb_strtou(*argv, NULL, 10); |
5175 | if (errno) { | 5243 | if (errno) { |