diff options
Diffstat (limited to 'src/libunixonacid/ancil_recv_fd.c')
-rw-r--r-- | src/libunixonacid/ancil_recv_fd.c | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/src/libunixonacid/ancil_recv_fd.c b/src/libunixonacid/ancil_recv_fd.c new file mode 100644 index 0000000..70f2c4f --- /dev/null +++ b/src/libunixonacid/ancil_recv_fd.c @@ -0,0 +1,69 @@ +/* ISC license. */ + +#include <skalibs/sysdeps.h> +#include <skalibs/nonposix.h> + +#include <errno.h> +#include <sys/uio.h> +#include <sys/socket.h> + +#include <skalibs/allreadwrite.h> +#include <skalibs/djbunix.h> +#include <skalibs/ancil.h> +#include <skalibs/posixishard.h> + +union aligner_u +{ + struct cmsghdr cmsghdr ; + int i ; +} ; + +int ancil_recv_fd (int sock, char expected_ch) +{ + static int const awesomeflags = +#ifdef SKALIBS_HASMSGDONTWAIT + MSG_DONTWAIT +#else + 0 +#endif + | +#ifdef SKALIBS_HASCMSGCLOEXEC + MSG_CMSG_CLOEXEC +#else + 0 +#endif + ; + struct cmsghdr *c ; + ssize_t r ; + char ch ; + struct iovec v = { .iov_base = &ch, .iov_len = 1 } ; + union aligner_u ancilbuf[1 + (CMSG_SPACE(sizeof(int)) - 1) / sizeof(union aligner_u)] ; + struct msghdr msghdr = + { + .msg_name = 0, + .msg_namelen = 0, + .msg_iov = &v, + .msg_iovlen = 1, + .msg_flags = 0, + .msg_control = ancilbuf, + .msg_controllen = CMSG_SPACE(sizeof(int)) + } ; + do r = recvmsg(sock, &msghdr, awesomeflags) ; + while (r < 0 && errno == EINTR) ; + if (r < 0) return r ; + if (!r) return (errno = EPIPE, -1) ; + c = CMSG_FIRSTHDR(&msghdr) ; + if (ch != expected_ch + || !c + || c->cmsg_level != SOL_SOCKET + || c->cmsg_type != SCM_RIGHTS + || (size_t)(c->cmsg_len - (CMSG_DATA(c) - (unsigned char *)c)) != sizeof(int)) return (errno = EPROTO, -1) ; +#ifndef SKALIBS_HASCMSGCLOEXEC + if (coe(*(int *)CMSG_DATA(c)) < 0) + { + fd_close(*(int *)CMSG_DATA(c)) ; + return -1 ; + } +#endif + return *(int *)CMSG_DATA(c) ; +} |