summaryrefslogtreecommitdiff
path: root/src/libunixonacid/ancil_recv_fd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libunixonacid/ancil_recv_fd.c')
-rw-r--r--src/libunixonacid/ancil_recv_fd.c69
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) ;
+}