#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;
}
if (msg.msg_flags & MSG_CTRUNC) {
return -1;
}
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg && cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
memcpy(out_fd, CMSG_DATA(cmsg), sizeof(int));
}
if ((size_t)n < meta_len) {
if (wire_recv_all(sock, (unsigned char *)meta_buf + n, meta_len - (size_t)n) < 0) {
if (*out_fd >= 0) {
close(*out_fd);
*out_fd = -1;
}
return -1;
}
}
return 0;
}
#endif