From db7f4d2c1ef70334af4cb8c1980f47d8a9f69249 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Sat, 20 Dec 2014 22:25:08 +0000 Subject: Found the BSD unixmessage bug. recvmsg(..., MSG_WAITALL) blocks until buffer full or socket shutdown on BSD, *even if the socket is nonblocking*. So I added an "okwaitall" sysdeps, and won't set MSG_WAITALL on retarded systems. --- src/sysdeps/tryokwaitall.c | 117 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 src/sysdeps/tryokwaitall.c (limited to 'src/sysdeps') diff --git a/src/sysdeps/tryokwaitall.c b/src/sysdeps/tryokwaitall.c new file mode 100644 index 0000000..15721b5 --- /dev/null +++ b/src/sysdeps/tryokwaitall.c @@ -0,0 +1,117 @@ +/* ISC license. */ + +#undef _POSIX_C_SOURCE +#undef _XOPEN_SOURCE + +#ifndef _XPG4_2 +# define _XPG4_2 +#endif + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +#include +#endif + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +static int ndelay_on (int fd) +{ + register int got = fcntl(fd, F_GETFL) ; + return (got == -1) ? -1 : fcntl(fd, F_SETFL, got | O_NONBLOCK) ; +} + +static int child (int p, int fd) +{ + char c ; + struct iovec v = { .iov_base = &c, .iov_len = 1 } ; + struct msghdr msg = + { + .msg_name = 0, + .msg_namelen = 0, + .msg_iov = &v, + .msg_iovlen = 1, + .msg_control = 0, + .msg_controllen = 0 + } ; + fd_set rfds ; + if (ndelay_on(fd) < 0) return 111 ; + FD_ZERO(&rfds) ; + FD_SET(fd, &rfds) ; + if (write(p, "", 1) < 1) return 111 ; /* sync with the parent */ + if (select(fd+1, &rfds, 0, 0, 0) < 1) return 111 ; + + /* On buggy systems, the following recvmsg will block, despite + setting the fd non-blocking */ + + if (recvmsg(fd, &msg, MSG_WAITALL) < 1) return 111 ; + if (write(p, "", 1) < 1) return 111 ; + return 0 ; +} + +static int parent (pid_t pid, int p, int fd) +{ + char c ; + struct iovec v = { .iov_base = &c, .iov_len = 1 } ; + struct msghdr msg = + { + .msg_name = 0, + .msg_namelen = 0, + .msg_iov = &v, + .msg_iovlen = 1, + .msg_control = 0, + .msg_controllen = 0 + } ; + struct timeval tv = { .tv_sec = 2, .tv_usec = 0 } ; + fd_set rfds ; + unsigned int n = p > fd ? p : fd ; + int r ; + FD_ZERO(&rfds) ; + FD_SET(p, &rfds) ; + if (read(p, &c, 1) < 1) return 111 ; + if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 1) return 111 ; + for (;;) + { + r = select(n+1, &rfds, 0, 0, &tv) ; + if (r >= 0 || errno != EINTR) break ; + } + if (!r) /* child is still blocking on recvmsg() after 2 seconds */ + { + kill(pid, SIGTERM) ; + return 1 ; + } + if (read(p, &c, 1) < 1) return 111 ; + return 0 ; +} + +int main (void) +{ + pid_t pid ; + int fd[2] ; + int p[2] ; + if (pipe(p) < 0) return 111 ; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) return 111 ; + pid = fork() ; + if (pid < 0) return 111 ; + if (!pid) + { + close(p[0]) ; + close(fd[0]) ; + return child(p[1], fd[1]) ; + } + close(p[1]) ; + close(fd[1]) ; + return parent(pid, p[0], fd[0]) ; +} -- cgit v1.2.3