2 * Copyright (c) 2006 Robert N. M. Watson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/types.h>
30 #include <sys/socket.h>
34 #include <netinet/in.h>
49 * Simple regression test for sendfile. Creates a file sized at four pages
50 * and then proceeds to send it over a series of sockets, exercising a number
51 * of cases and performing limited validation.
54 #define FAIL(msg) {printf("# %s\n", msg); \
57 #define FAIL_ERR(msg) {printf("# %s: %s\n", msg, strerror(errno)); \
60 #define TEST_PORT 5678
61 #define TEST_MAGIC 0x4440f7bb
63 #define TEST_SECONDS 30
67 uint32_t th_header_length;
73 struct sendfile_test {
84 static int test_th(struct test_header *th, uint32_t *header_length,
85 uint32_t *offset, uint32_t *length);
86 static void signal_alarm(int signum);
87 static void setup_alarm(int seconds);
88 static void cancel_alarm(void);
89 static int receive_test(void);
90 static void run_child(void);
91 static int new_test_socket(int *connect_socket);
92 static void init_th(struct test_header *th, uint32_t header_length,
93 uint32_t offset, uint32_t length);
94 static int send_test(int connect_socket, struct sendfile_test);
95 static void run_parent(void);
96 static void cleanup(void);
100 test_th(struct test_header *th, uint32_t *header_length, uint32_t *offset,
104 if (th->th_magic != htonl(TEST_MAGIC))
105 FAIL("magic number not found in header")
106 *header_length = ntohl(th->th_header_length);
107 *offset = ntohl(th->th_offset);
108 *length = ntohl(th->th_length);
113 signal_alarm(int signum)
117 printf("# test timeout\n");
119 if (accept_socket > 0)
120 close(accept_socket);
121 if (listen_socket > 0)
122 close(listen_socket);
128 setup_alarm(int seconds)
130 struct itimerval itv;
131 bzero(&itv, sizeof(itv));
133 itv.it_value.tv_sec = seconds;
135 signal(SIGALRM, signal_alarm);
136 setitimer(ITIMER_REAL, &itv, NULL);
142 struct itimerval itv;
143 bzero(&itv, sizeof(itv));
144 setitimer(ITIMER_REAL, &itv, NULL);
150 uint32_t header_length, offset, length, counter;
151 struct test_header th;
157 len = read(accept_socket, &th, sizeof(th));
158 if (len < 0 || (size_t)len < sizeof(th))
161 if (test_th(&th, &header_length, &offset, &length) != 0)
168 len = read(accept_socket, buf, sizeof(buf));
169 if (len < 0 || len == 0)
172 MD5Update(&md5ctx, buf, len);
175 rxmd5 = MD5End(&md5ctx, NULL);
177 if ((counter != header_length+length) ||
178 memcmp(th.th_md5, rxmd5, 33) != 0)
179 FAIL("receive length mismatch")
188 struct sockaddr_in sin;
191 listen_socket = socket(PF_INET, SOCK_STREAM, 0);
192 if (listen_socket < 0) {
193 printf("# socket: %s\n", strerror(errno));
198 bzero(&sin, sizeof(sin));
199 sin.sin_len = sizeof(sin);
200 sin.sin_family = AF_INET;
201 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
202 sin.sin_port = htons(TEST_PORT);
204 if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
205 printf("# bind: %s\n", strerror(errno));
210 if (!rc && listen(listen_socket, -1) < 0) {
211 printf("# listen: %s\n", strerror(errno));
216 accept_socket = accept(listen_socket, NULL, NULL);
217 setup_alarm(TEST_SECONDS);
218 if (receive_test() != 0)
223 if (accept_socket > 0)
224 close(accept_socket);
225 if (listen_socket > 0)
226 close(listen_socket);
232 new_test_socket(int *connect_socket)
234 struct sockaddr_in sin;
237 *connect_socket = socket(PF_INET, SOCK_STREAM, 0);
238 if (*connect_socket < 0)
241 bzero(&sin, sizeof(sin));
242 sin.sin_len = sizeof(sin);
243 sin.sin_family = AF_INET;
244 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
245 sin.sin_port = htons(TEST_PORT);
247 if (connect(*connect_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0)
254 init_th(struct test_header *th, uint32_t header_length, uint32_t offset,
257 bzero(th, sizeof(*th));
258 th->th_magic = htonl(TEST_MAGIC);
259 th->th_header_length = htonl(header_length);
260 th->th_offset = htonl(offset);
261 th->th_length = htonl(length);
263 MD5FileChunk(path, th->th_md5, offset, length);
267 send_test(int connect_socket, struct sendfile_test test)
269 struct test_header th;
270 struct sf_hdtr hdtr, *hdtrp;
271 struct iovec headers;
277 len = lseek(file_fd, 0, SEEK_SET);
281 if (test.length == 0) {
283 if (fstat(file_fd, &st) < 0)
285 length = st.st_size - test.offset;
288 length = test.length;
291 init_th(&th, test.hdr_length, test.offset, length);
293 len = write(connect_socket, &th, sizeof(th));
294 if (len != sizeof(th))
297 if (test.hdr_length != 0) {
298 header = malloc(test.hdr_length);
303 bzero(&headers, sizeof(headers));
304 headers.iov_base = header;
305 headers.iov_len = test.hdr_length;
306 bzero(&hdtr, sizeof(hdtr));
307 hdtr.headers = &headers;
309 hdtr.trailers = NULL;
316 if (sendfile(file_fd, connect_socket, test.offset, test.length,
317 hdtrp, &off, 0) < 0) {
326 if (fstat(file_fd, &sb) == 0)
327 length = sb.st_size - test.offset;
334 FAIL("offset != length")
347 const int pagesize = getpagesize();
349 struct sendfile_test tests[10] = {
350 { .hdr_length = 0, .offset = 0, .length = 1 },
351 { .hdr_length = 0, .offset = 0, .length = pagesize },
352 { .hdr_length = 0, .offset = 1, .length = 1 },
353 { .hdr_length = 0, .offset = 1, .length = pagesize },
354 { .hdr_length = 0, .offset = pagesize, .length = pagesize },
355 { .hdr_length = 0, .offset = 0, .length = 2*pagesize },
356 { .hdr_length = 0, .offset = 0, .length = 0 },
357 { .hdr_length = 0, .offset = pagesize, .length = 0 },
358 { .hdr_length = 0, .offset = 2*pagesize, .length = 0 },
359 { .hdr_length = 0, .offset = TEST_PAGES*pagesize, .length = 0 }
364 for (test_num = 1; test_num <= 10; test_num++) {
368 printf("not ok %d\n", test_num);
377 if (new_test_socket(&connect_socket) != 0) {
378 printf("not ok %d\n", test_num);
380 close(connect_socket);
384 if (send_test(connect_socket, tests[test_num-1]) != 0) {
385 printf("not ok %d\n", test_num);
387 close(connect_socket);
391 close(connect_socket);
392 if (waitpid(pid, &status, 0) == pid) {
393 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
394 printf("%s %d\n", "ok", test_num);
396 printf("%s %d\n", "not ok", test_num);
399 printf("not ok %d\n", test_num);
412 main(int argc, char *argv[])
420 pagesize = getpagesize();
421 page_buffer = malloc(TEST_PAGES * pagesize);
422 if (page_buffer == NULL)
424 bzero(page_buffer, TEST_PAGES * pagesize);
427 snprintf(path, PATH_MAX, "/tmp/sendfile.XXXXXXXXXXXX");
428 file_fd = mkstemp(path);
431 } else if (argc == 2) {
432 (void)strlcpy(path, argv[1], sizeof(path));
433 file_fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600);
437 FAIL("usage: sendfile [path]");
442 len = write(file_fd, page_buffer, TEST_PAGES * pagesize);
446 len = lseek(file_fd, 0, SEEK_SET);