diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2022-01-17 03:02:40 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2022-01-17 11:46:23 +0100 |
commit | 12566e7f9b5e5c5d445bc4d36991d134b431dc6c (patch) | |
tree | 2571356a77f7d421da368e9b31dad182e83b2408 /shell/hush.c | |
parent | a277506a64404e6c4472ff89c944c4f353db1c33 (diff) | |
download | busybox-w32-12566e7f9b5e5c5d445bc4d36991d134b431dc6c.tar.gz busybox-w32-12566e7f9b5e5c5d445bc4d36991d134b431dc6c.tar.bz2 busybox-w32-12566e7f9b5e5c5d445bc4d36991d134b431dc6c.zip |
ash,hush: fix handling of SIGINT while waiting for interactive input
function old new delta
lineedit_read_key 160 237 +77
__pgetc 522 589 +67
fgetc_interactive 244 309 +65
safe_read_key - 39 +39
read_key 588 607 +19
record_pending_signo 23 32 +9
signal_handler 75 81 +6
.rodata 104312 104309 -3
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 6/1 up/down: 282/-3) Total: 279 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 67 |
1 files changed, 47 insertions, 20 deletions
diff --git a/shell/hush.c b/shell/hush.c index 7d0dc67e4..6dc2ecaac 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -918,6 +918,7 @@ struct globals { | |||
918 | #if ENABLE_HUSH_INTERACTIVE | 918 | #if ENABLE_HUSH_INTERACTIVE |
919 | smallint promptmode; /* 0: PS1, 1: PS2 */ | 919 | smallint promptmode; /* 0: PS1, 1: PS2 */ |
920 | #endif | 920 | #endif |
921 | /* set by signal handler if SIGINT is received _and_ its trap is not set */ | ||
921 | smallint flag_SIGINT; | 922 | smallint flag_SIGINT; |
922 | #if ENABLE_HUSH_LOOPS | 923 | #if ENABLE_HUSH_LOOPS |
923 | smallint flag_break_continue; | 924 | smallint flag_break_continue; |
@@ -1944,6 +1945,9 @@ enum { | |||
1944 | static void record_pending_signo(int sig) | 1945 | static void record_pending_signo(int sig) |
1945 | { | 1946 | { |
1946 | sigaddset(&G.pending_set, sig); | 1947 | sigaddset(&G.pending_set, sig); |
1948 | #if ENABLE_FEATURE_EDITING | ||
1949 | bb_got_signal = sig; /* for read_line_input: "we got a signal" */ | ||
1950 | #endif | ||
1947 | #if ENABLE_HUSH_FAST | 1951 | #if ENABLE_HUSH_FAST |
1948 | if (sig == SIGCHLD) { | 1952 | if (sig == SIGCHLD) { |
1949 | G.count_SIGCHLD++; | 1953 | G.count_SIGCHLD++; |
@@ -2652,30 +2656,53 @@ static int get_user_input(struct in_str *i) | |||
2652 | for (;;) { | 2656 | for (;;) { |
2653 | reinit_unicode_for_hush(); | 2657 | reinit_unicode_for_hush(); |
2654 | G.flag_SIGINT = 0; | 2658 | G.flag_SIGINT = 0; |
2655 | /* buglet: SIGINT will not make new prompt to appear _at once_, | 2659 | |
2656 | * only after <Enter>. (^C works immediately) */ | 2660 | bb_got_signal = 0; |
2657 | r = read_line_input(G.line_input_state, prompt_str, | 2661 | if (!sigisemptyset(&G.pending_set)) { |
2662 | /* Whoops, already got a signal, do not call read_line_input */ | ||
2663 | bb_got_signal = r = -1; | ||
2664 | } else { | ||
2665 | /* For shell, LI_INTERRUPTIBLE is set: | ||
2666 | * read_line_input will abort on either | ||
2667 | * getting EINTR in poll(), or if it sees bb_got_signal != 0 | ||
2668 | * (IOW: if signal arrives before poll() is reached). | ||
2669 | * Interactive testcases: | ||
2670 | * (while kill -INT $$; do sleep 1; done) & | ||
2671 | * #^^^ prints ^C, prints prompt, repeats | ||
2672 | * trap 'echo I' int; (while kill -INT $$; do sleep 1; done) & | ||
2673 | * #^^^ prints ^C, prints "I", prints prompt, repeats | ||
2674 | * trap 'echo T' term; (while kill $$; do sleep 1; done) & | ||
2675 | * #^^^ prints "T", prints prompt, repeats | ||
2676 | * #(bash 5.0.17 exits after first "T", looks like a bug) | ||
2677 | */ | ||
2678 | r = read_line_input(G.line_input_state, prompt_str, | ||
2658 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1 | 2679 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1 |
2659 | ); | 2680 | ); |
2660 | /* read_line_input intercepts ^C, "convert" it to SIGINT */ | 2681 | /* read_line_input intercepts ^C, "convert" it to SIGINT */ |
2661 | if (r == 0) { | 2682 | if (r == 0) |
2662 | raise(SIGINT); | 2683 | raise(SIGINT); |
2684 | } | ||
2685 | /* bash prints ^C (before running a trap, if any) | ||
2686 | * both on keyboard ^C and on real SIGINT (non-kbd generated). | ||
2687 | */ | ||
2688 | if (sigismember(&G.pending_set, SIGINT)) { | ||
2689 | write(STDOUT_FILENO, "^C\n", 3); | ||
2690 | G.last_exitcode = 128 | SIGINT; | ||
2663 | } | 2691 | } |
2664 | check_and_run_traps(); | 2692 | check_and_run_traps(); |
2665 | if (r != 0 && !G.flag_SIGINT) | 2693 | if (r == 0) /* keyboard ^C? */ |
2694 | continue; /* go back, read another input line */ | ||
2695 | if (r > 0) /* normal input? (no ^C, no ^D, no signals) */ | ||
2666 | break; | 2696 | break; |
2667 | /* ^C or SIGINT: repeat */ | 2697 | if (!bb_got_signal) { |
2668 | /* bash prints ^C even on real SIGINT (non-kbd generated) */ | 2698 | /* r < 0: ^D/EOF/error detected (but not signal) */ |
2669 | write(STDOUT_FILENO, "^C\n", 3); | 2699 | /* ^D on interactive input goes to next line before exiting: */ |
2670 | G.last_exitcode = 128 | SIGINT; | 2700 | write(STDOUT_FILENO, "\n", 1); |
2671 | } | 2701 | i->p = NULL; |
2672 | if (r < 0) { | 2702 | i->peek_buf[0] = r = EOF; |
2673 | /* EOF/error detected */ | 2703 | return r; |
2674 | /* ^D on interactive input goes to next line before exiting: */ | 2704 | } |
2675 | write(STDOUT_FILENO, "\n", 1); | 2705 | /* it was a signal: go back, read another input line */ |
2676 | i->p = NULL; | ||
2677 | i->peek_buf[0] = r = EOF; | ||
2678 | return r; | ||
2679 | } | 2706 | } |
2680 | i->p = G.user_input_buf; | 2707 | i->p = G.user_input_buf; |
2681 | return (unsigned char)*i->p++; | 2708 | return (unsigned char)*i->p++; |