summaryrefslogtreecommitdiffstats
path: root/term-utils/script.c
diff options
context:
space:
mode:
authorKarel Zak2016-04-13 11:52:43 +0200
committerKarel Zak2016-04-13 11:52:43 +0200
commitb2bff0666101213d5ce9fc4166d8fc5b17581f57 (patch)
tree0dde8d72216fc84bfea4b0fbaee04c186c06784b /term-utils/script.c
parentbuild-sys: release++ (v2.28) (diff)
downloadkernel-qcow2-util-linux-b2bff0666101213d5ce9fc4166d8fc5b17581f57.tar.gz
kernel-qcow2-util-linux-b2bff0666101213d5ce9fc4166d8fc5b17581f57.tar.xz
kernel-qcow2-util-linux-b2bff0666101213d5ce9fc4166d8fc5b17581f57.zip
script: use empty-slave heuristic more carefully
script(1) waits for empty slave FD (shell stdin) before it writes to master. This feature has been intorduiced by 54c6611d6f7b73609a5331f4d0bcf63c4af6429e to avoid misbehavior when we need to send EOF to the shell. Unfortunately, this feature has been used all time for all messages. This is wrong because command in the session (or shell) may ignore stdin at all and wait forever in busy loop is really bad idea. Test case: script /dev/null tailf /etc/passwd <enter> <enter> ... script process taking 100% CPU. This patch forces script to use empty-stave detection only when we need to write EOF. The busy loop has been modified to use nanosleep and it does not wait forever... Addresses: http://bugs.debian.org/820843 Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'term-utils/script.c')
-rw-r--r--term-utils/script.c52
1 files changed, 28 insertions, 24 deletions
diff --git a/term-utils/script.c b/term-utils/script.c
index 23ca89cdc..119974230 100644
--- a/term-utils/script.c
+++ b/term-utils/script.c
@@ -122,7 +122,6 @@ struct script_control {
timing:1, /* include timing file */
force:1, /* write output to links */
isterm:1, /* is child process running as terminal */
- data_on_way:1, /* sent data to master */
die:1; /* terminate program */
sigset_t sigset; /* catch SIGCHLD and SIGWINCH with signalfd() */
@@ -276,15 +275,10 @@ static void write_output(struct script_control *ctl, char *obuf,
DBG(IO, ul_debug(" writing output *done*"));
}
-static void wait_for_empty_fd(int fd)
+static int write_to_shell(struct script_control *ctl,
+ char *buf, size_t bufsz)
{
- struct pollfd fds[] = {
- { .fd = fd, .events = POLLIN }
- };
-
- while (poll(fds, 1, 10) == 1) {
- DBG(IO, ul_debug(" data to read"));
- }
+ return write_all(ctl->master, buf, bufsz);
}
/*
@@ -295,26 +289,32 @@ static void wait_for_empty_fd(int fd)
* problem we wait until slave is empty. For example:
*
* echo "date" | script
+ *
+ * Unfortunately, the child (usually shell) can ignore stdin at all, so we
+ * don't wait forever to avoid dead locks...
+ *
+ * Note that script is primarily designed for interactive sessions as it
+ * maintains master+slave tty stuff within the session. Use pipe to write to
+ * script(1) and assume non-interactive (tee-like) behavior is NOT well
+ * supported.
*/
-static int write_to_shell(struct script_control *ctl, char *buf, size_t bufsz)
-{
- int rc;
-
- if (ctl->data_on_way) {
- wait_for_empty_fd(ctl->slave);
- ctl->data_on_way = 0;
- }
- rc = write_all(ctl->master, buf, bufsz);
- if (rc == 0)
- ctl->data_on_way = 1;
- return rc;
-
-}
-
static void write_eof_to_shell(struct script_control *ctl)
{
+ unsigned int tries = 0;
+ struct pollfd fds[] = {
+ { .fd = ctl->slave, .events = POLLIN }
+ };
char c = DEF_EOF;
+ DBG(IO, ul_debug(" waiting for empty slave"));
+ while (poll(fds, 1, 10) == 1 && tries < 8) {
+ DBG(IO, ul_debug(" slave is not empty"));
+ xusleep(250000);
+ tries++;
+ }
+ if (tries < 8)
+ DBG(IO, ul_debug(" slave is empty now"));
+
DBG(IO, ul_debug(" sending EOF to master"));
write_to_shell(ctl, &c, sizeof(char));
}
@@ -375,10 +375,12 @@ static void handle_signal(struct script_control *ctl, int fd)
switch (info.ssi_signo) {
case SIGCHLD:
+ DBG(SIGNAL, ul_debug(" get signal SIGCHLD"));
wait_for_child(ctl, 0);
ctl->poll_timeout = 10;
return;
case SIGWINCH:
+ DBG(SIGNAL, ul_debug(" get signal SIGWINCH"));
if (ctl->isterm) {
ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ctl->win);
ioctl(ctl->slave, TIOCSWINSZ, (char *)&ctl->win);
@@ -389,6 +391,7 @@ static void handle_signal(struct script_control *ctl, int fd)
case SIGINT:
/* fallthrough */
case SIGQUIT:
+ DBG(SIGNAL, ul_debug(" get signal SIG{TERM,INT,QUIT}"));
fprintf(stderr, _("\nSession terminated.\n"));
/* Child termination is going to generate SIGCHILD (see above) */
kill(ctl->child, SIGTERM);
@@ -485,6 +488,7 @@ static void do_io(struct script_control *ctl)
if (i == POLLFD_STDIN) {
ignore_stdin = 1;
write_eof_to_shell(ctl);
+ DBG(POLL, ul_debug(" ignore STDIN"));
}
}
continue;