#ifndef DRMTAP_WIRE_H
#define DRMTAP_WIRE_H
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
static inline int wire_send_all(int sock, const void *buf, size_t len) {
const unsigned char *p = (const unsigned char *)buf;
size_t sent = 0;
while (sent < len) {
ssize_t n = send(sock, p + sent, len - sent, MSG_NOSIGNAL);
if (n < 0) {
if (errno == EINTR) continue;
return -1;
}
if (n == 0) {
return -1;
}
sent += (size_t)n;
}
return 0;
}
static inline int wire_recv_all(int sock, void *buf, size_t len) {
unsigned char *p = (unsigned char *)buf;
size_t got = 0;
while (got < len) {
ssize_t n = recv(sock, p + got, len - got, 0);
if (n < 0) {
if (errno == EINTR) continue;
return -1;
}
if (n == 0) {
return -1;
}
got += (size_t)n;
}
return 0;
}
static inline int wire_send_fd(int sock, int fd, const void *meta, size_t meta_len) {
struct msghdr msg;
struct iovec iov;
union {
struct cmsghdr align;
char buf[CMSG_SPACE(sizeof(int))];
} cmsg_buf;
memset(&msg, 0, sizeof(msg));
iov.iov_base = (void *)meta;
iov.iov_len = meta_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_buf.buf;
msg.msg_controllen = sizeof(cmsg_buf.buf);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
ssize_t n;
do {
n = sendmsg(sock, &msg, MSG_NOSIGNAL);
} while (n < 0 && errno == EINTR);
if (n < 0) {
return -1;
}
if ((size_t)n < meta_len) {
return wire_send_all(sock, (const unsigned char *)meta + (size_t)n,
meta_len - (size_t)n);
}
return 0;
}
static inline int wire_recv_fd(int sock, void *meta_buf, size_t meta_len, int *out_fd) {
struct msghdr msg;
struct iovec iov;
union {
struct cmsghdr align;
char buf[CMSG_SPACE(sizeof(int))];
} cmsg_buf;
*out_fd = -1;
memset(&msg, 0, sizeof(msg));
iov.iov_base = meta_buf;
iov.iov_len = meta_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_buf.buf;
msg.msg_controllen = sizeof(cmsg_buf.buf);
ssize_t n = recvmsg(sock, &msg, MSG_CMSG_CLOEXEC);
if (n <= 0) {
return -1;
}
int accepted = -1;
int nfds = 0;
for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS ||
cmsg->cmsg_len < CMSG_LEN(0)) {
continue;
}
size_t count = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
for (size_t i = 0; i < count; i++) {
int fd;
memcpy(&fd, CMSG_DATA(cmsg) + i * sizeof(int), sizeof(int));
if (nfds == 0) {
accepted = fd;
} else {
close(fd);
}
nfds++;
}
}
if ((msg.msg_flags & MSG_CTRUNC) || nfds > 1) {
if (accepted >= 0) {
close(accepted);
}
return -1;
}
*out_fd = accepted;
if ((size_t)n < meta_len) {
if (wire_recv_all(sock, (unsigned char *)meta_buf + n, meta_len - (size_t)n) < 0) {
close(*out_fd);
*out_fd = -1;
return -1;
}
}
return 0;
}
#endif