diff options
author | Ron Yorston <rmy@pobox.com> | 2023-04-03 08:33:14 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2023-04-03 08:33:14 +0100 |
commit | 64c8f5f3d0bbfcaf3b88aa97a23d90c14326b79e (patch) | |
tree | 878917ac4dfd0d271494a1b5436db918b0a0ed88 | |
parent | 1b2ee3667543b2588446dba6655720eb929bfe03 (diff) | |
download | busybox-w32-64c8f5f3d0bbfcaf3b88aa97a23d90c14326b79e.tar.gz busybox-w32-64c8f5f3d0bbfcaf3b88aa97a23d90c14326b79e.tar.bz2 busybox-w32-64c8f5f3d0bbfcaf3b88aa97a23d90c14326b79e.zip |
ash: add support for INT trap
The trap builtin now handles INT. As before, other signals (from
a limited list) are accepted but their traps don't have any effect.
There's some variability in how shells handle traps. This patch
upholds that proud tradition.
Various changes are needed to make this work:
- Line editing has a new flag to ignore Ctrl-C.
- If INT is being trapped or ignored don't call raise_interrupt().
- A minimal implementation of dotrap() is provided.
- Call dotrap() when the read builtin or line input detect Ctrl-C.
- Adjust Ctrl-C detection when the INT trap is changed.
- Set may_have_traps when an INT trap is set.
Costs 368-448 bytes.
(GitHub issue #303)
-rw-r--r-- | include/libbb.h | 3 | ||||
-rw-r--r-- | libbb/lineedit.c | 4 | ||||
-rw-r--r-- | shell/ash.c | 91 |
3 files changed, 88 insertions, 10 deletions
diff --git a/include/libbb.h b/include/libbb.h index 4276bae61..2841d7fbf 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -2040,6 +2040,9 @@ enum { | |||
2040 | VI_MODE = 8 * ENABLE_FEATURE_EDITING_VI, | 2040 | VI_MODE = 8 * ENABLE_FEATURE_EDITING_VI, |
2041 | WITH_PATH_LOOKUP = 0x10, | 2041 | WITH_PATH_LOOKUP = 0x10, |
2042 | LI_INTERRUPTIBLE = 0x20, | 2042 | LI_INTERRUPTIBLE = 0x20, |
2043 | #if ENABLE_PLATFORM_MINGW32 | ||
2044 | IGNORE_CTRL_C = 0x40, | ||
2045 | #endif | ||
2043 | FOR_SHELL = DO_HISTORY | TAB_COMPLETION | USERNAME_COMPLETION | LI_INTERRUPTIBLE, | 2046 | FOR_SHELL = DO_HISTORY | TAB_COMPLETION | USERNAME_COMPLETION | LI_INTERRUPTIBLE, |
2044 | }; | 2047 | }; |
2045 | line_input_t *new_line_input_t(int flags) FAST_FUNC; | 2048 | line_input_t *new_line_input_t(int flags) FAST_FUNC; |
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 9220ddeb3..45d4c33f5 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -3030,6 +3030,10 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
3030 | && ic_raw == initial_settings.c_cc[VINTR] | 3030 | && ic_raw == initial_settings.c_cc[VINTR] |
3031 | ) { | 3031 | ) { |
3032 | /* Ctrl-C (usually) - stop gathering input */ | 3032 | /* Ctrl-C (usually) - stop gathering input */ |
3033 | #if ENABLE_PLATFORM_MINGW32 | ||
3034 | if (state->flags & IGNORE_CTRL_C) | ||
3035 | break; | ||
3036 | #endif | ||
3033 | command_len = 0; | 3037 | command_len = 0; |
3034 | break_out = -1; /* "do not append '\n'" */ | 3038 | break_out = -1; /* "do not append '\n'" */ |
3035 | break; | 3039 | break; |
diff --git a/shell/ash.c b/shell/ash.c index d15a6f685..a748b003f 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -627,8 +627,8 @@ struct globals_misc { | |||
627 | 627 | ||
628 | /* indicates specified signal received */ | 628 | /* indicates specified signal received */ |
629 | uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ | 629 | uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ |
630 | uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */ | ||
631 | #endif | 630 | #endif |
631 | uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */ | ||
632 | char *trap[NSIG + 1]; | 632 | char *trap[NSIG + 1]; |
633 | /* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */ | 633 | /* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */ |
634 | #define NTRAP_ERR NSIG | 634 | #define NTRAP_ERR NSIG |
@@ -687,10 +687,8 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc; | |||
687 | #if ENABLE_PLATFORM_MINGW32 | 687 | #if ENABLE_PLATFORM_MINGW32 |
688 | #undef got_sigchld | 688 | #undef got_sigchld |
689 | #undef pending_sig | 689 | #undef pending_sig |
690 | #undef may_have_traps | ||
691 | #undef trap_ptr | 690 | #undef trap_ptr |
692 | #define pending_sig (0) | 691 | #define pending_sig (0) |
693 | #define may_have_traps (0) | ||
694 | #define trap_ptr trap | 692 | #define trap_ptr trap |
695 | #endif | 693 | #endif |
696 | 694 | ||
@@ -869,10 +867,17 @@ raise_exception(int e) | |||
869 | * are held using the INT_OFF macro. (The test for iflag is just | 867 | * are held using the INT_OFF macro. (The test for iflag is just |
870 | * defensive programming.) | 868 | * defensive programming.) |
871 | */ | 869 | */ |
872 | static void raise_interrupt(void) NORETURN; | 870 | static void raise_interrupt(void) IF_NOT_PLATFORM_MINGW32(NORETURN); |
873 | static void | 871 | static void |
874 | raise_interrupt(void) | 872 | raise_interrupt(void) |
875 | { | 873 | { |
874 | #if ENABLE_PLATFORM_MINGW32 | ||
875 | /* Contrary to the comment above on Windows raise_interrupt() is | ||
876 | * called when SIGINT is trapped or ignored. We detect this here | ||
877 | * and return without doing anything. */ | ||
878 | if (trap[SIGINT]) | ||
879 | return; | ||
880 | #endif | ||
876 | pending_int = 0; | 881 | pending_int = 0; |
877 | #if !ENABLE_PLATFORM_MINGW32 | 882 | #if !ENABLE_PLATFORM_MINGW32 |
878 | /* Signal is not automatically unmasked after it is raised, | 883 | /* Signal is not automatically unmasked after it is raised, |
@@ -4820,8 +4825,11 @@ sprint_status48(char *os, int status, int sigonly) | |||
4820 | static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) | 4825 | static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) |
4821 | { | 4826 | { |
4822 | if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) { | 4827 | if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) { |
4823 | if (!suppress_int && !(rootshell && iflag)) | 4828 | # if ENABLE_FEATURE_EDITING |
4824 | raise_interrupt(); /* does not return */ | 4829 | bb_got_signal = SIGINT; /* for read_line_input: "we got a signal" */ |
4830 | # endif | ||
4831 | if (!suppress_int) | ||
4832 | raise_interrupt(); | ||
4825 | pending_int = 1; | 4833 | pending_int = 1; |
4826 | return TRUE; | 4834 | return TRUE; |
4827 | } | 4835 | } |
@@ -10184,7 +10192,43 @@ dotrap(void) | |||
10184 | TRACE(("dotrap returns\n")); | 10192 | TRACE(("dotrap returns\n")); |
10185 | } | 10193 | } |
10186 | #else | 10194 | #else |
10187 | # define dotrap() | 10195 | static void |
10196 | dotrap(void) | ||
10197 | { | ||
10198 | int status, last_status; | ||
10199 | char *p; | ||
10200 | |||
10201 | if (!pending_int) | ||
10202 | return; | ||
10203 | |||
10204 | status = savestatus; | ||
10205 | last_status = status; | ||
10206 | if (status < 0) { | ||
10207 | status = exitstatus; | ||
10208 | savestatus = status; | ||
10209 | } | ||
10210 | pending_int = 0; | ||
10211 | barrier(); | ||
10212 | |||
10213 | TRACE(("dotrap entered\n")); | ||
10214 | if (evalskip) { | ||
10215 | pending_int = 1; | ||
10216 | return; | ||
10217 | } | ||
10218 | |||
10219 | p = trap[SIGINT]; | ||
10220 | if (p) { | ||
10221 | TRACE(("sig %d is active, will run handler '%s'\n", SIGINT, p)); | ||
10222 | trap_depth++; | ||
10223 | evalstring(p, 0); | ||
10224 | trap_depth--; | ||
10225 | if (evalskip != SKIPFUNC) | ||
10226 | exitstatus = status; | ||
10227 | } | ||
10228 | |||
10229 | savestatus = last_status; | ||
10230 | TRACE(("dotrap returns\n")); | ||
10231 | } | ||
10188 | #endif | 10232 | #endif |
10189 | 10233 | ||
10190 | /* forward declarations - evaluation is fairly recursive business... */ | 10234 | /* forward declarations - evaluation is fairly recursive business... */ |
@@ -11853,8 +11897,13 @@ preadfd(void) | |||
11853 | */ | 11897 | */ |
11854 | # else | 11898 | # else |
11855 | raise_interrupt(); | 11899 | raise_interrupt(); |
11900 | write(STDOUT_FILENO, "^C\n", 3); | ||
11856 | # endif | 11901 | # endif |
11857 | if (trap[SIGINT]) { | 11902 | if (trap[SIGINT]) { |
11903 | # if ENABLE_PLATFORM_MINGW32 | ||
11904 | pending_int = 1; | ||
11905 | dotrap(); | ||
11906 | # endif | ||
11858 | empty_line_input: | 11907 | empty_line_input: |
11859 | buf[0] = '\n'; | 11908 | buf[0] = '\n'; |
11860 | buf[1] = '\0'; | 11909 | buf[1] = '\0'; |
@@ -15088,12 +15137,30 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
15088 | } | 15137 | } |
15089 | free(trap[signo]); | 15138 | free(trap[signo]); |
15090 | trap[signo] = action; | 15139 | trap[signo] = action; |
15140 | #if ENABLE_PLATFORM_MINGW32 | ||
15141 | if (signo == SIGINT) { | ||
15142 | // trap '' INT disables Ctrl-C, anything else enables it | ||
15143 | if (action && action[0] == '\0') { | ||
15144 | SetConsoleCtrlHandler(NULL, TRUE); | ||
15145 | if (line_input_state) { | ||
15146 | line_input_state->flags |= IGNORE_CTRL_C; | ||
15147 | } | ||
15148 | } else { | ||
15149 | SetConsoleCtrlHandler(NULL, FALSE); | ||
15150 | if (line_input_state) { | ||
15151 | line_input_state->flags &= ~IGNORE_CTRL_C; | ||
15152 | } | ||
15153 | } | ||
15154 | } | ||
15155 | #else | ||
15091 | if (signo != 0 && signo < NSIG) | 15156 | if (signo != 0 && signo < NSIG) |
15092 | setsignal(signo); | 15157 | setsignal(signo); |
15158 | #endif | ||
15093 | INT_ON; | 15159 | INT_ON; |
15094 | next: | 15160 | next: |
15095 | ap++; | 15161 | ap++; |
15096 | } | 15162 | } |
15163 | may_have_traps = trap[SIGINT] && trap[SIGINT][0] != '\0'; | ||
15097 | return exitcode; | 15164 | return exitcode; |
15098 | } | 15165 | } |
15099 | 15166 | ||
@@ -15391,10 +15458,14 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
15391 | #if ENABLE_PLATFORM_MINGW32 | 15458 | #if ENABLE_PLATFORM_MINGW32 |
15392 | if ((uintptr_t)r == 2) { | 15459 | if ((uintptr_t)r == 2) { |
15393 | /* ^C pressed, propagate event */ | 15460 | /* ^C pressed, propagate event */ |
15394 | if (iflag) { | 15461 | if (trap[SIGINT]) { |
15462 | write(STDOUT_FILENO, "^C", 2); | ||
15463 | pending_int = 1; | ||
15464 | dotrap(); | ||
15465 | goto again; | ||
15466 | } else if (iflag) { | ||
15395 | raise_interrupt(); | 15467 | raise_interrupt(); |
15396 | } | 15468 | } else { |
15397 | else { | ||
15398 | GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); | 15469 | GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); |
15399 | exitshell(); | 15470 | exitshell(); |
15400 | } | 15471 | } |