xref: /lighttpd1.4/src/network_write.c (revision 7ec82a55)
18abd06a7SGlenn Strauss #include "first.h"
28abd06a7SGlenn Strauss 
3142971a8SGlenn Strauss #include "network_write.h"
4bcdc6a3bSJan Kneschke 
5142971a8SGlenn Strauss #include "base.h"
61cd73b08SGlenn Strauss #include "ck.h"
7bcdc6a3bSJan Kneschke #include "log.h"
8bcdc6a3bSJan Kneschke 
9142971a8SGlenn Strauss #include <sys/types.h>
10bcdc6a3bSJan Kneschke #include "sys-socket.h"
11bcdc6a3bSJan Kneschke 
12593599f1SStefan Bühler #include <errno.h>
1322e8b456SStefan Bühler #include <string.h>
14142971a8SGlenn Strauss #include <unistd.h>
15bcdc6a3bSJan Kneschke 
16bcdc6a3bSJan Kneschke 
17142971a8SGlenn Strauss /* on linux 2.4.x you get either sendfile or LFS */
18142971a8SGlenn Strauss #if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE \
19142971a8SGlenn Strauss  && (!defined _LARGEFILE_SOURCE || defined HAVE_SENDFILE64) \
20142971a8SGlenn Strauss  && defined(__linux__) && !defined HAVE_SENDFILE_BROKEN
21142971a8SGlenn Strauss # ifdef NETWORK_WRITE_USE_SENDFILE
22142971a8SGlenn Strauss #  error "can't have more than one sendfile implementation"
23142971a8SGlenn Strauss # endif
24142971a8SGlenn Strauss # define NETWORK_WRITE_USE_SENDFILE "linux-sendfile"
25142971a8SGlenn Strauss # define NETWORK_WRITE_USE_LINUX_SENDFILE
26142971a8SGlenn Strauss #endif
27593599f1SStefan Bühler 
28142971a8SGlenn Strauss #if defined HAVE_SENDFILE && (defined(__FreeBSD__) || defined(__DragonFly__))
29142971a8SGlenn Strauss # ifdef NETWORK_WRITE_USE_SENDFILE
30142971a8SGlenn Strauss #  error "can't have more than one sendfile implementation"
31142971a8SGlenn Strauss # endif
32142971a8SGlenn Strauss # define NETWORK_WRITE_USE_SENDFILE "freebsd-sendfile"
33142971a8SGlenn Strauss # define NETWORK_WRITE_USE_FREEBSD_SENDFILE
34142971a8SGlenn Strauss #endif
35593599f1SStefan Bühler 
36142971a8SGlenn Strauss #if defined HAVE_SENDFILE && defined(__APPLE__)
37142971a8SGlenn Strauss # ifdef NETWORK_WRITE_USE_SENDFILE
38142971a8SGlenn Strauss #  error "can't have more than one sendfile implementation"
39142971a8SGlenn Strauss # endif
40142971a8SGlenn Strauss # define NETWORK_WRITE_USE_SENDFILE "darwin-sendfile"
41142971a8SGlenn Strauss # define NETWORK_WRITE_USE_DARWIN_SENDFILE
42142971a8SGlenn Strauss #endif
43bcdc6a3bSJan Kneschke 
44*7ec82a55SGlenn Strauss #if defined(__APPLE__) && defined(__MACH__)
45*7ec82a55SGlenn Strauss /* sendfile() on iOS/tvOS sendfile() raises SIGSYS insead of returning ENOSYS
46*7ec82a55SGlenn Strauss  * https://github.com/ndfred/iperf-ios/issues/17
47*7ec82a55SGlenn Strauss  * https://github.com/dotnet/runtime/pull/69436 */
48*7ec82a55SGlenn Strauss #include <TargetConditionals.h> /* TARGET_OS_IPHONE, TARGET_OS_MAC */
49*7ec82a55SGlenn Strauss #if TARGET_OS_IPHONE            /* iOS, tvOS, or watchOS device */
50*7ec82a55SGlenn Strauss #undef NETWORK_WRITE_USE_SENDFILE
51*7ec82a55SGlenn Strauss #undef NETWORK_WRITE_USE_DARWIN_SENDFILE
52*7ec82a55SGlenn Strauss #endif
53*7ec82a55SGlenn Strauss #endif
54*7ec82a55SGlenn Strauss 
55142971a8SGlenn Strauss #if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILEV && defined(__sun)
56142971a8SGlenn Strauss # ifdef NETWORK_WRITE_USE_SENDFILE
57142971a8SGlenn Strauss #  error "can't have more than one sendfile implementation"
58142971a8SGlenn Strauss # endif
59142971a8SGlenn Strauss # define NETWORK_WRITE_USE_SENDFILE "solaris-sendfilev"
60142971a8SGlenn Strauss # define NETWORK_WRITE_USE_SOLARIS_SENDFILEV
61142971a8SGlenn Strauss #endif
62142971a8SGlenn Strauss 
63142971a8SGlenn Strauss /* not supported so far
64142971a8SGlenn Strauss #if defined HAVE_SEND_FILE && defined(__aix)
65142971a8SGlenn Strauss # ifdef NETWORK_WRITE_USE_SENDFILE
66142971a8SGlenn Strauss #  error "can't have more than one sendfile implementation"
67142971a8SGlenn Strauss # endif
68142971a8SGlenn Strauss # define NETWORK_WRITE_USE_SENDFILE "aix-sendfile"
69142971a8SGlenn Strauss # define NETWORK_WRITE_USE_AIX_SENDFILE
70142971a8SGlenn Strauss #endif
71142971a8SGlenn Strauss */
72142971a8SGlenn Strauss 
73142971a8SGlenn Strauss #if defined HAVE_SYS_UIO_H && defined HAVE_WRITEV
74142971a8SGlenn Strauss # define NETWORK_WRITE_USE_WRITEV
75142971a8SGlenn Strauss #endif
76142971a8SGlenn Strauss 
77e05ce805SGlenn Strauss #if defined(HAVE_MMAP) || defined(_WIN32) /*(see local sys-mmap.h)*/
78e05ce805SGlenn Strauss #ifdef ENABLE_MMAP
79142971a8SGlenn Strauss # define NETWORK_WRITE_USE_MMAP
80142971a8SGlenn Strauss #endif
81e05ce805SGlenn Strauss #endif
82142971a8SGlenn Strauss 
83142971a8SGlenn Strauss 
84c5dd8905SGlenn Strauss __attribute_cold__
network_write_error(int fd,log_error_st * errh)85010c2894SGlenn Strauss static int network_write_error(int fd, log_error_st *errh) {
86593599f1SStefan Bühler   #if defined(__WIN32)
87593599f1SStefan Bühler     int lastError = WSAGetLastError();
88593599f1SStefan Bühler     switch (lastError) {
89593599f1SStefan Bühler       case WSAEINTR:
90593599f1SStefan Bühler       case WSAEWOULDBLOCK:
91142971a8SGlenn Strauss         return -3;
92593599f1SStefan Bühler       case WSAECONNRESET:
93593599f1SStefan Bühler       case WSAETIMEDOUT:
94593599f1SStefan Bühler       case WSAECONNABORTED:
959e6b2c63SStefan Bühler         return -2;
969e6b2c63SStefan Bühler       default:
97010c2894SGlenn Strauss         log_error(errh,__FILE__,__LINE__,"send failed: %d %d",lastError,fd);
98bcdc6a3bSJan Kneschke         return -1;
99bcdc6a3bSJan Kneschke     }
1009e6b2c63SStefan Bühler   #else /* __WIN32 */
1019e6b2c63SStefan Bühler     switch (errno) {
1029e6b2c63SStefan Bühler       case EAGAIN:
1039e6b2c63SStefan Bühler       case EINTR:
104142971a8SGlenn Strauss         return -3;
1059e6b2c63SStefan Bühler       case EPIPE:
1069e6b2c63SStefan Bühler       case ECONNRESET:
1079e6b2c63SStefan Bühler         return -2;
1089e6b2c63SStefan Bühler       default:
109010c2894SGlenn Strauss         log_perror(errh,__FILE__,__LINE__,"write failed: %d",fd);
1109e6b2c63SStefan Bühler         return -1;
1119e6b2c63SStefan Bühler     }
1129e6b2c63SStefan Bühler   #endif /* __WIN32 */
113142971a8SGlenn Strauss }
114142971a8SGlenn Strauss 
115c5dd8905SGlenn Strauss __attribute_cold__
network_remove_finished_chunks(chunkqueue * const cq,const off_t len)116c5dd8905SGlenn Strauss static int network_remove_finished_chunks(chunkqueue * const cq, const off_t len) {
117c5dd8905SGlenn Strauss     force_assert(len >= 0);
118c5dd8905SGlenn Strauss     chunkqueue_remove_finished_chunks(cq);
119c5dd8905SGlenn Strauss     return 0;
120c5dd8905SGlenn Strauss }
121c5dd8905SGlenn Strauss 
122142971a8SGlenn Strauss inline
network_write_data_len(int fd,const char * data,off_t len)123142971a8SGlenn Strauss static ssize_t network_write_data_len(int fd, const char *data, off_t len) {
124142971a8SGlenn Strauss   #if defined(__WIN32)
125142971a8SGlenn Strauss     return send(fd, data, len, 0);
126142971a8SGlenn Strauss   #else /* __WIN32 */
127142971a8SGlenn Strauss     return write(fd, data, len);
128142971a8SGlenn Strauss   #endif /* __WIN32 */
129142971a8SGlenn Strauss }
130142971a8SGlenn Strauss 
network_write_accounting(const int fd,chunkqueue * const cq,off_t * const p_max_bytes,log_error_st * const errh,const ssize_t wr,const off_t toSend)131c5dd8905SGlenn Strauss static int network_write_accounting(const int fd, chunkqueue * const cq, off_t * const p_max_bytes, log_error_st * const errh, const ssize_t wr, const off_t toSend) {
132c5dd8905SGlenn Strauss     if (wr >= 0) {
133c5dd8905SGlenn Strauss         *p_max_bytes -= wr;/*(toSend > 0 if we reach this func)*/
134c5dd8905SGlenn Strauss         const int rc = (wr == toSend && *p_max_bytes > 0) ? 0 : -3;
135c5dd8905SGlenn Strauss         chunkqueue_mark_written(cq, wr);
136c5dd8905SGlenn Strauss         return rc;
137c5dd8905SGlenn Strauss     }
138c5dd8905SGlenn Strauss     else
139c5dd8905SGlenn Strauss         return network_write_error(fd, errh);
140c5dd8905SGlenn Strauss }
141c5dd8905SGlenn Strauss 
142142971a8SGlenn Strauss 
143142971a8SGlenn Strauss 
144142971a8SGlenn Strauss 
145142971a8SGlenn Strauss /* write next chunk(s); finished chunks are removed afterwards after successful writes.
146c752d469SGlenn Strauss  * return values: similar as backends (0 success, -1 error, -2 remote close, -3 try again later (EINTR/EAGAIN)) */
147142971a8SGlenn Strauss /* next chunk must be MEM_CHUNK. use write()/send() */
14801fdccd8SGlenn Strauss #if !defined(NETWORK_WRITE_USE_WRITEV)
network_write_mem_chunk(const int fd,chunkqueue * const cq,off_t * const p_max_bytes,log_error_st * const errh)149c5dd8905SGlenn Strauss static int network_write_mem_chunk(const int fd, chunkqueue * const cq, off_t * const p_max_bytes, log_error_st * const errh) {
150142971a8SGlenn Strauss     chunk* const c = cq->first;
151af3df29aSGlenn Strauss     off_t c_len = (off_t)buffer_clen(c->mem) - c->offset;
152142971a8SGlenn Strauss     if (c_len > *p_max_bytes) c_len = *p_max_bytes;
153c5dd8905SGlenn Strauss     if (c_len <= 0) return network_remove_finished_chunks(cq, c_len);
154142971a8SGlenn Strauss 
155c5dd8905SGlenn Strauss     ssize_t wr = network_write_data_len(fd, c->mem->ptr + c->offset, c_len);
156c5dd8905SGlenn Strauss     return network_write_accounting(fd, cq, p_max_bytes, errh, wr, c_len);
157142971a8SGlenn Strauss }
15801fdccd8SGlenn Strauss #endif
159142971a8SGlenn Strauss 
160142971a8SGlenn Strauss 
161142971a8SGlenn Strauss 
162142971a8SGlenn Strauss 
163a314b8d0SGlenn Strauss __attribute_noinline__
network_write_file_chunk_no_mmap(const int fd,chunkqueue * const cq,off_t * const p_max_bytes,log_error_st * const errh)164c5dd8905SGlenn Strauss static int network_write_file_chunk_no_mmap(const int fd, chunkqueue * const cq, off_t * const p_max_bytes, log_error_st * const errh) {
165142971a8SGlenn Strauss     chunk* const c = cq->first;
166a314b8d0SGlenn Strauss     off_t toSend = c->file.length - c->offset;
167010c2894SGlenn Strauss     char buf[16384]; /* max read 16kb in one step */
168142971a8SGlenn Strauss 
169142971a8SGlenn Strauss     if (toSend > *p_max_bytes) toSend = *p_max_bytes;
170c5dd8905SGlenn Strauss     if (toSend <= 0) return network_remove_finished_chunks(cq, toSend);
171a314b8d0SGlenn Strauss     if (toSend > (off_t)sizeof(buf)) toSend = (off_t)sizeof(buf);
172142971a8SGlenn Strauss 
1737c1e8129SGlenn Strauss     if (c->file.fd < 0 && 0 != chunkqueue_open_file_chunk(cq, errh)) return -1;
174142971a8SGlenn Strauss 
1752d1b1672SGlenn Strauss     toSend = chunk_file_pread(c->file.fd, buf, toSend, c->offset);
176042622c8SGlenn Strauss     if (toSend <= 0) {
1777c1e8129SGlenn Strauss         log_perror(errh, __FILE__, __LINE__, "read");/* err or unexpected EOF */
178142971a8SGlenn Strauss         return -1;
179142971a8SGlenn Strauss     }
180142971a8SGlenn Strauss 
181c5dd8905SGlenn Strauss     ssize_t wr = network_write_data_len(fd, buf, toSend);
182c5dd8905SGlenn Strauss     return network_write_accounting(fd, cq, p_max_bytes, errh, wr, toSend);
183142971a8SGlenn Strauss }
184142971a8SGlenn Strauss 
185142971a8SGlenn Strauss 
186142971a8SGlenn Strauss 
187142971a8SGlenn Strauss 
188142971a8SGlenn Strauss #if defined(NETWORK_WRITE_USE_MMAP)
189142971a8SGlenn Strauss 
1906cd3b5f8SGlenn Strauss #include "sys-setjmp.h"
191142971a8SGlenn Strauss 
1926cd3b5f8SGlenn Strauss static off_t
network_write_setjmp_write_cb(void * fd,const void * data,off_t len)1936cd3b5f8SGlenn Strauss network_write_setjmp_write_cb (void *fd, const void *data, off_t len)
1946cd3b5f8SGlenn Strauss {
1956cd3b5f8SGlenn Strauss     return network_write_data_len((int)(uintptr_t)fd, data, len);
1966cd3b5f8SGlenn Strauss }
1976cd3b5f8SGlenn Strauss 
198a314b8d0SGlenn Strauss /* next chunk must be FILE_CHUNK. send mmap()ed file with write() */
network_write_file_chunk_mmap(const int fd,chunkqueue * const cq,off_t * const p_max_bytes,log_error_st * const errh)199a314b8d0SGlenn Strauss static int network_write_file_chunk_mmap(const int fd, chunkqueue * const cq, off_t * const p_max_bytes, log_error_st * const errh) {
200e05ce805SGlenn Strauss     chunk * const restrict c = cq->first;
201e05ce805SGlenn Strauss     const chunk_file_view * const restrict cfv = (!c->file.is_temp)
202e05ce805SGlenn Strauss       ? chunkqueue_chunk_file_view(cq->first, 0, errh)/*use default 512k block*/
203e05ce805SGlenn Strauss       : NULL;
204e05ce805SGlenn Strauss     if (NULL == cfv)
205e05ce805SGlenn Strauss         return network_write_file_chunk_no_mmap(fd, cq, p_max_bytes, errh);
206e05ce805SGlenn Strauss 
207a314b8d0SGlenn Strauss     off_t toSend = c->file.length - c->offset;
208a314b8d0SGlenn Strauss     if (toSend > *p_max_bytes) toSend = *p_max_bytes;
209a314b8d0SGlenn Strauss     if (toSend <= 0) return network_remove_finished_chunks(cq, toSend);
210a314b8d0SGlenn Strauss 
211e05ce805SGlenn Strauss     const off_t mmap_avail = chunk_file_view_dlen(cfv, c->offset);
212e05ce805SGlenn Strauss     const char * const data = chunk_file_view_dptr(cfv, c->offset);
213a314b8d0SGlenn Strauss     if (toSend > mmap_avail) toSend = mmap_avail;
2146cd3b5f8SGlenn Strauss     off_t wr = sys_setjmp_eval3(network_write_setjmp_write_cb,
2156cd3b5f8SGlenn Strauss                                 (void *)(uintptr_t)fd, data, toSend);
2166cd3b5f8SGlenn Strauss     return network_write_accounting(fd,cq,p_max_bytes,errh,(ssize_t)wr,toSend);
217142971a8SGlenn Strauss }
218142971a8SGlenn Strauss 
219142971a8SGlenn Strauss #endif /* NETWORK_WRITE_USE_MMAP */
220142971a8SGlenn Strauss 
221142971a8SGlenn Strauss 
222142971a8SGlenn Strauss 
223142971a8SGlenn Strauss 
224142971a8SGlenn Strauss #if defined(NETWORK_WRITE_USE_WRITEV)
225142971a8SGlenn Strauss 
226142971a8SGlenn Strauss #if defined(HAVE_SYS_UIO_H)
227142971a8SGlenn Strauss # include <sys/uio.h>
228142971a8SGlenn Strauss #endif
229142971a8SGlenn Strauss 
230142971a8SGlenn Strauss #if defined(UIO_MAXIOV)
231142971a8SGlenn Strauss # define SYS_MAX_CHUNKS UIO_MAXIOV
232142971a8SGlenn Strauss #elif defined(IOV_MAX)
233142971a8SGlenn Strauss /* new name for UIO_MAXIOV since IEEE Std 1003.1-2001 */
234142971a8SGlenn Strauss # define SYS_MAX_CHUNKS IOV_MAX
235142971a8SGlenn Strauss #elif defined(_XOPEN_IOV_MAX)
236142971a8SGlenn Strauss /* minimum value for sysconf(_SC_IOV_MAX); posix requires this to be at least 16, which is good enough - no need to call sysconf() */
237142971a8SGlenn Strauss # define SYS_MAX_CHUNKS _XOPEN_IOV_MAX
238142971a8SGlenn Strauss #else
239142971a8SGlenn Strauss # error neither UIO_MAXIOV nor IOV_MAX nor _XOPEN_IOV_MAX are defined
240142971a8SGlenn Strauss #endif
241142971a8SGlenn Strauss 
242142971a8SGlenn Strauss /* allocate iovec[MAX_CHUNKS] on stack, so pick a sane limit:
243142971a8SGlenn Strauss  * - each entry will use 1 pointer + 1 size_t
244142971a8SGlenn Strauss  * - 32 chunks -> 256 / 512 bytes (32-bit/64-bit pointers)
245142971a8SGlenn Strauss  */
246142971a8SGlenn Strauss #define STACK_MAX_ALLOC_CHUNKS 32
247142971a8SGlenn Strauss #if SYS_MAX_CHUNKS > STACK_MAX_ALLOC_CHUNKS
248142971a8SGlenn Strauss # define MAX_CHUNKS STACK_MAX_ALLOC_CHUNKS
249142971a8SGlenn Strauss #else
250142971a8SGlenn Strauss # define MAX_CHUNKS SYS_MAX_CHUNKS
251142971a8SGlenn Strauss #endif
252142971a8SGlenn Strauss 
253142971a8SGlenn Strauss /* next chunk must be MEM_CHUNK. send multiple mem chunks using writev() */
network_writev_mem_chunks(const int fd,chunkqueue * const cq,off_t * const p_max_bytes,log_error_st * const errh)254c5dd8905SGlenn Strauss static int network_writev_mem_chunks(const int fd, chunkqueue * const cq, off_t * const p_max_bytes, log_error_st * const errh) {
255142971a8SGlenn Strauss     size_t num_chunks = 0;
256142971a8SGlenn Strauss     off_t toSend = 0;
257c5dd8905SGlenn Strauss     struct iovec chunks[MAX_CHUNKS];
258142971a8SGlenn Strauss 
259c5dd8905SGlenn Strauss     for (const chunk *c = cq->first; c && MEM_CHUNK == c->type; c = c->next) {
260af3df29aSGlenn Strauss         const off_t c_len = (off_t)buffer_clen(c->mem) - c->offset;
261142971a8SGlenn Strauss         if (c_len > 0) {
262142971a8SGlenn Strauss             toSend += c_len;
263142971a8SGlenn Strauss 
264142971a8SGlenn Strauss             chunks[num_chunks].iov_base = c->mem->ptr + c->offset;
265c5dd8905SGlenn Strauss             chunks[num_chunks].iov_len = (size_t)c_len;
266142971a8SGlenn Strauss 
267c5dd8905SGlenn Strauss             if (++num_chunks == MAX_CHUNKS || toSend >= *p_max_bytes) break;
268142971a8SGlenn Strauss         }
269c5dd8905SGlenn Strauss         else if (c_len < 0) /*(should not happen; trigger assert)*/
270c5dd8905SGlenn Strauss             return network_remove_finished_chunks(cq, c_len);
271142971a8SGlenn Strauss     }
272c5dd8905SGlenn Strauss     if (0 == num_chunks) return network_remove_finished_chunks(cq, 0);
273142971a8SGlenn Strauss 
274c5dd8905SGlenn Strauss     ssize_t wr = writev(fd, chunks, num_chunks);
275c5dd8905SGlenn Strauss     return network_write_accounting(fd, cq, p_max_bytes, errh, wr, toSend);
276593599f1SStefan Bühler }
277593599f1SStefan Bühler 
278142971a8SGlenn Strauss #endif /* NETWORK_WRITE_USE_WRITEV */
279142971a8SGlenn Strauss 
280142971a8SGlenn Strauss 
281142971a8SGlenn Strauss 
282142971a8SGlenn Strauss 
283142971a8SGlenn Strauss #if defined(NETWORK_WRITE_USE_SENDFILE)
284142971a8SGlenn Strauss 
285142971a8SGlenn Strauss #if defined(NETWORK_WRITE_USE_LINUX_SENDFILE) \
286142971a8SGlenn Strauss  || defined(NETWORK_WRITE_USE_SOLARIS_SENDFILEV)
287142971a8SGlenn Strauss #include <sys/sendfile.h>
288142971a8SGlenn Strauss #endif
289142971a8SGlenn Strauss 
290142971a8SGlenn Strauss #if defined(NETWORK_WRITE_USE_FREEBSD_SENDFILE) \
291142971a8SGlenn Strauss  || defined(NETWORK_WRITE_USE_DARWIN_SENDFILE)
292142971a8SGlenn Strauss #include <sys/uio.h>
293142971a8SGlenn Strauss #endif
294142971a8SGlenn Strauss 
network_write_file_chunk_sendfile(const int fd,chunkqueue * const cq,off_t * const p_max_bytes,log_error_st * const errh)295c5dd8905SGlenn Strauss static int network_write_file_chunk_sendfile(const int fd, chunkqueue * const cq, off_t * const p_max_bytes, log_error_st * const errh) {
296142971a8SGlenn Strauss     chunk * const c = cq->first;
2970ff60d82SGlenn Strauss     ssize_t wr;
298142971a8SGlenn Strauss     off_t offset;
299142971a8SGlenn Strauss     off_t toSend;
300142971a8SGlenn Strauss     off_t written = 0;
301142971a8SGlenn Strauss 
3023f1a12e5SGlenn Strauss     offset = c->offset;
303142971a8SGlenn Strauss     toSend = c->file.length - c->offset;
304142971a8SGlenn Strauss     if (toSend > *p_max_bytes) toSend = *p_max_bytes;
305c5dd8905SGlenn Strauss     if (toSend <= 0) return network_remove_finished_chunks(cq, toSend);
306142971a8SGlenn Strauss 
3077c1e8129SGlenn Strauss     if (c->file.fd < 0 && 0 != chunkqueue_open_file_chunk(cq, errh)) return -1;
308142971a8SGlenn Strauss 
309142971a8SGlenn Strauss     /* Darwin, FreeBSD, and Solaris variants support iovecs and could
310142971a8SGlenn Strauss      * be optimized to send more than just file in single syscall */
311142971a8SGlenn Strauss 
312142971a8SGlenn Strauss   #if defined(NETWORK_WRITE_USE_LINUX_SENDFILE)
313142971a8SGlenn Strauss 
3140ff60d82SGlenn Strauss     wr = sendfile(fd, c->file.fd, &offset, toSend);
3150ff60d82SGlenn Strauss     if (wr > 0) written = (off_t)wr;
316142971a8SGlenn Strauss 
317142971a8SGlenn Strauss   #elif defined(NETWORK_WRITE_USE_DARWIN_SENDFILE)
318142971a8SGlenn Strauss 
319142971a8SGlenn Strauss     written = toSend;
3200ff60d82SGlenn Strauss     wr = sendfile(c->file.fd, fd, offset, &written, NULL, 0);
321142971a8SGlenn Strauss     /* (for EAGAIN/EINTR written still contains the sent bytes) */
322142971a8SGlenn Strauss 
323142971a8SGlenn Strauss   #elif defined(NETWORK_WRITE_USE_FREEBSD_SENDFILE)
324142971a8SGlenn Strauss 
3250ff60d82SGlenn Strauss     wr = sendfile(c->file.fd, fd, offset, toSend, NULL, &written, 0);
326142971a8SGlenn Strauss     /* (for EAGAIN/EINTR written still contains the sent bytes) */
327142971a8SGlenn Strauss 
328142971a8SGlenn Strauss   #elif defined(NETWORK_WRITE_USE_SOLARIS_SENDFILEV)
329142971a8SGlenn Strauss     {
330142971a8SGlenn Strauss         sendfilevec_t fvec;
331142971a8SGlenn Strauss         fvec.sfv_fd = c->file.fd;
332142971a8SGlenn Strauss         fvec.sfv_flag = 0;
333142971a8SGlenn Strauss         fvec.sfv_off = offset;
334142971a8SGlenn Strauss         fvec.sfv_len = toSend;
335142971a8SGlenn Strauss 
336142971a8SGlenn Strauss         /* Solaris sendfilev() */
3370ff60d82SGlenn Strauss         wr = sendfilev(fd, &fvec, 1, (size_t *)&written);
338142971a8SGlenn Strauss         /* (for EAGAIN/EINTR written still contains the sent bytes) */
339142971a8SGlenn Strauss     }
340142971a8SGlenn Strauss   #else
341142971a8SGlenn Strauss 
3420ff60d82SGlenn Strauss     wr = -1;
343142971a8SGlenn Strauss     errno = ENOSYS;
344142971a8SGlenn Strauss 
345142971a8SGlenn Strauss   #endif
346142971a8SGlenn Strauss 
3470ff60d82SGlenn Strauss     if (-1 == wr) {
348142971a8SGlenn Strauss         switch(errno) {
349142971a8SGlenn Strauss           case EAGAIN:
350142971a8SGlenn Strauss           case EINTR:
351142971a8SGlenn Strauss             break; /* try again later */
352142971a8SGlenn Strauss           case EPIPE:
353142971a8SGlenn Strauss           case ECONNRESET:
354142971a8SGlenn Strauss           case ENOTCONN:
355142971a8SGlenn Strauss             return -2;
356142971a8SGlenn Strauss           case EINVAL:
357142971a8SGlenn Strauss           case ENOSYS:
358142971a8SGlenn Strauss          #if defined(ENOTSUP) && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP)
359142971a8SGlenn Strauss           case ENOTSUP:
360142971a8SGlenn Strauss          #endif
361142971a8SGlenn Strauss          #ifdef EOPNOTSUPP
362142971a8SGlenn Strauss           case EOPNOTSUPP:
363142971a8SGlenn Strauss          #endif
364142971a8SGlenn Strauss          #ifdef ESOCKTNOSUPPORT
365142971a8SGlenn Strauss           case ESOCKTNOSUPPORT:
366142971a8SGlenn Strauss          #endif
367142971a8SGlenn Strauss          #ifdef EAFNOSUPPORT
368142971a8SGlenn Strauss           case EAFNOSUPPORT:
369142971a8SGlenn Strauss          #endif
370142971a8SGlenn Strauss            #ifdef NETWORK_WRITE_USE_MMAP
371010c2894SGlenn Strauss             return network_write_file_chunk_mmap(fd, cq, p_max_bytes, errh);
372142971a8SGlenn Strauss            #else
373010c2894SGlenn Strauss             return network_write_file_chunk_no_mmap(fd, cq, p_max_bytes, errh);
374142971a8SGlenn Strauss            #endif
375142971a8SGlenn Strauss           default:
376010c2894SGlenn Strauss             log_perror(errh, __FILE__, __LINE__, "sendfile(): fd: %d", fd);
377142971a8SGlenn Strauss             return -1;
378142971a8SGlenn Strauss         }
379142971a8SGlenn Strauss     }
380142971a8SGlenn Strauss 
3817c1e8129SGlenn Strauss     if (written > 0) {
382142971a8SGlenn Strauss         chunkqueue_mark_written(cq, written);
383142971a8SGlenn Strauss         *p_max_bytes -= written;
384c5dd8905SGlenn Strauss         if (__builtin_expect( (*p_max_bytes <= 0), 0)) return -3;
385142971a8SGlenn Strauss     }
3867c1e8129SGlenn Strauss     else if (0 == wr) { /*(-1 != wr && 0 == written)*/
3877c1e8129SGlenn Strauss         log_error(errh, __FILE__, __LINE__,
3887c1e8129SGlenn Strauss                   "sendfile(): fd: %d file truncated", fd);
3897c1e8129SGlenn Strauss         return -1;
3907c1e8129SGlenn Strauss     }
391142971a8SGlenn Strauss 
3920ff60d82SGlenn Strauss     return (wr >= 0 && written == toSend) ? 0 : -3;
393142971a8SGlenn Strauss }
394142971a8SGlenn Strauss 
395142971a8SGlenn Strauss #endif
396142971a8SGlenn Strauss 
397142971a8SGlenn Strauss 
398142971a8SGlenn Strauss 
399142971a8SGlenn Strauss 
400142971a8SGlenn Strauss /* return values:
401142971a8SGlenn Strauss  * >= 0 : no error
402142971a8SGlenn Strauss  *   -1 : error (on our side)
403142971a8SGlenn Strauss  *   -2 : remote close
404142971a8SGlenn Strauss  */
405142971a8SGlenn Strauss 
network_write_chunkqueue_writev(const int fd,chunkqueue * const cq,off_t max_bytes,log_error_st * const errh)406c5dd8905SGlenn Strauss static int network_write_chunkqueue_writev(const int fd, chunkqueue * const cq, off_t max_bytes, log_error_st * const errh) {
407c5dd8905SGlenn Strauss     while (NULL != cq->first) {
4080ff60d82SGlenn Strauss         int rc = -1;
409593599f1SStefan Bühler 
410593599f1SStefan Bühler         switch (cq->first->type) {
411593599f1SStefan Bühler         case MEM_CHUNK:
412142971a8SGlenn Strauss           #if defined(NETWORK_WRITE_USE_WRITEV)
4130ff60d82SGlenn Strauss             rc = network_writev_mem_chunks(fd, cq, &max_bytes, errh);
414142971a8SGlenn Strauss           #else
4150ff60d82SGlenn Strauss             rc = network_write_mem_chunk(fd, cq, &max_bytes, errh);
416142971a8SGlenn Strauss           #endif
417593599f1SStefan Bühler             break;
418593599f1SStefan Bühler         case FILE_CHUNK:
419142971a8SGlenn Strauss           #ifdef NETWORK_WRITE_USE_MMAP
4200ff60d82SGlenn Strauss             rc = network_write_file_chunk_mmap(fd, cq, &max_bytes, errh);
421142971a8SGlenn Strauss           #else
4220ff60d82SGlenn Strauss             rc = network_write_file_chunk_no_mmap(fd, cq, &max_bytes, errh);
423142971a8SGlenn Strauss           #endif
424593599f1SStefan Bühler             break;
425bcdc6a3bSJan Kneschke         }
426593599f1SStefan Bühler 
427c5dd8905SGlenn Strauss         if (__builtin_expect( (0 != rc), 0)) return (-3 == rc) ? 0 : rc;
428593599f1SStefan Bühler     }
429593599f1SStefan Bühler 
430593599f1SStefan Bühler     return 0;
431593599f1SStefan Bühler }
432142971a8SGlenn Strauss 
433142971a8SGlenn Strauss #if defined(NETWORK_WRITE_USE_SENDFILE)
network_write_chunkqueue_sendfile(const int fd,chunkqueue * const cq,off_t max_bytes,log_error_st * const errh)434c5dd8905SGlenn Strauss static int network_write_chunkqueue_sendfile(const int fd, chunkqueue * const cq, off_t max_bytes, log_error_st * const errh) {
435c5dd8905SGlenn Strauss     while (NULL != cq->first) {
4360ff60d82SGlenn Strauss         int rc = -1;
437142971a8SGlenn Strauss 
438142971a8SGlenn Strauss         switch (cq->first->type) {
439142971a8SGlenn Strauss         case MEM_CHUNK:
440142971a8SGlenn Strauss           #if defined(NETWORK_WRITE_USE_WRITEV)
4410ff60d82SGlenn Strauss             rc = network_writev_mem_chunks(fd, cq, &max_bytes, errh);
442142971a8SGlenn Strauss           #else
4430ff60d82SGlenn Strauss             rc = network_write_mem_chunk(fd, cq, &max_bytes, errh);
444142971a8SGlenn Strauss           #endif
445142971a8SGlenn Strauss             break;
446142971a8SGlenn Strauss         case FILE_CHUNK:
447142971a8SGlenn Strauss           #if defined(NETWORK_WRITE_USE_SENDFILE)
4480ff60d82SGlenn Strauss             rc = network_write_file_chunk_sendfile(fd, cq, &max_bytes, errh);
449142971a8SGlenn Strauss           #elif defined(NETWORK_WRITE_USE_MMAP)
4500ff60d82SGlenn Strauss             rc = network_write_file_chunk_mmap(fd, cq, &max_bytes, errh);
451142971a8SGlenn Strauss           #else
4520ff60d82SGlenn Strauss             rc = network_write_file_chunk_no_mmap(fd, cq, &max_bytes, errh);
453142971a8SGlenn Strauss           #endif
454142971a8SGlenn Strauss             break;
455142971a8SGlenn Strauss         }
456142971a8SGlenn Strauss 
457c5dd8905SGlenn Strauss         if (__builtin_expect( (0 != rc), 0)) return (-3 == rc) ? 0 : rc;
458142971a8SGlenn Strauss     }
459142971a8SGlenn Strauss 
460142971a8SGlenn Strauss     return 0;
461142971a8SGlenn Strauss }
462142971a8SGlenn Strauss #endif
463142971a8SGlenn Strauss 
network_write_init(server * srv)464142971a8SGlenn Strauss int network_write_init(server *srv) {
465142971a8SGlenn Strauss     typedef enum {
466142971a8SGlenn Strauss         NETWORK_BACKEND_UNSET,
467142971a8SGlenn Strauss         NETWORK_BACKEND_WRITE,
468142971a8SGlenn Strauss         NETWORK_BACKEND_WRITEV,
469142971a8SGlenn Strauss         NETWORK_BACKEND_SENDFILE,
470142971a8SGlenn Strauss     } network_backend_t;
471142971a8SGlenn Strauss 
472142971a8SGlenn Strauss     network_backend_t backend;
473142971a8SGlenn Strauss 
474142971a8SGlenn Strauss     struct nb_map {
475142971a8SGlenn Strauss         network_backend_t nb;
476142971a8SGlenn Strauss         const char *name;
477142971a8SGlenn Strauss     } network_backends[] = {
478142971a8SGlenn Strauss         /* lowest id wins */
479142971a8SGlenn Strauss         { NETWORK_BACKEND_SENDFILE, "sendfile" },
480142971a8SGlenn Strauss         { NETWORK_BACKEND_SENDFILE, "linux-sendfile" },
481142971a8SGlenn Strauss         { NETWORK_BACKEND_SENDFILE, "freebsd-sendfile" },
482142971a8SGlenn Strauss         { NETWORK_BACKEND_SENDFILE, "solaris-sendfilev" },
483142971a8SGlenn Strauss         { NETWORK_BACKEND_WRITEV,   "writev" },
484142971a8SGlenn Strauss         { NETWORK_BACKEND_WRITE,    "write" },
485142971a8SGlenn Strauss         { NETWORK_BACKEND_UNSET,    NULL }
486142971a8SGlenn Strauss     };
487142971a8SGlenn Strauss 
488142971a8SGlenn Strauss     /* get a useful default */
489142971a8SGlenn Strauss     backend = network_backends[0].nb;
490142971a8SGlenn Strauss 
491142971a8SGlenn Strauss     /* match name against known types */
492af3df29aSGlenn Strauss     if (srv->srvconf.network_backend) {
493010c2894SGlenn Strauss         const char *name, *confname = srv->srvconf.network_backend->ptr;
494142971a8SGlenn Strauss         for (size_t i = 0; NULL != (name = network_backends[i].name); ++i) {
495010c2894SGlenn Strauss             if (0 == strcmp(confname, name)) {
496142971a8SGlenn Strauss                 backend = network_backends[i].nb;
497142971a8SGlenn Strauss                 break;
498142971a8SGlenn Strauss             }
499142971a8SGlenn Strauss         }
500142971a8SGlenn Strauss         if (NULL == name) {
501010c2894SGlenn Strauss             log_error(srv->errh, __FILE__, __LINE__,
502010c2894SGlenn Strauss               "server.network-backend has an unknown value: %s", confname);
503142971a8SGlenn Strauss             return -1;
504142971a8SGlenn Strauss         }
505142971a8SGlenn Strauss     }
506142971a8SGlenn Strauss 
507142971a8SGlenn Strauss     switch(backend) {
508142971a8SGlenn Strauss     case NETWORK_BACKEND_SENDFILE:
509142971a8SGlenn Strauss       #if defined(NETWORK_WRITE_USE_SENDFILE)
510142971a8SGlenn Strauss         srv->network_backend_write = network_write_chunkqueue_sendfile;
511142971a8SGlenn Strauss         break;
512142971a8SGlenn Strauss       #endif
513142971a8SGlenn Strauss     case NETWORK_BACKEND_WRITEV:
514142971a8SGlenn Strauss     case NETWORK_BACKEND_WRITE:
51501fdccd8SGlenn Strauss         srv->network_backend_write = network_write_chunkqueue_writev;
516142971a8SGlenn Strauss         break;
517142971a8SGlenn Strauss     default:
518142971a8SGlenn Strauss         return -1;
519142971a8SGlenn Strauss     }
520142971a8SGlenn Strauss 
521142971a8SGlenn Strauss     return 0;
522142971a8SGlenn Strauss }
523142971a8SGlenn Strauss 
network_write_show_handlers(void)524142971a8SGlenn Strauss const char * network_write_show_handlers(void) {
525142971a8SGlenn Strauss     return
526142971a8SGlenn Strauss       "\nNetwork handler:\n\n"
527142971a8SGlenn Strauss      #if defined NETWORK_WRITE_USE_LINUX_SENDFILE
528142971a8SGlenn Strauss       "\t+ linux-sendfile\n"
529142971a8SGlenn Strauss      #else
530142971a8SGlenn Strauss       "\t- linux-sendfile\n"
531142971a8SGlenn Strauss      #endif
532142971a8SGlenn Strauss      #if defined NETWORK_WRITE_USE_FREEBSD_SENDFILE
533142971a8SGlenn Strauss       "\t+ freebsd-sendfile\n"
534142971a8SGlenn Strauss      #else
535142971a8SGlenn Strauss       "\t- freebsd-sendfile\n"
536142971a8SGlenn Strauss      #endif
537142971a8SGlenn Strauss      #if defined NETWORK_WRITE_USE_DARWIN_SENDFILE
538142971a8SGlenn Strauss       "\t+ darwin-sendfile\n"
539142971a8SGlenn Strauss      #else
540142971a8SGlenn Strauss       "\t- darwin-sendfile\n"
541142971a8SGlenn Strauss      #endif
542142971a8SGlenn Strauss      #if defined NETWORK_WRITE_USE_SOLARIS_SENDFILEV
543142971a8SGlenn Strauss       "\t+ solaris-sendfilev\n"
544142971a8SGlenn Strauss      #else
545142971a8SGlenn Strauss       "\t- solaris-sendfilev\n"
546142971a8SGlenn Strauss      #endif
547142971a8SGlenn Strauss      #if defined NETWORK_WRITE_USE_WRITEV
548142971a8SGlenn Strauss       "\t+ writev\n"
549142971a8SGlenn Strauss      #else
550142971a8SGlenn Strauss       "\t- writev\n"
551142971a8SGlenn Strauss      #endif
552142971a8SGlenn Strauss       "\t+ write\n"
553142971a8SGlenn Strauss      #ifdef NETWORK_WRITE_USE_MMAP
554142971a8SGlenn Strauss       "\t+ mmap support\n"
555142971a8SGlenn Strauss      #else
556142971a8SGlenn Strauss       "\t- mmap support\n"
557142971a8SGlenn Strauss      #endif
558142971a8SGlenn Strauss       ;
559142971a8SGlenn Strauss }
560