#include "system/select.h"
#include "system/time.h"
#include "system/network.h"
-#include "dlinklist.h"
+#include "lib/util/dlinklist.h"
#include "pthreadpool/pthreadpool.h"
+#include "lib/util/iov_buf.h"
+#include "lib/msghdr.h"
#include <fcntl.h>
/*
int sock;
ssize_t sent;
int sys_errno;
- size_t num_fds;
- int *fds;
- size_t buflen;
- uint8_t buf[];
};
struct unix_dgram_send_queue {
char path[];
};
-static ssize_t iov_buflen(const struct iovec *iov, int iovlen);
static void unix_dgram_recv_handler(struct poll_watch *w, int fd, short events,
void *private_data);
return prepare_socket_cloexec(sock);
}
+static size_t unix_dgram_msg_size(void)
+{
+ size_t msgsize = sizeof(struct unix_dgram_msg);
+ msgsize = (msgsize + 15) & ~15; /* align to 16 */
+ return msgsize;
+}
+
+static struct msghdr_buf *unix_dgram_msghdr(struct unix_dgram_msg *msg)
+{
+ /*
+ * Not portable in C99, but "msg" is aligned and so is
+ * unix_dgram_msg_size()
+ */
+ return (struct msghdr_buf *)(((char *)msg) + unix_dgram_msg_size());
+}
+
static void close_fd_array(int *fds, size_t num_fds)
{
size_t i;
}
}
+static void close_fd_array_dgram_msg(struct unix_dgram_msg *dmsg)
+{
+ struct msghdr_buf *hdr = unix_dgram_msghdr(dmsg);
+ struct msghdr *msg = msghdr_buf_msghdr(hdr);
+ size_t num_fds = msghdr_extract_fds(msg, NULL, 0);
+ int fds[num_fds];
+
+ msghdr_extract_fds(msg, fds, num_fds);
+
+ close_fd_array(fds, num_fds);
+}
+
static int unix_dgram_init(const struct sockaddr_un *addr, size_t max_msg,
const struct poll_funcs *ev_funcs,
void (*recv_callback)(struct unix_dgram_ctx *ctx,
int flags = 0;
struct msghdr msg;
struct iovec iov;
-#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
- char buf[CMSG_SPACE(sizeof(int)*INT8_MAX)] = { 0, };
- struct cmsghdr *cmsg;
-#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
- int *fds = NULL;
- size_t i, num_fds = 0;
+ size_t bufsize = msghdr_prep_recv_fds(NULL, NULL, 0, INT8_MAX);
+ uint8_t buf[bufsize];
iov = (struct iovec) {
.iov_base = (void *)ctx->recv_buf,
msg = (struct msghdr) {
.msg_iov = &iov,
.msg_iovlen = 1,
-#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
- .msg_control = buf,
- .msg_controllen = sizeof(buf),
-#endif
};
+ msghdr_prep_recv_fds(&msg, buf, bufsize, INT8_MAX);
+
#ifdef MSG_CMSG_CLOEXEC
flags |= MSG_CMSG_CLOEXEC;
#endif
return;
}
-#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
- for(cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
- cmsg = CMSG_NXTHDR(&msg, cmsg))
{
- void *data = CMSG_DATA(cmsg);
-
- if (cmsg->cmsg_type != SCM_RIGHTS) {
- continue;
- }
- if (cmsg->cmsg_level != SOL_SOCKET) {
- continue;
- }
+ size_t num_fds = msghdr_extract_fds(&msg, NULL, 0);
+ int fds[num_fds];
+ int i;
- fds = (int *)data;
- num_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof (int);
- break;
- }
-#endif
+ msghdr_extract_fds(&msg, fds, num_fds);
- for (i = 0; i < num_fds; i++) {
- int err;
+ for (i = 0; i < num_fds; i++) {
+ int err;
- err = prepare_socket_cloexec(fds[i]);
- if (err != 0) {
- goto cleanup_fds;
+ err = prepare_socket_cloexec(fds[i]);
+ if (err != 0) {
+ close_fd_array(fds, num_fds);
+ num_fds = 0;
+ }
}
- }
-
- ctx->recv_callback(ctx, ctx->recv_buf, received,
- fds, num_fds, ctx->private_data);
- return;
-
-cleanup_fds:
- close_fd_array(fds, num_fds);
- ctx->recv_callback(ctx, ctx->recv_buf, received,
- NULL, 0, ctx->private_data);
+ ctx->recv_callback(ctx, ctx->recv_buf, received,
+ fds, num_fds, ctx->private_data);
+ }
}
static void unix_dgram_job_finished(struct poll_watch *w, int fd, short events,
struct unix_dgram_msg *msg;
msg = q->msgs;
DLIST_REMOVE(q->msgs, msg);
- close_fd_array(msg->fds, msg->num_fds);
+ close_fd_array_dgram_msg(msg);
free(msg);
}
close(q->sock);
}
static int queue_msg(struct unix_dgram_send_queue *q,
- const struct iovec *iov, int iovlen,
+ const struct iovec *iov, int iovcnt,
const int *fds, size_t num_fds)
{
struct unix_dgram_msg *msg;
- ssize_t buflen;
- size_t msglen;
- size_t fds_size = sizeof(int) * num_fds;
+ struct msghdr_buf *hdr;
+ size_t msglen, needed;
+ ssize_t msghdrlen;
int fds_copy[MIN(num_fds, INT8_MAX)];
- size_t fds_padding = 0;
- int i;
- size_t tmp;
- int ret = -1;
-
- if (num_fds > INT8_MAX) {
- return EINVAL;
- }
+ int i, ret;
- for (i = 0; i < num_fds; i++) {
+ for (i=0; i<num_fds; i++) {
fds_copy[i] = -1;
}
}
}
- buflen = iov_buflen(iov, iovlen);
- if (buflen == -1) {
- goto invalid;
- }
+ msglen = unix_dgram_msg_size();
- msglen = offsetof(struct unix_dgram_msg, buf);
- tmp = msglen + buflen;
- if ((tmp < msglen) || (tmp < buflen)) {
- /* overflow */
- goto invalid;
+ msghdrlen = msghdr_copy(NULL, 0, NULL, 0, iov, iovcnt,
+ fds_copy, num_fds);
+ if (msghdrlen == -1) {
+ ret = EMSGSIZE;
+ goto fail;
}
- msglen = tmp;
- if (num_fds > 0) {
- const size_t fds_align = sizeof(int) - 1;
-
- tmp = msglen + fds_align;
- if ((tmp < msglen) || (tmp < fds_align)) {
- /* overflow */
- goto invalid;
- }
- tmp &= ~fds_align;
-
- fds_padding = tmp - msglen;
- msglen = tmp;
-
- tmp = msglen + fds_size;
- if ((tmp < msglen) || (tmp < fds_size)) {
- /* overflow */
- goto invalid;
- }
- msglen = tmp;
+ needed = msglen + msghdrlen;
+ if (needed < msglen) {
+ ret = EMSGSIZE;
+ goto fail;
}
- msg = malloc(msglen);
+ msg = malloc(needed);
if (msg == NULL) {
ret = ENOMEM;
goto fail;
}
- msg->buflen = buflen;
- msg->sock = q->sock;
-
- buflen = 0;
- for (i=0; i<iovlen; i++) {
- memcpy(&msg->buf[buflen], iov[i].iov_base, iov[i].iov_len);
- buflen += iov[i].iov_len;
- }
+ hdr = unix_dgram_msghdr(msg);
- msg->num_fds = num_fds;
- if (msg->num_fds > 0) {
- void *fds_ptr = (void *)&msg->buf[buflen+fds_padding];
- memcpy(fds_ptr, fds_copy, fds_size);
- msg->fds = (int *)fds_ptr;
- } else {
- msg->fds = NULL;
- }
+ msg->sock = q->sock;
+ msghdr_copy(hdr, msghdrlen, NULL, 0, iov, iovcnt,
+ fds_copy, num_fds);
DLIST_ADD_END(q->msgs, msg, struct unix_dgram_msg);
return 0;
-
-invalid:
- ret = EINVAL;
fail:
close_fd_array(fds_copy, num_fds);
return ret;
static void unix_dgram_send_job(void *private_data)
{
struct unix_dgram_msg *dmsg = private_data;
- struct iovec iov = {
- .iov_base = (void *)dmsg->buf,
- .iov_len = dmsg->buflen,
- };
- struct msghdr msg = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- };
-#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
- struct cmsghdr *cmsg;
- size_t fds_size = sizeof(int) * dmsg->num_fds;
- size_t cmsg_len = CMSG_LEN(fds_size);
- size_t cmsg_space = CMSG_SPACE(fds_size);
- char cmsg_buf[cmsg_space];
-
- if (dmsg->num_fds > 0) {
- void *fdptr;
-
- memset(cmsg_buf, 0, cmsg_space);
-
- msg.msg_control = cmsg_buf;
- msg.msg_controllen = cmsg_space;
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = cmsg_len;
- fdptr = CMSG_DATA(cmsg);
- memcpy(fdptr, dmsg->fds, fds_size);
- msg.msg_controllen = cmsg->cmsg_len;
- }
-#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
do {
- dmsg->sent = sendmsg(dmsg->sock, &msg, 0);
+ struct msghdr_buf *hdr = unix_dgram_msghdr(dmsg);
+ struct msghdr *msg = msghdr_buf_msghdr(hdr);
+ dmsg->sent = sendmsg(dmsg->sock, msg, 0);
} while ((dmsg->sent == -1) && (errno == EINTR));
- close_fd_array(dmsg->fds, dmsg->num_fds);
+ if (dmsg->sent == -1) {
+ dmsg->sys_errno = errno;
+ }
}
static void unix_dgram_job_finished(struct poll_watch *w, int fd, short events,
msg = q->msgs;
DLIST_REMOVE(q->msgs, msg);
- close_fd_array(msg->fds, msg->num_fds);
+ close_fd_array_dgram_msg(msg);
free(msg);
if (q->msgs != NULL) {
{
struct unix_dgram_send_queue *q;
struct msghdr msg;
-#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
- struct cmsghdr *cmsg;
- size_t fds_size = sizeof(int) * num_fds;
- size_t cmsg_len = CMSG_LEN(fds_size);
- size_t cmsg_space = CMSG_SPACE(fds_size);
- char cmsg_buf[cmsg_space];
-#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
+ ssize_t fdlen;
int ret;
int i;
return EINVAL;
}
-#ifndef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+#if !defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && !defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
if (num_fds > 0) {
return ENOSYS;
}
-#endif /* ! HAVE_STRUCT_MSGHDR_MSG_CONTROL */
+#endif
for (i = 0; i < num_fds; i++) {
/*
.msg_iov = discard_const_p(struct iovec, iov),
.msg_iovlen = iovlen
};
-#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
- if (num_fds > 0) {
- void *fdptr;
- memset(cmsg_buf, 0, cmsg_space);
+ fdlen = msghdr_prep_fds(&msg, NULL, 0, fds, num_fds);
+ if (fdlen == -1) {
+ return EINVAL;
+ }
+
+ {
+ uint8_t buf[fdlen];
+ msghdr_prep_fds(&msg, buf, fdlen, fds, num_fds);
- msg.msg_control = cmsg_buf;
- msg.msg_controllen = cmsg_space;
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = cmsg_len;
- fdptr = CMSG_DATA(cmsg);
- memcpy(fdptr, fds, fds_size);
- msg.msg_controllen = cmsg->cmsg_len;
+ ret = sendmsg(ctx->sock, &msg, 0);
}
-#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
- ret = sendmsg(ctx->sock, &msg, 0);
if (ret >= 0) {
return 0;
}
- if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINTR)) {
+ if ((errno != EWOULDBLOCK) &&
+ (errno != EAGAIN) &&
+#ifdef ENOBUFS
+ /* FreeBSD can give this for large messages */
+ (errno != ENOBUFS) &&
+#endif
+ (errno != EINTR)) {
return errno;
}
ctx->ev_funcs->watch_free(ctx->sock_read_watch);
+ close(ctx->sock);
if (getpid() == ctx->created_pid) {
/* If we created it, unlink. Otherwise someone else might
* still have it open */
unlink(ctx->path);
}
- close(ctx->sock);
free(ctx->recv_buf);
free(ctx);
return 0;
int unix_msg_init(const struct sockaddr_un *addr,
const struct poll_funcs *ev_funcs,
- size_t fragment_len, uint64_t cookie,
+ size_t fragment_len,
void (*recv_callback)(struct unix_msg_ctx *ctx,
uint8_t *msg, size_t msg_len,
int *fds, size_t num_fds,
*ctx = (struct unix_msg_ctx) {
.fragment_len = fragment_len,
- .cookie = cookie,
+ .cookie = 1,
.recv_callback = recv_callback,
.private_data = private_data
};
return EINVAL;
}
-#ifndef HAVE_STRUCT_MSGHDR_MSG_CONTROL
- if (num_fds > 0) {
- return ENOSYS;
- }
-#endif /* ! HAVE_STRUCT_MSGHDR_MSG_CONTROL */
-
if (num_fds > INT8_MAX) {
return EINVAL;
}
buflen -= sizeof(cookie);
if (cookie == 0) {
- ctx->recv_callback(ctx, buf, buflen, fds, num_fds, ctx->private_data);
+ ctx->recv_callback(ctx, buf, buflen, fds, num_fds,
+ ctx->private_data);
return;
}
}
DLIST_REMOVE(ctx->msgs, msg);
- ctx->recv_callback(ctx, msg->buf, msg->msglen, fds, num_fds, ctx->private_data);
+ ctx->recv_callback(ctx, msg->buf, msg->msglen, fds, num_fds,
+ ctx->private_data);
free(msg);
return;
free(ctx);
return 0;
}
-
-static ssize_t iov_buflen(const struct iovec *iov, int iovlen)
-{
- size_t buflen = 0;
- int i;
-
- for (i=0; i<iovlen; i++) {
- size_t thislen = iov[i].iov_len;
- size_t tmp = buflen + thislen;
-
- if ((tmp < buflen) || (tmp < thislen)) {
- /* overflow */
- return -1;
- }
- buflen = tmp;
- }
- return buflen;
-}