sendfile.c revision 204294
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: head/tools/regression/sockets/sendfile/sendfile.c 204294 2010-02-24 23:00:16Z brucec $ 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 <limits.h> 39#include <md5.h> 40#include <signal.h> 41#include <stdint.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <string.h> 45#include <unistd.h> 46 47/* 48 * Simple regression test for sendfile. Creates a file sized at four pages 49 * and then proceeds to send it over a series of sockets, exercising a number 50 * of cases and performing limited validation. 51 */ 52 53#define FAIL(msg) {printf("# %s\n", msg); \ 54 return (-1);} 55 56#define FAIL_ERR(msg) {printf("# %s: %s\n", msg, strerror(errno)); \ 57 return (-1);} 58 59#define TEST_PORT 5678 60#define TEST_MAGIC 0x4440f7bb 61#define TEST_PAGES 4 62#define TEST_SECONDS 30 63 64struct test_header { 65 uint32_t th_magic; 66 uint32_t th_header_length; 67 uint32_t th_offset; 68 uint32_t th_length; 69 char th_md5[33]; 70}; 71 72struct sendfile_test { 73 uint32_t hdr_length; 74 uint32_t offset; 75 uint32_t length; 76}; 77 78int file_fd; 79char path[PATH_MAX]; 80int listen_socket; 81int accept_socket; 82 83static int test_th(struct test_header *th, uint32_t *header_length, 84 uint32_t *offset, uint32_t *length); 85static void signal_alarm(int signum); 86static void setup_alarm(int seconds); 87static void cancel_alarm(void); 88static int receive_test(void); 89static void run_child(void); 90static int new_test_socket(int *connect_socket); 91static void init_th(struct test_header *th, uint32_t header_length, 92 uint32_t offset, uint32_t length); 93static int send_test(int connect_socket, struct sendfile_test); 94static void run_parent(void); 95static void cleanup(void); 96 97 98static int 99test_th(struct test_header *th, uint32_t *header_length, uint32_t *offset, 100 uint32_t *length) 101{ 102 103 if (th->th_magic != htonl(TEST_MAGIC)) 104 FAIL("magic number not found in header") 105 *header_length = ntohl(th->th_header_length); 106 *offset = ntohl(th->th_offset); 107 *length = ntohl(th->th_length); 108 return (0); 109} 110 111static void 112signal_alarm(int signum) 113{ 114 (void)signum; 115 116 printf("# test timeout\n"); 117 118 if (accept_socket > 0) 119 close(accept_socket); 120 if (listen_socket > 0) 121 close(listen_socket); 122 123 _exit(-1); 124} 125 126static void 127setup_alarm(int seconds) 128{ 129 struct itimerval itv; 130 bzero(&itv, sizeof(itv)); 131 (void)seconds; 132 itv.it_value.tv_sec = seconds; 133 134 signal(SIGALRM, signal_alarm); 135 setitimer(ITIMER_REAL, &itv, NULL); 136} 137 138static void 139cancel_alarm(void) 140{ 141 struct itimerval itv; 142 bzero(&itv, sizeof(itv)); 143 setitimer(ITIMER_REAL, &itv, NULL); 144} 145 146static int 147receive_test(void) 148{ 149 uint32_t header_length, offset, length, counter; 150 struct test_header th; 151 ssize_t len; 152 char buf[10240]; 153 MD5_CTX md5ctx; 154 char *rxmd5; 155 156 len = read(accept_socket, &th, sizeof(th)); 157 if (len < 0 || (size_t)len < sizeof(th)) 158 FAIL_ERR("read") 159 160 if (test_th(&th, &header_length, &offset, &length) != 0) 161 return (-1); 162 163 MD5Init(&md5ctx); 164 165 counter = 0; 166 while (1) { 167 len = read(accept_socket, buf, sizeof(buf)); 168 if (len < 0 || len == 0) 169 break; 170 counter += len; 171 MD5Update(&md5ctx, buf, len); 172 } 173 174 rxmd5 = MD5End(&md5ctx, NULL); 175 176 if ((counter != header_length+length) || 177 memcmp(th.th_md5, rxmd5, 33) != 0) 178 FAIL("receive length mismatch") 179 180 free(rxmd5); 181 return (0); 182} 183 184static void 185run_child(void) 186{ 187 struct sockaddr_in sin; 188 int rc = 0; 189 190 listen_socket = socket(PF_INET, SOCK_STREAM, 0); 191 if (listen_socket < 0) { 192 printf("# socket: %s\n", strerror(errno)); 193 rc = -1; 194 } 195 196 if (!rc) { 197 bzero(&sin, sizeof(sin)); 198 sin.sin_len = sizeof(sin); 199 sin.sin_family = AF_INET; 200 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 201 sin.sin_port = htons(TEST_PORT); 202 203 if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 204 printf("# bind: %s\n", strerror(errno)); 205 rc = -1; 206 } 207 } 208 209 if (!rc && listen(listen_socket, -1) < 0) { 210 printf("# listen: %s\n", strerror(errno)); 211 rc = -1; 212 } 213 214 if (!rc) { 215 accept_socket = accept(listen_socket, NULL, NULL); 216 setup_alarm(TEST_SECONDS); 217 if (receive_test() != 0) 218 rc = -1; 219 } 220 221 cancel_alarm(); 222 if (accept_socket > 0) 223 close(accept_socket); 224 if (listen_socket > 0) 225 close(listen_socket); 226 227 _exit(rc); 228} 229 230static int 231new_test_socket(int *connect_socket) 232{ 233 struct sockaddr_in sin; 234 int rc = 0; 235 236 *connect_socket = socket(PF_INET, SOCK_STREAM, 0); 237 if (*connect_socket < 0) 238 FAIL_ERR("socket") 239 240 bzero(&sin, sizeof(sin)); 241 sin.sin_len = sizeof(sin); 242 sin.sin_family = AF_INET; 243 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 244 sin.sin_port = htons(TEST_PORT); 245 246 if (connect(*connect_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) 247 FAIL_ERR("connect") 248 249 return (rc); 250} 251 252static void 253init_th(struct test_header *th, uint32_t header_length, uint32_t offset, 254 uint32_t length) 255{ 256 bzero(th, sizeof(*th)); 257 th->th_magic = htonl(TEST_MAGIC); 258 th->th_header_length = htonl(header_length); 259 th->th_offset = htonl(offset); 260 th->th_length = htonl(length); 261 262 MD5FileChunk(path, th->th_md5, offset, length); 263} 264 265static int 266send_test(int connect_socket, struct sendfile_test test) 267{ 268 struct test_header th; 269 struct sf_hdtr hdtr, *hdtrp; 270 struct iovec headers; 271 char *header; 272 ssize_t len; 273 int length; 274 off_t off; 275 276 len = lseek(file_fd, 0, SEEK_SET); 277 if (len != 0) 278 FAIL_ERR("lseek") 279 280 if (test.length == 0) { 281 struct stat st; 282 if (fstat(file_fd, &st) < 0) 283 FAIL_ERR("fstat") 284 length = st.st_size - test.offset; 285 } 286 else { 287 length = test.length; 288 } 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 338static void 339run_parent(void) 340{ 341 int connect_socket; 342 int status; 343 int test_num; 344 int pid; 345 346 const int pagesize = getpagesize(); 347 348 struct sendfile_test tests[10] = { 349 { .hdr_length = 0, .offset = 0, .length = 1 }, 350 { .hdr_length = 0, .offset = 0, .length = pagesize }, 351 { .hdr_length = 0, .offset = 1, .length = 1 }, 352 { .hdr_length = 0, .offset = 1, .length = pagesize }, 353 { .hdr_length = 0, .offset = pagesize, .length = pagesize }, 354 { .hdr_length = 0, .offset = 0, .length = 2*pagesize }, 355 { .hdr_length = 0, .offset = 0, .length = 0 }, 356 { .hdr_length = 0, .offset = pagesize, .length = 0 }, 357 { .hdr_length = 0, .offset = 2*pagesize, .length = 0 }, 358 { .hdr_length = 0, .offset = TEST_PAGES*pagesize, .length = 0 } 359 }; 360 361 printf("1..10\n"); 362 363 for (test_num = 1; test_num <= 10; test_num++) { 364 365 pid = fork(); 366 if (pid == -1) { 367 printf("not ok %d\n", test_num); 368 continue; 369 } 370 371 if (pid == 0) 372 run_child(); 373 374 usleep(250000); 375 376 if (new_test_socket(&connect_socket) != 0) { 377 printf("not ok %d\n", test_num); 378 kill(pid, SIGALRM); 379 close(connect_socket); 380 continue; 381 } 382 383 if (send_test(connect_socket, tests[test_num-1]) != 0) { 384 printf("not ok %d\n", test_num); 385 kill(pid, SIGALRM); 386 close(connect_socket); 387 continue; 388 } 389 390 close(connect_socket); 391 if (waitpid(pid, &status, 0) == pid) { 392 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 393 printf("%s %d\n", "ok", test_num); 394 else 395 printf("%s %d\n", "not ok", test_num); 396 } 397 else { 398 printf("not ok %d\n", test_num); 399 } 400 } 401} 402 403static void 404cleanup(void) 405{ 406 if (*path != '\0') 407 unlink(path); 408} 409 410int 411main(void) 412{ 413 char *page_buffer; 414 int pagesize; 415 ssize_t len; 416 417 *path = '\0'; 418 419 pagesize = getpagesize(); 420 page_buffer = malloc(TEST_PAGES * pagesize); 421 if (page_buffer == NULL) 422 FAIL_ERR("malloc") 423 bzero(page_buffer, TEST_PAGES * pagesize); 424 425 snprintf(path, PATH_MAX, "/tmp/sendfile.XXXXXXXXXXXX"); 426 file_fd = mkstemp(path); 427 atexit(cleanup); 428 429 len = write(file_fd, page_buffer, TEST_PAGES * pagesize); 430 if (len < 0) 431 FAIL_ERR("write") 432 433 len = lseek(file_fd, 0, SEEK_SET); 434 if (len < 0) 435 FAIL_ERR("lseek") 436 if (len != 0) 437 FAIL("len != 0") 438 439 run_parent(); 440 return (0); 441} 442