[PATCH 17/22] script: merge doinput() and output() functions to do_io()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxx>
CC: Ruediger Meier <ruediger.meier@xxxxxxxxxxx>
Signed-off-by: Sami Kerola <kerolasa@xxxxxx>
---
 term-utils/script.c | 220 ++++++++++++----------------------------------------
 1 file changed, 51 insertions(+), 169 deletions(-)

diff --git a/term-utils/script.c b/term-utils/script.c
index 456355e..4a761c6 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 */
@@ -157,39 +156,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))
@@ -206,15 +181,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;
@@ -231,97 +197,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
-		 */
-		int c = DEF_EOF;
-
-		if (write_all(ctl->master, &c, 1)) {
-			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)
 {
@@ -349,34 +224,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 = 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) {
@@ -388,17 +260,33 @@ 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)) {
+						int c = DEF_EOF;
+						write_all(ctl->master, &c, 1);
+					}
+				} else
+					write_output(ctl, buf, bytes, &oldtime);
 				continue;
 			}
-			if (i == 1) {
+			if (i == 2) {
 				struct signalfd_siginfo info;
 				ssize_t bytes;
 
@@ -406,10 +294,13 @@ static void dooutput(struct script_control *ctl)
 				assert(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();
@@ -417,7 +308,9 @@ static void dooutput(struct script_control *ctl)
 			}
 		}
 	}
-	abort();
+	if (!ctl->die)
+		finish(ctl, 1); /* wait for children */
+	done(ctl);
 }
 
 static void getslave(struct script_control *ctl)
@@ -680,20 +573,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;
 }
-- 
2.2.1

--
To unsubscribe from this list: send the line "unsubscribe util-linux" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux