xref: /freebsd/tests/sys/common/sendfile_helper.c (revision 2039fe8ea421a93c466657485ba83b4b055f81dd)
16a5ba658SKUROSAWA Takahiro /*-
26a5ba658SKUROSAWA Takahiro  * SPDX-License-Identifier: BSD-2-Clause
36a5ba658SKUROSAWA Takahiro  *
46a5ba658SKUROSAWA Takahiro  * Copyright (c) 2020 Netflix, Inc.
56a5ba658SKUROSAWA Takahiro  *
66a5ba658SKUROSAWA Takahiro  * Redistribution and use in source and binary forms, with or without
76a5ba658SKUROSAWA Takahiro  * modification, are permitted provided that the following conditions
86a5ba658SKUROSAWA Takahiro  * are met:
96a5ba658SKUROSAWA Takahiro  * 1. Redistributions of source code must retain the above copyright
106a5ba658SKUROSAWA Takahiro  *    notice, this list of conditions and the following disclaimer.
116a5ba658SKUROSAWA Takahiro  * 2. Redistributions in binary form must reproduce the above copyright
126a5ba658SKUROSAWA Takahiro  *    notice, this list of conditions and the following disclaimer in the
136a5ba658SKUROSAWA Takahiro  *    documentation and/or other materials provided with the distribution.
146a5ba658SKUROSAWA Takahiro  *
156a5ba658SKUROSAWA Takahiro  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
166a5ba658SKUROSAWA Takahiro  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
176a5ba658SKUROSAWA Takahiro  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
186a5ba658SKUROSAWA Takahiro  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
196a5ba658SKUROSAWA Takahiro  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
206a5ba658SKUROSAWA Takahiro  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
216a5ba658SKUROSAWA Takahiro  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
226a5ba658SKUROSAWA Takahiro  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
236a5ba658SKUROSAWA Takahiro  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
246a5ba658SKUROSAWA Takahiro  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
256a5ba658SKUROSAWA Takahiro  * SUCH DAMAGE.
266a5ba658SKUROSAWA Takahiro  */
276a5ba658SKUROSAWA Takahiro 
286a5ba658SKUROSAWA Takahiro #include <sys/types.h>
296a5ba658SKUROSAWA Takahiro #include <sys/socket.h>
306a5ba658SKUROSAWA Takahiro #include <sys/sysctl.h>
316a5ba658SKUROSAWA Takahiro #include <sys/uio.h>
326a5ba658SKUROSAWA Takahiro 
336a5ba658SKUROSAWA Takahiro #include <netinet/in.h>
34*2039fe8eSKUROSAWA Takahiro #include <netdb.h>
356a5ba658SKUROSAWA Takahiro 
366a5ba658SKUROSAWA Takahiro #include <err.h>
376a5ba658SKUROSAWA Takahiro #include <errno.h>
386a5ba658SKUROSAWA Takahiro #include <fcntl.h>
396a5ba658SKUROSAWA Takahiro #include <pthread.h>
406a5ba658SKUROSAWA Takahiro #include <stdbool.h>
416a5ba658SKUROSAWA Takahiro #include <stdio.h>
426a5ba658SKUROSAWA Takahiro #include <stdlib.h>
436a5ba658SKUROSAWA Takahiro #include <string.h>
446a5ba658SKUROSAWA Takahiro #include <unistd.h>
456a5ba658SKUROSAWA Takahiro 
466a5ba658SKUROSAWA Takahiro static char buf[1024*1024];
476a5ba658SKUROSAWA Takahiro static ssize_t readlen;
486a5ba658SKUROSAWA Takahiro static volatile bool read_done = false;
496a5ba658SKUROSAWA Takahiro 
506a5ba658SKUROSAWA Takahiro static int
tcp_socketpair(int * sv)516a5ba658SKUROSAWA Takahiro tcp_socketpair(int *sv)
526a5ba658SKUROSAWA Takahiro {
536a5ba658SKUROSAWA Takahiro 	struct sockaddr_in sin = {
546a5ba658SKUROSAWA Takahiro 		.sin_len = sizeof(struct sockaddr_in),
556a5ba658SKUROSAWA Takahiro 		.sin_family = AF_INET,
566a5ba658SKUROSAWA Takahiro 		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
576a5ba658SKUROSAWA Takahiro 	};
586a5ba658SKUROSAWA Takahiro 	int flags;
596a5ba658SKUROSAWA Takahiro 	int ls;
606a5ba658SKUROSAWA Takahiro 
616a5ba658SKUROSAWA Takahiro 	ls = socket(PF_INET, SOCK_STREAM, 0);
626a5ba658SKUROSAWA Takahiro 	if (ls < 0)
636a5ba658SKUROSAWA Takahiro 		err(1, "socket ls");
646a5ba658SKUROSAWA Takahiro 
656a5ba658SKUROSAWA Takahiro 	if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &(socklen_t){1},
666a5ba658SKUROSAWA Takahiro 	    sizeof(int)) < 0)
676a5ba658SKUROSAWA Takahiro 		err(1, "SO_REUSEADDR");
686a5ba658SKUROSAWA Takahiro 
696a5ba658SKUROSAWA Takahiro 	if (bind(ls, (struct sockaddr *)&sin, sizeof(sin)) < 0)
706a5ba658SKUROSAWA Takahiro 		err(1, "bind ls");
716a5ba658SKUROSAWA Takahiro 
726a5ba658SKUROSAWA Takahiro 	if (getsockname(ls, (struct sockaddr *)&sin,
736a5ba658SKUROSAWA Takahiro 	    &(socklen_t){ sizeof(sin) }) < 0)
746a5ba658SKUROSAWA Takahiro 		err(1, "getsockname");
756a5ba658SKUROSAWA Takahiro 
766a5ba658SKUROSAWA Takahiro 	if (listen(ls, 5) < 0)
776a5ba658SKUROSAWA Takahiro 		err(1, "listen ls");
786a5ba658SKUROSAWA Takahiro 
796a5ba658SKUROSAWA Takahiro 	sv[0] = socket(PF_INET, SOCK_STREAM, 0);
806a5ba658SKUROSAWA Takahiro 	if (sv[0] < 0)
816a5ba658SKUROSAWA Takahiro 		err(1, "socket cs");
826a5ba658SKUROSAWA Takahiro 
836a5ba658SKUROSAWA Takahiro 	flags = fcntl(sv[0], F_GETFL);
846a5ba658SKUROSAWA Takahiro 	flags |= O_NONBLOCK;
856a5ba658SKUROSAWA Takahiro 	if (fcntl(sv[0], F_SETFL, flags) == -1)
866a5ba658SKUROSAWA Takahiro 		err(1, "fcntl +O_NONBLOCK");
876a5ba658SKUROSAWA Takahiro 
886a5ba658SKUROSAWA Takahiro 	if (connect(sv[0], (void *)&sin, sizeof(sin)) == -1 &&
896a5ba658SKUROSAWA Takahiro 	    errno != EINPROGRESS)
906a5ba658SKUROSAWA Takahiro 		err(1, "connect cs");
916a5ba658SKUROSAWA Takahiro 
926a5ba658SKUROSAWA Takahiro 	sv[1] = accept(ls, NULL, 0);
936a5ba658SKUROSAWA Takahiro 	if (sv[1] < 0)
946a5ba658SKUROSAWA Takahiro 		err(1, "accept ls");
956a5ba658SKUROSAWA Takahiro 
966a5ba658SKUROSAWA Takahiro 	flags &= ~O_NONBLOCK;
976a5ba658SKUROSAWA Takahiro 	if (fcntl(sv[0], F_SETFL, flags) == -1)
986a5ba658SKUROSAWA Takahiro 		err(1, "fcntl -O_NONBLOCK");
996a5ba658SKUROSAWA Takahiro 
1006a5ba658SKUROSAWA Takahiro 	close(ls);
1016a5ba658SKUROSAWA Takahiro 
1026a5ba658SKUROSAWA Takahiro 	return (0);
1036a5ba658SKUROSAWA Takahiro }
1046a5ba658SKUROSAWA Takahiro 
105*2039fe8eSKUROSAWA Takahiro static int
tcp_client_socket(const char * host,const char * port)106*2039fe8eSKUROSAWA Takahiro tcp_client_socket(const char *host, const char *port)
107*2039fe8eSKUROSAWA Takahiro {
108*2039fe8eSKUROSAWA Takahiro 	struct addrinfo hints, *res, *res0;
109*2039fe8eSKUROSAWA Takahiro 	int error;
110*2039fe8eSKUROSAWA Takahiro 	int s;
111*2039fe8eSKUROSAWA Takahiro 
112*2039fe8eSKUROSAWA Takahiro 	memset(&hints, 0, sizeof(hints));
113*2039fe8eSKUROSAWA Takahiro 	hints.ai_family = AF_UNSPEC;
114*2039fe8eSKUROSAWA Takahiro 	hints.ai_socktype = SOCK_STREAM;
115*2039fe8eSKUROSAWA Takahiro 	if ((error = getaddrinfo(host, port, &hints, &res0)) != 0)
116*2039fe8eSKUROSAWA Takahiro 		errx(1, "host %s port %s: %s.",
117*2039fe8eSKUROSAWA Takahiro 		    host, port, gai_strerror(error));
118*2039fe8eSKUROSAWA Takahiro 	s = -1;
119*2039fe8eSKUROSAWA Takahiro 	for (res = res0; res != NULL; res = res->ai_next) {
120*2039fe8eSKUROSAWA Takahiro 		s = socket(res->ai_family, res->ai_socktype,
121*2039fe8eSKUROSAWA Takahiro 			res->ai_protocol);
122*2039fe8eSKUROSAWA Takahiro 		if (s < 0) {
123*2039fe8eSKUROSAWA Takahiro 			warn("socket(pf:%d, type:%d, proto:%d)",
124*2039fe8eSKUROSAWA Takahiro 			    res->ai_family, res->ai_socktype,
125*2039fe8eSKUROSAWA Takahiro 			    res->ai_protocol);
126*2039fe8eSKUROSAWA Takahiro 			continue;
127*2039fe8eSKUROSAWA Takahiro 		}
128*2039fe8eSKUROSAWA Takahiro 		if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
129*2039fe8eSKUROSAWA Takahiro 			warn("connect(%s, %s)", host, port);
130*2039fe8eSKUROSAWA Takahiro 			close(s);
131*2039fe8eSKUROSAWA Takahiro 			s = -1;
132*2039fe8eSKUROSAWA Takahiro 			continue;
133*2039fe8eSKUROSAWA Takahiro 		} else
134*2039fe8eSKUROSAWA Takahiro 			break;
135*2039fe8eSKUROSAWA Takahiro 	}
136*2039fe8eSKUROSAWA Takahiro 	if (s < 0)
137*2039fe8eSKUROSAWA Takahiro 		exit(1);
138*2039fe8eSKUROSAWA Takahiro 	freeaddrinfo(res0);
139*2039fe8eSKUROSAWA Takahiro 
140*2039fe8eSKUROSAWA Takahiro 	return s;
141*2039fe8eSKUROSAWA Takahiro }
142*2039fe8eSKUROSAWA Takahiro 
1436a5ba658SKUROSAWA Takahiro static void *
receiver(void * arg)1446a5ba658SKUROSAWA Takahiro receiver(void *arg)
1456a5ba658SKUROSAWA Takahiro {
1466a5ba658SKUROSAWA Takahiro 	int s = *(int *)arg;
1476a5ba658SKUROSAWA Takahiro 	ssize_t rv;
1486a5ba658SKUROSAWA Takahiro 
1496a5ba658SKUROSAWA Takahiro 	do {
1506a5ba658SKUROSAWA Takahiro 		rv = read(s, buf, sizeof(buf));
1516a5ba658SKUROSAWA Takahiro 		if (rv == -1)
1526a5ba658SKUROSAWA Takahiro 			err(2, "read receiver");
1536a5ba658SKUROSAWA Takahiro 		if (rv == 0)
1546a5ba658SKUROSAWA Takahiro 			break;
1556a5ba658SKUROSAWA Takahiro 		readlen -= rv;
1566a5ba658SKUROSAWA Takahiro 	} while (readlen != 0);
1576a5ba658SKUROSAWA Takahiro 
1586a5ba658SKUROSAWA Takahiro 	read_done = true;
1596a5ba658SKUROSAWA Takahiro 
1606a5ba658SKUROSAWA Takahiro 	return NULL;
1616a5ba658SKUROSAWA Takahiro }
1626a5ba658SKUROSAWA Takahiro 
1636a5ba658SKUROSAWA Takahiro static void
usage(void)1646a5ba658SKUROSAWA Takahiro usage(void)
1656a5ba658SKUROSAWA Takahiro {
166*2039fe8eSKUROSAWA Takahiro 	errx(1, "usage: %s [-u] [-c host] [-p port] "
167*2039fe8eSKUROSAWA Takahiro 	    "<file> <start> <len> <flags>", getprogname());
1686a5ba658SKUROSAWA Takahiro }
1696a5ba658SKUROSAWA Takahiro 
1706a5ba658SKUROSAWA Takahiro int
main(int argc,char ** argv)1716a5ba658SKUROSAWA Takahiro main(int argc, char **argv)
1726a5ba658SKUROSAWA Takahiro {
1736a5ba658SKUROSAWA Takahiro 	pthread_t pt;
1746a5ba658SKUROSAWA Takahiro 	off_t start;
1756a5ba658SKUROSAWA Takahiro 	int ch, fd, ss[2], flags, error;
1766a5ba658SKUROSAWA Takahiro 	bool pf_unix = false;
177*2039fe8eSKUROSAWA Takahiro 	bool tcp_client = false;
178*2039fe8eSKUROSAWA Takahiro 	const char *host, *port;
1796a5ba658SKUROSAWA Takahiro 
180*2039fe8eSKUROSAWA Takahiro 	while ((ch = getopt(argc, argv, "c:p:u")) != -1)
1816a5ba658SKUROSAWA Takahiro 		switch (ch) {
182*2039fe8eSKUROSAWA Takahiro 		case 'c':
183*2039fe8eSKUROSAWA Takahiro 			host = optarg;
184*2039fe8eSKUROSAWA Takahiro 			tcp_client = true;
185*2039fe8eSKUROSAWA Takahiro 			break;
186*2039fe8eSKUROSAWA Takahiro 		case 'p':
187*2039fe8eSKUROSAWA Takahiro 			port = optarg;
188*2039fe8eSKUROSAWA Takahiro 			tcp_client = true;
189*2039fe8eSKUROSAWA Takahiro 			break;
1906a5ba658SKUROSAWA Takahiro 		case 'u':
1916a5ba658SKUROSAWA Takahiro 			pf_unix = true;
1926a5ba658SKUROSAWA Takahiro 			break;
1936a5ba658SKUROSAWA Takahiro 		default:
1946a5ba658SKUROSAWA Takahiro 			usage();
1956a5ba658SKUROSAWA Takahiro 		}
1966a5ba658SKUROSAWA Takahiro 	argc -= optind;
1976a5ba658SKUROSAWA Takahiro 	argv += optind;
1986a5ba658SKUROSAWA Takahiro 
1996a5ba658SKUROSAWA Takahiro 	if (argc != 4)
2006a5ba658SKUROSAWA Takahiro 		usage();
201*2039fe8eSKUROSAWA Takahiro 	if (tcp_client && (host == NULL || port == NULL))
202*2039fe8eSKUROSAWA Takahiro 		errx(1, "Need to specify host and port.");
2036a5ba658SKUROSAWA Takahiro 
2046a5ba658SKUROSAWA Takahiro 	start = strtoull(argv[1], NULL, 0);
2056a5ba658SKUROSAWA Takahiro 	readlen = strtoull(argv[2], NULL, 0);
2066a5ba658SKUROSAWA Takahiro 	flags = strtoul(argv[3], NULL, 0);
2076a5ba658SKUROSAWA Takahiro 
2086a5ba658SKUROSAWA Takahiro 	fd = open(argv[0], O_RDONLY);
2096a5ba658SKUROSAWA Takahiro 	if (fd < 0)
2106a5ba658SKUROSAWA Takahiro 		err(1, "open");
2116a5ba658SKUROSAWA Takahiro 
2126a5ba658SKUROSAWA Takahiro 	if (pf_unix) {
2136a5ba658SKUROSAWA Takahiro 		if (socketpair(PF_LOCAL, SOCK_STREAM, 0, ss) != 0)
2146a5ba658SKUROSAWA Takahiro 			err(1, "socketpair");
215*2039fe8eSKUROSAWA Takahiro 	} else if (tcp_client)
216*2039fe8eSKUROSAWA Takahiro 		ss[0] = tcp_client_socket(host, port);
217*2039fe8eSKUROSAWA Takahiro 	else
2186a5ba658SKUROSAWA Takahiro 		tcp_socketpair(ss);
2196a5ba658SKUROSAWA Takahiro 
220*2039fe8eSKUROSAWA Takahiro 	if (tcp_client)
221*2039fe8eSKUROSAWA Takahiro 		read_done = true;	/* The receiver is another process. */
222*2039fe8eSKUROSAWA Takahiro 	else {
2236a5ba658SKUROSAWA Takahiro 		error = pthread_create(&pt, NULL, receiver, &ss[1]);
2246a5ba658SKUROSAWA Takahiro 		if (error)
2256a5ba658SKUROSAWA Takahiro 			errc(1, error, "pthread_create");
226*2039fe8eSKUROSAWA Takahiro 	}
2276a5ba658SKUROSAWA Takahiro 
2286a5ba658SKUROSAWA Takahiro 	if (sendfile(fd, ss[0], start, readlen, NULL, NULL, flags) < 0)
2296a5ba658SKUROSAWA Takahiro 		err(3, "sendfile");
2306a5ba658SKUROSAWA Takahiro 
2316a5ba658SKUROSAWA Takahiro 	while (!read_done)
2326a5ba658SKUROSAWA Takahiro 		usleep(1000);
2336a5ba658SKUROSAWA Takahiro 
2346a5ba658SKUROSAWA Takahiro 	exit(0);
2356a5ba658SKUROSAWA Takahiro }
236