Flush the iobufs on suspend or child exit using the same logic
as the main event loop.
This commit is contained in:
169
src/script.c
169
src/script.c
@@ -124,6 +124,7 @@ static char slavename[PATH_MAX];
|
|||||||
|
|
||||||
static int suspend_parent(int signo, int fd, struct io_buffer *output);
|
static int suspend_parent(int signo, int fd, struct io_buffer *output);
|
||||||
static void flush_output(struct io_buffer *iobufs);
|
static void flush_output(struct io_buffer *iobufs);
|
||||||
|
static int perform_io(struct io_buffer *iobufs, fd_set *fdsr, fd_set *fdsw);
|
||||||
static void handler(int s);
|
static void handler(int s);
|
||||||
static int script_child(const char *path, char *argv[], char *envp[], int, int);
|
static int script_child(const char *path, char *argv[], char *envp[], int, int);
|
||||||
static void script_run(const char *path, char *argv[], char *envp[], int);
|
static void script_run(const char *path, char *argv[], char *envp[], int);
|
||||||
@@ -412,6 +413,50 @@ io_buf_new(int rfd, int wfd, int (*action)(char *, unsigned int),
|
|||||||
return iob;
|
return iob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read/write iobufs depending on fdsr and fdsw.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
perform_io(struct io_buffer *iobufs, fd_set *fdsr, fd_set *fdsw)
|
||||||
|
{
|
||||||
|
struct io_buffer *iob;
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
for (iob = iobufs; iob; iob = iob->next) {
|
||||||
|
if (iob->rfd != -1 && FD_ISSET(iob->rfd, fdsr)) {
|
||||||
|
do {
|
||||||
|
n = read(iob->rfd, iob->buf + iob->len,
|
||||||
|
sizeof(iob->buf) - iob->len);
|
||||||
|
} while (n == -1 && errno == EINTR);
|
||||||
|
if (n == -1) {
|
||||||
|
if (errno != EAGAIN)
|
||||||
|
break;
|
||||||
|
} else if (n == 0) {
|
||||||
|
/* got EOF */
|
||||||
|
close(iob->rfd);
|
||||||
|
iob->rfd = -1;
|
||||||
|
} else {
|
||||||
|
if (!iob->action(iob->buf + iob->len, n))
|
||||||
|
terminate_child(child, TRUE);
|
||||||
|
iob->len += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (iob->wfd != -1 && FD_ISSET(iob->wfd, fdsw)) {
|
||||||
|
do {
|
||||||
|
n = write(iob->wfd, iob->buf + iob->off,
|
||||||
|
iob->len - iob->off);
|
||||||
|
} while (n == -1 && errno == EINTR);
|
||||||
|
if (n == -1) {
|
||||||
|
if (errno != EAGAIN)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
iob->off += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n == -1 ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a little bit tricky due to how POSIX job control works and
|
* This is a little bit tricky due to how POSIX job control works and
|
||||||
* we fact that we have two different controlling terminals to deal with.
|
* we fact that we have two different controlling terminals to deal with.
|
||||||
@@ -681,6 +726,7 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
iob->off = iob->len = 0;
|
iob->off = iob->len = 0;
|
||||||
/* Forward the EOF from reader to writer. */
|
/* Forward the EOF from reader to writer. */
|
||||||
if (iob->rfd == -1) {
|
if (iob->rfd == -1) {
|
||||||
|
if (iob->wfd != script_fds[SFD_USERTTY])
|
||||||
close(iob->wfd);
|
close(iob->wfd);
|
||||||
iob->wfd = -1;
|
iob->wfd = -1;
|
||||||
}
|
}
|
||||||
@@ -713,7 +759,6 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retry:
|
|
||||||
nready = select(maxfd + 1, fdsr, fdsw, NULL, NULL);
|
nready = select(maxfd + 1, fdsr, fdsw, NULL, NULL);
|
||||||
if (nready == -1) {
|
if (nready == -1) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
@@ -766,42 +811,10 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (perform_io(iobufs, fdsr, fdsw) == -1)
|
||||||
for (iob = iobufs; iob; iob = iob->next) {
|
break;
|
||||||
if (iob->rfd != -1 && FD_ISSET(iob->rfd, fdsr)) {
|
|
||||||
n = read(iob->rfd, iob->buf + iob->len,
|
|
||||||
sizeof(iob->buf) - iob->len);
|
|
||||||
if (n == -1) {
|
|
||||||
if (errno == EINTR)
|
|
||||||
goto retry;
|
|
||||||
if (errno != EAGAIN)
|
|
||||||
goto io_error;
|
|
||||||
} else if (n == 0) {
|
|
||||||
/* got EOF */
|
|
||||||
close(iob->rfd);
|
|
||||||
iob->rfd = -1;
|
|
||||||
} else {
|
|
||||||
if (!iob->action(iob->buf + iob->len, n))
|
|
||||||
terminate_child(child, TRUE);
|
|
||||||
iob->len += n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (iob->wfd != -1 && FD_ISSET(iob->wfd, fdsw)) {
|
|
||||||
n = write(iob->wfd, iob->buf + iob->off,
|
|
||||||
iob->len - iob->off);
|
|
||||||
if (n == -1) {
|
|
||||||
if (errno == EINTR)
|
|
||||||
goto retry;
|
|
||||||
if (errno != EAGAIN)
|
|
||||||
goto io_error;
|
|
||||||
} else {
|
|
||||||
iob->off += n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
io_error:
|
|
||||||
if (log_io) {
|
if (log_io) {
|
||||||
/* Flush any remaining output (the plugin already got it) */
|
/* Flush any remaining output (the plugin already got it) */
|
||||||
n = fcntl(script_fds[SFD_USERTTY], F_GETFL, 0);
|
n = fcntl(script_fds[SFD_USERTTY], F_GETFL, 0);
|
||||||
@@ -1118,59 +1131,73 @@ bad:
|
|||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX - should use select in poll mode to flush things */
|
/*
|
||||||
|
* Flush any output buffered in iobufs or readable from the fds.
|
||||||
|
* Does not read from /dev/tty.
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
flush_output(struct io_buffer *iobufs)
|
flush_output(struct io_buffer *iobufs)
|
||||||
{
|
{
|
||||||
struct io_buffer *iob;
|
struct io_buffer *iob;
|
||||||
int n;
|
struct timeval tv;
|
||||||
|
fd_set *fdsr, *fdsw;
|
||||||
|
int nready, maxfd = -1;
|
||||||
|
|
||||||
/* Drain output buffers. */
|
/* Determine maxfd */
|
||||||
for (iob = iobufs; iob; iob = iob->next) {
|
for (iob = iobufs; iob; iob = iob->next) {
|
||||||
if (iob->wfd == -1 && iob->wfd == script_fds[SFD_SLAVE])
|
if (iob->rfd > maxfd)
|
||||||
|
maxfd = iob->rfd;
|
||||||
|
if (iob->wfd > maxfd)
|
||||||
|
maxfd = iob->wfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
|
||||||
|
fdsw = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
|
||||||
|
for (;;) {
|
||||||
|
zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
|
||||||
|
zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
|
||||||
|
|
||||||
|
for (iob = iobufs; iob; iob = iob->next) {
|
||||||
|
/* Don't read from /dev/tty while flushing. */
|
||||||
|
if (iob->rfd == script_fds[SFD_USERTTY])
|
||||||
continue;
|
continue;
|
||||||
while (iob->len > iob->off) {
|
if (iob->rfd == -1 && iob->wfd == -1)
|
||||||
n = write(iob->wfd, iob->buf + iob->off, iob->len - iob->off);
|
continue;
|
||||||
if (n <= 0)
|
if (iob->off == iob->len) {
|
||||||
break;
|
iob->off = iob->len = 0;
|
||||||
iob->off += n;
|
/* Forward the EOF from reader to writer. */
|
||||||
if (iob->rfd == -1) {
|
if (iob->rfd == -1) {
|
||||||
|
if (iob->wfd != script_fds[SFD_USERTTY])
|
||||||
close(iob->wfd);
|
close(iob->wfd);
|
||||||
iob->wfd = -1;
|
iob->wfd = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (iob->rfd != -1) {
|
||||||
|
if (iob->len != sizeof(iob->buf))
|
||||||
|
FD_SET(iob->rfd, fdsr);
|
||||||
|
}
|
||||||
|
if (iob->wfd != -1) {
|
||||||
|
if (iob->len > iob->off)
|
||||||
|
FD_SET(iob->wfd, fdsw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure there is no output remaining on the master pty or in pipes. */
|
/* Effect a poll (no sleeping in select) */
|
||||||
for (iob = iobufs; iob; iob = iob->next) {
|
tv.tv_sec = 0;
|
||||||
if (iob->rfd == -1 || iob->wfd == -1) /* XXX */
|
tv.tv_usec = 0;
|
||||||
continue;
|
nready = select(maxfd + 1, fdsr, fdsw, NULL, &tv);
|
||||||
if (iob->rfd == script_fds[SFD_USERTTY])
|
if (nready <= 0) {
|
||||||
continue;
|
if (nready == 0)
|
||||||
|
break; /* all I/O flushed */
|
||||||
for (;;) {
|
if (errno == EINTR)
|
||||||
n = read(iob->rfd, iob->buf + iob->len,
|
|
||||||
sizeof(iob->buf) - iob->len);
|
|
||||||
if (n <= 0) {
|
|
||||||
if (n == -1 && errno == EINTR)
|
|
||||||
continue;
|
continue;
|
||||||
|
error(1, "select failed");
|
||||||
|
}
|
||||||
|
if (perform_io(iobufs, fdsr, fdsw) == -1)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!iob->action(iob->buf + iob->len, n))
|
efree(fdsr);
|
||||||
break;
|
efree(fdsw);
|
||||||
iob->len += n;
|
|
||||||
|
|
||||||
do {
|
|
||||||
n = write(iob->wfd, iob->buf + iob->off, iob->len - iob->off);
|
|
||||||
if (n <= 0) {
|
|
||||||
if (n == -1 && errno == EINTR)
|
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
iob->off += n;
|
|
||||||
} while (iob->len > iob->off);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
Reference in New Issue
Block a user