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/types.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/uio.h>
#ifdef HAVE_SYS_SYSMACROS_H #ifdef HAVE_SYS_SYSMACROS_H
# include <sys/sysmacros.h> # include <sys/sysmacros.h>
#endif #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 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 struct log_info *parse_logfile(char *logfile);
static void free_log_info(struct log_info *li); 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 #ifdef HAVE_REGCOMP
# define REGEX_T regex_t # define REGEX_T regex_t
@@ -248,6 +249,8 @@ main(int argc, char *argv[])
sigaction_t sa; sigaction_t sa;
size_t len, nbytes, nread, nwritten, off; size_t len, nbytes, nread, nwritten, off;
struct log_info *li; struct log_info *li;
struct iovec *iov = NULL;
int iovcnt = 0, iovmax = 0;
debug_decl(main, SUDO_DEBUG_MAIN) debug_decl(main, SUDO_DEBUG_MAIN)
#if defined(SUDO_DEVEL) && defined(__OpenBSD__) #if defined(SUDO_DEVEL) && defined(__OpenBSD__)
@@ -396,6 +399,9 @@ main(int argc, char *argv[])
(void) fcntl(STDIN_FILENO, F_SETFL, ch | O_NONBLOCK); (void) fcntl(STDIN_FILENO, F_SETFL, ch | O_NONBLOCK);
if (!term_raw(STDIN_FILENO, 1)) if (!term_raw(STDIN_FILENO, 1))
error(1, _("unable to set tty to raw mode")); 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 { do {
/* Convert newline to carriage return + linefeed if needed. */ /* Convert newline to carriage return + linefeed if needed. */
if (need_nlcr) { if (need_nlcr) {
size_t linelen;
cp = buf + off; cp = buf + off;
while ((cp = memchr(cp, '\n', nread - off)) != NULL) { iovcnt = 0;
/* Write out line followed by \r\n pair. */ while ((ep = memchr(cp, '\n', nread - off)) != NULL) {
nwritten = blocking_write(STDOUT_FILENO, buf + off, /* Is there already a carriage return? */
(size_t)(cp - (buf + off))) + 1; if (cp != ep && ep[-1] == '\r')
(void)blocking_write(STDOUT_FILENO, "\r\n", 2); continue;
off += nwritten;
cp++; /* 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;
} }
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;
} }
nwritten = blocking_write(STDOUT_FILENO, buf + off, nread - off);
off += nwritten;
} while (nread > off); } while (nread > off);
} }
} }
@@ -503,32 +537,64 @@ open_io_fd(char *path, int len, const char *suffix, union io_fd *fdp)
debug_return_int(fdp->v ? 0 : -1); 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 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; ssize_t n, nwritten = 0;
debug_decl(blocking_write, SUDO_DEBUG_UTIL) size_t count, remainder, nbytes = 0;
int i;
debug_decl(atomic_writev, SUDO_DEBUG_UTIL)
if (nbytes != 0) { for (i = 0; i < iovcnt; i++)
for (;;) { nbytes += iov[i].iov_len;
nwritten = write(STDOUT_FILENO, buf, nbytes);
if (nwritten >= 0) for (;;) {
n = writev(STDOUT_FILENO, iov, iovcnt);
if (n > 0) {
nwritten += n;
remainder = nbytes - nwritten;
if (remainder == 0)
break; break;
if (errno == EINTR) /* short writev, adjust iov and do the rest. */
continue; count = 0;
if (errno == EAGAIN) { i = iovcnt;
int nready; while (i--) {
fd_set fdsw; count += iov[i].iov_len;
FD_ZERO(&fdsw); if (count == remainder) {
FD_SET(STDOUT_FILENO, &fdsw); iov += i;
do { iovcnt -= i;
nready = select(STDOUT_FILENO + 1, NULL, &fdsw, NULL, NULL); break;
} while (nready == -1 && errno == EINTR); }
if (nready == 1) if (count > remainder) {
continue; 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;
}
} }
error(1, _("writing to standard output")); continue;
} }
if (n == 0 || errno == EAGAIN) {
int nready;
fd_set fdsw;
FD_ZERO(&fdsw);
FD_SET(STDOUT_FILENO, &fdsw);
do {
nready = select(STDOUT_FILENO + 1, NULL, &fdsw, NULL, NULL);
} while (nready == -1 && errno == EINTR);
if (nready == 1)
continue;
}
if (errno == EINTR)
continue;
error(1, "writing to standard output");
} }
debug_return_size_t(nwritten); debug_return_size_t(nwritten);
} }