Browse Source

shell: fix SIGWINCH and SIGCHLD (in hush) interrupting line input, closes 15256

function                                             old     new   delta
record_pending_signo                                  32      63     +31
lineedit_read_key                                    231     224      -7
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 31/-7)              Total: 24 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Denys Vlasenko 1 year ago
parent
commit
93e0898c66
3 changed files with 20 additions and 10 deletions
  1. 10 7
      libbb/lineedit.c
  2. 2 1
      shell/ash.c
  3. 8 2
      shell/hush.c

+ 10 - 7
libbb/lineedit.c

@@ -2180,7 +2180,8 @@ static int lineedit_read_key(char *read_key_buffer, int timeout)
 		 * "\xff\n",pause,"ls\n" invalid and thus won't lose "ls".
 		 *
 		 * If LI_INTERRUPTIBLE, return -1 if got EINTR in poll()
-		 * inside read_key, or if bb_got_signal != 0 (IOW: if signal
+		 * inside read_key and bb_got_signal became != 0,
+		 * or if bb_got_signal != 0 (IOW: if signal
 		 * arrived before poll() is reached).
 		 *
 		 * Note: read_key sets errno to 0 on success.
@@ -2197,14 +2198,16 @@ static int lineedit_read_key(char *read_key_buffer, int timeout)
 			IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 0;)
 			if (errno != EINTR)
 				break;
+			/* It was EINTR. Repeat read_key() unless... */
 			if (state->flags & LI_INTERRUPTIBLE) {
-				/* LI_INTERRUPTIBLE bails out on EINTR,
-				 * but nothing really guarantees that bb_got_signal
-				 * is nonzero. Follow the least surprise principle:
+				/* LI_INTERRUPTIBLE bails out on EINTR
+				 * if bb_got_signal became nonzero.
+				 * (It may stay zero: for example, our SIGWINCH
+				 * handler does not set it. This is used for signals
+				 * which should not interrupt line editing).
 				 */
-				if (bb_got_signal == 0)
-					bb_got_signal = 255;
-				goto ret;
+				if (bb_got_signal != 0)
+					goto ret; /* will return -1 */
 			}
 		}
 

+ 2 - 1
shell/ash.c

@@ -10821,7 +10821,8 @@ preadfd(void)
  again:
 		/* 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
+		 * getting EINTR in poll() and bb_got_signal became != 0,
+		 * 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) &

+ 8 - 2
shell/hush.c

@@ -1946,7 +1946,12 @@ static void record_pending_signo(int sig)
 {
 	sigaddset(&G.pending_set, sig);
 #if ENABLE_FEATURE_EDITING
-	bb_got_signal = sig; /* for read_line_input: "we got a signal" */
+	if (sig != SIGCHLD
+	 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0])
+	 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */
+	) {
+		bb_got_signal = sig; /* for read_line_input: "we got a signal" */
+	}
 #endif
 #if ENABLE_HUSH_FAST
 	if (sig == SIGCHLD) {
@@ -2669,7 +2674,8 @@ static int get_user_input(struct in_str *i)
 		} else {
 			/* 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
+			 * getting EINTR in poll() and bb_got_signal became != 0,
+			 * 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) &