summaryrefslogtreecommitdiffstats
path: root/term-utils/script.c
diff options
context:
space:
mode:
authorSami Kerola2014-12-26 16:57:02 +0100
committerSami Kerola2015-06-08 22:52:36 +0200
commit6ddf53a5b4151591ec68d701770252a3a09e1c18 (patch)
treee589161932f44a2c976e331c7a9c1a9b6267d701 /term-utils/script.c
parentscript: use poll() rather than select() (diff)
downloadkernel-qcow2-util-linux-6ddf53a5b4151591ec68d701770252a3a09e1c18.tar.gz
kernel-qcow2-util-linux-6ddf53a5b4151591ec68d701770252a3a09e1c18.tar.xz
kernel-qcow2-util-linux-6ddf53a5b4151591ec68d701770252a3a09e1c18.zip
script: merge doinput() and output() functions to do_io()
One item to poll() more is a lot less work for system than separete input and output processes. 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>
Diffstat (limited to 'term-utils/script.c')
-rw-r--r--term-utils/script.c231
1 files changed, 59 insertions, 172 deletions
diff --git a/term-utils/script.c b/term-utils/script.c
index 26ec8ff9c..ea776cf72 100644
--- a/term-utils/script.c
+++ b/term-utils/script.c
@@ -82,7 +82,7 @@
#define DEFAULT_OUTPUT "typescript"
-enum { POLLFDS = 2 };
+enum { POLLFDS = 3 };
struct script_control {
char *shell; /* shell to be executed */
@@ -93,7 +93,6 @@ struct script_control {
int master; /* pseudoterminal master file descriptor */
int slave; /* pseudoterminal slave file descriptor */
pid_t child; /* child pid */
- pid_t subchild; /* subchild pid */
int childstatus; /* child process exit value */
struct termios tt; /* slave terminal runtime attributes */
struct winsize win; /* terminal window size */
@@ -177,39 +176,15 @@ static void my_strftime(char *buf, size_t len, const char *fmt, const struct tm
static void __attribute__((__noreturn__)) done(struct script_control *ctl)
{
- time_t tvec;
-
- if (ctl->subchild) {
- /* output process */
- if (ctl->typescriptfp) {
- if (!ctl->qflg) {
- char buf[BUFSIZ];
- tvec = time((time_t *)NULL);
- my_strftime(buf, sizeof buf, "%c\n", localtime(&tvec));
- fprintf(ctl->typescriptfp, _("\nScript done on %s"), buf);
- }
- if (close_stream(ctl->typescriptfp) != 0)
- errx(EXIT_FAILURE, _("write error"));
- ctl->typescriptfp = NULL;
- }
- if (ctl->timingfp && close_stream(ctl->timingfp) != 0)
- errx(EXIT_FAILURE, _("write error"));
- ctl->timingfp = NULL;
-
- close(ctl->master);
- ctl->master = -1;
- } else {
- /* input process */
- if (ctl->isterm)
- tcsetattr(STDIN_FILENO, TCSADRAIN, &ctl->tt);
- if (!ctl->qflg)
- printf(_("Script done, file is %s\n"), ctl->fname);
+ if (ctl->isterm)
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &ctl->tt);
+ if (!ctl->qflg)
+ printf(_("Script done, file is %s\n"), ctl->fname);
#ifdef HAVE_LIBUTEMPTER
- if (ctl->master >= 0)
- utempter_remove_record(ctl->master);
+ if (ctl->master >= 0)
+ utempter_remove_record(ctl->master);
#endif
- kill(ctl->child, SIGTERM); /* make sure we don't create orphans */
- }
+ kill(ctl->child, SIGTERM); /* make sure we don't create orphans */
if (ctl->eflg) {
if (WIFSIGNALED(ctl->childstatus))
@@ -226,15 +201,6 @@ static void fail(struct script_control *ctl)
done(ctl);
}
-static void wait_for_empty_fd(struct script_control *ctl, int fd)
-{
- struct pollfd fds[] = {
- {.fd = fd, .events = POLLIN}
- };
-
- while (ctl->die == 0 && poll(fds, 1, 100) == 1) ;
-}
-
static void finish(struct script_control *ctl, int wait)
{
int status;
@@ -251,97 +217,6 @@ static void finish(struct script_control *ctl, int wait)
errno = errsv;
}
-static void doinput(struct script_control *ctl)
-{
- char ibuf[BUFSIZ];
- struct pollfd pfd[POLLFDS];
- int ret, i;
- ssize_t bytes;
-
- /* close things irrelevant for this process */
- if (ctl->typescriptfp)
- fclose(ctl->typescriptfp);
- if (ctl->timingfp)
- fclose(ctl->timingfp);
- ctl->typescriptfp = ctl->timingfp = NULL;
-
- pfd[0].fd = STDIN_FILENO;
- pfd[0].events = POLLIN;
- pfd[1].fd = ctl->sigfd;
- pfd[1].events = POLLIN | POLLERR | POLLHUP;
-
- while (!ctl->die) {
- /* 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;
-
- 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();
- }
- }
- }
- }
-
- /* To be sure that we don't miss any data */
- wait_for_empty_fd(ctl, ctl->slave);
- wait_for_empty_fd(ctl, ctl->master);
-
- if (ctl->die == 0) {
- /*
- * Forward EOF from stdin (detected by read() above) to slave
- * (shell) to correctly terminate the session. It seems we have
- * to wait for empty terminal FDs otherwise EOF maybe ignored
- * (why?) and typescript is incomplete. -- kzak Dec-2013
- *
- * We usually use this when stdin is not a tty, for example:
- * echo "ps" | script
- */
- char c = DEF_EOF;
-
- if (write_all(ctl->master, &c, sizeof(char))) {
- warn(_("write failed"));
- fail(ctl);
- }
-
- /* wait for "exit" message from shell before we print "Script
- * done" in done() */
- wait_for_empty_fd(ctl, ctl->master);
- }
-
- if (!ctl->die)
- finish(ctl, 1); /* wait for children */
- done(ctl);
-}
-
static void write_output(struct script_control *ctl, char *obuf,
ssize_t bytes, double *oldtime)
{
@@ -369,34 +244,31 @@ static void write_output(struct script_control *ctl, char *obuf,
}
}
-
-static void dooutput(struct script_control *ctl)
+static void do_io(struct script_control *ctl)
{
- char obuf[BUFSIZ];
+ char buf[BUFSIZ];
struct pollfd pfd[POLLFDS];
int ret, i;
ssize_t bytes;
double oldtime = time(NULL);
- close(STDIN_FILENO);
-#ifdef HAVE_LIBUTIL
- close(ctl->slave);
-#endif
if (ctl->tflg && !ctl->timingfp)
ctl->timingfp = fdopen(STDERR_FILENO, "w");
+ pfd[0].fd = STDIN_FILENO;
+ pfd[0].events = POLLIN;
+ pfd[1].fd = ctl->master;
+ pfd[1].events = POLLIN;
+ pfd[2].fd = ctl->sigfd;
+ pfd[2].events = POLLIN | POLLERR | POLLHUP;
+
if (!ctl->qflg) {
time_t tvec = script_time((time_t *)NULL);
- my_strftime(obuf, sizeof obuf, "%c\n", localtime(&tvec));
- fprintf(ctl->typescriptfp, _("Script started on %s"), obuf);
+ my_strftime(buf, sizeof buf, "%c\n", localtime(&tvec));
+ fprintf(ctl->typescriptfp, _("Script started on %s"), buf);
}
- pfd[0].fd = ctl->master;
- pfd[0].events = POLLIN;
- pfd[1].fd = ctl->sigfd;
- pfd[1].events = POLLIN | POLLERR | POLLHUP;
-
- while (1) {
+ while (!ctl->die) {
/* wait for input or signal */
ret = poll(pfd, POLLFDS, -1);
if (ret < 0) {
@@ -408,28 +280,47 @@ static void dooutput(struct script_control *ctl)
for (i = 0; i < POLLFDS; i++) {
if (pfd[i].revents == 0)
continue;
- if (i == 0) {
- bytes = read(pfd[i].fd, obuf, BUFSIZ);
+ if (i < 2) {
+ bytes = read(pfd[i].fd, buf, BUFSIZ);
if (bytes < 0) {
if (errno == EAGAIN)
continue;
fail(ctl);
}
- write_output(ctl, obuf, bytes, &oldtime);
+ if (i == 0) {
+ if (write_all(ctl->master, buf, bytes)) {
+ warn(_("write failed"));
+ fail(ctl);
+ } else
+
+ /* without sync write_output()
+ * will write both input &
+ * shell output that looks like
+ * double echoing */
+ fdatasync(ctl->master);
+ if (!ctl->isterm && feof(stdin)) {
+ char c = DEF_EOF;
+ write_all(ctl->master, &c, sizeof(char));
+ }
+ } else
+ write_output(ctl, buf, bytes, &oldtime);
continue;
}
- if (i == 1) {
+ if (i == 2) {
struct signalfd_siginfo info;
- ssize_t bytes;
+ ssize_t read_bytes;
- bytes = read(pfd[i].fd, &info, sizeof(info));
- assert(bytes == sizeof(info));
+ read_bytes = read(pfd[i].fd, &info, sizeof(info));
+ assert(read_bytes == sizeof(info));
switch (info.ssi_signo) {
case SIGCHLD:
- done(ctl);
+ finish(ctl, 0);
break;
case SIGWINCH:
- /* nothing */
+ if (ctl->isterm) {
+ ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
+ ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
+ }
break;
default:
abort();
@@ -437,7 +328,14 @@ static void dooutput(struct script_control *ctl)
}
}
}
- abort();
+ if (!ctl->die)
+ finish(ctl, 1); /* wait for children */
+ if (!ctl->qflg && ctl->typescriptfp) {
+ time_t tvec = script_time((time_t *)NULL);
+ my_strftime(buf, sizeof buf, "%c\n", localtime(&tvec));
+ fprintf(ctl->typescriptfp, _("\nScript done on %s"), buf);
+ }
+ done(ctl);
}
static void getslave(struct script_control *ctl)
@@ -701,20 +599,9 @@ int main(int argc, char **argv)
warn(_("fork failed"));
fail(&ctl);
}
- if (ctl.child == 0) {
-
- ctl.subchild = ctl.child = fork();
-
- if (ctl.child < 0) {
- warn(_("fork failed"));
- fail(&ctl);
- }
- if (ctl.child)
- dooutput(&ctl);
- else
- doshell(&ctl);
- }
- doinput(&ctl);
+ if (ctl.child == 0)
+ doshell(&ctl);
+ do_io(&ctl);
return EXIT_SUCCESS;
}