From: Raphaƫl Gertz <haproxy@rapsys.eu> Date: Tue Oct 31 19:00:00 CET 2023 Subject: [PATCH] Keep O_APPEND flag to prevent log corruption by haproxy when using stdout This fix prevent the flag clear of all other O_* flags to drop O_APPEND as well when setting O_NONBLOCK. It allows to redirect haproxy stdout log to a file without corruption. 1/ Bug configuration: global log stdout format short daemon 2/ Test case: $ echo 'line1\nline2' > stdout.log; haproxy -f haproxy.conf >> stdout.log 3/ Corrupted result: <6> [...] 4/ Result after patch: line1 line2 <6> [...] 5/ Donation Like my work, request a feature or want to do a donation ? You may send me a beer, tea, coffee or more via PayPal at paypal@rapsys.eu via: https://www.paypal.com/myaccount/transfer/homepage/pay diff -urNp haproxy-2.8.3.orig/include/haproxy/fd.h haproxy-2.8.3/include/haproxy/fd.h --- haproxy-2.8.3.orig/include/haproxy/fd.h 2023-09-07 11:32:42.000000000 +0200 +++ haproxy-2.8.3/include/haproxy/fd.h 2023-10-31 18:30:17.592943791 +0100 @@ -59,6 +59,12 @@ extern volatile int ha_used_fds; // Numb void fd_delete(int fd); void _fd_delete_orphan(int fd); +/* makes the new fd non-blocking and clears all other O_* flags + * except extra; this is meant to be used on new FDs. + * Returns -1 on failure. + */ +int fd_set_nonblock_extra(int fd, uint extra); + /* makes the new fd non-blocking and clears all other O_* flags; * this is meant to be used on new FDs. Returns -1 on failure. */ diff -urNp haproxy-2.8.3.orig/src/fd.c haproxy-2.8.3/src/fd.c --- haproxy-2.8.3.orig/src/fd.c 2023-09-07 11:32:42.000000000 +0200 +++ haproxy-2.8.3/src/fd.c 2023-10-31 18:46:31.821142586 +0100 @@ -436,6 +436,18 @@ void fd_delete(int fd) } } +/* makes the new fd non-blocking and clears all other O_* flags except extra; + * this is meant to be used on new FDs. Returns -1 on failure. The result is + * disguised at the end because some callers need to be able to ignore it + * regardless of the libc attributes. + */ +int fd_set_nonblock_extra(int fd, uint extra) +{ + int ret = fcntl(fd, F_SETFL, extra|O_NONBLOCK); + + return DISGUISE(ret); +} + /* makes the new fd non-blocking and clears all other O_* flags; this is meant * to be used on new FDs. Returns -1 on failure. The result is disguised at the * end because some callers need to be able to ignore it regardless of the libc @@ -443,7 +455,7 @@ void fd_delete(int fd) */ int fd_set_nonblock(int fd) { - int ret = fcntl(fd, F_SETFL, O_NONBLOCK); + int ret = fd_set_nonblock_extra(fd, 0); return DISGUISE(ret); } @@ -921,8 +933,12 @@ ssize_t fd_write_frag_line(int fd, size_ if (unlikely(!(fdtab[fd].state & FD_INITIALIZED))) { HA_ATOMIC_OR(&fdtab[fd].state, FD_INITIALIZED); - if (!isatty(fd)) - fd_set_nonblock(fd); + if (!isatty(fd)) { + /* Retriev fd mode */ + uint mode = fcntl(fd, F_GETFL); + /* Pass O_APPEND */ + fd_set_nonblock_extra(fd, mode & O_APPEND); + } } sent = writev(fd, iovec, vec); HA_ATOMIC_BTR(&fdtab[fd].state, FD_EXCL_SYSCALL_BIT);