summaryrefslogtreecommitdiffstats
path: root/term-utils/script.c
diff options
context:
space:
mode:
authorKarel Zak2013-12-03 13:32:55 +0100
committerKarel Zak2013-12-03 13:32:55 +0100
commit3822032da3b8431afb9ee2c0a66c210dd2223f8c (patch)
tree20075510ed40aee604b68f94ad2125eb241a26d9 /term-utils/script.c
parenthexdump: Create struct hexdump containing previously global variables. (diff)
downloadkernel-qcow2-util-linux-3822032da3b8431afb9ee2c0a66c210dd2223f8c.tar.gz
kernel-qcow2-util-linux-3822032da3b8431afb9ee2c0a66c210dd2223f8c.tar.xz
kernel-qcow2-util-linux-3822032da3b8431afb9ee2c0a66c210dd2223f8c.zip
script: script input redirection / eof handling
echo "ps uf" | script does not work because script assume that stdin is terminal and it does not forward EOF to the pty. This patch: * make non-tty use-case more robust (don't call tty ioclts to non-tty file descriptors. * send EOF (CTL('D') control char) to the master channel when detected eof by read() on stdin * wait for empty master and slave file descriptors to be sure that we don't miss date for typescript. This is also necessary to be sure that slave channel (shell) is completely initialized otherwise EOF is ignored. Reported-by: Phillip Susi <psusi@ubuntu.com> Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'term-utils/script.c')
-rw-r--r--term-utils/script.c98
1 files changed, 79 insertions, 19 deletions
diff --git a/term-utils/script.c b/term-utils/script.c
index c18274c1c..277499d5b 100644
--- a/term-utils/script.c
+++ b/term-utils/script.c
@@ -58,10 +58,13 @@
#include <limits.h>
#include <locale.h>
#include <stddef.h>
+#include <sys/wait.h>
+#include <poll.h>
#include "closestream.h"
#include "nls.h"
#include "c.h"
+#include "ttyutils.h"
#if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
# include <pty.h>
@@ -107,6 +110,7 @@ int fflg = 0;
int qflg = 0;
int tflg = 0;
int forceflg = 0;
+int isterm;
int die;
int resized;
@@ -230,9 +234,6 @@ main(int argc, char **argv) {
die_if_link(fname);
}
- if (!isatty(STDIN_FILENO))
- errx(EXIT_FAILURE, _("The stdin is not a terminal."));
-
if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
warn(_("cannot open %s"), fname);
fail();
@@ -295,9 +296,18 @@ main(int argc, char **argv) {
return EXIT_SUCCESS;
}
+static void wait_for_empty_fd(int fd)
+{
+ struct pollfd fds[] = {
+ { .fd = fd, .events = POLLIN }
+ };
+
+ while (poll(fds, 1, 50) == 1);
+}
+
void
doinput(void) {
- ssize_t cc;
+ ssize_t cc = 0;
char ibuf[BUFSIZ];
if (close_stream(fscript) != 0)
@@ -314,19 +324,46 @@ doinput(void) {
else if (cc < 0 && errno == EINTR && resized)
{
/* transmit window change information to the child */
- ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&win);
- ioctl(slave, TIOCSWINSZ, (char *)&win);
+ if (isterm) {
+ ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&win);
+ ioctl(slave, TIOCSWINSZ, (char *)&win);
+ }
resized = 0;
- }
- else
+
+ } else
break;
}
+ /* To be sure that we don't miss any data */
+ wait_for_empty_fd(slave);
+ wait_for_empty_fd(master);
+
+ if (cc == 0 && errno == 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(master, &c, 1) < 0) {
+ warn (_("write failed"));
+ fail();
+ }
+
+ /* wait for "exit" message from shell before we print "Script
+ * done" in done() */
+ wait_for_empty_fd(master);
+ }
+
+ finish(0); /* wait for childern */
done();
}
-#include <sys/wait.h>
-
void
finish(int dummy __attribute__ ((__unused__))) {
int status;
@@ -473,6 +510,9 @@ void
fixtty(void) {
struct termios rtt;
+ if (!isterm)
+ return;
+
rtt = tt;
cfmakeraw(&rtt);
rtt.c_lflag &= ~ECHO;
@@ -491,6 +531,7 @@ done(void) {
time_t tvec;
if (subchild) {
+ /* output process */
if (!qflg) {
char buf[BUFSIZ];
tvec = time((time_t *)NULL);
@@ -503,13 +544,16 @@ done(void) {
master = -1;
} else {
- tcsetattr(STDIN_FILENO, TCSADRAIN, &tt);
+ /* input process */
+ if (isterm)
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &tt);
if (!qflg)
printf(_("Script done, file is %s\n"), fname);
#ifdef HAVE_LIBUTEMPTER
if (master >= 0)
utempter_remove_record(master);
#endif
+ kill(child, SIGTERM); /* make sure we don't create orphans */
}
if(eflg) {
@@ -524,9 +568,19 @@ done(void) {
void
getmaster(void) {
#if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
- tcgetattr(STDIN_FILENO, &tt);
- ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&win);
- if (openpty(&master, &slave, NULL, &tt, &win) < 0) {
+ int rc;
+
+ isterm = isatty(STDIN_FILENO);
+
+ if (isterm) {
+ if (tcgetattr(STDIN_FILENO, &tt) != 0)
+ err(EXIT_FAILURE, _("failed to get terminal attributes"));
+ ioctl(STDIN_FILENO, TIOCGWINSZ, (char *) &win);
+ rc = openpty(&master, &slave, NULL, &tt, &win);
+ } else
+ rc = openpty(&master, &slave, NULL, NULL, NULL);
+
+ if (rc < 0) {
warn(_("openpty failed"));
fail();
}
@@ -534,6 +588,8 @@ getmaster(void) {
char *pty, *bank, *cp;
struct stat stb;
+ isterm = isatty(STDIN_FILENO);
+
pty = &line[strlen("/dev/ptyp")];
for (bank = "pqrs"; *bank; bank++) {
line[strlen("/dev/pty")] = *bank;
@@ -552,9 +608,11 @@ getmaster(void) {
ok = access(line, R_OK|W_OK) == 0;
*tp = 'p';
if (ok) {
- tcgetattr(STDIN_FILENO, &tt);
- ioctl(STDIN_FILENO, TIOCGWINSZ,
- (char *)&win);
+ if (isterm) {
+ tcgetattr(STDIN_FILENO, &tt);
+ ioctl(STDIN_FILENO, TIOCGWINSZ,
+ (char *)&win);
+ }
return;
}
close(master);
@@ -577,8 +635,10 @@ getslave(void) {
warn(_("cannot open %s"), line);
fail();
}
- tcsetattr(slave, TCSANOW, &tt);
- ioctl(slave, TIOCSWINSZ, (char *)&win);
+ if (isterm) {
+ tcsetattr(slave, TCSANOW, &tt);
+ ioctl(slave, TIOCSWINSZ, (char *)&win);
+ }
#endif
setsid();
ioctl(slave, TIOCSCTTY, 0);