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:
@@ -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);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user