xref: /lighttpd1.4/src/network_write.c (revision b598bb53)
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