]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/regression/sockets/sendfile/sendfile.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 #include <sys/wait.h>
33
34 #include <netinet/in.h>
35
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <limits.h>
40 #include <md5.h>
41 #include <signal.h>
42 #include <stdint.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 /*
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.
52  */
53
54 #define FAIL(msg)       {printf("# %s\n", msg); \
55                         return (-1);}
56
57 #define FAIL_ERR(msg)   {printf("# %s: %s\n", msg, strerror(errno)); \
58                         return (-1);}
59
60 #define TEST_PORT       5678
61 #define TEST_MAGIC      0x4440f7bb
62 #define TEST_PAGES      4
63 #define TEST_SECONDS    30
64
65 struct test_header {
66         uint32_t        th_magic;
67         uint32_t        th_header_length;
68         uint32_t        th_offset;
69         uint32_t        th_length;
70         char            th_md5[33];
71 };
72
73 struct sendfile_test {
74         uint32_t        hdr_length;
75         uint32_t        offset;
76         uint32_t        length;
77         uint32_t        file_size;
78 };
79
80 static int      file_fd;
81 static char     path[PATH_MAX];
82 static int      listen_socket;
83 static int      accept_socket;
84
85 static int test_th(struct test_header *th, uint32_t *header_length,
86                 uint32_t *offset, uint32_t *length);
87 static void signal_alarm(int signum);
88 static void setup_alarm(int seconds);
89 static void cancel_alarm(void);
90 static int receive_test(void);
91 static void run_child(void);
92 static int new_test_socket(int *connect_socket);
93 static void init_th(struct test_header *th, uint32_t header_length, 
94                 uint32_t offset, uint32_t length);
95 static int send_test(int connect_socket, struct sendfile_test);
96 static int write_test_file(size_t file_size);
97 static void run_parent(void);
98 static void cleanup(void);
99
100
101 static int
102 test_th(struct test_header *th, uint32_t *header_length, uint32_t *offset, 
103                 uint32_t *length)
104 {
105
106         if (th->th_magic != htonl(TEST_MAGIC))
107                 FAIL("magic number not found in header")
108         *header_length = ntohl(th->th_header_length);
109         *offset = ntohl(th->th_offset);
110         *length = ntohl(th->th_length);
111         return (0);
112 }
113
114 static void
115 signal_alarm(int signum)
116 {
117         (void)signum;
118
119         printf("# test timeout\n");
120
121         if (accept_socket > 0)
122                 close(accept_socket);
123         if (listen_socket > 0)
124                 close(listen_socket);
125
126         _exit(-1);
127 }
128
129 static void
130 setup_alarm(int seconds)
131 {
132         struct itimerval itv;
133         bzero(&itv, sizeof(itv));
134         (void)seconds;
135         itv.it_value.tv_sec = seconds;
136
137         signal(SIGALRM, signal_alarm);
138         setitimer(ITIMER_REAL, &itv, NULL);
139 }
140
141 static void
142 cancel_alarm(void)
143 {
144         struct itimerval itv;
145         bzero(&itv, sizeof(itv));
146         setitimer(ITIMER_REAL, &itv, NULL);
147 }
148
149 static int
150 receive_test(void)
151 {
152         uint32_t header_length, offset, length, counter;
153         struct test_header th;
154         ssize_t len;
155         char buf[10240];
156         MD5_CTX md5ctx;
157         char *rxmd5;
158
159         len = read(accept_socket, &th, sizeof(th));
160         if (len < 0 || (size_t)len < sizeof(th))
161                 FAIL_ERR("read")
162
163         if (test_th(&th, &header_length, &offset, &length) != 0)
164                 return (-1);
165
166         MD5Init(&md5ctx);
167
168         counter = 0;
169         while (1) {
170                 len = read(accept_socket, buf, sizeof(buf));
171                 if (len < 0 || len == 0)
172                         break;
173                 counter += len;
174                 MD5Update(&md5ctx, buf, len);
175         }
176
177         rxmd5 = MD5End(&md5ctx, NULL);
178         
179         if ((counter != header_length+length) || 
180                         memcmp(th.th_md5, rxmd5, 33) != 0)
181                 FAIL("receive length mismatch")
182
183         free(rxmd5);
184         return (0);
185 }
186
187 static void
188 run_child(void)
189 {
190         struct sockaddr_in sin;
191         int rc = 0;
192
193         listen_socket = socket(PF_INET, SOCK_STREAM, 0);
194         if (listen_socket < 0) {
195                 printf("# socket: %s\n", strerror(errno));
196                 rc = -1;
197         }
198
199         if (!rc) {
200                 bzero(&sin, sizeof(sin));
201                 sin.sin_len = sizeof(sin);
202                 sin.sin_family = AF_INET;
203                 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
204                 sin.sin_port = htons(TEST_PORT);
205
206                 if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
207                         printf("# bind: %s\n", strerror(errno));
208                         rc = -1;
209                 }
210         }
211
212         if (!rc && listen(listen_socket, -1) < 0) {
213                 printf("# listen: %s\n", strerror(errno));
214                 rc = -1;
215         }
216
217         if (!rc) {
218                 accept_socket = accept(listen_socket, NULL, NULL);      
219                 setup_alarm(TEST_SECONDS);
220                 if (receive_test() != 0)
221                         rc = -1;
222         }
223
224         cancel_alarm();
225         if (accept_socket > 0)
226                 close(accept_socket);
227         if (listen_socket > 0)
228                 close(listen_socket);
229
230         _exit(rc);
231 }
232
233 static int
234 new_test_socket(int *connect_socket)
235 {
236         struct sockaddr_in sin;
237         int rc = 0;
238
239         *connect_socket = socket(PF_INET, SOCK_STREAM, 0);
240         if (*connect_socket < 0)
241                 FAIL_ERR("socket")
242
243         bzero(&sin, sizeof(sin));
244         sin.sin_len = sizeof(sin);
245         sin.sin_family = AF_INET;
246         sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
247         sin.sin_port = htons(TEST_PORT);
248
249         if (connect(*connect_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0)
250                 FAIL_ERR("connect")
251
252         return (rc);
253 }
254
255 static void
256 init_th(struct test_header *th, uint32_t header_length, uint32_t offset, 
257                 uint32_t length)
258 {
259         bzero(th, sizeof(*th));
260         th->th_magic = htonl(TEST_MAGIC);
261         th->th_header_length = htonl(header_length);
262         th->th_offset = htonl(offset);
263         th->th_length = htonl(length);
264
265         MD5FileChunk(path, th->th_md5, offset, length);
266 }
267
268 static int
269 send_test(int connect_socket, struct sendfile_test test)
270 {
271         struct test_header th;
272         struct sf_hdtr hdtr, *hdtrp;
273         struct iovec headers;
274         char *header;
275         ssize_t len;
276         int length;
277         off_t off;
278
279         len = lseek(file_fd, 0, SEEK_SET);
280         if (len != 0)
281                 FAIL_ERR("lseek")
282
283         struct stat st;
284         if (fstat(file_fd, &st) < 0)
285                 FAIL_ERR("fstat")
286         length = st.st_size - test.offset;
287         if (test.length > 0 && test.length < (uint32_t)length)
288                 length = test.length;
289
290         init_th(&th, test.hdr_length, test.offset, length);
291
292         len = write(connect_socket, &th, sizeof(th));
293         if (len != sizeof(th))
294                 return (-1);
295
296         if (test.hdr_length != 0) {
297                 header = malloc(test.hdr_length);
298                 if (header == NULL)
299                         FAIL_ERR("malloc")
300
301                 hdtrp = &hdtr;
302                 bzero(&headers, sizeof(headers));
303                 headers.iov_base = header;
304                 headers.iov_len = test.hdr_length;
305                 bzero(&hdtr, sizeof(hdtr));
306                 hdtr.headers = &headers;
307                 hdtr.hdr_cnt = 1;
308                 hdtr.trailers = NULL;
309                 hdtr.trl_cnt = 0;
310         } else {
311                 hdtrp = NULL;
312                 header = NULL;
313         }
314
315         if (sendfile(file_fd, connect_socket, test.offset, test.length, 
316                                 hdtrp, &off, 0) < 0) {
317                 if (header != NULL)
318                         free(header);
319                 FAIL_ERR("sendfile")
320         }
321
322         if (length == 0) {
323                 struct stat sb;
324
325                 if (fstat(file_fd, &sb) == 0)
326                         length = sb.st_size - test.offset;
327         }
328
329         if (header != NULL)
330                 free(header);
331
332         if (off != length)
333                 FAIL("offset != length")
334
335         return (0);
336 }
337
338 static int
339 write_test_file(size_t file_size)
340 {
341         char *page_buffer;
342         ssize_t len;
343         static size_t current_file_size = 0;
344
345         if (file_size == current_file_size)
346                 return (0);
347         else if (file_size < current_file_size) {
348                 if (ftruncate(file_fd, file_size) != 0)
349                         FAIL_ERR("ftruncate");
350                 current_file_size = file_size;
351                 return (0);
352         }
353
354         page_buffer = malloc(file_size);
355         if (page_buffer == NULL)
356                 FAIL_ERR("malloc")
357         bzero(page_buffer, file_size);
358
359         len = write(file_fd, page_buffer, file_size);
360         if (len < 0)
361                 FAIL_ERR("write")
362
363         len = lseek(file_fd, 0, SEEK_SET);
364         if (len < 0)
365                 FAIL_ERR("lseek")
366         if (len != 0)
367                 FAIL("len != 0")
368
369         free(page_buffer);
370         current_file_size = file_size;
371         return (0);
372 }
373
374 static void
375 run_parent(void)
376 {
377         int connect_socket;
378         int status;
379         int test_num;
380         int test_count;
381         int pid;
382         size_t desired_file_size = 0;
383
384         const int pagesize = getpagesize();
385
386         struct sendfile_test tests[] = {
387                 { .hdr_length = 0, .offset = 0, .length = 1 },
388                 { .hdr_length = 0, .offset = 0, .length = pagesize },
389                 { .hdr_length = 0, .offset = 1, .length = 1 },
390                 { .hdr_length = 0, .offset = 1, .length = pagesize },
391                 { .hdr_length = 0, .offset = pagesize, .length = pagesize },
392                 { .hdr_length = 0, .offset = 0, .length = 2*pagesize },
393                 { .hdr_length = 0, .offset = 0, .length = 0 },
394                 { .hdr_length = 0, .offset = pagesize, .length = 0 },
395                 { .hdr_length = 0, .offset = 2*pagesize, .length = 0 },
396                 { .hdr_length = 0, .offset = TEST_PAGES*pagesize, .length = 0 },
397                 { .hdr_length = 0, .offset = 0, .length = pagesize,
398                     .file_size = 1 }
399         };
400
401         test_count = sizeof(tests) / sizeof(tests[0]);
402         printf("1..%d\n", test_count);
403
404         for (test_num = 1; test_num <= test_count; test_num++) {
405
406                 desired_file_size = tests[test_num - 1].file_size;
407                 if (desired_file_size == 0)
408                         desired_file_size = TEST_PAGES * pagesize;
409                 if (write_test_file(desired_file_size) != 0) {
410                         printf("not ok %d\n", test_num);
411                         continue;
412                 }
413
414                 pid = fork();
415                 if (pid == -1) {
416                         printf("not ok %d\n", test_num);
417                         continue;
418                 }
419
420                 if (pid == 0)
421                         run_child();
422
423                 usleep(250000);
424
425                 if (new_test_socket(&connect_socket) != 0) {
426                         printf("not ok %d\n", test_num);
427                         kill(pid, SIGALRM);
428                         close(connect_socket);
429                         continue;
430                 }
431
432                 if (send_test(connect_socket, tests[test_num-1]) != 0) {
433                         printf("not ok %d\n", test_num);
434                         kill(pid, SIGALRM);
435                         close(connect_socket);
436                         continue;
437                 }
438
439                 close(connect_socket);
440                 if (waitpid(pid, &status, 0) == pid) {
441                         if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
442                                 printf("%s %d\n", "ok", test_num);
443                         else
444                                 printf("%s %d\n", "not ok", test_num);
445                 }
446                 else {
447                         printf("not ok %d\n", test_num);
448                 }
449         }
450 }
451
452 static void
453 cleanup(void)
454 {
455         if (*path != '\0')
456                 unlink(path);
457 }
458
459 int
460 main(int argc, char *argv[])
461 {
462         int pagesize;
463
464         *path = '\0';
465
466         pagesize = getpagesize();
467
468         if (argc == 1) {
469                 snprintf(path, PATH_MAX, "/tmp/sendfile.XXXXXXXXXXXX");
470                 file_fd = mkstemp(path);
471                 if (file_fd == -1)
472                         FAIL_ERR("mkstemp");
473         } else if (argc == 2) {
474                 (void)strlcpy(path, argv[1], sizeof(path));
475                 file_fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600);
476                 if (file_fd == -1)
477                         FAIL_ERR("open");
478         } else {
479                 FAIL("usage: sendfile [path]");
480         }
481
482         atexit(cleanup);
483
484         run_parent();
485         return (0);
486 }