summaryrefslogtreecommitdiff
path: root/shell/ash.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/ash.c
parenta277506a64404e6c4472ff89c944c4f353db1c33 (diff)
downloadbusybox-12566e7f9b5e5c5d445bc4d36991d134b431dc6c.tar.gz
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/ash.c')
-rw-r--r--shell/ash.c39
1 files changed, 30 insertions, 9 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 086773dd7..55df54bd0 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -3679,7 +3679,9 @@ signal_handler(int signo)
if (!trap[SIGCHLD])
return;
}
-
+#if ENABLE_FEATURE_EDITING
+ bb_got_signal = signo; /* for read_line_input: "we got a signal" */
+#endif
gotsig[signo - 1] = 1;
pending_sig = signo;
@@ -10784,33 +10786,52 @@ preadfd(void)
# endif
reinit_unicode_for_ash();
again:
-//BUG: not in INT_OFF/INT_ON section - SIGINT et al would longjmp out of read_line_input()!
-//This would cause a memory leak in interactive shell
-//(repeated internal allocations in read_line_input):
-// (while kill -INT $$; do :; done) &
+ /* For shell, LI_INTERRUPTIBLE is set:
+ * read_line_input will abort on either
+ * getting EINTR in poll(), or if it sees bb_got_signal != 0
+ * (IOW: if signal arrives before poll() is reached).
+ * Interactive testcases:
+ * (while kill -INT $$; do sleep 1; done) &
+ * #^^^ prints ^C, prints prompt, repeats
+ * trap 'echo I' int; (while kill -INT $$; do sleep 1; done) &
+ * #^^^ prints ^C, prints "I", prints prompt, repeats
+ * trap 'echo T' term; (while kill $$; do sleep 1; done) &
+ * #^^^ prints "T", prints prompt, repeats
+ * #(bash 5.0.17 exits after first "T", looks like a bug)
+ */
+ bb_got_signal = 0;
+ INT_OFF; /* no longjmp'ing out of read_line_input please */
nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ);
+ if (bb_got_signal == SIGINT)
+ write(STDOUT_FILENO, "^C\n", 3);
+ INT_ON; /* here non-blocked SIGINT will longjmp */
if (nr == 0) {
/* ^C pressed, "convert" to SIGINT */
- write(STDOUT_FILENO, "^C", 2);
- raise(SIGINT);
+ write(STDOUT_FILENO, "^C\n", 3);
+ raise(SIGINT); /* here non-blocked SIGINT will longjmp */
/* raise(SIGINT) did not work! (e.g. if SIGINT
* is SIG_IGNed on startup, it stays SIG_IGNed)
*/
if (trap[SIGINT]) {
+ empty_line_input:
buf[0] = '\n';
buf[1] = '\0';
return 1;
}
exitstatus = 128 + SIGINT;
/* bash behavior on ^C + ignored SIGINT: */
- write(STDOUT_FILENO, "\n", 1);
goto again;
}
if (nr < 0) {
if (errno == 0) {
- /* Ctrl+D pressed */
+ /* ^D pressed */
nr = 0;
}
+ else if (errno == EINTR) { /* got signal? */
+ if (bb_got_signal != SIGINT)
+ write(STDOUT_FILENO, "\n", 1);
+ goto empty_line_input;
+ }
# if ENABLE_ASH_IDLE_TIMEOUT
else if (errno == EAGAIN && timeout > 0) {
puts("\007timed out waiting for input: auto-logout");