]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: fd: add fd_write_frag_line() to send a fragmented line to an fd
authorWilly Tarreau <w@1wt.eu>
Tue, 27 Aug 2019 09:08:17 +0000 (11:08 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 27 Aug 2019 15:14:19 +0000 (17:14 +0200)
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
include/types/fd.h
src/fd.c

index e146976b2a2ef990580c500b793be6ae3acaaff9..9038be5117c4af57951e69672bdb341553f57f6d 100644 (file)
@@ -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 <start> */
 void my_closefrom(int start);
 
index 16845378c7d26982a8a53754ee73243130c02207..53364612b611cacaad8a725c10a70dc3d499ad1e 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <common/config.h>
 #include <common/hathreads.h>
+#include <common/ist.h>
 #include <types/port_range.h>
 
 /* Direction for each FD event update */
index c4155aa86fc9153fff81d14f7fc25924e8970e65..620de59799459a81e4c5c458b481eb59b9d683fb 100644 (file)
--- a/src/fd.c
+++ b/src/fd.c
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/resource.h>
+#include <sys/uio.h>
 
 #if defined(USE_POLL)
 #include <poll.h>
@@ -362,6 +363,65 @@ void fd_remove(int fd)
        fd_dodelete(fd, 0);
 }
 
+/* Tries to send <npfx> parts from <prefix> followed by <nmsg> parts from <msg>
+ * optionally followed by a newline if <nl> is non-null, to file descriptor
+ * <fd>. The message is sent atomically using writev(). It may be truncated to
+ * <maxlen> bytes if <maxlen> 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)
 {