1 #include "first.h" 2 3 #include "network_write.h" 4 5 #include "base.h" 6 #include "log.h" 7 8 #include <sys/types.h> 9 #include "sys-socket.h" 10 11 #include <errno.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 16 /* on linux 2.4.x you get either sendfile or LFS */ 17 #if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE \ 18 && (!defined _LARGEFILE_SOURCE || defined HAVE_SENDFILE64) \ 19 && defined(__linux__) && !defined HAVE_SENDFILE_BROKEN 20 # ifdef NETWORK_WRITE_USE_SENDFILE 21 # error "can't have more than one sendfile implementation" 22 # endif 23 # define NETWORK_WRITE_USE_SENDFILE "linux-sendfile" 24 # define NETWORK_WRITE_USE_LINUX_SENDFILE 25 #endif 26 27 #if defined HAVE_SENDFILE && (defined(__FreeBSD__) || defined(__DragonFly__)) 28 # ifdef NETWORK_WRITE_USE_SENDFILE 29 # error "can't have more than one sendfile implementation" 30 # endif 31 # define NETWORK_WRITE_USE_SENDFILE "freebsd-sendfile" 32 # define NETWORK_WRITE_USE_FREEBSD_SENDFILE 33 #endif 34 35 #if defined HAVE_SENDFILE && defined(__APPLE__) 36 # ifdef NETWORK_WRITE_USE_SENDFILE 37 # error "can't have more than one sendfile implementation" 38 # endif 39 # define NETWORK_WRITE_USE_SENDFILE "darwin-sendfile" 40 # define NETWORK_WRITE_USE_DARWIN_SENDFILE 41 #endif 42 43 #if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILEV && defined(__sun) 44 # ifdef NETWORK_WRITE_USE_SENDFILE 45 # error "can't have more than one sendfile implementation" 46 # endif 47 # define NETWORK_WRITE_USE_SENDFILE "solaris-sendfilev" 48 # define NETWORK_WRITE_USE_SOLARIS_SENDFILEV 49 #endif 50 51 /* not supported so far 52 #if defined HAVE_SEND_FILE && defined(__aix) 53 # ifdef NETWORK_WRITE_USE_SENDFILE 54 # error "can't have more than one sendfile implementation" 55 # endif 56 # define NETWORK_WRITE_USE_SENDFILE "aix-sendfile" 57 # define NETWORK_WRITE_USE_AIX_SENDFILE 58 #endif 59 */ 60 61 #if defined HAVE_SYS_UIO_H && defined HAVE_WRITEV 62 # define NETWORK_WRITE_USE_WRITEV 63 #endif 64 65 #if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP 66 # define NETWORK_WRITE_USE_MMAP 67 #endif 68 69 70 static int network_write_error(int fd, log_error_st *errh) { 71 #if defined(__WIN32) 72 int lastError = WSAGetLastError(); 73 switch (lastError) { 74 case WSAEINTR: 75 case WSAEWOULDBLOCK: 76 return -3; 77 case WSAECONNRESET: 78 case WSAETIMEDOUT: 79 case WSAECONNABORTED: 80 return -2; 81 default: 82 log_error(errh,__FILE__,__LINE__,"send failed: %d %d",lastError,fd); 83 return -1; 84 } 85 #else /* __WIN32 */ 86 switch (errno) { 87 case EAGAIN: 88 case EINTR: 89 return -3; 90 case EPIPE: 91 case ECONNRESET: 92 return -2; 93 default: 94 log_perror(errh,__FILE__,__LINE__,"write failed: %d",fd); 95 return -1; 96 } 97 #endif /* __WIN32 */ 98 } 99 100 inline 101 static ssize_t network_write_data_len(int fd, const char *data, off_t len) { 102 #if defined(__WIN32) 103 return send(fd, data, len, 0); 104 #else /* __WIN32 */ 105 return write(fd, data, len); 106 #endif /* __WIN32 */ 107 } 108 109 110 111 112 /* write next chunk(s); finished chunks are removed afterwards after successful writes. 113 * return values: similar as backends (0 success, -1 error, -2 remote close, -3 try again later (EINTR/EAGAIN)) */ 114 /* next chunk must be MEM_CHUNK. use write()/send() */ 115 static int network_write_mem_chunk(int fd, chunkqueue *cq, off_t *p_max_bytes, log_error_st *errh) { 116 chunk* const c = cq->first; 117 ssize_t wr; 118 off_t c_len = (off_t)buffer_string_length(c->mem); 119 force_assert(c->offset >= 0 && c->offset <= c_len); 120 c_len -= c->offset; 121 if (c_len > *p_max_bytes) c_len = *p_max_bytes; 122 123 if (0 == c_len) { 124 chunkqueue_remove_finished_chunks(cq); 125 return 0; 126 } 127 128 wr = network_write_data_len(fd, c->mem->ptr + c->offset, c_len); 129 if (wr >= 0) { 130 *p_max_bytes -= wr; 131 chunkqueue_mark_written(cq, wr); 132 return (wr > 0 && wr == c_len) ? 0 : -3; 133 } else { 134 return network_write_error(fd, errh); 135 } 136 } 137 138 139 140 141 #if !defined(NETWORK_WRITE_USE_MMAP) 142 143 static int network_write_file_chunk_no_mmap(int fd, chunkqueue *cq, off_t *p_max_bytes, log_error_st *errh) { 144 chunk* const c = cq->first; 145 off_t offset, toSend; 146 ssize_t wr; 147 char buf[16384]; /* max read 16kb in one step */ 148 149 force_assert(c->offset >= 0 && c->offset <= c->file.length); 150 151 offset = c->offset; 152 toSend = c->file.length - c->offset; 153 if (toSend > *p_max_bytes) toSend = *p_max_bytes; 154 155 if (0 == toSend) { 156 chunkqueue_remove_finished_chunks(cq); 157 return 0; 158 } 159 160 if (c->file.fd < 0 && 0 != chunkqueue_open_file_chunk(cq, errh)) return -1; 161 162 if (toSend > (off_t)sizeof(buf)) toSend = (off_t)sizeof(buf); 163 164 if (-1 == lseek(c->file.fd, offset, SEEK_SET)) { 165 log_perror(errh, __FILE__, __LINE__, "lseek"); 166 return -1; 167 } 168 if ((toSend = read(c->file.fd, buf, toSend)) <= 0) { 169 log_perror(errh, __FILE__, __LINE__, "read");/* err or unexpected EOF */ 170 return -1; 171 } 172 173 wr = network_write_data_len(fd, buf, toSend); 174 if (wr >= 0) { 175 *p_max_bytes -= wr; 176 chunkqueue_mark_written(cq, wr); 177 return (wr > 0 && wr == toSend) ? 0 : -3; 178 } else { 179 return network_write_error(fd, errh); 180 } 181 } 182 183 #endif 184 185 186 187 188 #if defined(NETWORK_WRITE_USE_MMAP) 189 190 #include "sys-mmap.h" 191 192 #include <setjmp.h> 193 #include <signal.h> 194 195 #define MMAP_CHUNK_SIZE (512*1024) 196 197 static off_t mmap_align_offset(off_t start) { 198 static long pagesize = 0; 199 if (0 == pagesize) { 200 pagesize = sysconf(_SC_PAGESIZE); 201 force_assert(pagesize < MMAP_CHUNK_SIZE); 202 } 203 force_assert(start >= (start % pagesize)); 204 return start - (start % pagesize); 205 } 206 207 static volatile int sigbus_jmp_valid; 208 static sigjmp_buf sigbus_jmp; 209 210 static void sigbus_handler(int sig) { 211 UNUSED(sig); 212 if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1); 213 log_failed_assert(__FILE__, __LINE__, "SIGBUS"); 214 } 215 216 /* next chunk must be FILE_CHUNK. send mmap()ed file with write() */ 217 static int network_write_file_chunk_mmap(int fd, chunkqueue *cq, off_t *p_max_bytes, log_error_st *errh) { 218 chunk* const c = cq->first; 219 off_t offset, toSend, file_end; 220 ssize_t wr; 221 size_t mmap_offset, mmap_avail; 222 const char *data; 223 224 force_assert(c->offset >= 0 && c->offset <= c->file.length); 225 226 offset = c->offset; 227 toSend = c->file.length - c->offset; 228 if (toSend > *p_max_bytes) toSend = *p_max_bytes; 229 file_end = c->file.length; /*file end offset in this chunk*/ 230 231 if (0 == toSend) { 232 chunkqueue_remove_finished_chunks(cq); 233 return 0; 234 } 235 236 if (c->file.fd < 0 && 0 != chunkqueue_open_file_chunk(cq, errh)) return -1; 237 238 /* mmap buffer if offset is outside old mmap area or not mapped at all */ 239 if (MAP_FAILED == c->file.mmap.start 240 || offset < c->file.mmap.offset 241 || offset >= (off_t)(c->file.mmap.offset + c->file.mmap.length)) { 242 243 if (MAP_FAILED != c->file.mmap.start) { 244 munmap(c->file.mmap.start, c->file.mmap.length); 245 c->file.mmap.start = MAP_FAILED; 246 } 247 248 /* Optimizations for the future: 249 * 250 * adaptive mem-mapping 251 * the problem: 252 * we mmap() the whole file. If someone has a lot of large files and 253 * 32-bit machine the virtual address area will be exhausted and we 254 * will have a failing mmap() call. 255 * solution: 256 * only mmap 16M in one chunk and move the window as soon as we have 257 * finished the first 8M 258 * 259 * read-ahead buffering 260 * the problem: 261 * sending out several large files in parallel trashes read-ahead 262 * of the kernel leading to long wait-for-seek times. 263 * solutions: (increasing complexity) 264 * 1. use madvise 265 * 2. use a internal read-ahead buffer in the chunk-structure 266 * 3. use non-blocking IO for file-transfers 267 * */ 268 269 c->file.mmap.offset = mmap_align_offset(offset); 270 271 /* all mmap()ed areas are MMAP_CHUNK_SIZE 272 * except the last which might be smaller */ 273 c->file.mmap.length = MMAP_CHUNK_SIZE; 274 if (c->file.mmap.offset > file_end - (off_t)c->file.mmap.length) { 275 c->file.mmap.length = file_end - c->file.mmap.offset; 276 } 277 278 c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, 279 MAP_SHARED, c->file.fd, c->file.mmap.offset); 280 if (MAP_FAILED == c->file.mmap.start) { 281 log_perror(errh, __FILE__, __LINE__, 282 "mmap failed: %s %d %lld %zu", c->mem->ptr, c->file.fd, 283 (long long)c->file.mmap.offset, c->file.mmap.length); 284 return -1; 285 } 286 287 #if defined(HAVE_MADVISE) 288 /* don't advise files < 64Kb */ 289 if (c->file.mmap.length > (64*1024)) { 290 /* darwin 7 is returning EINVAL all the time and I don't know how to 291 * detect this at runtime. 292 * 293 * ignore the return value for now */ 294 madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED); 295 } 296 #endif 297 } 298 299 force_assert(offset >= c->file.mmap.offset); 300 mmap_offset = offset - c->file.mmap.offset; 301 force_assert(c->file.mmap.length > mmap_offset); 302 mmap_avail = c->file.mmap.length - mmap_offset; 303 if (toSend > (off_t) mmap_avail) toSend = mmap_avail; 304 305 data = c->file.mmap.start + mmap_offset; 306 307 /* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */ 308 if (0 == sigsetjmp(sigbus_jmp, 1)) { 309 signal(SIGBUS, sigbus_handler); 310 311 sigbus_jmp_valid = 1; 312 wr = network_write_data_len(fd, data, toSend); 313 sigbus_jmp_valid = 0; 314 } else { 315 sigbus_jmp_valid = 0; 316 317 log_error(errh, __FILE__, __LINE__, 318 "SIGBUS in mmap: %s %d", c->mem->ptr, c->file.fd); 319 320 munmap(c->file.mmap.start, c->file.mmap.length); 321 c->file.mmap.start = MAP_FAILED; 322 return -1; 323 } 324 325 if (wr >= 0) { 326 *p_max_bytes -= wr; 327 chunkqueue_mark_written(cq, wr); 328 return (wr > 0 && wr == toSend) ? 0 : -3; 329 } else { 330 return network_write_error(fd, errh); 331 } 332 } 333 334 #endif /* NETWORK_WRITE_USE_MMAP */ 335 336 337 338 339 #if defined(NETWORK_WRITE_USE_WRITEV) 340 341 #if defined(HAVE_SYS_UIO_H) 342 # include <sys/uio.h> 343 #endif 344 345 #if defined(UIO_MAXIOV) 346 # define SYS_MAX_CHUNKS UIO_MAXIOV 347 #elif defined(IOV_MAX) 348 /* new name for UIO_MAXIOV since IEEE Std 1003.1-2001 */ 349 # define SYS_MAX_CHUNKS IOV_MAX 350 #elif defined(_XOPEN_IOV_MAX) 351 /* minimum value for sysconf(_SC_IOV_MAX); posix requires this to be at least 16, which is good enough - no need to call sysconf() */ 352 # define SYS_MAX_CHUNKS _XOPEN_IOV_MAX 353 #else 354 # error neither UIO_MAXIOV nor IOV_MAX nor _XOPEN_IOV_MAX are defined 355 #endif 356 357 /* allocate iovec[MAX_CHUNKS] on stack, so pick a sane limit: 358 * - each entry will use 1 pointer + 1 size_t 359 * - 32 chunks -> 256 / 512 bytes (32-bit/64-bit pointers) 360 */ 361 #define STACK_MAX_ALLOC_CHUNKS 32 362 #if SYS_MAX_CHUNKS > STACK_MAX_ALLOC_CHUNKS 363 # define MAX_CHUNKS STACK_MAX_ALLOC_CHUNKS 364 #else 365 # define MAX_CHUNKS SYS_MAX_CHUNKS 366 #endif 367 368 /* next chunk must be MEM_CHUNK. send multiple mem chunks using writev() */ 369 static int network_writev_mem_chunks(int fd, chunkqueue *cq, off_t *p_max_bytes, log_error_st *errh) { 370 struct iovec chunks[MAX_CHUNKS]; 371 size_t num_chunks = 0; 372 off_t max_bytes = *p_max_bytes; 373 off_t toSend = 0; 374 ssize_t wr; 375 376 for (const chunk *c = cq->first; 377 NULL != c && MEM_CHUNK == c->type 378 && num_chunks < MAX_CHUNKS && toSend < max_bytes; 379 c = c->next) { 380 size_t c_len = buffer_string_length(c->mem); 381 force_assert(c->offset >= 0 && c->offset <= (off_t)c_len); 382 c_len -= c->offset; 383 if (c_len > 0) { 384 toSend += c_len; 385 386 chunks[num_chunks].iov_base = c->mem->ptr + c->offset; 387 chunks[num_chunks].iov_len = c_len; 388 389 ++num_chunks; 390 } 391 } 392 393 if (0 == num_chunks) { 394 chunkqueue_remove_finished_chunks(cq); 395 return 0; 396 } 397 398 wr = writev(fd, chunks, num_chunks); 399 400 if (wr < 0) switch (errno) { 401 case EAGAIN: 402 case EINTR: 403 break; 404 case EPIPE: 405 case ECONNRESET: 406 return -2; 407 default: 408 log_perror(errh, __FILE__, __LINE__, "writev failed: %d", fd); 409 return -1; 410 } 411 412 if (wr >= 0) { 413 *p_max_bytes -= wr; 414 chunkqueue_mark_written(cq, wr); 415 } 416 417 return (wr > 0 && wr == toSend) ? 0 : -3; 418 } 419 420 #endif /* NETWORK_WRITE_USE_WRITEV */ 421 422 423 424 425 #if defined(NETWORK_WRITE_USE_SENDFILE) 426 427 #if defined(NETWORK_WRITE_USE_LINUX_SENDFILE) \ 428 || defined(NETWORK_WRITE_USE_SOLARIS_SENDFILEV) 429 #include <sys/sendfile.h> 430 #endif 431 432 #if defined(NETWORK_WRITE_USE_FREEBSD_SENDFILE) \ 433 || defined(NETWORK_WRITE_USE_DARWIN_SENDFILE) 434 #include <sys/uio.h> 435 #endif 436 437 static int network_write_file_chunk_sendfile(int fd, chunkqueue *cq, off_t *p_max_bytes, log_error_st *errh) { 438 chunk * const c = cq->first; 439 ssize_t wr; 440 off_t offset; 441 off_t toSend; 442 off_t written = 0; 443 444 force_assert(c->offset >= 0 && c->offset <= c->file.length); 445 446 offset = c->offset; 447 toSend = c->file.length - c->offset; 448 if (toSend > *p_max_bytes) toSend = *p_max_bytes; 449 450 if (0 == toSend) { 451 chunkqueue_remove_finished_chunks(cq); 452 return 0; 453 } 454 455 if (c->file.fd < 0 && 0 != chunkqueue_open_file_chunk(cq, errh)) return -1; 456 457 /* Darwin, FreeBSD, and Solaris variants support iovecs and could 458 * be optimized to send more than just file in single syscall */ 459 460 #if defined(NETWORK_WRITE_USE_LINUX_SENDFILE) 461 462 wr = sendfile(fd, c->file.fd, &offset, toSend); 463 if (wr > 0) written = (off_t)wr; 464 465 #elif defined(NETWORK_WRITE_USE_DARWIN_SENDFILE) 466 467 written = toSend; 468 wr = sendfile(c->file.fd, fd, offset, &written, NULL, 0); 469 /* (for EAGAIN/EINTR written still contains the sent bytes) */ 470 471 #elif defined(NETWORK_WRITE_USE_FREEBSD_SENDFILE) 472 473 wr = sendfile(c->file.fd, fd, offset, toSend, NULL, &written, 0); 474 /* (for EAGAIN/EINTR written still contains the sent bytes) */ 475 476 #elif defined(NETWORK_WRITE_USE_SOLARIS_SENDFILEV) 477 { 478 sendfilevec_t fvec; 479 fvec.sfv_fd = c->file.fd; 480 fvec.sfv_flag = 0; 481 fvec.sfv_off = offset; 482 fvec.sfv_len = toSend; 483 484 /* Solaris sendfilev() */ 485 wr = sendfilev(fd, &fvec, 1, (size_t *)&written); 486 /* (for EAGAIN/EINTR written still contains the sent bytes) */ 487 } 488 #else 489 490 wr = -1; 491 errno = ENOSYS; 492 493 #endif 494 495 if (-1 == wr) { 496 switch(errno) { 497 case EAGAIN: 498 case EINTR: 499 break; /* try again later */ 500 case EPIPE: 501 case ECONNRESET: 502 case ENOTCONN: 503 return -2; 504 case EINVAL: 505 case ENOSYS: 506 #if defined(ENOTSUP) && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP) 507 case ENOTSUP: 508 #endif 509 #ifdef EOPNOTSUPP 510 case EOPNOTSUPP: 511 #endif 512 #ifdef ESOCKTNOSUPPORT 513 case ESOCKTNOSUPPORT: 514 #endif 515 #ifdef EAFNOSUPPORT 516 case EAFNOSUPPORT: 517 #endif 518 #ifdef NETWORK_WRITE_USE_MMAP 519 return network_write_file_chunk_mmap(fd, cq, p_max_bytes, errh); 520 #else 521 return network_write_file_chunk_no_mmap(fd, cq, p_max_bytes, errh); 522 #endif 523 default: 524 log_perror(errh, __FILE__, __LINE__, "sendfile(): fd: %d", fd); 525 return -1; 526 } 527 } 528 529 if (written > 0) { 530 chunkqueue_mark_written(cq, written); 531 *p_max_bytes -= written; 532 } 533 else if (0 == wr) { /*(-1 != wr && 0 == written)*/ 534 log_error(errh, __FILE__, __LINE__, 535 "sendfile(): fd: %d file truncated", fd); 536 return -1; 537 } 538 539 return (wr >= 0 && written == toSend) ? 0 : -3; 540 } 541 542 #endif 543 544 545 546 547 /* return values: 548 * >= 0 : no error 549 * -1 : error (on our side) 550 * -2 : remote close 551 */ 552 553 static int network_write_chunkqueue_write(int fd, chunkqueue *cq, off_t max_bytes, log_error_st *errh) { 554 while (max_bytes > 0 && NULL != cq->first) { 555 int rc = -1; 556 557 switch (cq->first->type) { 558 case MEM_CHUNK: 559 rc = network_write_mem_chunk(fd, cq, &max_bytes, errh); 560 break; 561 case FILE_CHUNK: 562 #ifdef NETWORK_WRITE_USE_MMAP 563 rc = network_write_file_chunk_mmap(fd, cq, &max_bytes, errh); 564 #else 565 rc = network_write_file_chunk_no_mmap(fd, cq, &max_bytes, errh); 566 #endif 567 break; 568 } 569 570 if (-3 == rc) return 0; 571 if (0 != rc) return rc; 572 } 573 574 return 0; 575 } 576 577 #if defined(NETWORK_WRITE_USE_WRITEV) 578 static int network_write_chunkqueue_writev(int fd, chunkqueue *cq, off_t max_bytes, log_error_st *errh) { 579 while (max_bytes > 0 && NULL != cq->first) { 580 int rc = -1; 581 582 switch (cq->first->type) { 583 case MEM_CHUNK: 584 #if defined(NETWORK_WRITE_USE_WRITEV) 585 rc = network_writev_mem_chunks(fd, cq, &max_bytes, errh); 586 #else 587 rc = network_write_mem_chunk(fd, cq, &max_bytes, errh); 588 #endif 589 break; 590 case FILE_CHUNK: 591 #ifdef NETWORK_WRITE_USE_MMAP 592 rc = network_write_file_chunk_mmap(fd, cq, &max_bytes, errh); 593 #else 594 rc = network_write_file_chunk_no_mmap(fd, cq, &max_bytes, errh); 595 #endif 596 break; 597 } 598 599 if (-3 == rc) return 0; 600 if (0 != rc) return rc; 601 } 602 603 return 0; 604 } 605 #endif 606 607 #if defined(NETWORK_WRITE_USE_SENDFILE) 608 static int network_write_chunkqueue_sendfile(int fd, chunkqueue *cq, off_t max_bytes, log_error_st *errh) { 609 while (max_bytes > 0 && NULL != cq->first) { 610 int rc = -1; 611 612 switch (cq->first->type) { 613 case MEM_CHUNK: 614 #if defined(NETWORK_WRITE_USE_WRITEV) 615 rc = network_writev_mem_chunks(fd, cq, &max_bytes, errh); 616 #else 617 rc = network_write_mem_chunk(fd, cq, &max_bytes, errh); 618 #endif 619 break; 620 case FILE_CHUNK: 621 #if defined(NETWORK_WRITE_USE_SENDFILE) 622 rc = network_write_file_chunk_sendfile(fd, cq, &max_bytes, errh); 623 #elif defined(NETWORK_WRITE_USE_MMAP) 624 rc = network_write_file_chunk_mmap(fd, cq, &max_bytes, errh); 625 #else 626 rc = network_write_file_chunk_no_mmap(fd, cq, &max_bytes, errh); 627 #endif 628 break; 629 } 630 631 if (-3 == rc) return 0; 632 if (0 != rc) return rc; 633 } 634 635 return 0; 636 } 637 #endif 638 639 int network_write_init(server *srv) { 640 typedef enum { 641 NETWORK_BACKEND_UNSET, 642 NETWORK_BACKEND_WRITE, 643 NETWORK_BACKEND_WRITEV, 644 NETWORK_BACKEND_SENDFILE, 645 } network_backend_t; 646 647 network_backend_t backend; 648 649 struct nb_map { 650 network_backend_t nb; 651 const char *name; 652 } network_backends[] = { 653 /* lowest id wins */ 654 { NETWORK_BACKEND_SENDFILE, "sendfile" }, 655 { NETWORK_BACKEND_SENDFILE, "linux-sendfile" }, 656 { NETWORK_BACKEND_SENDFILE, "freebsd-sendfile" }, 657 { NETWORK_BACKEND_SENDFILE, "solaris-sendfilev" }, 658 { NETWORK_BACKEND_WRITEV, "writev" }, 659 { NETWORK_BACKEND_WRITE, "write" }, 660 { NETWORK_BACKEND_UNSET, NULL } 661 }; 662 663 /* get a useful default */ 664 backend = network_backends[0].nb; 665 666 /* match name against known types */ 667 if (!buffer_string_is_empty(srv->srvconf.network_backend)) { 668 const char *name, *confname = srv->srvconf.network_backend->ptr; 669 for (size_t i = 0; NULL != (name = network_backends[i].name); ++i) { 670 if (0 == strcmp(confname, name)) { 671 backend = network_backends[i].nb; 672 break; 673 } 674 } 675 if (NULL == name) { 676 log_error(srv->errh, __FILE__, __LINE__, 677 "server.network-backend has an unknown value: %s", confname); 678 return -1; 679 } 680 } 681 682 switch(backend) { 683 case NETWORK_BACKEND_SENDFILE: 684 #if defined(NETWORK_WRITE_USE_SENDFILE) 685 srv->network_backend_write = network_write_chunkqueue_sendfile; 686 break; 687 #endif 688 case NETWORK_BACKEND_WRITEV: 689 #if defined(NETWORK_WRITE_USE_WRITEV) 690 srv->network_backend_write = network_write_chunkqueue_writev; 691 break; 692 #endif 693 case NETWORK_BACKEND_WRITE: 694 srv->network_backend_write = network_write_chunkqueue_write; 695 break; 696 default: 697 return -1; 698 } 699 700 return 0; 701 } 702 703 const char * network_write_show_handlers(void) { 704 return 705 "\nNetwork handler:\n\n" 706 #if defined NETWORK_WRITE_USE_LINUX_SENDFILE 707 "\t+ linux-sendfile\n" 708 #else 709 "\t- linux-sendfile\n" 710 #endif 711 #if defined NETWORK_WRITE_USE_FREEBSD_SENDFILE 712 "\t+ freebsd-sendfile\n" 713 #else 714 "\t- freebsd-sendfile\n" 715 #endif 716 #if defined NETWORK_WRITE_USE_DARWIN_SENDFILE 717 "\t+ darwin-sendfile\n" 718 #else 719 "\t- darwin-sendfile\n" 720 #endif 721 #if defined NETWORK_WRITE_USE_SOLARIS_SENDFILEV 722 "\t+ solaris-sendfilev\n" 723 #else 724 "\t- solaris-sendfilev\n" 725 #endif 726 #if defined NETWORK_WRITE_USE_WRITEV 727 "\t+ writev\n" 728 #else 729 "\t- writev\n" 730 #endif 731 "\t+ write\n" 732 #ifdef NETWORK_WRITE_USE_MMAP 733 "\t+ mmap support\n" 734 #else 735 "\t- mmap support\n" 736 #endif 737 ; 738 } 739