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