script: use poll() rather than select()
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>
This commit is contained in:
parent
cc1a88fb59
commit
cf470183ea
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue