aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2022-01-08 13:19:14 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2022-01-10 15:34:51 +0000
commitc0ba4c2ee2c48ec9892d10c0aca797f3685c53ee (patch)
tree7dca3b10f844a8ead0f1b8d6978e1073d0042739
parentba94a95402f335c8e7aa8e28ebdad43361c65909 (diff)
downloadsrc-c0ba4c2ee2c48ec9892d10c0aca797f3685c53ee.tar.gz
src-c0ba4c2ee2c48ec9892d10c0aca797f3685c53ee.zip
script(1): work around slow reading child
If child is slow reading from its input, or even completely stops doing the read, script(1) hangs in write(2) to the pts master waiting until there is a space in the terminal discipline buffer. This also stops handling any outer io, as well as child output. Work around the problem by making pts master fd non-blocking, and be prepared for short writes to it. The data to be written to master is buffered in the tailq which is processed when select(2) detects that master is ready for write. PR: 260938 Reported by: наб <nabijaczleweli@nabijaczleweli.xyz> See also: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1003095 Reviewed by: markj Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D33789
-rw-r--r--usr.bin/script/script.c56
1 files changed, 51 insertions, 5 deletions
diff --git a/usr.bin/script/script.c b/usr.bin/script/script.c
index 4ecc2099926f..9c18dc73390f 100644
--- a/usr.bin/script/script.c
+++ b/usr.bin/script/script.c
@@ -45,6 +45,7 @@ static const char sccsid[] = "@(#)script.c 8.1 (Berkeley) 6/6/93";
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
+#include <sys/queue.h>
#include <sys/uio.h>
#include <sys/endian.h>
#include <dev/filemon/filemon.h>
@@ -70,6 +71,13 @@ struct stamp {
uint32_t scr_direction; /* 'i', 'o', etc (also indicates endianness) */
};
+struct buf_elm {
+ TAILQ_ENTRY(buf_elm) link;
+ int rpos;
+ int len;
+ char ibuf[];
+};
+
static FILE *fscript;
static int master, slave;
static int child;
@@ -77,6 +85,7 @@ static const char *fname;
static char *fmfname;
static int fflg, qflg, ttyflg;
static int usesleep, rawout, showexit;
+static TAILQ_HEAD(, buf_elm) obuf_list = TAILQ_HEAD_INITIALIZER(obuf_list);
static struct termios tt;
@@ -98,8 +107,9 @@ main(int argc, char *argv[])
time_t tvec, start;
char obuf[BUFSIZ];
char ibuf[BUFSIZ];
- fd_set rfd;
- int aflg, Fflg, kflg, pflg, ch, k, n;
+ fd_set rfd, wfd;
+ struct buf_elm *be;
+ int aflg, Fflg, kflg, pflg, ch, k, n, fcm;
int flushtime, readstdin;
int fm_fd, fm_log;
@@ -189,6 +199,12 @@ main(int argc, char *argv[])
err(1, "openpty");
ttyflg = 1;
}
+ fcm = fcntl(master, F_GETFL);
+ if (fcm == -1)
+ err(1, "master F_GETFL");
+ fcm |= O_NONBLOCK;
+ if (fcntl(master, F_SETFL, fcm) == -1)
+ err(1, "master F_SETFL");
if (rawout)
record(fscript, NULL, 0, 's');
@@ -243,9 +259,12 @@ main(int argc, char *argv[])
readstdin = 1;
for (;;) {
FD_ZERO(&rfd);
+ FD_ZERO(&wfd);
FD_SET(master, &rfd);
if (readstdin)
FD_SET(STDIN_FILENO, &rfd);
+ if (!TAILQ_EMPTY(&obuf_list))
+ FD_SET(master, &wfd);
if (!readstdin && ttyflg) {
tv.tv_sec = 1;
tv.tv_usec = 0;
@@ -258,7 +277,7 @@ main(int argc, char *argv[])
} else {
tvp = NULL;
}
- n = select(master + 1, &rfd, 0, 0, tvp);
+ n = select(master + 1, &rfd, &wfd, NULL, tvp);
if (n < 0 && errno != EINTR)
break;
if (n > 0 && FD_ISSET(STDIN_FILENO, &rfd)) {
@@ -275,10 +294,37 @@ main(int argc, char *argv[])
if (cc > 0) {
if (rawout)
record(fscript, ibuf, cc, 'i');
- (void)write(master, ibuf, cc);
+ be = malloc(sizeof(*be) + cc);
+ be->rpos = 0;
+ be->len = cc;
+ memcpy(be->ibuf, ibuf, cc);
+ TAILQ_INSERT_TAIL(&obuf_list, be, link);
+ }
+ }
+ if (n > 0 && FD_ISSET(master, &wfd)) {
+ while ((be = TAILQ_FIRST(&obuf_list)) != NULL) {
+ cc = write(master, be->ibuf + be->rpos,
+ be->len);
+ if (cc == -1) {
+ if (errno == EWOULDBLOCK ||
+ errno == EINTR)
+ break;
+ warn("write master");
+ done(1);
+ }
+ if (cc == 0)
+ break; /* retry later ? */
if (kflg && tcgetattr(master, &stt) >= 0 &&
((stt.c_lflag & ECHO) == 0)) {
- (void)fwrite(ibuf, 1, cc, fscript);
+ (void)fwrite(be->ibuf + be->rpos,
+ 1, cc, fscript);
+ }
+ be->len -= cc;
+ if (be->len == 0) {
+ TAILQ_REMOVE(&obuf_list, be, link);
+ free(be);
+ } else {
+ be->rpos += cc;
}
}
}