diff --git a/term-utils/script.c b/term-utils/script.c index 12abdd674..26ec8ff9c 100644 --- a/term-utils/script.c +++ b/term-utils/script.c @@ -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 (i == 1) { + struct signalfd_siginfo info; + ssize_t bytes; - 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); + 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); + + 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 resize(int dummy __attribute__((__unused__))) -{ - gctl->resized = 1; -} 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); + pfd[0].fd = ctl->master; + pfd[0].events = POLLIN; + pfd[1].fd = ctl->sigfd; + pfd[1].events = POLLIN | POLLERR | POLLHUP; - 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")); + 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); - } - } while (1); + 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; - done(ctl); + 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(); + } + } + } + } + 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) {