aboutsummaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2022-01-17 03:02:40 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2022-01-17 11:46:23 +0100
commit12566e7f9b5e5c5d445bc4d36991d134b431dc6c (patch)
tree2571356a77f7d421da368e9b31dad182e83b2408 /shell/hush.c
parenta277506a64404e6c4472ff89c944c4f353db1c33 (diff)
downloadbusybox-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.c67
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 {
1944static void record_pending_signo(int sig) 1945static 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++;