]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
script: use poll() rather than select()
authorSami Kerola <kerolasa@iki.fi>
Fri, 26 Dec 2014 12:37:36 +0000 (12:37 +0000)
committerSami Kerola <kerolasa@iki.fi>
Sat, 6 Jun 2015 13:53:56 +0000 (14:53 +0100)
Finalize the signalfd() change by adding file descriptors to poll() loop.

Addresses: https://github.com/karelzak/util-linux/pull/62
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

index 12abdd67431dbbfc89112724eecbe274579cd5b3..26ec8ff9ce47678ab0aab29f8f5f48a5f1480a37 100644 (file)
@@ -82,6 +82,8 @@
 
 #define DEFAULT_OUTPUT "typescript"
 
+enum { POLLFDS = 2 };
+
 struct script_control {
        char *shell;            /* shell to be executed */
        char *cflg;             /* command to be executed */
@@ -106,12 +108,10 @@ struct script_control {
         tflg:1,                /* include timing file */
         forceflg:1,            /* write output to links */
         isterm:1,              /* is child process running as terminal */
-        resized:1,             /* has terminal been resized */
         die:1;                 /* terminate program */
        sigset_t sigset;        /* catch SIGCHLD and SIGWINCH with signalfd() */
        int sigfd;              /* file descriptor for signalfd() */
 };
-struct script_control *gctl;   /* global control structure, used in signal handlers */
 
 /*
  * For tests we want to be able to control time output
@@ -253,10 +253,10 @@ static void finish(struct script_control *ctl, int wait)
 
 static void doinput(struct script_control *ctl)
 {
-       int errsv = 0;
-       ssize_t cc = 0;
        char ibuf[BUFSIZ];
-       fd_set readfds;
+       struct pollfd pfd[POLLFDS];
+       int ret, i;
+       ssize_t bytes;
 
        /* close things irrelevant for this process */
        if (ctl->typescriptfp)
@@ -265,35 +265,49 @@ static void doinput(struct script_control *ctl)
                fclose(ctl->timingfp);
        ctl->typescriptfp = ctl->timingfp = NULL;
 
-       FD_ZERO(&readfds);
+       pfd[0].fd = STDIN_FILENO;
+       pfd[0].events = POLLIN;
+       pfd[1].fd = ctl->sigfd;
+       pfd[1].events = POLLIN | POLLERR | POLLHUP;
 
        while (!ctl->die) {
-               FD_SET(STDIN_FILENO, &readfds);
-
-               errno = 0;
-               /* wait for input or signal (including SIGCHLD) */
-               if ((cc = pselect(STDIN_FILENO + 1, &readfds, NULL, NULL, NULL,
-                                 NULL)) > 0) {
-
-                       if ((cc = read(STDIN_FILENO, ibuf, BUFSIZ)) > 0) {
-                               if (write_all(ctl->master, ibuf, cc)) {
+               /* wait for input or signal */
+               ret = poll(pfd, POLLFDS, -1);
+               if (ret < 0) {
+                       if (errno == EAGAIN)
+                               continue;
+                       warn(_("poll failed"));
+                       fail(ctl);
+               }
+               for (i = 0; i < POLLFDS; i++) {
+                       if (pfd[i].revents == 0)
+                               continue;
+                       if (i == 0 && (bytes = read(pfd[i].fd, ibuf, BUFSIZ)) > 0) {
+                               if (write_all(ctl->master, ibuf, bytes)) {
                                        warn(_("write failed"));
                                        fail(ctl);
                                }
                        }
-               }
-
-               if (cc < 0 && errno == EINTR && ctl->resized) {
-                       /* transmit window change information to the child */
-                       if (ctl->isterm) {
-                               ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
-                               ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
+                       if (i == 1) {
+                               struct signalfd_siginfo info;
+                               ssize_t bytes;
+
+                               bytes = read(pfd[i].fd, &info, sizeof(info));
+                               assert(bytes == sizeof(info));
+                               switch (info.ssi_signo) {
+                               case SIGCHLD:
+                                       finish(ctl, 0);
+                                       break;
+                               case SIGWINCH:
+                                       if (ctl->isterm) {
+                                               ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
+                                               ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
+                                       }
+                                       break;
+                               default:
+                                       abort();
+                               }
                        }
-                       ctl->resized = 0;
-
-               } else if (cc <= 0 && errno != EINTR) {
-                       errsv = errno;
-                       break;
                }
        }
 
@@ -301,7 +315,7 @@ static void doinput(struct script_control *ctl)
        wait_for_empty_fd(ctl, ctl->slave);
        wait_for_empty_fd(ctl, ctl->master);
 
-       if (ctl->die == 0 && cc == 0 && errsv == 0) {
+       if (ctl->die == 0) {
                /*
                 * Forward EOF from stdin (detected by read() above) to slave
                 * (shell) to correctly terminate the session. It seems we have
@@ -328,24 +342,41 @@ static void doinput(struct script_control *ctl)
        done(ctl);
 }
 
-static void sig_finish(int dummy __attribute__((__unused__)))
+static void write_output(struct script_control *ctl, char *obuf,
+                           ssize_t bytes, double *oldtime)
 {
-       finish(gctl, 0);
-}
 
-static void resize(int dummy __attribute__((__unused__)))
-{
-       gctl->resized = 1;
+       if (ctl->tflg && ctl->timingfp) {
+               struct timeval tv;
+               double newtime;
+
+               gettimeofday(&tv, NULL);
+               newtime = tv.tv_sec + (double)tv.tv_usec / 1000000;
+               fprintf(ctl->timingfp, "%f %zd\n", newtime - *oldtime, bytes);
+               if (ctl->fflg)
+                       fflush(ctl->timingfp);
+               *oldtime = newtime;
+       }
+       if (fwrite_all(obuf, 1, bytes, ctl->typescriptfp)) {
+               warn(_("cannot write script file"));
+               fail(ctl);
+       }
+       if (ctl->fflg)
+               fflush(ctl->typescriptfp);
+       if (write_all(STDOUT_FILENO, obuf, bytes)) {
+               warn(_("write failed"));
+               fail(ctl);
+       }
 }
 
+
 static void dooutput(struct script_control *ctl)
 {
-       ssize_t cc;
        char obuf[BUFSIZ];
-       struct timeval tv;
-       double oldtime = time(NULL), newtime;
-       int errsv = 0;
-       fd_set readfds;
+       struct pollfd pfd[POLLFDS];
+       int ret, i;
+       ssize_t bytes;
+       double oldtime = time(NULL);
 
        close(STDIN_FILENO);
 #ifdef HAVE_LIBUTIL
@@ -360,57 +391,53 @@ static void dooutput(struct script_control *ctl)
                fprintf(ctl->typescriptfp, _("Script started on %s"), obuf);
        }
 
-       FD_ZERO(&readfds);
-
-       do {
-               if (ctl->die || errsv == EINTR) {
-                       struct pollfd fds[] = {
-                               {.fd = ctl->sigfd, .events = POLLIN | POLLERR | POLLHUP},
-                               {.fd = ctl->master, .events = POLLIN}
-                       };
-                       if (poll(fds, 1, 50) <= 0)
-                               break;
-               }
-
-               FD_SET(ctl->master, &readfds);
-               errno = 0;
-
-               /* wait for input or signal (including SIGCHLD) */
-               if ((cc = pselect(ctl->master + 1, &readfds, NULL, NULL, NULL,
-                                 NULL)) > 0) {
-
-                       cc = read(ctl->master, obuf, sizeof(obuf));
-               }
-               errsv = errno;
-
-               if (ctl->tflg)
-                       gettimeofday(&tv, NULL);
-
-               if (errsv == EINTR && cc <= 0)
-                       continue;       /* try it again */
-               if (cc <= 0)
-                       break;
-               if (ctl->tflg && ctl->timingfp) {
-                       newtime = tv.tv_sec + (double)tv.tv_usec / 1000000;
-                       fprintf(ctl->timingfp, "%f %zd\n", newtime - oldtime, cc);
-                       oldtime = newtime;
-               }
-               if (fwrite_all(obuf, 1, cc, ctl->typescriptfp)) {
-                       warn(_("cannot write script file"));
+       pfd[0].fd = ctl->master;
+       pfd[0].events = POLLIN;
+       pfd[1].fd = ctl->sigfd;
+       pfd[1].events = POLLIN | POLLERR | POLLHUP;
+
+       while (1) {
+               /* wait for input or signal */
+               ret = poll(pfd, POLLFDS, -1);
+               if (ret < 0) {
+                       if (errno == EAGAIN)
+                               continue;
+                       warn(_("poll failed"));
                        fail(ctl);
                }
-               if (ctl->fflg) {
-                       fflush(ctl->typescriptfp);
-                       if (ctl->tflg && ctl->timingfp)
-                               fflush(ctl->timingfp);
-               }
-               if (write_all(STDOUT_FILENO, obuf, cc)) {
-                       warn(_("write failed"));
-                       fail(ctl);
+               for (i = 0; i < POLLFDS; i++) {
+                       if (pfd[i].revents == 0)
+                               continue;
+                       if (i == 0) {
+                               bytes = read(pfd[i].fd, obuf, BUFSIZ);
+                               if (bytes < 0) {
+                                       if (errno == EAGAIN)
+                                               continue;
+                                       fail(ctl);
+                               }
+                               write_output(ctl, obuf, bytes, &oldtime);
+                               continue;
+                       }
+                       if (i == 1) {
+                               struct signalfd_siginfo info;
+                               ssize_t bytes;
+
+                               bytes = read(pfd[i].fd, &info, sizeof(info));
+                               assert(bytes == sizeof(info));
+                               switch (info.ssi_signo) {
+                               case SIGCHLD:
+                                       done(ctl);
+                                       break;
+                               case SIGWINCH:
+                                       /* nothing */
+                                       break;
+                               default:
+                                       abort();
+                               }
+                       }
                }
-       } while (1);
-
-       done(ctl);
+       }
+       abort();
 }
 
 static void getslave(struct script_control *ctl)
@@ -595,7 +622,6 @@ int main(int argc, char **argv)
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
        atexit(close_stdout);
-       gctl = &ctl;
 
        while ((ch = getopt_long(argc, argv, "ac:efqt::Vh", longopts, NULL)) != -1)
                switch (ch) {