From 931d8b79a88b20573a04c3f11866e934a58c9b34 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 27 Aug 2019 11:08:17 +0200 Subject: [PATCH] MINOR: fd: add fd_write_frag_line() to send a fragmented line to an fd Currently both logs and event sinks may use a file descriptor to atomically emit some output contents. The two may use the same FD though nothing is done to make sure they use the same lock. Also there is quite some redundancy between the two. Better make a specific function to send a fragmented message to a file descriptor which will take care of the locking via the fd's lock. The function is also able to truncate a message and to enforce addition of a trailing LF when building the output message. --- include/proto/fd.h | 2 ++ include/types/fd.h | 1 + src/fd.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/include/proto/fd.h b/include/proto/fd.h index e146976b2..9038be511 100644 --- a/include/proto/fd.h +++ b/include/proto/fd.h @@ -60,6 +60,8 @@ void fd_delete(int fd); */ void fd_remove(int fd); +ssize_t fd_write_frag_line(int fd, size_t maxlen, const struct ist pfx[], size_t npfx, const struct ist msg[], size_t nmsg, int nl); + /* close all FDs starting from */ void my_closefrom(int start); diff --git a/include/types/fd.h b/include/types/fd.h index 16845378c..53364612b 100644 --- a/include/types/fd.h +++ b/include/types/fd.h @@ -24,6 +24,7 @@ #include #include +#include #include /* Direction for each FD event update */ diff --git a/src/fd.c b/src/fd.c index c4155aa86..620de5979 100644 --- a/src/fd.c +++ b/src/fd.c @@ -106,6 +106,7 @@ #include #include #include +#include #if defined(USE_POLL) #include @@ -362,6 +363,65 @@ void fd_remove(int fd) fd_dodelete(fd, 0); } +/* Tries to send parts from followed by parts from + * optionally followed by a newline if is non-null, to file descriptor + * . The message is sent atomically using writev(). It may be truncated to + * bytes if is non-null. There is no distinction between the + * two lists, it's just a convenience to help the caller prepend some prefixes + * when necessary. It takes the fd's lock to make sure no other thread will + * write to the same fd in parallel. Returns the number of bytes sent, or <=0 + * on failure. A limit to 31 total non-empty segments is enforced. The caller + * is responsible for taking care of making the fd non-blocking. + */ +ssize_t fd_write_frag_line(int fd, size_t maxlen, const struct ist pfx[], size_t npfx, const struct ist msg[], size_t nmsg, int nl) +{ + struct iovec iovec[32]; + size_t totlen = 0; + size_t sent = 0; + int vec = 0; + + if (!maxlen) + maxlen = ~0; + + /* keep one char for a possible trailing '\n' in any case */ + maxlen--; + + /* make an iovec from the concatenation of all parts of the original + * message. Skip empty fields and truncate the whole message to maxlen, + * leaving one spare iovec for the '\n'. + */ + while (vec < (sizeof(iovec) / sizeof(iovec[0]) - 1)) { + if (!npfx) { + pfx = msg; + npfx = nmsg; + nmsg = 0; + if (!npfx) + break; + } + + iovec[vec].iov_base = pfx->ptr; + iovec[vec].iov_len = MIN(maxlen, pfx->len); + maxlen -= iovec[vec].iov_len; + totlen += iovec[vec].iov_len; + if (iovec[vec].iov_len) + vec++; + pfx++; npfx--; + }; + + if (nl) { + iovec[vec].iov_base = "\n"; + iovec[vec].iov_len = 1; + vec++; + } + + HA_SPIN_LOCK(FD_LOCK, &fdtab[fd].lock); + sent = writev(fd, iovec, vec); + HA_SPIN_UNLOCK(FD_LOCK, &fdtab[fd].lock); + + /* sent > 0 if the message was delivered */ + return sent; +} + #if defined(USE_CLOSEFROM) void my_closefrom(int start) {