Flush the iobufs on suspend or child exit using the same logic

as the main event loop.
This commit is contained in:
Todd C. Miller
2010-05-07 10:15:25 -04:00
parent b08a96eea7
commit 23a9bf2c07

View File

@@ -124,6 +124,7 @@ static char slavename[PATH_MAX];
static int suspend_parent(int signo, int fd, struct io_buffer *output);
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 int script_child(const char *path, char *argv[], char *envp[], int, 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;
}
/*
* 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
* we fact that we have two different controlling terminals to deal with.
@@ -681,7 +726,8 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
iob->off = iob->len = 0;
/* Forward the EOF from reader to writer. */
if (iob->rfd == -1) {
close(iob->wfd);
if (iob->wfd != script_fds[SFD_USERTTY])
close(iob->wfd);
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);
if (nready == -1) {
if (errno == EINTR)
@@ -766,42 +811,10 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
}
}
}
for (iob = iobufs; iob; iob = iob->next) {
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;
}
}
}
if (perform_io(iobufs, fdsr, fdsw) == -1)
break;
}
io_error:
if (log_io) {
/* Flush any remaining output (the plugin already got it) */
n = fcntl(script_fds[SFD_USERTTY], F_GETFL, 0);
@@ -1118,59 +1131,73 @@ bad:
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
flush_output(struct io_buffer *iobufs)
{
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) {
if (iob->wfd == -1 && iob->wfd == script_fds[SFD_SLAVE])
continue;
while (iob->len > iob->off) {
n = write(iob->wfd, iob->buf + iob->off, iob->len - iob->off);
if (n <= 0)
break;
iob->off += n;
if (iob->rfd == -1) {
close(iob->wfd);
iob->wfd = -1;
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;
if (iob->rfd == -1 && iob->wfd == -1)
continue;
if (iob->off == iob->len) {
iob->off = iob->len = 0;
/* Forward the EOF from reader to writer. */
if (iob->rfd == -1) {
if (iob->wfd != script_fds[SFD_USERTTY])
close(iob->wfd);
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. */
for (iob = iobufs; iob; iob = iob->next) {
if (iob->rfd == -1 || iob->wfd == -1) /* XXX */
continue;
if (iob->rfd == script_fds[SFD_USERTTY])
continue;
for (;;) {
n = read(iob->rfd, iob->buf + iob->len,
sizeof(iob->buf) - iob->len);
if (n <= 0) {
if (n == -1 && errno == EINTR)
continue;
break;
}
if (!iob->action(iob->buf + iob->len, n))
break;
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);
/* Effect a poll (no sleeping in select) */
tv.tv_sec = 0;
tv.tv_usec = 0;
nready = select(maxfd + 1, fdsr, fdsw, NULL, &tv);
if (nready <= 0) {
if (nready == 0)
break; /* all I/O flushed */
if (errno == EINTR)
continue;
error(1, "select failed");
}
if (perform_io(iobufs, fdsr, fdsw) == -1)
break;
}
efree(fdsr);
efree(fdsw);
}
static void