Merge pull request #28557 from bluca/utils

utils: add serialize_bool_elide() and send/receive helpers for FD array
This commit is contained in:
Luca Boccassi
2023-07-29 12:39:06 +01:00
committed by GitHub
5 changed files with 223 additions and 3 deletions
+124
View File
@@ -41,6 +41,11 @@
# define IDN_FLAGS 0
#endif
/* From the kernel's include/net/scm.h */
#ifndef SCM_MAX_FD
# define SCM_MAX_FD 253
#endif
static const char* const socket_address_type_table[] = {
[SOCK_STREAM] = "Stream",
[SOCK_DGRAM] = "Datagram",
@@ -951,6 +956,53 @@ int getpeergroups(int fd, gid_t **ret) {
return (int) n;
}
ssize_t send_many_fds_iov_sa(
int transport_fd,
int *fds_array, size_t n_fds_array,
const struct iovec *iov, size_t iovlen,
const struct sockaddr *sa, socklen_t len,
int flags) {
_cleanup_free_ struct cmsghdr *cmsg = NULL;
struct msghdr mh = {
.msg_name = (struct sockaddr*) sa,
.msg_namelen = len,
.msg_iov = (struct iovec *)iov,
.msg_iovlen = iovlen,
};
ssize_t k;
assert(transport_fd >= 0);
assert(fds_array || n_fds_array == 0);
/* The kernel will reject sending more than SCM_MAX_FD FDs at once */
if (n_fds_array > SCM_MAX_FD)
return -E2BIG;
/* We need either an FD array or data to send. If there's nothing, return an error. */
if (n_fds_array == 0 && !iov)
return -EINVAL;
if (n_fds_array > 0) {
mh.msg_controllen = CMSG_SPACE(sizeof(int) * n_fds_array);
mh.msg_control = cmsg = malloc(mh.msg_controllen);
if (!cmsg)
return -ENOMEM;
*cmsg = (struct cmsghdr) {
.cmsg_len = CMSG_LEN(sizeof(int) * n_fds_array),
.cmsg_level = SOL_SOCKET,
.cmsg_type = SCM_RIGHTS,
};
memcpy(CMSG_DATA(cmsg), fds_array, sizeof(int) * n_fds_array);
}
k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags);
if (k < 0)
return (ssize_t) -errno;
return k;
}
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
@@ -1006,6 +1058,78 @@ int send_one_fd_sa(
return (int) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, sa, len, flags);
}
ssize_t receive_many_fds_iov(
int transport_fd,
struct iovec *iov, size_t iovlen,
int **ret_fds_array, size_t *ret_n_fds_array,
int flags) {
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int) * SCM_MAX_FD)) control;
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
.msg_iov = iov,
.msg_iovlen = iovlen,
};
_cleanup_free_ int *fds_array = NULL;
size_t n_fds_array = 0;
struct cmsghdr *cmsg;
ssize_t k;
assert(transport_fd >= 0);
assert(ret_fds_array);
assert(ret_n_fds_array);
/*
* Receive many FDs via @transport_fd. We don't care for the transport-type. We retrieve all the FDs
* at once. This is best used in combination with send_many_fds().
*/
k = recvmsg_safe(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags);
if (k < 0)
return k;
CMSG_FOREACH(cmsg, &mh)
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
size_t n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
fds_array = GREEDY_REALLOC(fds_array, n_fds_array + n);
if (!fds_array) {
cmsg_close_all(&mh);
return -ENOMEM;
}
memcpy(fds_array + n_fds_array, CMSG_TYPED_DATA(cmsg, int), sizeof(int) * n);
n_fds_array += n;
}
if (n_fds_array == 0) {
cmsg_close_all(&mh);
/* If didn't receive an FD or any data, return an error. */
if (k == 0)
return -EIO;
}
*ret_fds_array = TAKE_PTR(fds_array);
*ret_n_fds_array = n_fds_array;
return k;
}
int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_array, int flags) {
ssize_t k;
k = receive_many_fds_iov(transport_fd, NULL, 0, ret_fds_array, ret_n_fds_array, flags);
if (k == 0)
return 0;
/* k must be negative, since receive_many_fds_iov() only returns a positive value if data was received
* through the iov. */
assert(k < 0);
return (int) k;
}
ssize_t receive_one_fd_iov(
int transport_fd,
struct iovec *iov, size_t iovlen,
+24
View File
@@ -153,6 +153,28 @@ int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
int getpeergroups(int fd, gid_t **ret);
ssize_t send_many_fds_iov_sa(
int transport_fd,
int *fds_array, size_t n_fds_array,
const struct iovec *iov, size_t iovlen,
const struct sockaddr *sa, socklen_t len,
int flags);
static inline ssize_t send_many_fds_iov(
int transport_fd,
int *fds_array, size_t n_fds_array,
const struct iovec *iov, size_t iovlen,
int flags) {
return send_many_fds_iov_sa(transport_fd, fds_array, n_fds_array, iov, iovlen, NULL, 0, flags);
}
static inline int send_many_fds(
int transport_fd,
int *fds_array,
size_t n_fds_array,
int flags) {
return send_many_fds_iov_sa(transport_fd, fds_array, n_fds_array, NULL, 0, NULL, 0, flags);
}
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
@@ -167,6 +189,8 @@ int send_one_fd_sa(int transport_fd,
#define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags)
ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd);
int receive_one_fd(int transport_fd, int flags);
ssize_t receive_many_fds_iov(int transport_fd, struct iovec *iov, size_t iovlen, int **ret_fds_array, size_t *ret_n_fds_array, int flags);
int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_array, int flags);
ssize_t next_datagram_size_fd(int fd);