hush: use SA_RESTARTed signal handlers across read.

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/shell/hush.c b/shell/hush.c
index 71972f7..509bd41 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -792,8 +792,13 @@
 	unsigned handled_SIGCHLD;
 	smallint we_have_children;
 #endif
-	/* which signals have non-DFL handler (even with no traps set)? */
-	unsigned non_DFL_mask;
+	/* Which signals have non-DFL handler (even with no traps set)?
+	 * Set at the start to:
+	 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOB_SIGS)
+	 * SPECIAL_INTERACTIVE_SIGS are cleared after fork.
+	 * Other than these two times, never modified.
+	 */
+	unsigned special_sig_mask;
 	char **traps; /* char *traps[NSIG] */
 	/* Signal mask on the entry to the (top-level) shell. Never modified. */
 	sigset_t inherited_set;
@@ -1341,11 +1346,11 @@
  * After each pipe execution, we extract any pending signals via sigtimedwait()
  * and act on them.
  *
- * unsigned non_DFL_mask: a mask of such "special" signals
+ * unsigned special_sig_mask: a mask of such "special" signals
  * sigset_t blocked_set:  current blocked signal set
  *
  * "trap - SIGxxx":
- *    clear bit in blocked_set unless it is also in non_DFL_mask
+ *    clear bit in blocked_set unless it is also in special_sig_mask
  * "trap 'cmd' SIGxxx":
  *    set bit in blocked_set (even if 'cmd' is '')
  * after [v]fork, if we plan to be a shell:
@@ -5376,7 +5381,7 @@
 	 * Testcase: (while :; do :; done) + ^Z should background.
 	 * Same goes for SIGTERM, SIGHUP, SIGINT.
 	 */
-	if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS))
+	if (!G.traps && !(G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS))
 		return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */
 
 	/* Switching off SPECIAL_INTERACTIVE_SIGS.
@@ -5394,10 +5399,10 @@
 		}
 	}
 	/* Our homegrown sig mask is saner to work with :) */
-	G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS;
+	G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS;
 
 	/* Resetting all traps to default except empty ones */
-	mask = G.non_DFL_mask;
+	mask = G.special_sig_mask;
 	if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) {
 		if (!G.traps[sig] || !G.traps[sig][0])
 			continue;
@@ -7440,9 +7445,6 @@
 
 	/* POSIX allows shell to re-enable SIGCHLD
 	 * even if it was SIG_IGN on entry */
-#if ENABLE_HUSH_FAST
-	G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
-#endif
 	if (!G.inherited_set_is_saved) {
 #if ENABLE_HUSH_FAST
 		signal(SIGCHLD, SIGCHLD_handler);
@@ -7460,7 +7462,7 @@
 		if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */
 			mask |= SPECIAL_JOB_SIGS;
 	}
-	G.non_DFL_mask = mask;
+	G.special_sig_mask = mask;
 
 	/* Block them. And unblock SIGCHLD */
 	sig = 0;
@@ -7504,10 +7506,10 @@
 		/*+ (1 << SIGINT )*/
 	;
 
-	/* non_DFL_mask'ed signals are, well, masked,
+	/* special_sig_mask'ed signals are, well, masked,
 	 * no need to set handler for them.
 	 */
-	fatal_sigs &= ~G.non_DFL_mask;
+	fatal_sigs &= ~G.special_sig_mask;
 
         /* For each sig in fatal_sigs... */
 	sig = 0;
@@ -7571,8 +7573,11 @@
 	struct variable *shell_ver;
 
 	INIT_G();
-	if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */
+	if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
 		G.last_exitcode = EXIT_SUCCESS;
+#if ENABLE_HUSH_FAST
+	G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
+#endif
 #if !BB_MMU
 	G.argv0_for_re_execing = argv[0];
 #endif
@@ -8303,7 +8308,7 @@
 				/* There was a trap handler, we are removing it
 				 * (if sig has non-DFL handling,
 				 * we don't need to do anything) */
-				if (sig < 32 && (G.non_DFL_mask & (1 << sig)))
+				if (sig < sizeof(G.special_sig_mask)*8 && (G.special_sig_mask & (1 << sig)))
 					continue;
 				sigdelset(&G.blocked_set, sig);
 			}
@@ -8565,7 +8570,7 @@
 
 		memset(&sa, 0, sizeof(sa));
 		sigfillset(&sa.sa_mask);
-		/*sa.sa_flags = 0;*/
+		sa.sa_flags = SA_RESTART;
 		sa.sa_handler = record_signal;
 
 		sig = 0;
diff --git a/shell/shell_common.c b/shell/shell_common.c
index a5c455c..bbc22ed 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -159,32 +159,40 @@
 	bufpos = 0;
 	do {
 		char c;
+		struct pollfd pfd[1];
+		int timeout;
 
-		errno = 0;
+		if ((bufpos & 0xff) == 0)
+			buffer = xrealloc(buffer, bufpos + 0x100);
 
+		timeout = -1;
 		if (end_ms) {
-			int timeout;
-			struct pollfd pfd[1];
-
-			pfd[0].fd = fd;
-			pfd[0].events = POLLIN;
 			timeout = end_ms - (unsigned)monotonic_ms();
-			if (timeout <= 0 /* already late? */
-			 || poll(pfd, 1, timeout) != 1 /* no? wait... */
-			) { /* timed out! */
-				err = errno;
+			if (timeout <= 0) { /* already late? */
 				retval = (const char *)(uintptr_t)1;
 				goto ret;
 			}
 		}
 
-		if ((bufpos & 0xff) == 0)
-			buffer = xrealloc(buffer, bufpos + 0x100);
-		if (nonblock_immune_read(fd, &buffer[bufpos], 1, /*loop_on_EINTR:*/ 0) != 1) {
+		/* We must poll even if timeout is -1:
+		 * we want to be interrupted if signal arrives,
+		 * regardless of SA_RESTART-ness of that signal!
+		 */
+		errno = 0;
+		pfd[0].fd = fd;
+		pfd[0].events = POLLIN;
+		if (poll(pfd, 1, timeout) != 1) {
+			/* timed out, or EINTR */
+			err = errno;
+			retval = (const char *)(uintptr_t)1;
+			goto ret;
+		}
+		if (read(fd, &buffer[bufpos], 1) != 1) {
 			err = errno;
 			retval = (const char *)(uintptr_t)1;
 			break;
 		}
+
 		c = buffer[bufpos];
 		if (c == '\0')
 			continue;