ash,hush: fix SIGCHLD interrupting read builtin
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 22 May 2017 17:34:45 +0000 (19:34 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 22 May 2017 17:34:45 +0000 (19:34 +0200)
function                                             old     new   delta
readcmd                                              169     217     +48
shell_builtin_read                                  1087    1097     +10
localcmd                                             366     364      -2
builtin_read                                         197     193      -4
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/2 up/down: 58/-6)              Total: 52 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c
shell/ash_test/ash-read/read_SIGCHLD.right [new file with mode: 0644]
shell/ash_test/ash-read/read_SIGCHLD.tests [new file with mode: 0755]
shell/hush.c
shell/hush_test/hush-read/read_SIGCHLD.right [new file with mode: 0644]
shell/hush_test/hush-read/read_SIGCHLD.tests [new file with mode: 0755]
shell/shell_common.c

index 70ee15ed8016607c48aaf6cc4587e28ac19d41b0..60c8ffeb7d45c1a1da641c900e8fac3dce8ae0d4 100644 (file)
@@ -13268,6 +13268,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
        /* "read -s" needs to save/restore termios, can't allow ^C
         * to jump out of it.
         */
+ again:
        INT_OFF;
        r = shell_builtin_read(setvar0,
                argptr,
@@ -13280,6 +13281,12 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
        );
        INT_ON;
 
+       if ((uintptr_t)r == 1 && errno == EINTR) {
+               /* to get SIGCHLD: sleep 1 & read x; echo $x */
+               if (pending_sig == 0)
+                       goto again;
+       }
+
        if ((uintptr_t)r > 1)
                ash_msg_and_raise_error(r);
 
diff --git a/shell/ash_test/ash-read/read_SIGCHLD.right b/shell/ash_test/ash-read/read_SIGCHLD.right
new file mode 100644 (file)
index 0000000..b3dc7ab
--- /dev/null
@@ -0,0 +1,2 @@
+x='Ok'
+exitcode:0
diff --git a/shell/ash_test/ash-read/read_SIGCHLD.tests b/shell/ash_test/ash-read/read_SIGCHLD.tests
new file mode 100755 (executable)
index 0000000..c5f673a
--- /dev/null
@@ -0,0 +1,4 @@
+x=BAD
+{ sleep 0.4; echo Ok; } | { sleep 0.2 & read x; echo "x='$x'"; }
+echo "exitcode:$?"
+
index e18920f508f5b98daf5963c3fb7c9697d727c8a8..125463a56a0d4a295d9a7659f771b1a73c4646aa 100644 (file)
@@ -9038,6 +9038,9 @@ static int FAST_FUNC builtin_type(char **argv)
  * - terminates shell (regardless of interactivity);
  * if it has non-empty trap:
  * - executes trap and returns to read;
+ * SIGCHLD from children:
+ * - does not interrupt read regardless of interactivity:
+ *   try: sleep 1 & read x; echo $x
  */
 static int FAST_FUNC builtin_read(char **argv)
 {
@@ -9071,7 +9074,7 @@ static int FAST_FUNC builtin_read(char **argv)
 
        if ((uintptr_t)r == 1 && errno == EINTR) {
                unsigned sig = check_and_run_traps();
-               if (sig && sig != SIGINT)
+               if (sig != SIGINT)
                        goto again;
        }
 
diff --git a/shell/hush_test/hush-read/read_SIGCHLD.right b/shell/hush_test/hush-read/read_SIGCHLD.right
new file mode 100644 (file)
index 0000000..b3dc7ab
--- /dev/null
@@ -0,0 +1,2 @@
+x='Ok'
+exitcode:0
diff --git a/shell/hush_test/hush-read/read_SIGCHLD.tests b/shell/hush_test/hush-read/read_SIGCHLD.tests
new file mode 100755 (executable)
index 0000000..c5f673a
--- /dev/null
@@ -0,0 +1,4 @@
+x=BAD
+{ sleep 0.4; echo Ok; } | { sleep 0.2 & read x; echo "x='$x'"; }
+echo "exitcode:$?"
+
index fb86e680fbdf39f638380854321cf4d916a906c0..03b7d0b7571c11a9fe2fe6f6d1fe3126419d6da8 100644 (file)
@@ -204,15 +204,17 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
                c = buffer[bufpos];
                if (c == '\0')
                        continue;
-               if (backslash) {
-                       backslash = 0;
-                       if (c != '\n')
-                               goto put;
-                       continue;
-               }
-               if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') {
-                       backslash = 1;
-                       continue;
+               if (!(read_flags & BUILTIN_READ_RAW)) {
+                       if (backslash) {
+                               backslash = 0;
+                               if (c != '\n')
+                                       goto put;
+                               continue;
+                       }
+                       if (c == '\\') {
+                               backslash = 1;
+                               continue;
+                       }
                }
                if (c == '\n')
                        break;