]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
script: remove io vs signal race
authorSami Kerola <kerolasa@iki.fi>
Fri, 26 Dec 2014 17:43:49 +0000 (17:43 +0000)
committerSami Kerola <kerolasa@iki.fi>
Mon, 8 Jun 2015 20:52:51 +0000 (21:52 +0100)
Make do_io() to run poll() until all streams are empty.  This should
remove the signal from child versus io handling race for good.

Addresses: https://github.com/karelzak/util-linux/pull/62
Addresses: https://bugs.launchpad.net/bugs/264967
Addresses: https://bugs.debian.org/305808
CC: Wolfgang Richter <wolf@cs.cmu.edu>
CC: Ruediger Meier <ruediger.meier@ga-group.nl>
Signed-off-by: Sami Kerola <kerolasa@iki.fi>
term-utils/script.c
tests/ts/script/race

index ea776cf72a687e88948e713abce9a6c242125e76..391aa9e5ec2f434fa97d3bfacf2ed5db3b140060 100644 (file)
@@ -92,6 +92,7 @@ struct script_control {
        FILE *timingfp;         /* timing file pointer */
        int master;             /* pseudoterminal master file descriptor */
        int slave;              /* pseudoterminal slave file descriptor */
+       int poll_timeout;       /* poll() timeout, used in end of execution */
        pid_t child;            /* child pid */
        int childstatus;        /* child process exit value */
        struct termios tt;      /* slave terminal runtime attributes */
@@ -205,16 +206,11 @@ static void finish(struct script_control *ctl, int wait)
 {
        int status;
        pid_t pid;
-       int errsv = errno;
        int options = wait ? 0 : WNOHANG;
 
        while ((pid = wait3(&status, options, 0)) > 0)
-               if (pid == ctl->child) {
+               if (pid == ctl->child)
                        ctl->childstatus = status;
-                       ctl->die = 1;
-               }
-
-       errno = errsv;
 }
 
 static void write_output(struct script_control *ctl, char *obuf,
@@ -270,13 +266,15 @@ static void do_io(struct script_control *ctl)
 
        while (!ctl->die) {
                /* wait for input or signal */
-               ret = poll(pfd, POLLFDS, -1);
+               ret = poll(pfd, POLLFDS, ctl->poll_timeout);
                if (ret < 0) {
                        if (errno == EAGAIN)
                                continue;
                        warn(_("poll failed"));
                        fail(ctl);
                }
+               if (ret == 0)
+                       ctl->die = 1;
                for (i = 0; i < POLLFDS; i++) {
                        if (pfd[i].revents == 0)
                                continue;
@@ -315,6 +313,13 @@ static void do_io(struct script_control *ctl)
                                switch (info.ssi_signo) {
                                case SIGCHLD:
                                        finish(ctl, 0);
+                                       ctl->poll_timeout = 10;
+                                       if (!ctl->isterm)
+                                               /* In situation such as 'date' in
+                                               * $ echo date | ./script
+                                               * ignore input when shell has
+                                               * exited.  */
+                                               pfd[0].fd = -1;
                                        break;
                                case SIGWINCH:
                                        if (ctl->isterm) {
@@ -496,6 +501,7 @@ int main(int argc, char **argv)
                .line = "/dev/ptyXX",
 #endif
                .master = -1,
+               .poll_timeout = -1,
                0
        };
        int ch;
index 02c8187d6ad661ebe7f58bf71aad80a1ce13030c..65d48e29fdb11d19606d29f52805b76e4601b22c 100755 (executable)
@@ -27,10 +27,6 @@ ts_init "$*"
 
 ts_check_test_command "$TS_CMD_SCRIPT"
 
-# TODO see comments about script design
-# https://github.com/karelzak/util-linux/pull/62
-TS_KNOWN_FAIL="yes"
-
 bingofile="$TS_OUTDIR/${TS_TESTNAME}-bingo"
 
 count=1000