Instead of doing extra write()s when replaying stdout, build up a

vector for writev() instead.  This results in far fewer system
calls.
This commit is contained in:
Todd C. Miller
2012-05-29 13:46:28 -04:00
parent af9492d117
commit 12be3e7f54

View File

@@ -18,6 +18,7 @@
#include <sys/types.h>
#include <sys/param.h>
#include <sys/uio.h>
#ifdef HAVE_SYS_SYSMACROS_H
# include <sys/sysmacros.h>
#endif
@@ -215,7 +216,7 @@ static int open_io_fd(char *pathbuf, int len, const char *suffix, union io_fd *f
static int parse_timing(const char *buf, const char *decimal, int *idx, double *seconds, size_t *nbytes);
static struct log_info *parse_logfile(char *logfile);
static void free_log_info(struct log_info *li);
static size_t blocking_write(int fd, const void *buf, size_t nbytes);
static size_t atomic_writev(int fd, struct iovec *iov, int iovcnt);
#ifdef HAVE_REGCOMP
# define REGEX_T regex_t
@@ -248,6 +249,8 @@ main(int argc, char *argv[])
sigaction_t sa;
size_t len, nbytes, nread, nwritten, off;
struct log_info *li;
struct iovec *iov = NULL;
int iovcnt = 0, iovmax = 0;
debug_decl(main, SUDO_DEBUG_MAIN)
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
@@ -396,6 +399,9 @@ main(int argc, char *argv[])
(void) fcntl(STDIN_FILENO, F_SETFL, ch | O_NONBLOCK);
if (!term_raw(STDIN_FILENO, 1))
error(1, _("unable to set tty to raw mode"));
iovcnt = 0;
iovmax = 32;
iov = ecalloc(iovmax, sizeof(*iov));
}
/*
@@ -442,18 +448,46 @@ main(int argc, char *argv[])
do {
/* Convert newline to carriage return + linefeed if needed. */
if (need_nlcr) {
size_t linelen;
cp = buf + off;
while ((cp = memchr(cp, '\n', nread - off)) != NULL) {
/* Write out line followed by \r\n pair. */
nwritten = blocking_write(STDOUT_FILENO, buf + off,
(size_t)(cp - (buf + off))) + 1;
(void)blocking_write(STDOUT_FILENO, "\r\n", 2);
off += nwritten;
cp++;
iovcnt = 0;
while ((ep = memchr(cp, '\n', nread - off)) != NULL) {
/* Is there already a carriage return? */
if (cp != ep && ep[-1] == '\r')
continue;
/* Store the line in iov followed by \r\n pair. */
if (iovcnt + 3 > iovmax) {
iovmax <<= 1;
iov = erealloc3(iov, iovmax, sizeof(*iov));
}
linelen = (size_t)(ep - cp);
iov[iovcnt].iov_base = cp;
iov[iovcnt].iov_len = linelen;
iovcnt++;
iov[iovcnt].iov_base = "\r\n";
iov[iovcnt].iov_len = 2;
iovcnt++;
off += linelen + 1;
cp = ep + 1;
}
nwritten = blocking_write(STDOUT_FILENO, buf + off, nread - off);
if (nread - off != 0) {
linelen = nread - off;
iov[iovcnt].iov_base = cp;
iov[iovcnt].iov_len = linelen;
iovcnt++;
off += linelen;
}
/* Note: off already adjusted above. */
(void)atomic_writev(STDOUT_FILENO, iov, iovcnt);
} else {
/* No conversion needed. */
iov[0].iov_base = buf + off;
iov[0].iov_len = nread - off;
iovcnt = 1;
nwritten = atomic_writev(STDOUT_FILENO, iov, iovcnt);
off += nwritten;
}
} while (nread > off);
}
}
@@ -503,20 +537,51 @@ open_io_fd(char *path, int len, const char *suffix, union io_fd *fdp)
debug_return_int(fdp->v ? 0 : -1);
}
/*
* Call writev(), restarting as needed and handling EAGAIN since
* fd may be in non-blocking mode.
*/
static size_t
blocking_write(int fd, const void *buf, size_t nbytes)
atomic_writev(int fd, struct iovec *iov, int iovcnt)
{
ssize_t nwritten = 0;
debug_decl(blocking_write, SUDO_DEBUG_UTIL)
ssize_t n, nwritten = 0;
size_t count, remainder, nbytes = 0;
int i;
debug_decl(atomic_writev, SUDO_DEBUG_UTIL)
for (i = 0; i < iovcnt; i++)
nbytes += iov[i].iov_len;
if (nbytes != 0) {
for (;;) {
nwritten = write(STDOUT_FILENO, buf, nbytes);
if (nwritten >= 0)
n = writev(STDOUT_FILENO, iov, iovcnt);
if (n > 0) {
nwritten += n;
remainder = nbytes - nwritten;
if (remainder == 0)
break;
if (errno == EINTR)
/* short writev, adjust iov and do the rest. */
count = 0;
i = iovcnt;
while (i--) {
count += iov[i].iov_len;
if (count == remainder) {
iov += i;
iovcnt -= i;
break;
}
if (count > remainder) {
size_t off = (count - remainder);
/* XXX - side effect prevents iov from being const */
iov[i].iov_base = (char *)iov[i].iov_base + off;
iov[i].iov_len -= off;
iov += i;
iovcnt -= i;
break;
}
}
continue;
if (errno == EAGAIN) {
}
if (n == 0 || errno == EAGAIN) {
int nready;
fd_set fdsw;
FD_ZERO(&fdsw);
@@ -527,8 +592,9 @@ blocking_write(int fd, const void *buf, size_t nbytes)
if (nready == 1)
continue;
}
error(1, _("writing to standard output"));
}
if (errno == EINTR)
continue;
error(1, "writing to standard output");
}
debug_return_size_t(nwritten);
}