sendfile.c revision 168911
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 168911 2007-04-20 19:00:43Z pjd $ 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 three 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 55struct 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 62pid_t child_pid, parent_pid; 63int listen_socket; 64int file_fd; 65 66static int 67test_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 79static void 80signal_alarm(int signum) 81{ 82 83 (void)signum; 84} 85 86static void 87setup_alarm(int seconds) 88{ 89 90 signal(SIGALRM, signal_alarm); 91 alarm(seconds); 92} 93 94static void 95cancel_alarm(void) 96{ 97 98 alarm(0); 99 signal(SIGALRM, SIG_DFL); 100} 101 102static void 103receive_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 134static void 135run_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 148static int 149new_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 170static void 171init_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 182static void 183send_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 246static void 247run_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 (void)kill(child_pid, SIGKILL); 306} 307 308int 309main(void) 310{ 311 char path[PATH_MAX], *page_buffer; 312 struct sockaddr_in sin; 313 int pagesize; 314 ssize_t len; 315 316 pagesize = getpagesize(); 317 page_buffer = malloc(TEST_PAGES * pagesize); 318 if (page_buffer == NULL) 319 err(1, "malloc"); 320 bzero(page_buffer, TEST_PAGES * pagesize); 321 322 listen_socket = socket(PF_INET, SOCK_STREAM, 0); 323 if (listen_socket < 0) 324 err(1, "socket"); 325 326 bzero(&sin, sizeof(sin)); 327 sin.sin_len = sizeof(sin); 328 sin.sin_family = AF_INET; 329 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 330 sin.sin_port = htons(TEST_PORT); 331 332 snprintf(path, PATH_MAX, "/tmp/sendfile.XXXXXXXXXXXX"); 333 file_fd = mkstemp(path); 334 (void)unlink(path); 335 336 len = write(file_fd, page_buffer, TEST_PAGES * pagesize); 337 if (len < 0) 338 err(1, "write"); 339 340 len = lseek(file_fd, 0, SEEK_SET); 341 if (len < 0) 342 err(1, "lseek"); 343 if (len != 0) 344 errx(1, "lseek: %zd", len); 345 346 if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) 347 err(1, "bind"); 348 349 if (listen(listen_socket, -1) < 0) 350 err(1, "listen"); 351 352 parent_pid = getpid(); 353 child_pid = fork(); 354 if (child_pid < 0) 355 err(1, "fork"); 356 if (child_pid == 0) { 357 child_pid = getpid(); 358 run_child(); 359 } else 360 run_parent(); 361 362 return (0); 363} 364