sendfile.c revision 168854
1158910Srwatson/*-
2158910Srwatson * Copyright (c) 2006 Robert N. M. Watson
3158910Srwatson * All rights reserved.
4158910Srwatson *
5158910Srwatson * Redistribution and use in source and binary forms, with or without
6158910Srwatson * modification, are permitted provided that the following conditions
7158910Srwatson * are met:
8158910Srwatson * 1. Redistributions of source code must retain the above copyright
9158910Srwatson *    notice, this list of conditions and the following disclaimer.
10158910Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11158910Srwatson *    notice, this list of conditions and the following disclaimer in the
12158910Srwatson *    documentation and/or other materials provided with the distribution.
13158910Srwatson *
14158910Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15158910Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16158910Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17158910Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18158910Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19158910Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20158910Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21158910Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22158910Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23158910Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24158910Srwatson * SUCH DAMAGE.
25158910Srwatson *
26158910Srwatson * $FreeBSD: head/tools/regression/sockets/sendfile/sendfile.c 168854 2007-04-19 06:01:08Z pjd $
27158910Srwatson */
28158910Srwatson
29158910Srwatson#include <sys/types.h>
30158910Srwatson#include <sys/socket.h>
31168854Spjd#include <sys/stat.h>
32158910Srwatson
33158910Srwatson#include <netinet/in.h>
34158910Srwatson
35158910Srwatson#include <err.h>
36158910Srwatson#include <limits.h>
37158910Srwatson#include <signal.h>
38158910Srwatson#include <stdio.h>
39158910Srwatson#include <stdlib.h>
40158910Srwatson#include <string.h>
41158910Srwatson#include <unistd.h>
42158910Srwatson
43158910Srwatson/*
44158910Srwatson * Simple regression test for sendfile.  Creates a file sized at three pages
45158910Srwatson * and then proceeds to send it over a series of sockets, exercising a number
46158910Srwatson * of cases and performing limited validation.
47158910Srwatson */
48158910Srwatson
49158910Srwatson#define	TEST_PORT	5678
50158910Srwatson#define	TEST_MAGIC	0x4440f7bb
51168854Spjd#define	TEST_PAGES	4
52158910Srwatson#define	TEST_SECONDS	30
53158910Srwatson
54158910Srwatsonstruct test_header {
55158910Srwatson	u_int32_t	th_magic;
56158910Srwatson	u_int32_t	th_header_length;
57158910Srwatson	u_int32_t	th_offset;
58158910Srwatson	u_int32_t	th_length;
59158910Srwatson};
60158910Srwatson
61158910Srwatsonpid_t	child_pid, parent_pid;
62158910Srwatsonint	listen_socket;
63158910Srwatsonint	file_fd;
64158910Srwatson
65158910Srwatsonstatic int
66158910Srwatsontest_th(struct test_header *th, u_int32_t *header_length, u_int32_t *offset,
67158910Srwatson    u_int32_t *length)
68158910Srwatson{
69158910Srwatson
70158910Srwatson	if (th->th_magic != htonl(TEST_MAGIC))
71158910Srwatson		return (0);
72158910Srwatson	*header_length = ntohl(th->th_header_length);
73158910Srwatson	*offset = ntohl(th->th_offset);
74158910Srwatson	*length = ntohl(th->th_length);
75158910Srwatson	return (1);
76158910Srwatson}
77158910Srwatson
78158910Srwatsonstatic void
79158910Srwatsonsignal_alarm(int signum)
80158910Srwatson{
81158910Srwatson
82158910Srwatson}
83158910Srwatson
84158910Srwatsonstatic void
85158910Srwatsonsetup_alarm(int seconds)
86158910Srwatson{
87158910Srwatson
88158910Srwatson	signal(SIGALRM, signal_alarm);
89158910Srwatson	alarm(seconds);
90158910Srwatson}
91158910Srwatson
92158910Srwatsonstatic void
93158910Srwatsoncancel_alarm(void)
94158910Srwatson{
95158910Srwatson
96158910Srwatson	alarm(0);
97158910Srwatson	signal(SIGALRM, SIG_DFL);
98158910Srwatson}
99158910Srwatson
100158910Srwatsonstatic void
101158910Srwatsonreceive_test(int accept_socket)
102158910Srwatson{
103158910Srwatson	u_int32_t header_length, offset, length, counter;
104158910Srwatson	struct test_header th;
105158910Srwatson	ssize_t len;
106158910Srwatson	char ch;
107158910Srwatson
108158910Srwatson	len = read(accept_socket, &th, sizeof(th));
109158910Srwatson	if (len < 0)
110158910Srwatson		err(-1, "read");
111158910Srwatson	if (len < sizeof(th))
112158910Srwatson		errx(-1, "read: %d", len);
113158910Srwatson
114158910Srwatson	if (test_th(&th, &header_length, &offset, &length) == 0)
115158910Srwatson		errx(-1, "test_th: bad");
116158910Srwatson
117158910Srwatson	counter = 0;
118158910Srwatson	while (1) {
119158910Srwatson		len = read(accept_socket, &ch, sizeof(ch));
120158910Srwatson		if (len < 0)
121158910Srwatson			err(-1, "read");
122158910Srwatson		if (len == 0)
123158910Srwatson			break;
124158910Srwatson		counter++;
125158910Srwatson		/* XXXRW: Validate byte here. */
126158910Srwatson	}
127158910Srwatson	if (counter != header_length + length)
128158910Srwatson		errx(-1, "receive_test: expected (%d, %d) received %d",
129158910Srwatson		    header_length, length, counter);
130158910Srwatson}
131158910Srwatson
132158910Srwatsonstatic void
133158910Srwatsonrun_child(void)
134158910Srwatson{
135158910Srwatson	int accept_socket;
136158910Srwatson
137158910Srwatson	while (1) {
138158910Srwatson		accept_socket = accept(listen_socket, NULL, NULL);
139158910Srwatson		setup_alarm(TEST_SECONDS);
140158910Srwatson		receive_test(accept_socket);
141158910Srwatson		cancel_alarm();
142158910Srwatson		close(accept_socket);
143158910Srwatson	}
144158910Srwatson}
145158910Srwatson
146158910Srwatsonstatic int
147158910Srwatsonnew_test_socket(void)
148158910Srwatson{
149158910Srwatson	struct sockaddr_in sin;
150158910Srwatson	int connect_socket;
151158910Srwatson
152158910Srwatson	connect_socket = socket(PF_INET, SOCK_STREAM, 0);
153158910Srwatson	if (connect_socket < 0)
154158910Srwatson		err(-1, "socket");
155158910Srwatson
156158910Srwatson	bzero(&sin, sizeof(sin));
157158910Srwatson	sin.sin_len = sizeof(sin);
158158910Srwatson	sin.sin_family = AF_INET;
159158910Srwatson	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
160158910Srwatson	sin.sin_port = htons(TEST_PORT);
161158910Srwatson
162158910Srwatson	if (connect(connect_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0)
163158910Srwatson		err(-1, "connect");
164158910Srwatson
165158910Srwatson	return (connect_socket);
166158910Srwatson}
167158910Srwatson
168158910Srwatsonstatic void
169158910Srwatsoninit_th(struct test_header *th, u_int32_t header_length, u_int32_t offset,
170158910Srwatson    u_int32_t length)
171158910Srwatson{
172158910Srwatson
173158910Srwatson	bzero(th, sizeof(*th));
174158910Srwatson	th->th_magic = htonl(TEST_MAGIC);
175158910Srwatson	th->th_header_length = htonl(header_length);
176158910Srwatson	th->th_offset = htonl(offset);
177158910Srwatson	th->th_length = htonl(length);
178158910Srwatson}
179158910Srwatson
180158910Srwatsonstatic void
181158910Srwatsonsend_test(int connect_socket, u_int32_t header_length, u_int32_t offset,
182158910Srwatson    u_int32_t length)
183158910Srwatson{
184158910Srwatson	struct test_header th;
185158910Srwatson	struct sf_hdtr hdtr, *hdtrp;
186158910Srwatson	struct iovec headers;
187158910Srwatson	char *header;
188158910Srwatson	ssize_t len;
189158910Srwatson	off_t off;
190158910Srwatson
191158910Srwatson	len = lseek(file_fd, 0, SEEK_SET);
192158910Srwatson	if (len < 0)
193158910Srwatson		err(-1, "lseek");
194158910Srwatson	if (len != 0)
195158910Srwatson		errx(-1, "lseek: %d", len);
196158910Srwatson
197158910Srwatson	init_th(&th, header_length, offset, length);
198158910Srwatson
199158910Srwatson	len = write(connect_socket, &th, sizeof(th));
200158910Srwatson	if (len < 0)
201158910Srwatson		err(-1, "send");
202158910Srwatson	if (len != sizeof(th))
203158910Srwatson		err(-1, "send: %d", len);
204158910Srwatson
205158910Srwatson	if (header_length != 0) {
206158910Srwatson		header = malloc(header_length);
207158910Srwatson		if (header == NULL)
208158910Srwatson			err(-1, "malloc");
209158910Srwatson		hdtrp = &hdtr;
210158910Srwatson		bzero(&headers, sizeof(headers));
211158910Srwatson		headers.iov_base = header;
212158910Srwatson		headers.iov_len = header_length;
213158910Srwatson		bzero(&hdtr, sizeof(hdtr));
214158910Srwatson		hdtr.headers = &headers;
215158910Srwatson		hdtr.hdr_cnt = 1;
216158910Srwatson		hdtr.trailers = NULL;
217158910Srwatson		hdtr.trl_cnt = 0;
218158910Srwatson	} else {
219158910Srwatson		hdtrp = NULL;
220158910Srwatson		header = NULL;
221158910Srwatson	}
222158910Srwatson
223158910Srwatson	if (sendfile(file_fd, connect_socket, offset, length, hdtrp, &off,
224158910Srwatson	    0) < 0)
225158910Srwatson		err(-1, "sendfile");
226158910Srwatson
227168854Spjd	if (length == 0) {
228168854Spjd		struct stat sb;
229158910Srwatson
230168854Spjd		if (fstat(file_fd, &sb) < 0)
231168854Spjd			err(-1, "fstat");
232168854Spjd		length = sb.st_size;
233168854Spjd	}
234168854Spjd
235168854Spjd	if (off != length) {
236168854Spjd		errx(-1, "sendfile: off(%llu) != length(%llu)", off,
237168854Spjd		    (unsigned long long)length);
238168854Spjd	}
239168854Spjd
240158910Srwatson	if (header != NULL)
241158910Srwatson		free(header);
242158910Srwatson}
243158910Srwatson
244158910Srwatsonstatic void
245158910Srwatsonrun_parent(void)
246158910Srwatson{
247158910Srwatson	int connect_socket;
248158910Srwatson
249158910Srwatson	connect_socket = new_test_socket();
250158910Srwatson	send_test(connect_socket, 0, 0, 1);
251158910Srwatson	close(connect_socket);
252158910Srwatson
253158910Srwatson	sleep(1);
254158910Srwatson
255158910Srwatson	connect_socket = new_test_socket();
256158910Srwatson	send_test(connect_socket, 0, 0, getpagesize());
257158910Srwatson	close(connect_socket);
258158910Srwatson
259158910Srwatson	sleep(1);
260158910Srwatson
261158910Srwatson	connect_socket = new_test_socket();
262158910Srwatson	send_test(connect_socket, 0, 1, 1);
263158910Srwatson	close(connect_socket);
264158910Srwatson
265158910Srwatson	sleep(1);
266158910Srwatson
267158910Srwatson	connect_socket = new_test_socket();
268158910Srwatson	send_test(connect_socket, 0, 1, getpagesize());
269158910Srwatson	close(connect_socket);
270158910Srwatson
271158910Srwatson	sleep(1);
272158910Srwatson
273158910Srwatson	connect_socket = new_test_socket();
274158910Srwatson	send_test(connect_socket, 0, getpagesize(), getpagesize());
275158910Srwatson	close(connect_socket);
276158910Srwatson
277158910Srwatson	sleep(1);
278158910Srwatson
279168854Spjd	connect_socket = new_test_socket();
280168854Spjd	send_test(connect_socket, 0, 0, 2 * getpagesize());
281168854Spjd	close(connect_socket);
282168854Spjd
283168854Spjd	sleep(1);
284168854Spjd
285168854Spjd	connect_socket = new_test_socket();
286168854Spjd	send_test(connect_socket, 0, 0, 0);
287168854Spjd	close(connect_socket);
288168854Spjd
289168854Spjd	sleep(1);
290168854Spjd
291168854Spjd	connect_socket = new_test_socket();
292168854Spjd	send_test(connect_socket, 0, getpagesize(), 0);
293168854Spjd	close(connect_socket);
294168854Spjd
295168854Spjd	sleep(1);
296168854Spjd
297168854Spjd	connect_socket = new_test_socket();
298168854Spjd	send_test(connect_socket, 0, 2 * getpagesize(), 0);
299168854Spjd	close(connect_socket);
300168854Spjd
301168854Spjd	sleep(1);
302168854Spjd
303158910Srwatson	(void)kill(child_pid, SIGKILL);
304158910Srwatson}
305158910Srwatson
306158910Srwatsonint
307158910Srwatsonmain(int argc, char *argv[])
308158910Srwatson{
309158910Srwatson	char path[PATH_MAX], *page_buffer;
310158910Srwatson	struct sockaddr_in sin;
311158910Srwatson	int pagesize;
312158910Srwatson	ssize_t len;
313158910Srwatson
314158910Srwatson	pagesize = getpagesize();
315158910Srwatson	page_buffer = malloc(TEST_PAGES * pagesize);
316158910Srwatson	if (page_buffer == NULL)
317158910Srwatson		err(-1, "malloc");
318158910Srwatson	bzero(page_buffer, TEST_PAGES * pagesize);
319158910Srwatson
320158910Srwatson	listen_socket = socket(PF_INET, SOCK_STREAM, 0);
321158910Srwatson	if (listen_socket < 0)
322158910Srwatson		err(-1, "socket");
323158910Srwatson
324158910Srwatson	bzero(&sin, sizeof(sin));
325158910Srwatson	sin.sin_len = sizeof(sin);
326158910Srwatson	sin.sin_family = AF_INET;
327158910Srwatson	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
328158910Srwatson	sin.sin_port = htons(TEST_PORT);
329158910Srwatson
330158910Srwatson	snprintf(path, PATH_MAX, "/tmp/sendfile.XXXXXXXXXXXX");
331158910Srwatson	file_fd = mkstemp(path);
332158910Srwatson	(void)unlink(path);
333158910Srwatson
334158910Srwatson	len = write(file_fd, page_buffer, TEST_PAGES * pagesize);
335158910Srwatson	if (len < 0)
336158910Srwatson		err(-1, "write");
337158910Srwatson
338158910Srwatson	len = lseek(file_fd, 0, SEEK_SET);
339158910Srwatson	if (len < 0)
340158910Srwatson		err(-1, "lseek");
341158910Srwatson	if (len != 0)
342158910Srwatson		errx(-1, "lseek: %d", len);
343158910Srwatson
344158910Srwatson	if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0)
345158910Srwatson		err(-1, "bind");
346158910Srwatson
347158910Srwatson	if (listen(listen_socket, -1) < 0)
348158910Srwatson		err(-1, "listen");
349158910Srwatson
350158910Srwatson	parent_pid = getpid();
351158910Srwatson	child_pid = fork();
352158910Srwatson	if (child_pid < 0)
353158910Srwatson		err(-1, "fork");
354158910Srwatson	if (child_pid == 0) {
355158910Srwatson		child_pid = getpid();
356158910Srwatson		run_child();
357158910Srwatson	} else
358158910Srwatson		run_parent();
359158910Srwatson
360158910Srwatson	return (0);
361158910Srwatson}
362