diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2014-12-20 22:25:08 +0000 |
---|---|---|
committer | Laurent Bercot <ska-skaware@skarnet.org> | 2014-12-20 22:25:08 +0000 |
commit | db7f4d2c1ef70334af4cb8c1980f47d8a9f69249 (patch) | |
tree | 4d878ef25e634be9b9ae2b870905a548603ecc50 /src/sysdeps | |
parent | 0d82edd9f8bebb396a9154d4e1003340dbf6b967 (diff) | |
download | skalibs-db7f4d2c1ef70334af4cb8c1980f47d8a9f69249.tar.xz |
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.
Diffstat (limited to 'src/sysdeps')
-rw-r--r-- | src/sysdeps/tryokwaitall.c | 117 |
1 files changed, 117 insertions, 0 deletions
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 <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/uio.h> +#if defined(__FreeBSD__) +#include <sys/param.h> +#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]) ; +} |