xref: /freebsd/lib/libnv/msgio.c (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
136da5199SPawel Jakub Dawidek /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni  *
436da5199SPawel Jakub Dawidek  * Copyright (c) 2013 The FreeBSD Foundation
536da5199SPawel Jakub Dawidek  * Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org>
636da5199SPawel Jakub Dawidek  * All rights reserved.
736da5199SPawel Jakub Dawidek  *
836da5199SPawel Jakub Dawidek  * This software was developed by Pawel Jakub Dawidek under sponsorship from
936da5199SPawel Jakub Dawidek  * the FreeBSD Foundation.
1036da5199SPawel Jakub Dawidek  *
1136da5199SPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
1236da5199SPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
1336da5199SPawel Jakub Dawidek  * are met:
1436da5199SPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
1536da5199SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
1636da5199SPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
1736da5199SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
1836da5199SPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
1936da5199SPawel Jakub Dawidek  *
2036da5199SPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
2136da5199SPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2236da5199SPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2336da5199SPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2436da5199SPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2536da5199SPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2636da5199SPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2736da5199SPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2836da5199SPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2936da5199SPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3036da5199SPawel Jakub Dawidek  * SUCH DAMAGE.
3136da5199SPawel Jakub Dawidek  */
3236da5199SPawel Jakub Dawidek 
3336da5199SPawel Jakub Dawidek #include <sys/cdefs.h>
3436da5199SPawel Jakub Dawidek __FBSDID("$FreeBSD$");
3536da5199SPawel Jakub Dawidek 
36586c5854SPawel Jakub Dawidek #include <sys/param.h>
3736da5199SPawel Jakub Dawidek #include <sys/socket.h>
383810ba1bSMariusz Zaborski #include <sys/select.h>
3936da5199SPawel Jakub Dawidek 
4036da5199SPawel Jakub Dawidek #include <errno.h>
4136da5199SPawel Jakub Dawidek #include <fcntl.h>
4236da5199SPawel Jakub Dawidek #include <stdbool.h>
4336da5199SPawel Jakub Dawidek #include <stdint.h>
4436da5199SPawel Jakub Dawidek #include <stdlib.h>
4536da5199SPawel Jakub Dawidek #include <string.h>
4636da5199SPawel Jakub Dawidek #include <unistd.h>
4736da5199SPawel Jakub Dawidek 
4836da5199SPawel Jakub Dawidek #ifdef HAVE_PJDLOG
4936da5199SPawel Jakub Dawidek #include <pjdlog.h>
5036da5199SPawel Jakub Dawidek #endif
5136da5199SPawel Jakub Dawidek 
5236da5199SPawel Jakub Dawidek #include "common_impl.h"
5336da5199SPawel Jakub Dawidek #include "msgio.h"
5436da5199SPawel Jakub Dawidek 
5536da5199SPawel Jakub Dawidek #ifndef	HAVE_PJDLOG
5636da5199SPawel Jakub Dawidek #include <assert.h>
5736da5199SPawel Jakub Dawidek #define	PJDLOG_ASSERT(...)		assert(__VA_ARGS__)
5836da5199SPawel Jakub Dawidek #define	PJDLOG_RASSERT(expr, ...)	assert(expr)
5936da5199SPawel Jakub Dawidek #define	PJDLOG_ABORT(...)		abort()
6036da5199SPawel Jakub Dawidek #endif
6136da5199SPawel Jakub Dawidek 
623810ba1bSMariusz Zaborski #ifdef __linux__
633810ba1bSMariusz Zaborski /* Linux: arbitrary size, but must be lower than SCM_MAX_FD. */
643810ba1bSMariusz Zaborski #define	PKG_MAX_SIZE	((64U - 1) * CMSG_SPACE(sizeof(int)))
653810ba1bSMariusz Zaborski #else
6607cf2bb6SMark Johnston /*
6707cf2bb6SMark Johnston  * To work around limitations in 32-bit emulation on 64-bit kernels, use a
6807cf2bb6SMark Johnston  * machine-independent limit on the number of FDs per message.  Each control
6907cf2bb6SMark Johnston  * message contains 1 FD and requires 12 bytes for the header, 4 pad bytes,
7007cf2bb6SMark Johnston  * 4 bytes for the descriptor, and another 4 pad bytes.
7107cf2bb6SMark Johnston  */
7207cf2bb6SMark Johnston #define	PKG_MAX_SIZE	(MCLBYTES / 24)
733810ba1bSMariusz Zaborski #endif
74586c5854SPawel Jakub Dawidek 
7536da5199SPawel Jakub Dawidek static int
7636da5199SPawel Jakub Dawidek msghdr_add_fd(struct cmsghdr *cmsg, int fd)
7736da5199SPawel Jakub Dawidek {
7836da5199SPawel Jakub Dawidek 
7936da5199SPawel Jakub Dawidek 	PJDLOG_ASSERT(fd >= 0);
8036da5199SPawel Jakub Dawidek 
8136da5199SPawel Jakub Dawidek 	cmsg->cmsg_level = SOL_SOCKET;
8236da5199SPawel Jakub Dawidek 	cmsg->cmsg_type = SCM_RIGHTS;
8336da5199SPawel Jakub Dawidek 	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
8436da5199SPawel Jakub Dawidek 	bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
8536da5199SPawel Jakub Dawidek 
8636da5199SPawel Jakub Dawidek 	return (0);
8736da5199SPawel Jakub Dawidek }
8836da5199SPawel Jakub Dawidek 
8936da5199SPawel Jakub Dawidek static void
9036da5199SPawel Jakub Dawidek fd_wait(int fd, bool doread)
9136da5199SPawel Jakub Dawidek {
9236da5199SPawel Jakub Dawidek 	fd_set fds;
9336da5199SPawel Jakub Dawidek 
9436da5199SPawel Jakub Dawidek 	PJDLOG_ASSERT(fd >= 0);
9536da5199SPawel Jakub Dawidek 
9636da5199SPawel Jakub Dawidek 	FD_ZERO(&fds);
9736da5199SPawel Jakub Dawidek 	FD_SET(fd, &fds);
9836da5199SPawel Jakub Dawidek 	(void)select(fd + 1, doread ? &fds : NULL, doread ? NULL : &fds,
9936da5199SPawel Jakub Dawidek 	    NULL, NULL);
10036da5199SPawel Jakub Dawidek }
10136da5199SPawel Jakub Dawidek 
10236da5199SPawel Jakub Dawidek static int
10336da5199SPawel Jakub Dawidek msg_recv(int sock, struct msghdr *msg)
10436da5199SPawel Jakub Dawidek {
10536da5199SPawel Jakub Dawidek 	int flags;
10636da5199SPawel Jakub Dawidek 
10736da5199SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
10836da5199SPawel Jakub Dawidek 
10936da5199SPawel Jakub Dawidek #ifdef MSG_CMSG_CLOEXEC
11036da5199SPawel Jakub Dawidek 	flags = MSG_CMSG_CLOEXEC;
11136da5199SPawel Jakub Dawidek #else
11236da5199SPawel Jakub Dawidek 	flags = 0;
11336da5199SPawel Jakub Dawidek #endif
11436da5199SPawel Jakub Dawidek 
11536da5199SPawel Jakub Dawidek 	for (;;) {
11636da5199SPawel Jakub Dawidek 		fd_wait(sock, true);
11736da5199SPawel Jakub Dawidek 		if (recvmsg(sock, msg, flags) == -1) {
11836da5199SPawel Jakub Dawidek 			if (errno == EINTR)
11936da5199SPawel Jakub Dawidek 				continue;
12036da5199SPawel Jakub Dawidek 			return (-1);
12136da5199SPawel Jakub Dawidek 		}
12236da5199SPawel Jakub Dawidek 		break;
12336da5199SPawel Jakub Dawidek 	}
12436da5199SPawel Jakub Dawidek 
12536da5199SPawel Jakub Dawidek 	return (0);
12636da5199SPawel Jakub Dawidek }
12736da5199SPawel Jakub Dawidek 
12836da5199SPawel Jakub Dawidek static int
12936da5199SPawel Jakub Dawidek msg_send(int sock, const struct msghdr *msg)
13036da5199SPawel Jakub Dawidek {
13136da5199SPawel Jakub Dawidek 
13236da5199SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
13336da5199SPawel Jakub Dawidek 
13436da5199SPawel Jakub Dawidek 	for (;;) {
13536da5199SPawel Jakub Dawidek 		fd_wait(sock, false);
13636da5199SPawel Jakub Dawidek 		if (sendmsg(sock, msg, 0) == -1) {
13736da5199SPawel Jakub Dawidek 			if (errno == EINTR)
13836da5199SPawel Jakub Dawidek 				continue;
13936da5199SPawel Jakub Dawidek 			return (-1);
14036da5199SPawel Jakub Dawidek 		}
14136da5199SPawel Jakub Dawidek 		break;
14236da5199SPawel Jakub Dawidek 	}
14336da5199SPawel Jakub Dawidek 
14436da5199SPawel Jakub Dawidek 	return (0);
14536da5199SPawel Jakub Dawidek }
14636da5199SPawel Jakub Dawidek 
1473810ba1bSMariusz Zaborski #ifdef __FreeBSD__
14836da5199SPawel Jakub Dawidek int
14936da5199SPawel Jakub Dawidek cred_send(int sock)
15036da5199SPawel Jakub Dawidek {
15136da5199SPawel Jakub Dawidek 	unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
15236da5199SPawel Jakub Dawidek 	struct msghdr msg;
15336da5199SPawel Jakub Dawidek 	struct cmsghdr *cmsg;
15436da5199SPawel Jakub Dawidek 	struct iovec iov;
15536da5199SPawel Jakub Dawidek 	uint8_t dummy;
15636da5199SPawel Jakub Dawidek 
15736da5199SPawel Jakub Dawidek 	bzero(credbuf, sizeof(credbuf));
15836da5199SPawel Jakub Dawidek 	bzero(&msg, sizeof(msg));
15936da5199SPawel Jakub Dawidek 	bzero(&iov, sizeof(iov));
16036da5199SPawel Jakub Dawidek 
16136da5199SPawel Jakub Dawidek 	/*
16236da5199SPawel Jakub Dawidek 	 * XXX: We send one byte along with the control message, because
16336da5199SPawel Jakub Dawidek 	 *      setting msg_iov to NULL only works if this is the first
16436da5199SPawel Jakub Dawidek 	 *      packet send over the socket. Once we send some data we
16536da5199SPawel Jakub Dawidek 	 *      won't be able to send credentials anymore. This is most
16636da5199SPawel Jakub Dawidek 	 *      likely a kernel bug.
16736da5199SPawel Jakub Dawidek 	 */
16836da5199SPawel Jakub Dawidek 	dummy = 0;
16936da5199SPawel Jakub Dawidek 	iov.iov_base = &dummy;
17036da5199SPawel Jakub Dawidek 	iov.iov_len = sizeof(dummy);
17136da5199SPawel Jakub Dawidek 
17236da5199SPawel Jakub Dawidek 	msg.msg_iov = &iov;
17336da5199SPawel Jakub Dawidek 	msg.msg_iovlen = 1;
17436da5199SPawel Jakub Dawidek 	msg.msg_control = credbuf;
17536da5199SPawel Jakub Dawidek 	msg.msg_controllen = sizeof(credbuf);
17636da5199SPawel Jakub Dawidek 
17736da5199SPawel Jakub Dawidek 	cmsg = CMSG_FIRSTHDR(&msg);
17836da5199SPawel Jakub Dawidek 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
17936da5199SPawel Jakub Dawidek 	cmsg->cmsg_level = SOL_SOCKET;
18036da5199SPawel Jakub Dawidek 	cmsg->cmsg_type = SCM_CREDS;
18136da5199SPawel Jakub Dawidek 
18236da5199SPawel Jakub Dawidek 	if (msg_send(sock, &msg) == -1)
18336da5199SPawel Jakub Dawidek 		return (-1);
18436da5199SPawel Jakub Dawidek 
18536da5199SPawel Jakub Dawidek 	return (0);
18636da5199SPawel Jakub Dawidek }
18736da5199SPawel Jakub Dawidek 
18836da5199SPawel Jakub Dawidek int
18936da5199SPawel Jakub Dawidek cred_recv(int sock, struct cmsgcred *cred)
19036da5199SPawel Jakub Dawidek {
19136da5199SPawel Jakub Dawidek 	unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
19236da5199SPawel Jakub Dawidek 	struct msghdr msg;
19336da5199SPawel Jakub Dawidek 	struct cmsghdr *cmsg;
19436da5199SPawel Jakub Dawidek 	struct iovec iov;
19536da5199SPawel Jakub Dawidek 	uint8_t dummy;
19636da5199SPawel Jakub Dawidek 
19736da5199SPawel Jakub Dawidek 	bzero(credbuf, sizeof(credbuf));
19836da5199SPawel Jakub Dawidek 	bzero(&msg, sizeof(msg));
19936da5199SPawel Jakub Dawidek 	bzero(&iov, sizeof(iov));
20036da5199SPawel Jakub Dawidek 
20136da5199SPawel Jakub Dawidek 	iov.iov_base = &dummy;
20236da5199SPawel Jakub Dawidek 	iov.iov_len = sizeof(dummy);
20336da5199SPawel Jakub Dawidek 
20436da5199SPawel Jakub Dawidek 	msg.msg_iov = &iov;
20536da5199SPawel Jakub Dawidek 	msg.msg_iovlen = 1;
20636da5199SPawel Jakub Dawidek 	msg.msg_control = credbuf;
20736da5199SPawel Jakub Dawidek 	msg.msg_controllen = sizeof(credbuf);
20836da5199SPawel Jakub Dawidek 
20936da5199SPawel Jakub Dawidek 	if (msg_recv(sock, &msg) == -1)
21036da5199SPawel Jakub Dawidek 		return (-1);
21136da5199SPawel Jakub Dawidek 
21236da5199SPawel Jakub Dawidek 	cmsg = CMSG_FIRSTHDR(&msg);
21336da5199SPawel Jakub Dawidek 	if (cmsg == NULL ||
21436da5199SPawel Jakub Dawidek 	    cmsg->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred)) ||
21536da5199SPawel Jakub Dawidek 	    cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDS) {
21636da5199SPawel Jakub Dawidek 		errno = EINVAL;
21736da5199SPawel Jakub Dawidek 		return (-1);
21836da5199SPawel Jakub Dawidek 	}
21936da5199SPawel Jakub Dawidek 	bcopy(CMSG_DATA(cmsg), cred, sizeof(*cred));
22036da5199SPawel Jakub Dawidek 
22136da5199SPawel Jakub Dawidek 	return (0);
22236da5199SPawel Jakub Dawidek }
223032f0fbbSAlex Richardson #endif
22436da5199SPawel Jakub Dawidek 
225586c5854SPawel Jakub Dawidek static int
226586c5854SPawel Jakub Dawidek fd_package_send(int sock, const int *fds, size_t nfds)
22736da5199SPawel Jakub Dawidek {
22836da5199SPawel Jakub Dawidek 	struct msghdr msg;
22936da5199SPawel Jakub Dawidek 	struct cmsghdr *cmsg;
230586c5854SPawel Jakub Dawidek 	struct iovec iov;
23136da5199SPawel Jakub Dawidek 	unsigned int i;
23236da5199SPawel Jakub Dawidek 	int serrno, ret;
233586c5854SPawel Jakub Dawidek 	uint8_t dummy;
23436da5199SPawel Jakub Dawidek 
235586c5854SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
236586c5854SPawel Jakub Dawidek 	PJDLOG_ASSERT(fds != NULL);
237586c5854SPawel Jakub Dawidek 	PJDLOG_ASSERT(nfds > 0);
23836da5199SPawel Jakub Dawidek 
23936da5199SPawel Jakub Dawidek 	bzero(&msg, sizeof(msg));
240586c5854SPawel Jakub Dawidek 
241586c5854SPawel Jakub Dawidek 	/*
242586c5854SPawel Jakub Dawidek 	 * XXX: Look into cred_send function for more details.
243586c5854SPawel Jakub Dawidek 	 */
244586c5854SPawel Jakub Dawidek 	dummy = 0;
245586c5854SPawel Jakub Dawidek 	iov.iov_base = &dummy;
246586c5854SPawel Jakub Dawidek 	iov.iov_len = sizeof(dummy);
247586c5854SPawel Jakub Dawidek 
248586c5854SPawel Jakub Dawidek 	msg.msg_iov = &iov;
249586c5854SPawel Jakub Dawidek 	msg.msg_iovlen = 1;
25036da5199SPawel Jakub Dawidek 	msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
25136da5199SPawel Jakub Dawidek 	msg.msg_control = calloc(1, msg.msg_controllen);
25236da5199SPawel Jakub Dawidek 	if (msg.msg_control == NULL)
25336da5199SPawel Jakub Dawidek 		return (-1);
25436da5199SPawel Jakub Dawidek 
25536da5199SPawel Jakub Dawidek 	ret = -1;
25636da5199SPawel Jakub Dawidek 
25736da5199SPawel Jakub Dawidek 	for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL;
25836da5199SPawel Jakub Dawidek 	    i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) {
25936da5199SPawel Jakub Dawidek 		if (msghdr_add_fd(cmsg, fds[i]) == -1)
26036da5199SPawel Jakub Dawidek 			goto end;
26136da5199SPawel Jakub Dawidek 	}
26236da5199SPawel Jakub Dawidek 
26336da5199SPawel Jakub Dawidek 	if (msg_send(sock, &msg) == -1)
26436da5199SPawel Jakub Dawidek 		goto end;
26536da5199SPawel Jakub Dawidek 
26636da5199SPawel Jakub Dawidek 	ret = 0;
26736da5199SPawel Jakub Dawidek end:
26836da5199SPawel Jakub Dawidek 	serrno = errno;
26936da5199SPawel Jakub Dawidek 	free(msg.msg_control);
27036da5199SPawel Jakub Dawidek 	errno = serrno;
27136da5199SPawel Jakub Dawidek 	return (ret);
27236da5199SPawel Jakub Dawidek }
27336da5199SPawel Jakub Dawidek 
274586c5854SPawel Jakub Dawidek static int
275586c5854SPawel Jakub Dawidek fd_package_recv(int sock, int *fds, size_t nfds)
27636da5199SPawel Jakub Dawidek {
27736da5199SPawel Jakub Dawidek 	struct msghdr msg;
27836da5199SPawel Jakub Dawidek 	struct cmsghdr *cmsg;
27936da5199SPawel Jakub Dawidek 	unsigned int i;
28036da5199SPawel Jakub Dawidek 	int serrno, ret;
281586c5854SPawel Jakub Dawidek 	struct iovec iov;
282586c5854SPawel Jakub Dawidek 	uint8_t dummy;
28336da5199SPawel Jakub Dawidek 
284586c5854SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
285586c5854SPawel Jakub Dawidek 	PJDLOG_ASSERT(nfds > 0);
286586c5854SPawel Jakub Dawidek 	PJDLOG_ASSERT(fds != NULL);
28736da5199SPawel Jakub Dawidek 
28836da5199SPawel Jakub Dawidek 	bzero(&msg, sizeof(msg));
289586c5854SPawel Jakub Dawidek 	bzero(&iov, sizeof(iov));
290586c5854SPawel Jakub Dawidek 
291586c5854SPawel Jakub Dawidek 	/*
292586c5854SPawel Jakub Dawidek 	 * XXX: Look into cred_send function for more details.
293586c5854SPawel Jakub Dawidek 	 */
294586c5854SPawel Jakub Dawidek 	iov.iov_base = &dummy;
295586c5854SPawel Jakub Dawidek 	iov.iov_len = sizeof(dummy);
296586c5854SPawel Jakub Dawidek 
297586c5854SPawel Jakub Dawidek 	msg.msg_iov = &iov;
298586c5854SPawel Jakub Dawidek 	msg.msg_iovlen = 1;
29936da5199SPawel Jakub Dawidek 	msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
30036da5199SPawel Jakub Dawidek 	msg.msg_control = calloc(1, msg.msg_controllen);
30136da5199SPawel Jakub Dawidek 	if (msg.msg_control == NULL)
30236da5199SPawel Jakub Dawidek 		return (-1);
30336da5199SPawel Jakub Dawidek 
30436da5199SPawel Jakub Dawidek 	ret = -1;
30536da5199SPawel Jakub Dawidek 
30636da5199SPawel Jakub Dawidek 	if (msg_recv(sock, &msg) == -1)
30736da5199SPawel Jakub Dawidek 		goto end;
30836da5199SPawel Jakub Dawidek 
3093810ba1bSMariusz Zaborski 	i = 0;
3103810ba1bSMariusz Zaborski 	cmsg = CMSG_FIRSTHDR(&msg);
3113810ba1bSMariusz Zaborski 	while (cmsg && i < nfds) {
3123810ba1bSMariusz Zaborski 		unsigned int n;
3133810ba1bSMariusz Zaborski 
3143810ba1bSMariusz Zaborski 		if (cmsg->cmsg_level != SOL_SOCKET ||
3153810ba1bSMariusz Zaborski 		    cmsg->cmsg_type != SCM_RIGHTS) {
3163810ba1bSMariusz Zaborski 			errno = EINVAL;
31736da5199SPawel Jakub Dawidek 			break;
31836da5199SPawel Jakub Dawidek 		}
3193810ba1bSMariusz Zaborski 		n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
3203810ba1bSMariusz Zaborski 		if (i + n > nfds) {
3213810ba1bSMariusz Zaborski 			errno = EINVAL;
3223810ba1bSMariusz Zaborski 			break;
3233810ba1bSMariusz Zaborski 		}
3243810ba1bSMariusz Zaborski 		bcopy(CMSG_DATA(cmsg), fds + i, sizeof(int) * n);
3253810ba1bSMariusz Zaborski 		cmsg = CMSG_NXTHDR(&msg, cmsg);
3263810ba1bSMariusz Zaborski 		i += n;
3273810ba1bSMariusz Zaborski 	}
32836da5199SPawel Jakub Dawidek 
32936da5199SPawel Jakub Dawidek 	if (cmsg != NULL || i < nfds) {
3303810ba1bSMariusz Zaborski 		unsigned int last;
33136da5199SPawel Jakub Dawidek 
33236da5199SPawel Jakub Dawidek 		/*
33336da5199SPawel Jakub Dawidek 		 * We need to close all received descriptors, even if we have
33436da5199SPawel Jakub Dawidek 		 * different control message (eg. SCM_CREDS) in between.
33536da5199SPawel Jakub Dawidek 		 */
3363810ba1bSMariusz Zaborski 		last = i;
3373810ba1bSMariusz Zaborski 		for (i = 0; i < last; i++) {
3383810ba1bSMariusz Zaborski 			if (fds[i] >= 0) {
3393810ba1bSMariusz Zaborski 				close(fds[i]);
3403810ba1bSMariusz Zaborski 			}
34136da5199SPawel Jakub Dawidek 		}
34236da5199SPawel Jakub Dawidek 		errno = EINVAL;
34336da5199SPawel Jakub Dawidek 		goto end;
34436da5199SPawel Jakub Dawidek 	}
34536da5199SPawel Jakub Dawidek 
3463810ba1bSMariusz Zaborski #ifndef MSG_CMSG_CLOEXEC
3473810ba1bSMariusz Zaborski 	/*
3483810ba1bSMariusz Zaborski 	 * If the MSG_CMSG_CLOEXEC flag is not available we cannot set the
3493810ba1bSMariusz Zaborski 	 * close-on-exec flag atomically, but we still want to set it for
3503810ba1bSMariusz Zaborski 	 * consistency.
3513810ba1bSMariusz Zaborski 	 */
3523810ba1bSMariusz Zaborski 	for (i = 0; i < nfds; i++) {
3533810ba1bSMariusz Zaborski 		(void) fcntl(fds[i], F_SETFD, FD_CLOEXEC);
3543810ba1bSMariusz Zaborski 	}
3553810ba1bSMariusz Zaborski #endif
3563810ba1bSMariusz Zaborski 
35736da5199SPawel Jakub Dawidek 	ret = 0;
35836da5199SPawel Jakub Dawidek end:
35936da5199SPawel Jakub Dawidek 	serrno = errno;
36036da5199SPawel Jakub Dawidek 	free(msg.msg_control);
36136da5199SPawel Jakub Dawidek 	errno = serrno;
36236da5199SPawel Jakub Dawidek 	return (ret);
36336da5199SPawel Jakub Dawidek }
36436da5199SPawel Jakub Dawidek 
36536da5199SPawel Jakub Dawidek int
366586c5854SPawel Jakub Dawidek fd_recv(int sock, int *fds, size_t nfds)
367586c5854SPawel Jakub Dawidek {
368586c5854SPawel Jakub Dawidek 	unsigned int i, step, j;
369586c5854SPawel Jakub Dawidek 	int ret, serrno;
370586c5854SPawel Jakub Dawidek 
371586c5854SPawel Jakub Dawidek 	if (nfds == 0 || fds == NULL) {
372586c5854SPawel Jakub Dawidek 		errno = EINVAL;
373586c5854SPawel Jakub Dawidek 		return (-1);
374586c5854SPawel Jakub Dawidek 	}
375586c5854SPawel Jakub Dawidek 
376586c5854SPawel Jakub Dawidek 	ret = i = step = 0;
377586c5854SPawel Jakub Dawidek 	while (i < nfds) {
378586c5854SPawel Jakub Dawidek 		if (PKG_MAX_SIZE < nfds - i)
379586c5854SPawel Jakub Dawidek 			step = PKG_MAX_SIZE;
380586c5854SPawel Jakub Dawidek 		else
381586c5854SPawel Jakub Dawidek 			step = nfds - i;
382586c5854SPawel Jakub Dawidek 		ret = fd_package_recv(sock, fds + i, step);
383586c5854SPawel Jakub Dawidek 		if (ret != 0) {
384586c5854SPawel Jakub Dawidek 			/* Close all received descriptors. */
385586c5854SPawel Jakub Dawidek 			serrno = errno;
386586c5854SPawel Jakub Dawidek 			for (j = 0; j < i; j++)
387586c5854SPawel Jakub Dawidek 				close(fds[j]);
388586c5854SPawel Jakub Dawidek 			errno = serrno;
389586c5854SPawel Jakub Dawidek 			break;
390586c5854SPawel Jakub Dawidek 		}
391586c5854SPawel Jakub Dawidek 		i += step;
392586c5854SPawel Jakub Dawidek 	}
393586c5854SPawel Jakub Dawidek 
394586c5854SPawel Jakub Dawidek 	return (ret);
395586c5854SPawel Jakub Dawidek }
396586c5854SPawel Jakub Dawidek 
397586c5854SPawel Jakub Dawidek int
398586c5854SPawel Jakub Dawidek fd_send(int sock, const int *fds, size_t nfds)
399586c5854SPawel Jakub Dawidek {
400586c5854SPawel Jakub Dawidek 	unsigned int i, step;
401586c5854SPawel Jakub Dawidek 	int ret;
402586c5854SPawel Jakub Dawidek 
403586c5854SPawel Jakub Dawidek 	if (nfds == 0 || fds == NULL) {
404586c5854SPawel Jakub Dawidek 		errno = EINVAL;
405586c5854SPawel Jakub Dawidek 		return (-1);
406586c5854SPawel Jakub Dawidek 	}
407586c5854SPawel Jakub Dawidek 
408586c5854SPawel Jakub Dawidek 	ret = i = step = 0;
409586c5854SPawel Jakub Dawidek 	while (i < nfds) {
410586c5854SPawel Jakub Dawidek 		if (PKG_MAX_SIZE < nfds - i)
411586c5854SPawel Jakub Dawidek 			step = PKG_MAX_SIZE;
412586c5854SPawel Jakub Dawidek 		else
413586c5854SPawel Jakub Dawidek 			step = nfds - i;
414586c5854SPawel Jakub Dawidek 		ret = fd_package_send(sock, fds + i, step);
415586c5854SPawel Jakub Dawidek 		if (ret != 0)
416586c5854SPawel Jakub Dawidek 			break;
417586c5854SPawel Jakub Dawidek 		i += step;
418586c5854SPawel Jakub Dawidek 	}
419586c5854SPawel Jakub Dawidek 
420586c5854SPawel Jakub Dawidek 	return (ret);
421586c5854SPawel Jakub Dawidek }
422586c5854SPawel Jakub Dawidek 
423586c5854SPawel Jakub Dawidek int
42436da5199SPawel Jakub Dawidek buf_send(int sock, void *buf, size_t size)
42536da5199SPawel Jakub Dawidek {
42636da5199SPawel Jakub Dawidek 	ssize_t done;
42736da5199SPawel Jakub Dawidek 	unsigned char *ptr;
42836da5199SPawel Jakub Dawidek 
4293d34eceaSPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
4303d34eceaSPawel Jakub Dawidek 	PJDLOG_ASSERT(size > 0);
4313d34eceaSPawel Jakub Dawidek 	PJDLOG_ASSERT(buf != NULL);
4323d34eceaSPawel Jakub Dawidek 
43336da5199SPawel Jakub Dawidek 	ptr = buf;
43436da5199SPawel Jakub Dawidek 	do {
43536da5199SPawel Jakub Dawidek 		fd_wait(sock, false);
43636da5199SPawel Jakub Dawidek 		done = send(sock, ptr, size, 0);
43736da5199SPawel Jakub Dawidek 		if (done == -1) {
43836da5199SPawel Jakub Dawidek 			if (errno == EINTR)
43936da5199SPawel Jakub Dawidek 				continue;
44036da5199SPawel Jakub Dawidek 			return (-1);
44136da5199SPawel Jakub Dawidek 		} else if (done == 0) {
44236da5199SPawel Jakub Dawidek 			errno = ENOTCONN;
44336da5199SPawel Jakub Dawidek 			return (-1);
44436da5199SPawel Jakub Dawidek 		}
44536da5199SPawel Jakub Dawidek 		size -= done;
44636da5199SPawel Jakub Dawidek 		ptr += done;
44736da5199SPawel Jakub Dawidek 	} while (size > 0);
44836da5199SPawel Jakub Dawidek 
44936da5199SPawel Jakub Dawidek 	return (0);
45036da5199SPawel Jakub Dawidek }
45136da5199SPawel Jakub Dawidek 
45236da5199SPawel Jakub Dawidek int
453db158b99SRobert Wing buf_recv(int sock, void *buf, size_t size, int flags)
45436da5199SPawel Jakub Dawidek {
45536da5199SPawel Jakub Dawidek 	ssize_t done;
45636da5199SPawel Jakub Dawidek 	unsigned char *ptr;
45736da5199SPawel Jakub Dawidek 
4583d34eceaSPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
4593d34eceaSPawel Jakub Dawidek 	PJDLOG_ASSERT(buf != NULL);
4603d34eceaSPawel Jakub Dawidek 
46136da5199SPawel Jakub Dawidek 	ptr = buf;
4627f7fe890SPawel Jakub Dawidek 	while (size > 0) {
46336da5199SPawel Jakub Dawidek 		fd_wait(sock, true);
464db158b99SRobert Wing 		done = recv(sock, ptr, size, flags);
46536da5199SPawel Jakub Dawidek 		if (done == -1) {
46636da5199SPawel Jakub Dawidek 			if (errno == EINTR)
46736da5199SPawel Jakub Dawidek 				continue;
46836da5199SPawel Jakub Dawidek 			return (-1);
46936da5199SPawel Jakub Dawidek 		} else if (done == 0) {
47036da5199SPawel Jakub Dawidek 			errno = ENOTCONN;
47136da5199SPawel Jakub Dawidek 			return (-1);
47236da5199SPawel Jakub Dawidek 		}
47336da5199SPawel Jakub Dawidek 		size -= done;
47436da5199SPawel Jakub Dawidek 		ptr += done;
4757f7fe890SPawel Jakub Dawidek 	}
47636da5199SPawel Jakub Dawidek 
47736da5199SPawel Jakub Dawidek 	return (0);
47836da5199SPawel Jakub Dawidek }
479