]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - tools/regression/sockets/sendfile/sendfile.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / tools / regression / sockets / sendfile / sendfile.c
1 /*-
2  * Copyright (c) 2006 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32
33 #include <netinet/in.h>
34
35 #include <err.h>
36 #include <limits.h>
37 #include <signal.h>
38 #include <stdint.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 /*
45  * Simple regression test for sendfile.  Creates a file sized at four pages
46  * and then proceeds to send it over a series of sockets, exercising a number
47  * of cases and performing limited validation.
48  */
49
50 #define TEST_PORT       5678
51 #define TEST_MAGIC      0x4440f7bb
52 #define TEST_PAGES      4
53 #define TEST_SECONDS    30
54
55 struct test_header {
56         u_int32_t       th_magic;
57         u_int32_t       th_header_length;
58         u_int32_t       th_offset;
59         u_int32_t       th_length;
60 };
61
62 pid_t   child_pid, parent_pid;
63 int     listen_socket;
64 int     file_fd;
65
66 static int
67 test_th(struct test_header *th, u_int32_t *header_length, u_int32_t *offset,
68     u_int32_t *length)
69 {
70
71         if (th->th_magic != htonl(TEST_MAGIC))
72                 return (0);
73         *header_length = ntohl(th->th_header_length);
74         *offset = ntohl(th->th_offset);
75         *length = ntohl(th->th_length);
76         return (1);
77 }
78
79 static void
80 signal_alarm(int signum)
81 {
82
83         (void)signum;
84 }
85
86 static void
87 setup_alarm(int seconds)
88 {
89
90         signal(SIGALRM, signal_alarm);
91         alarm(seconds);
92 }
93
94 static void
95 cancel_alarm(void)
96 {
97
98         alarm(0);
99         signal(SIGALRM, SIG_DFL);
100 }
101
102 static void
103 receive_test(int accept_socket)
104 {
105         u_int32_t header_length, offset, length, counter;
106         struct test_header th;
107         ssize_t len;
108         char ch;
109
110         len = read(accept_socket, &th, sizeof(th));
111         if (len < 0)
112                 err(1, "read");
113         if ((size_t)len < sizeof(th))
114                 errx(1, "read: %zd", len);
115
116         if (test_th(&th, &header_length, &offset, &length) == 0)
117                 errx(1, "test_th: bad");
118
119         counter = 0;
120         while (1) {
121                 len = read(accept_socket, &ch, sizeof(ch));
122                 if (len < 0)
123                         err(1, "read");
124                 if (len == 0)
125                         break;
126                 counter++;
127                 /* XXXRW: Validate byte here. */
128         }
129         if (counter != header_length + length)
130                 errx(1, "receive_test: expected (%d, %d) received %d",
131                     header_length, length, counter);
132 }
133
134 static void
135 run_child(void)
136 {
137         int accept_socket;
138
139         while (1) {
140                 accept_socket = accept(listen_socket, NULL, NULL);      
141                 setup_alarm(TEST_SECONDS);
142                 receive_test(accept_socket);
143                 cancel_alarm();
144                 close(accept_socket);
145         }
146 }
147
148 static int
149 new_test_socket(void)
150 {
151         struct sockaddr_in sin;
152         int connect_socket;
153
154         connect_socket = socket(PF_INET, SOCK_STREAM, 0);
155         if (connect_socket < 0)
156                 err(1, "socket");
157
158         bzero(&sin, sizeof(sin));
159         sin.sin_len = sizeof(sin);
160         sin.sin_family = AF_INET;
161         sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
162         sin.sin_port = htons(TEST_PORT);
163
164         if (connect(connect_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0)
165                 err(1, "connect");
166
167         return (connect_socket);
168 }
169
170 static void
171 init_th(struct test_header *th, u_int32_t header_length, u_int32_t offset,
172     u_int32_t length)
173 {
174
175         bzero(th, sizeof(*th));
176         th->th_magic = htonl(TEST_MAGIC);
177         th->th_header_length = htonl(header_length);
178         th->th_offset = htonl(offset);
179         th->th_length = htonl(length);
180 }
181
182 static void
183 send_test(int connect_socket, u_int32_t header_length, u_int32_t offset,
184     u_int32_t length)
185 {
186         struct test_header th;
187         struct sf_hdtr hdtr, *hdtrp;
188         struct iovec headers;
189         char *header;
190         ssize_t len;
191         off_t off;
192
193         len = lseek(file_fd, 0, SEEK_SET);
194         if (len < 0)
195                 err(1, "lseek");
196         if (len != 0)
197                 errx(1, "lseek: %zd", len);
198
199         init_th(&th, header_length, offset, length);
200
201         len = write(connect_socket, &th, sizeof(th));
202         if (len < 0)
203                 err(1, "send");
204         if (len != sizeof(th))
205                 err(1, "send: %zd", len);
206
207         if (header_length != 0) {
208                 header = malloc(header_length);
209                 if (header == NULL)
210                         err(1, "malloc");
211                 hdtrp = &hdtr;
212                 bzero(&headers, sizeof(headers));
213                 headers.iov_base = header;
214                 headers.iov_len = header_length;
215                 bzero(&hdtr, sizeof(hdtr));
216                 hdtr.headers = &headers;
217                 hdtr.hdr_cnt = 1;
218                 hdtr.trailers = NULL;
219                 hdtr.trl_cnt = 0;
220         } else {
221                 hdtrp = NULL;
222                 header = NULL;
223         }
224
225         if (sendfile(file_fd, connect_socket, offset, length, hdtrp, &off,
226             0) < 0)
227                 err(1, "sendfile");
228
229         if (length == 0) {
230                 struct stat sb;
231
232                 if (fstat(file_fd, &sb) < 0)
233                         err(1, "fstat");
234                 length = sb.st_size - offset;
235         }
236
237         if (off != length) {
238                 errx(1, "sendfile: off(%ju) != length(%ju)",
239                     (uintmax_t)off, (uintmax_t)length);
240         }
241
242         if (header != NULL)
243                 free(header);
244 }
245
246 static void
247 run_parent(void)
248 {
249         int connect_socket;
250
251         connect_socket = new_test_socket();
252         send_test(connect_socket, 0, 0, 1);
253         close(connect_socket);
254
255         sleep(1);
256
257         connect_socket = new_test_socket();
258         send_test(connect_socket, 0, 0, getpagesize());
259         close(connect_socket);
260
261         sleep(1);
262
263         connect_socket = new_test_socket();
264         send_test(connect_socket, 0, 1, 1);
265         close(connect_socket);
266
267         sleep(1);
268
269         connect_socket = new_test_socket();
270         send_test(connect_socket, 0, 1, getpagesize());
271         close(connect_socket);
272
273         sleep(1);
274
275         connect_socket = new_test_socket();
276         send_test(connect_socket, 0, getpagesize(), getpagesize());
277         close(connect_socket);
278
279         sleep(1);
280
281         connect_socket = new_test_socket();
282         send_test(connect_socket, 0, 0, 2 * getpagesize());
283         close(connect_socket);
284
285         sleep(1);
286
287         connect_socket = new_test_socket();
288         send_test(connect_socket, 0, 0, 0);
289         close(connect_socket);
290
291         sleep(1);
292
293         connect_socket = new_test_socket();
294         send_test(connect_socket, 0, getpagesize(), 0);
295         close(connect_socket);
296
297         sleep(1);
298
299         connect_socket = new_test_socket();
300         send_test(connect_socket, 0, 2 * getpagesize(), 0);
301         close(connect_socket);
302
303         sleep(1);
304
305         connect_socket = new_test_socket();
306         send_test(connect_socket, 0, TEST_PAGES * getpagesize(), 0);
307         close(connect_socket);
308
309         sleep(1);
310
311         (void)kill(child_pid, SIGKILL);
312 }
313
314 int
315 main(void)
316 {
317         char path[PATH_MAX], *page_buffer;
318         struct sockaddr_in sin;
319         int pagesize;
320         ssize_t len;
321
322         pagesize = getpagesize();
323         page_buffer = malloc(TEST_PAGES * pagesize);
324         if (page_buffer == NULL)
325                 err(1, "malloc");
326         bzero(page_buffer, TEST_PAGES * pagesize);
327
328         listen_socket = socket(PF_INET, SOCK_STREAM, 0);
329         if (listen_socket < 0)
330                 err(1, "socket");
331
332         bzero(&sin, sizeof(sin));
333         sin.sin_len = sizeof(sin);
334         sin.sin_family = AF_INET;
335         sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
336         sin.sin_port = htons(TEST_PORT);
337
338         snprintf(path, PATH_MAX, "/tmp/sendfile.XXXXXXXXXXXX");
339         file_fd = mkstemp(path);
340         (void)unlink(path);
341
342         len = write(file_fd, page_buffer, TEST_PAGES * pagesize);
343         if (len < 0)
344                 err(1, "write");
345
346         len = lseek(file_fd, 0, SEEK_SET);
347         if (len < 0)
348                 err(1, "lseek");
349         if (len != 0)
350                 errx(1, "lseek: %zd", len);
351
352         if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0)
353                 err(1, "bind");
354
355         if (listen(listen_socket, -1) < 0)
356                 err(1, "listen");
357
358         parent_pid = getpid();
359         child_pid = fork();
360         if (child_pid < 0)
361                 err(1, "fork");
362         if (child_pid == 0) {
363                 child_pid = getpid();
364                 run_child();
365         } else
366                 run_parent();
367
368         return (0);
369 }