xref: /freebsd/tools/regression/sockets/sendfile/sendfile.c (revision b74bcac4bd3e6a7a68bca0239ec9946b06889549)
10fb57f87SRobert Watson /*-
20fb57f87SRobert Watson  * Copyright (c) 2006 Robert N. M. Watson
30fb57f87SRobert Watson  * All rights reserved.
40fb57f87SRobert Watson  *
50fb57f87SRobert Watson  * Redistribution and use in source and binary forms, with or without
60fb57f87SRobert Watson  * modification, are permitted provided that the following conditions
70fb57f87SRobert Watson  * are met:
80fb57f87SRobert Watson  * 1. Redistributions of source code must retain the above copyright
90fb57f87SRobert Watson  *    notice, this list of conditions and the following disclaimer.
100fb57f87SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
110fb57f87SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
120fb57f87SRobert Watson  *    documentation and/or other materials provided with the distribution.
130fb57f87SRobert Watson  *
140fb57f87SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
150fb57f87SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
160fb57f87SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
170fb57f87SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
180fb57f87SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
190fb57f87SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
200fb57f87SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
210fb57f87SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
220fb57f87SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
230fb57f87SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
240fb57f87SRobert Watson  * SUCH DAMAGE.
250fb57f87SRobert Watson  *
260fb57f87SRobert Watson  * $FreeBSD$
270fb57f87SRobert Watson  */
280fb57f87SRobert Watson 
290fb57f87SRobert Watson #include <sys/types.h>
300fb57f87SRobert Watson #include <sys/socket.h>
31ed3d9b6eSPawel Jakub Dawidek #include <sys/stat.h>
328dd83f1eSRebecca Cran #include <sys/wait.h>
330fb57f87SRobert Watson 
340fb57f87SRobert Watson #include <netinet/in.h>
350fb57f87SRobert Watson 
360fb57f87SRobert Watson #include <err.h>
378dd83f1eSRebecca Cran #include <errno.h>
3876423e6eSPawel Jakub Dawidek #include <fcntl.h>
390fb57f87SRobert Watson #include <limits.h>
408dd83f1eSRebecca Cran #include <md5.h>
410fb57f87SRobert Watson #include <signal.h>
42f7754b09SDag-Erling Smørgrav #include <stdint.h>
430fb57f87SRobert Watson #include <stdio.h>
440fb57f87SRobert Watson #include <stdlib.h>
450fb57f87SRobert Watson #include <string.h>
460fb57f87SRobert Watson #include <unistd.h>
470fb57f87SRobert Watson 
480fb57f87SRobert Watson /*
494225b7d6SMaxim Konovalov  * Simple regression test for sendfile.  Creates a file sized at four pages
500fb57f87SRobert Watson  * and then proceeds to send it over a series of sockets, exercising a number
510fb57f87SRobert Watson  * of cases and performing limited validation.
520fb57f87SRobert Watson  */
530fb57f87SRobert Watson 
548dd83f1eSRebecca Cran #define FAIL(msg)	{printf("# %s\n", msg); \
558dd83f1eSRebecca Cran 			return (-1);}
568dd83f1eSRebecca Cran 
578dd83f1eSRebecca Cran #define FAIL_ERR(msg)	{printf("# %s: %s\n", msg, strerror(errno)); \
588dd83f1eSRebecca Cran 			return (-1);}
598dd83f1eSRebecca Cran 
600fb57f87SRobert Watson #define	TEST_PORT	5678
610fb57f87SRobert Watson #define	TEST_MAGIC	0x4440f7bb
62ed3d9b6eSPawel Jakub Dawidek #define	TEST_PAGES	4
630fb57f87SRobert Watson #define	TEST_SECONDS	30
640fb57f87SRobert Watson 
650fb57f87SRobert Watson struct test_header {
668dd83f1eSRebecca Cran 	uint32_t	th_magic;
678dd83f1eSRebecca Cran 	uint32_t	th_header_length;
688dd83f1eSRebecca Cran 	uint32_t	th_offset;
698dd83f1eSRebecca Cran 	uint32_t	th_length;
708dd83f1eSRebecca Cran 	char		th_md5[33];
710fb57f87SRobert Watson };
720fb57f87SRobert Watson 
738dd83f1eSRebecca Cran struct sendfile_test {
748dd83f1eSRebecca Cran 	uint32_t	hdr_length;
758dd83f1eSRebecca Cran 	uint32_t	offset;
768dd83f1eSRebecca Cran 	uint32_t	length;
7750185946SEd Maste 	uint32_t	file_size;
788dd83f1eSRebecca Cran };
798dd83f1eSRebecca Cran 
8050185946SEd Maste static int	file_fd;
8150185946SEd Maste static char	path[PATH_MAX];
8250185946SEd Maste static int	listen_socket;
8350185946SEd Maste static int	accept_socket;
848dd83f1eSRebecca Cran 
858dd83f1eSRebecca Cran static int test_th(struct test_header *th, uint32_t *header_length,
868dd83f1eSRebecca Cran 		uint32_t *offset, uint32_t *length);
878dd83f1eSRebecca Cran static void signal_alarm(int signum);
888dd83f1eSRebecca Cran static void setup_alarm(int seconds);
898dd83f1eSRebecca Cran static void cancel_alarm(void);
908dd83f1eSRebecca Cran static int receive_test(void);
918dd83f1eSRebecca Cran static void run_child(void);
928dd83f1eSRebecca Cran static int new_test_socket(int *connect_socket);
938dd83f1eSRebecca Cran static void init_th(struct test_header *th, uint32_t header_length,
948dd83f1eSRebecca Cran 		uint32_t offset, uint32_t length);
958dd83f1eSRebecca Cran static int send_test(int connect_socket, struct sendfile_test);
9650185946SEd Maste static int write_test_file(size_t file_size);
978dd83f1eSRebecca Cran static void run_parent(void);
988dd83f1eSRebecca Cran static void cleanup(void);
998dd83f1eSRebecca Cran 
1000fb57f87SRobert Watson 
1010fb57f87SRobert Watson static int
1028dd83f1eSRebecca Cran test_th(struct test_header *th, uint32_t *header_length, uint32_t *offset,
1038dd83f1eSRebecca Cran 		uint32_t *length)
1040fb57f87SRobert Watson {
1050fb57f87SRobert Watson 
1060fb57f87SRobert Watson 	if (th->th_magic != htonl(TEST_MAGIC))
1078dd83f1eSRebecca Cran 		FAIL("magic number not found in header")
1080fb57f87SRobert Watson 	*header_length = ntohl(th->th_header_length);
1090fb57f87SRobert Watson 	*offset = ntohl(th->th_offset);
1100fb57f87SRobert Watson 	*length = ntohl(th->th_length);
1118dd83f1eSRebecca Cran 	return (0);
1120fb57f87SRobert Watson }
1130fb57f87SRobert Watson 
1140fb57f87SRobert Watson static void
1150fb57f87SRobert Watson signal_alarm(int signum)
1160fb57f87SRobert Watson {
117f7754b09SDag-Erling Smørgrav 	(void)signum;
1188dd83f1eSRebecca Cran 
1198dd83f1eSRebecca Cran 	printf("# test timeout\n");
1208dd83f1eSRebecca Cran 
1218dd83f1eSRebecca Cran 	if (accept_socket > 0)
1228dd83f1eSRebecca Cran 		close(accept_socket);
1238dd83f1eSRebecca Cran 	if (listen_socket > 0)
1248dd83f1eSRebecca Cran 		close(listen_socket);
1258dd83f1eSRebecca Cran 
1268dd83f1eSRebecca Cran 	_exit(-1);
1270fb57f87SRobert Watson }
1280fb57f87SRobert Watson 
1290fb57f87SRobert Watson static void
1300fb57f87SRobert Watson setup_alarm(int seconds)
1310fb57f87SRobert Watson {
1328dd83f1eSRebecca Cran 	struct itimerval itv;
1338dd83f1eSRebecca Cran 	bzero(&itv, sizeof(itv));
1348dd83f1eSRebecca Cran 	(void)seconds;
1358dd83f1eSRebecca Cran 	itv.it_value.tv_sec = seconds;
1360fb57f87SRobert Watson 
1370fb57f87SRobert Watson 	signal(SIGALRM, signal_alarm);
1388dd83f1eSRebecca Cran 	setitimer(ITIMER_REAL, &itv, NULL);
1390fb57f87SRobert Watson }
1400fb57f87SRobert Watson 
1410fb57f87SRobert Watson static void
1420fb57f87SRobert Watson cancel_alarm(void)
1430fb57f87SRobert Watson {
1448dd83f1eSRebecca Cran 	struct itimerval itv;
1458dd83f1eSRebecca Cran 	bzero(&itv, sizeof(itv));
1468dd83f1eSRebecca Cran 	setitimer(ITIMER_REAL, &itv, NULL);
1470fb57f87SRobert Watson }
1480fb57f87SRobert Watson 
1498dd83f1eSRebecca Cran static int
1508dd83f1eSRebecca Cran receive_test(void)
1510fb57f87SRobert Watson {
1528dd83f1eSRebecca Cran 	uint32_t header_length, offset, length, counter;
1530fb57f87SRobert Watson 	struct test_header th;
1540fb57f87SRobert Watson 	ssize_t len;
1558dd83f1eSRebecca Cran 	char buf[10240];
1568dd83f1eSRebecca Cran 	MD5_CTX md5ctx;
1578dd83f1eSRebecca Cran 	char *rxmd5;
1580fb57f87SRobert Watson 
1590fb57f87SRobert Watson 	len = read(accept_socket, &th, sizeof(th));
1608dd83f1eSRebecca Cran 	if (len < 0 || (size_t)len < sizeof(th))
1618dd83f1eSRebecca Cran 		FAIL_ERR("read")
1620fb57f87SRobert Watson 
1638dd83f1eSRebecca Cran 	if (test_th(&th, &header_length, &offset, &length) != 0)
1648dd83f1eSRebecca Cran 		return (-1);
1658dd83f1eSRebecca Cran 
1668dd83f1eSRebecca Cran 	MD5Init(&md5ctx);
1670fb57f87SRobert Watson 
1680fb57f87SRobert Watson 	counter = 0;
1690fb57f87SRobert Watson 	while (1) {
1708dd83f1eSRebecca Cran 		len = read(accept_socket, buf, sizeof(buf));
1718dd83f1eSRebecca Cran 		if (len < 0 || len == 0)
1720fb57f87SRobert Watson 			break;
1738dd83f1eSRebecca Cran 		counter += len;
1748dd83f1eSRebecca Cran 		MD5Update(&md5ctx, buf, len);
1750fb57f87SRobert Watson 	}
1768dd83f1eSRebecca Cran 
1778dd83f1eSRebecca Cran 	rxmd5 = MD5End(&md5ctx, NULL);
1788dd83f1eSRebecca Cran 
1798dd83f1eSRebecca Cran 	if ((counter != header_length+length) ||
1808dd83f1eSRebecca Cran 			memcmp(th.th_md5, rxmd5, 33) != 0)
1818dd83f1eSRebecca Cran 		FAIL("receive length mismatch")
1828dd83f1eSRebecca Cran 
1838dd83f1eSRebecca Cran 	free(rxmd5);
1848dd83f1eSRebecca Cran 	return (0);
1850fb57f87SRobert Watson }
1860fb57f87SRobert Watson 
1870fb57f87SRobert Watson static void
1880fb57f87SRobert Watson run_child(void)
1890fb57f87SRobert Watson {
1908dd83f1eSRebecca Cran 	struct sockaddr_in sin;
1918dd83f1eSRebecca Cran 	int rc = 0;
1920fb57f87SRobert Watson 
1938dd83f1eSRebecca Cran 	listen_socket = socket(PF_INET, SOCK_STREAM, 0);
1948dd83f1eSRebecca Cran 	if (listen_socket < 0) {
1958dd83f1eSRebecca Cran 		printf("# socket: %s\n", strerror(errno));
1968dd83f1eSRebecca Cran 		rc = -1;
1978dd83f1eSRebecca Cran 	}
1988dd83f1eSRebecca Cran 
1998dd83f1eSRebecca Cran 	if (!rc) {
2008dd83f1eSRebecca Cran 		bzero(&sin, sizeof(sin));
2018dd83f1eSRebecca Cran 		sin.sin_len = sizeof(sin);
2028dd83f1eSRebecca Cran 		sin.sin_family = AF_INET;
2038dd83f1eSRebecca Cran 		sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
2048dd83f1eSRebecca Cran 		sin.sin_port = htons(TEST_PORT);
2058dd83f1eSRebecca Cran 
2068dd83f1eSRebecca Cran 		if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
2078dd83f1eSRebecca Cran 			printf("# bind: %s\n", strerror(errno));
2088dd83f1eSRebecca Cran 			rc = -1;
2098dd83f1eSRebecca Cran 		}
2108dd83f1eSRebecca Cran 	}
2118dd83f1eSRebecca Cran 
2128dd83f1eSRebecca Cran 	if (!rc && listen(listen_socket, -1) < 0) {
2138dd83f1eSRebecca Cran 		printf("# listen: %s\n", strerror(errno));
2148dd83f1eSRebecca Cran 		rc = -1;
2158dd83f1eSRebecca Cran 	}
2168dd83f1eSRebecca Cran 
2178dd83f1eSRebecca Cran 	if (!rc) {
2180fb57f87SRobert Watson 		accept_socket = accept(listen_socket, NULL, NULL);
2190fb57f87SRobert Watson 		setup_alarm(TEST_SECONDS);
2208dd83f1eSRebecca Cran 		if (receive_test() != 0)
2218dd83f1eSRebecca Cran 			rc = -1;
2220fb57f87SRobert Watson 	}
2238dd83f1eSRebecca Cran 
2248dd83f1eSRebecca Cran 	cancel_alarm();
2258dd83f1eSRebecca Cran 	if (accept_socket > 0)
2268dd83f1eSRebecca Cran 		close(accept_socket);
2278dd83f1eSRebecca Cran 	if (listen_socket > 0)
2288dd83f1eSRebecca Cran 		close(listen_socket);
2298dd83f1eSRebecca Cran 
2308dd83f1eSRebecca Cran 	_exit(rc);
2310fb57f87SRobert Watson }
2320fb57f87SRobert Watson 
2330fb57f87SRobert Watson static int
2348dd83f1eSRebecca Cran new_test_socket(int *connect_socket)
2350fb57f87SRobert Watson {
2360fb57f87SRobert Watson 	struct sockaddr_in sin;
2378dd83f1eSRebecca Cran 	int rc = 0;
2380fb57f87SRobert Watson 
2398dd83f1eSRebecca Cran 	*connect_socket = socket(PF_INET, SOCK_STREAM, 0);
2408dd83f1eSRebecca Cran 	if (*connect_socket < 0)
2418dd83f1eSRebecca Cran 		FAIL_ERR("socket")
2420fb57f87SRobert Watson 
2430fb57f87SRobert Watson 	bzero(&sin, sizeof(sin));
2440fb57f87SRobert Watson 	sin.sin_len = sizeof(sin);
2450fb57f87SRobert Watson 	sin.sin_family = AF_INET;
2460fb57f87SRobert Watson 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
2470fb57f87SRobert Watson 	sin.sin_port = htons(TEST_PORT);
2480fb57f87SRobert Watson 
2498dd83f1eSRebecca Cran 	if (connect(*connect_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0)
2508dd83f1eSRebecca Cran 		FAIL_ERR("connect")
2510fb57f87SRobert Watson 
2528dd83f1eSRebecca Cran 	return (rc);
2530fb57f87SRobert Watson }
2540fb57f87SRobert Watson 
2550fb57f87SRobert Watson static void
2568dd83f1eSRebecca Cran init_th(struct test_header *th, uint32_t header_length, uint32_t offset,
2578dd83f1eSRebecca Cran 		uint32_t length)
2580fb57f87SRobert Watson {
2590fb57f87SRobert Watson 	bzero(th, sizeof(*th));
2600fb57f87SRobert Watson 	th->th_magic = htonl(TEST_MAGIC);
2610fb57f87SRobert Watson 	th->th_header_length = htonl(header_length);
2620fb57f87SRobert Watson 	th->th_offset = htonl(offset);
2630fb57f87SRobert Watson 	th->th_length = htonl(length);
2648dd83f1eSRebecca Cran 
2658dd83f1eSRebecca Cran 	MD5FileChunk(path, th->th_md5, offset, length);
2660fb57f87SRobert Watson }
2670fb57f87SRobert Watson 
2688dd83f1eSRebecca Cran static int
2698dd83f1eSRebecca Cran send_test(int connect_socket, struct sendfile_test test)
2700fb57f87SRobert Watson {
2710fb57f87SRobert Watson 	struct test_header th;
2720fb57f87SRobert Watson 	struct sf_hdtr hdtr, *hdtrp;
2730fb57f87SRobert Watson 	struct iovec headers;
2740fb57f87SRobert Watson 	char *header;
2750fb57f87SRobert Watson 	ssize_t len;
2768dd83f1eSRebecca Cran 	int length;
2770fb57f87SRobert Watson 	off_t off;
2780fb57f87SRobert Watson 
2790fb57f87SRobert Watson 	len = lseek(file_fd, 0, SEEK_SET);
2800fb57f87SRobert Watson 	if (len != 0)
2818dd83f1eSRebecca Cran 		FAIL_ERR("lseek")
2820fb57f87SRobert Watson 
2838dd83f1eSRebecca Cran 	struct stat st;
2848dd83f1eSRebecca Cran 	if (fstat(file_fd, &st) < 0)
2858dd83f1eSRebecca Cran 		FAIL_ERR("fstat")
2868dd83f1eSRebecca Cran 	length = st.st_size - test.offset;
28750185946SEd Maste 	if (test.length > 0 && test.length < (uint32_t)length)
2888dd83f1eSRebecca Cran 		length = test.length;
2898dd83f1eSRebecca Cran 
2908dd83f1eSRebecca Cran 	init_th(&th, test.hdr_length, test.offset, length);
2910fb57f87SRobert Watson 
2920fb57f87SRobert Watson 	len = write(connect_socket, &th, sizeof(th));
2930fb57f87SRobert Watson 	if (len != sizeof(th))
2948dd83f1eSRebecca Cran 		return (-1);
2950fb57f87SRobert Watson 
2968dd83f1eSRebecca Cran 	if (test.hdr_length != 0) {
2978dd83f1eSRebecca Cran 		header = malloc(test.hdr_length);
2980fb57f87SRobert Watson 		if (header == NULL)
2998dd83f1eSRebecca Cran 			FAIL_ERR("malloc")
3008dd83f1eSRebecca Cran 
3010fb57f87SRobert Watson 		hdtrp = &hdtr;
3020fb57f87SRobert Watson 		bzero(&headers, sizeof(headers));
3030fb57f87SRobert Watson 		headers.iov_base = header;
3048dd83f1eSRebecca Cran 		headers.iov_len = test.hdr_length;
3050fb57f87SRobert Watson 		bzero(&hdtr, sizeof(hdtr));
3060fb57f87SRobert Watson 		hdtr.headers = &headers;
3070fb57f87SRobert Watson 		hdtr.hdr_cnt = 1;
3080fb57f87SRobert Watson 		hdtr.trailers = NULL;
3090fb57f87SRobert Watson 		hdtr.trl_cnt = 0;
3100fb57f87SRobert Watson 	} else {
3110fb57f87SRobert Watson 		hdtrp = NULL;
3120fb57f87SRobert Watson 		header = NULL;
3130fb57f87SRobert Watson 	}
3140fb57f87SRobert Watson 
3158dd83f1eSRebecca Cran 	if (sendfile(file_fd, connect_socket, test.offset, test.length,
3168dd83f1eSRebecca Cran 				hdtrp, &off, 0) < 0) {
3178dd83f1eSRebecca Cran 		if (header != NULL)
3188dd83f1eSRebecca Cran 			free(header);
3198dd83f1eSRebecca Cran 		FAIL_ERR("sendfile")
3208dd83f1eSRebecca Cran 	}
3210fb57f87SRobert Watson 
322ed3d9b6eSPawel Jakub Dawidek 	if (length == 0) {
323ed3d9b6eSPawel Jakub Dawidek 		struct stat sb;
324ed3d9b6eSPawel Jakub Dawidek 
3258dd83f1eSRebecca Cran 		if (fstat(file_fd, &sb) == 0)
3268dd83f1eSRebecca Cran 			length = sb.st_size - test.offset;
327ed3d9b6eSPawel Jakub Dawidek 	}
3280fb57f87SRobert Watson 
3290fb57f87SRobert Watson 	if (header != NULL)
3300fb57f87SRobert Watson 		free(header);
3318dd83f1eSRebecca Cran 
3328dd83f1eSRebecca Cran 	if (off != length)
3338dd83f1eSRebecca Cran 		FAIL("offset != length")
3348dd83f1eSRebecca Cran 
3358dd83f1eSRebecca Cran 	return (0);
3360fb57f87SRobert Watson }
3370fb57f87SRobert Watson 
33850185946SEd Maste static int
33950185946SEd Maste write_test_file(size_t file_size)
34050185946SEd Maste {
34150185946SEd Maste 	char *page_buffer;
34250185946SEd Maste 	ssize_t len;
34350185946SEd Maste 	static size_t current_file_size = 0;
34450185946SEd Maste 
34550185946SEd Maste 	if (file_size == current_file_size)
34650185946SEd Maste 		return (0);
34750185946SEd Maste 	else if (file_size < current_file_size) {
34850185946SEd Maste 		if (ftruncate(file_fd, file_size) != 0)
34950185946SEd Maste 			FAIL_ERR("ftruncate");
35050185946SEd Maste 		current_file_size = file_size;
35150185946SEd Maste 		return (0);
35250185946SEd Maste 	}
35350185946SEd Maste 
35450185946SEd Maste 	page_buffer = malloc(file_size);
35550185946SEd Maste 	if (page_buffer == NULL)
35650185946SEd Maste 		FAIL_ERR("malloc")
35750185946SEd Maste 	bzero(page_buffer, file_size);
35850185946SEd Maste 
35950185946SEd Maste 	len = write(file_fd, page_buffer, file_size);
36050185946SEd Maste 	if (len < 0)
36150185946SEd Maste 		FAIL_ERR("write")
36250185946SEd Maste 
36350185946SEd Maste 	len = lseek(file_fd, 0, SEEK_SET);
36450185946SEd Maste 	if (len < 0)
36550185946SEd Maste 		FAIL_ERR("lseek")
36650185946SEd Maste 	if (len != 0)
36750185946SEd Maste 		FAIL("len != 0")
36850185946SEd Maste 
36950185946SEd Maste 	free(page_buffer);
37050185946SEd Maste 	current_file_size = file_size;
37150185946SEd Maste 	return (0);
37250185946SEd Maste }
37350185946SEd Maste 
3740fb57f87SRobert Watson static void
3750fb57f87SRobert Watson run_parent(void)
3760fb57f87SRobert Watson {
3770fb57f87SRobert Watson 	int connect_socket;
3788dd83f1eSRebecca Cran 	int status;
3798dd83f1eSRebecca Cran 	int test_num;
38050185946SEd Maste 	int test_count;
3818dd83f1eSRebecca Cran 	int pid;
38250185946SEd Maste 	size_t desired_file_size = 0;
3830fb57f87SRobert Watson 
3848dd83f1eSRebecca Cran 	const int pagesize = getpagesize();
3858dd83f1eSRebecca Cran 
38650185946SEd Maste 	struct sendfile_test tests[] = {
3878dd83f1eSRebecca Cran  		{ .hdr_length = 0, .offset = 0, .length = 1 },
3888dd83f1eSRebecca Cran 		{ .hdr_length = 0, .offset = 0, .length = pagesize },
3898dd83f1eSRebecca Cran 		{ .hdr_length = 0, .offset = 1, .length = 1 },
3908dd83f1eSRebecca Cran 		{ .hdr_length = 0, .offset = 1, .length = pagesize },
3918dd83f1eSRebecca Cran 		{ .hdr_length = 0, .offset = pagesize, .length = pagesize },
3928dd83f1eSRebecca Cran 		{ .hdr_length = 0, .offset = 0, .length = 2*pagesize },
3938dd83f1eSRebecca Cran 		{ .hdr_length = 0, .offset = 0, .length = 0 },
3948dd83f1eSRebecca Cran 		{ .hdr_length = 0, .offset = pagesize, .length = 0 },
3958dd83f1eSRebecca Cran 		{ .hdr_length = 0, .offset = 2*pagesize, .length = 0 },
39650185946SEd Maste 		{ .hdr_length = 0, .offset = TEST_PAGES*pagesize, .length = 0 },
39750185946SEd Maste 		{ .hdr_length = 0, .offset = 0, .length = pagesize,
39850185946SEd Maste 		    .file_size = 1 }
3998dd83f1eSRebecca Cran 	};
4008dd83f1eSRebecca Cran 
40150185946SEd Maste 	test_count = sizeof(tests) / sizeof(tests[0]);
40250185946SEd Maste 	printf("1..%d\n", test_count);
4038dd83f1eSRebecca Cran 
40450185946SEd Maste 	for (test_num = 1; test_num <= test_count; test_num++) {
40550185946SEd Maste 
40650185946SEd Maste 		desired_file_size = tests[test_num - 1].file_size;
40750185946SEd Maste 		if (desired_file_size == 0)
40850185946SEd Maste 			desired_file_size = TEST_PAGES * pagesize;
40950185946SEd Maste 		if (write_test_file(desired_file_size) != 0) {
41050185946SEd Maste 			printf("not ok %d\n", test_num);
41150185946SEd Maste 			continue;
41250185946SEd Maste 		}
4138dd83f1eSRebecca Cran 
4148dd83f1eSRebecca Cran 		pid = fork();
4158dd83f1eSRebecca Cran 		if (pid == -1) {
4168dd83f1eSRebecca Cran 			printf("not ok %d\n", test_num);
4178dd83f1eSRebecca Cran 			continue;
4188dd83f1eSRebecca Cran 		}
4198dd83f1eSRebecca Cran 
4208dd83f1eSRebecca Cran 		if (pid == 0)
4218dd83f1eSRebecca Cran 			run_child();
4228dd83f1eSRebecca Cran 
4238dd83f1eSRebecca Cran 		usleep(250000);
4248dd83f1eSRebecca Cran 
4258dd83f1eSRebecca Cran 		if (new_test_socket(&connect_socket) != 0) {
4268dd83f1eSRebecca Cran 			printf("not ok %d\n", test_num);
4278dd83f1eSRebecca Cran 			kill(pid, SIGALRM);
4280fb57f87SRobert Watson 			close(connect_socket);
4298dd83f1eSRebecca Cran 			continue;
4308dd83f1eSRebecca Cran 		}
4310fb57f87SRobert Watson 
4328dd83f1eSRebecca Cran 		if (send_test(connect_socket, tests[test_num-1]) != 0) {
4338dd83f1eSRebecca Cran 			printf("not ok %d\n", test_num);
4348dd83f1eSRebecca Cran 			kill(pid, SIGALRM);
4350fb57f87SRobert Watson 			close(connect_socket);
4368dd83f1eSRebecca Cran 			continue;
4378dd83f1eSRebecca Cran 		}
4380fb57f87SRobert Watson 
4390fb57f87SRobert Watson 		close(connect_socket);
4408dd83f1eSRebecca Cran 		if (waitpid(pid, &status, 0) == pid) {
4418dd83f1eSRebecca Cran 			if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
4428dd83f1eSRebecca Cran 				printf("%s %d\n", "ok", test_num);
4438dd83f1eSRebecca Cran 			else
4448dd83f1eSRebecca Cran 				printf("%s %d\n", "not ok", test_num);
4458dd83f1eSRebecca Cran 		}
4468dd83f1eSRebecca Cran 		else {
4478dd83f1eSRebecca Cran 			printf("not ok %d\n", test_num);
4488dd83f1eSRebecca Cran 		}
4498dd83f1eSRebecca Cran 	}
4508dd83f1eSRebecca Cran }
4510fb57f87SRobert Watson 
4528dd83f1eSRebecca Cran static void
4538dd83f1eSRebecca Cran cleanup(void)
4548dd83f1eSRebecca Cran {
455*b74bcac4SEnji Cooper 
4568dd83f1eSRebecca Cran 	unlink(path);
4570fb57f87SRobert Watson }
4580fb57f87SRobert Watson 
4590fb57f87SRobert Watson int
46076423e6eSPawel Jakub Dawidek main(int argc, char *argv[])
4610fb57f87SRobert Watson {
4620fb57f87SRobert Watson 	int pagesize;
4630fb57f87SRobert Watson 
464*b74bcac4SEnji Cooper 	path[0] = '\0';
4658dd83f1eSRebecca Cran 
4660fb57f87SRobert Watson 	pagesize = getpagesize();
4670fb57f87SRobert Watson 
46876423e6eSPawel Jakub Dawidek 	if (argc == 1) {
469*b74bcac4SEnji Cooper 		snprintf(path, sizeof(path), "sendfile.XXXXXXXXXXXX");
4700fb57f87SRobert Watson 		file_fd = mkstemp(path);
47176423e6eSPawel Jakub Dawidek 		if (file_fd == -1)
47276423e6eSPawel Jakub Dawidek 			FAIL_ERR("mkstemp");
47376423e6eSPawel Jakub Dawidek 	} else if (argc == 2) {
47476423e6eSPawel Jakub Dawidek 		(void)strlcpy(path, argv[1], sizeof(path));
47576423e6eSPawel Jakub Dawidek 		file_fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600);
47676423e6eSPawel Jakub Dawidek 		if (file_fd == -1)
47776423e6eSPawel Jakub Dawidek 			FAIL_ERR("open");
47876423e6eSPawel Jakub Dawidek 	} else {
47976423e6eSPawel Jakub Dawidek 		FAIL("usage: sendfile [path]");
48076423e6eSPawel Jakub Dawidek 	}
48176423e6eSPawel Jakub Dawidek 
4828dd83f1eSRebecca Cran 	atexit(cleanup);
4830fb57f87SRobert Watson 
4840fb57f87SRobert Watson 	run_parent();
4850fb57f87SRobert Watson 	return (0);
4860fb57f87SRobert Watson }
487