sendrecv.c revision 251875
1251875Speter/* Licensed to the Apache Software Foundation (ASF) under one or more
2251875Speter * contributor license agreements.  See the NOTICE file distributed with
3251875Speter * this work for additional information regarding copyright ownership.
4251875Speter * The ASF licenses this file to You under the Apache License, Version 2.0
5251875Speter * (the "License"); you may not use this file except in compliance with
6251875Speter * the License.  You may obtain a copy of the License at
7251875Speter *
8251875Speter *     http://www.apache.org/licenses/LICENSE-2.0
9251875Speter *
10251875Speter * Unless required by applicable law or agreed to in writing, software
11251875Speter * distributed under the License is distributed on an "AS IS" BASIS,
12251875Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13251875Speter * See the License for the specific language governing permissions and
14251875Speter * limitations under the License.
15251875Speter */
16251875Speter
17251875Speter#include "apr_arch_networkio.h"
18251875Speter#include "apr_support.h"
19251875Speter
20251875Speter#if APR_HAS_SENDFILE
21251875Speter/* This file is needed to allow us access to the apr_file_t internals. */
22251875Speter#include "apr_arch_file_io.h"
23251875Speter#endif /* APR_HAS_SENDFILE */
24251875Speter
25251875Speter/* osreldate.h is only needed on FreeBSD for sendfile detection */
26251875Speter#if defined(__FreeBSD__)
27251875Speter#include <osreldate.h>
28251875Speter#endif
29251875Speter
30251875Speterapr_status_t apr_socket_send(apr_socket_t *sock, const char *buf,
31251875Speter                             apr_size_t *len)
32251875Speter{
33251875Speter    apr_ssize_t rv;
34251875Speter
35251875Speter    if (sock->options & APR_INCOMPLETE_WRITE) {
36251875Speter        sock->options &= ~APR_INCOMPLETE_WRITE;
37251875Speter        goto do_select;
38251875Speter    }
39251875Speter
40251875Speter    do {
41251875Speter        rv = write(sock->socketdes, buf, (*len));
42251875Speter    } while (rv == -1 && errno == EINTR);
43251875Speter
44251875Speter    while (rv == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)
45251875Speter                    && (sock->timeout > 0)) {
46251875Speter        apr_status_t arv;
47251875Speterdo_select:
48251875Speter        arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
49251875Speter        if (arv != APR_SUCCESS) {
50251875Speter            *len = 0;
51251875Speter            return arv;
52251875Speter        }
53251875Speter        else {
54251875Speter            do {
55251875Speter                rv = write(sock->socketdes, buf, (*len));
56251875Speter            } while (rv == -1 && errno == EINTR);
57251875Speter        }
58251875Speter    }
59251875Speter    if (rv == -1) {
60251875Speter        *len = 0;
61251875Speter        return errno;
62251875Speter    }
63251875Speter    if ((sock->timeout > 0) && (rv < *len)) {
64251875Speter        sock->options |= APR_INCOMPLETE_WRITE;
65251875Speter    }
66251875Speter    (*len) = rv;
67251875Speter    return APR_SUCCESS;
68251875Speter}
69251875Speter
70251875Speterapr_status_t apr_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len)
71251875Speter{
72251875Speter    apr_ssize_t rv;
73251875Speter    apr_status_t arv;
74251875Speter
75251875Speter    if (sock->options & APR_INCOMPLETE_READ) {
76251875Speter        sock->options &= ~APR_INCOMPLETE_READ;
77251875Speter        goto do_select;
78251875Speter    }
79251875Speter
80251875Speter    do {
81251875Speter        rv = read(sock->socketdes, buf, (*len));
82251875Speter    } while (rv == -1 && errno == EINTR);
83251875Speter
84251875Speter    while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
85251875Speter                      && (sock->timeout > 0)) {
86251875Speterdo_select:
87251875Speter        arv = apr_wait_for_io_or_timeout(NULL, sock, 1);
88251875Speter        if (arv != APR_SUCCESS) {
89251875Speter            *len = 0;
90251875Speter            return arv;
91251875Speter        }
92251875Speter        else {
93251875Speter            do {
94251875Speter                rv = read(sock->socketdes, buf, (*len));
95251875Speter            } while (rv == -1 && errno == EINTR);
96251875Speter        }
97251875Speter    }
98251875Speter    if (rv == -1) {
99251875Speter        (*len) = 0;
100251875Speter        return errno;
101251875Speter    }
102251875Speter    if ((sock->timeout > 0) && (rv < *len)) {
103251875Speter        sock->options |= APR_INCOMPLETE_READ;
104251875Speter    }
105251875Speter    (*len) = rv;
106251875Speter    if (rv == 0) {
107251875Speter        return APR_EOF;
108251875Speter    }
109251875Speter    return APR_SUCCESS;
110251875Speter}
111251875Speter
112251875Speterapr_status_t apr_socket_sendto(apr_socket_t *sock, apr_sockaddr_t *where,
113251875Speter                               apr_int32_t flags, const char *buf,
114251875Speter                               apr_size_t *len)
115251875Speter{
116251875Speter    apr_ssize_t rv;
117251875Speter
118251875Speter    do {
119251875Speter        rv = sendto(sock->socketdes, buf, (*len), flags,
120251875Speter                    (const struct sockaddr*)&where->sa,
121251875Speter                    where->salen);
122251875Speter    } while (rv == -1 && errno == EINTR);
123251875Speter
124251875Speter    while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
125251875Speter                      && (sock->timeout > 0)) {
126251875Speter        apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
127251875Speter        if (arv != APR_SUCCESS) {
128251875Speter            *len = 0;
129251875Speter            return arv;
130251875Speter        } else {
131251875Speter            do {
132251875Speter                rv = sendto(sock->socketdes, buf, (*len), flags,
133251875Speter                            (const struct sockaddr*)&where->sa,
134251875Speter                            where->salen);
135251875Speter            } while (rv == -1 && errno == EINTR);
136251875Speter        }
137251875Speter    }
138251875Speter    if (rv == -1) {
139251875Speter        *len = 0;
140251875Speter        return errno;
141251875Speter    }
142251875Speter    *len = rv;
143251875Speter    return APR_SUCCESS;
144251875Speter}
145251875Speter
146251875Speterapr_status_t apr_socket_recvfrom(apr_sockaddr_t *from, apr_socket_t *sock,
147251875Speter                                 apr_int32_t flags, char *buf,
148251875Speter                                 apr_size_t *len)
149251875Speter{
150251875Speter    apr_ssize_t rv;
151251875Speter
152251875Speter    from->salen = sizeof(from->sa);
153251875Speter
154251875Speter    do {
155251875Speter        rv = recvfrom(sock->socketdes, buf, (*len), flags,
156251875Speter                      (struct sockaddr*)&from->sa, &from->salen);
157251875Speter    } while (rv == -1 && errno == EINTR);
158251875Speter
159251875Speter    while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
160251875Speter                      && (sock->timeout > 0)) {
161251875Speter        apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 1);
162251875Speter        if (arv != APR_SUCCESS) {
163251875Speter            *len = 0;
164251875Speter            return arv;
165251875Speter        } else {
166251875Speter            do {
167251875Speter                rv = recvfrom(sock->socketdes, buf, (*len), flags,
168251875Speter                              (struct sockaddr*)&from->sa, &from->salen);
169251875Speter            } while (rv == -1 && errno == EINTR);
170251875Speter        }
171251875Speter    }
172251875Speter    if (rv == -1) {
173251875Speter        (*len) = 0;
174251875Speter        return errno;
175251875Speter    }
176251875Speter
177251875Speter    apr_sockaddr_vars_set(from, from->sa.sin.sin_family, ntohs(from->sa.sin.sin_port));
178251875Speter
179251875Speter    (*len) = rv;
180251875Speter    if (rv == 0 && sock->type == SOCK_STREAM) {
181251875Speter        return APR_EOF;
182251875Speter    }
183251875Speter
184251875Speter    return APR_SUCCESS;
185251875Speter}
186251875Speter
187251875Speterapr_status_t apr_socket_sendv(apr_socket_t * sock, const struct iovec *vec,
188251875Speter                              apr_int32_t nvec, apr_size_t *len)
189251875Speter{
190251875Speter#ifdef HAVE_WRITEV
191251875Speter    apr_ssize_t rv;
192251875Speter    apr_size_t requested_len = 0;
193251875Speter    apr_int32_t i;
194251875Speter
195251875Speter    for (i = 0; i < nvec; i++) {
196251875Speter        requested_len += vec[i].iov_len;
197251875Speter    }
198251875Speter
199251875Speter    if (sock->options & APR_INCOMPLETE_WRITE) {
200251875Speter        sock->options &= ~APR_INCOMPLETE_WRITE;
201251875Speter        goto do_select;
202251875Speter    }
203251875Speter
204251875Speter    do {
205251875Speter        rv = writev(sock->socketdes, vec, nvec);
206251875Speter    } while (rv == -1 && errno == EINTR);
207251875Speter
208251875Speter    while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
209251875Speter                      && (sock->timeout > 0)) {
210251875Speter        apr_status_t arv;
211251875Speterdo_select:
212251875Speter        arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
213251875Speter        if (arv != APR_SUCCESS) {
214251875Speter            *len = 0;
215251875Speter            return arv;
216251875Speter        }
217251875Speter        else {
218251875Speter            do {
219251875Speter                rv = writev(sock->socketdes, vec, nvec);
220251875Speter            } while (rv == -1 && errno == EINTR);
221251875Speter        }
222251875Speter    }
223251875Speter    if (rv == -1) {
224251875Speter        *len = 0;
225251875Speter        return errno;
226251875Speter    }
227251875Speter    if ((sock->timeout > 0) && (rv < requested_len)) {
228251875Speter        sock->options |= APR_INCOMPLETE_WRITE;
229251875Speter    }
230251875Speter    (*len) = rv;
231251875Speter    return APR_SUCCESS;
232251875Speter#else
233251875Speter    *len = vec[0].iov_len;
234251875Speter    return apr_socket_send(sock, vec[0].iov_base, len);
235251875Speter#endif
236251875Speter}
237251875Speter
238251875Speter#if APR_HAS_SENDFILE
239251875Speter
240251875Speter/* TODO: Verify that all platforms handle the fd the same way,
241251875Speter * i.e. that they don't move the file pointer.
242251875Speter */
243251875Speter/* TODO: what should flags be?  int_32? */
244251875Speter
245251875Speter/* Define a structure to pass in when we have a NULL header value */
246251875Speterstatic apr_hdtr_t no_hdtr;
247251875Speter
248251875Speter#if defined(__linux__) && defined(HAVE_WRITEV)
249251875Speter
250251875Speterapr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file,
251251875Speter                                 apr_hdtr_t *hdtr, apr_off_t *offset,
252251875Speter                                 apr_size_t *len, apr_int32_t flags)
253251875Speter{
254251875Speter    int rv, nbytes = 0, total_hdrbytes, i;
255251875Speter    apr_status_t arv;
256251875Speter
257251875Speter#if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILE64)
258251875Speter    apr_off_t off = *offset;
259251875Speter#define sendfile sendfile64
260251875Speter
261251875Speter#elif APR_HAS_LARGE_FILES && SIZEOF_OFF_T == 4
262251875Speter    /* 64-bit apr_off_t but no sendfile64(): fail if trying to send
263251875Speter     * past the 2Gb limit. */
264251875Speter    off_t off;
265251875Speter
266251875Speter    if ((apr_int64_t)*offset + *len > INT_MAX) {
267251875Speter        return EINVAL;
268251875Speter    }
269251875Speter
270251875Speter    off = *offset;
271251875Speter
272251875Speter#else
273251875Speter    off_t off = *offset;
274251875Speter
275251875Speter    /* Multiple reports have shown sendfile failing with EINVAL if
276251875Speter     * passed a >=2Gb count value on some 64-bit kernels.  It won't
277251875Speter     * noticably hurt performance to limit each call to <2Gb at a
278251875Speter     * time, so avoid that issue here: */
279251875Speter    if (sizeof(off_t) == 8 && *len > INT_MAX) {
280251875Speter        *len = INT_MAX;
281251875Speter    }
282251875Speter#endif
283251875Speter
284251875Speter    if (!hdtr) {
285251875Speter        hdtr = &no_hdtr;
286251875Speter    }
287251875Speter
288251875Speter    /* Ignore flags for now. */
289251875Speter    flags = 0;
290251875Speter
291251875Speter    if (hdtr->numheaders > 0) {
292251875Speter        apr_size_t hdrbytes;
293251875Speter
294251875Speter        /* cork before writing headers */
295251875Speter        rv = apr_socket_opt_set(sock, APR_TCP_NOPUSH, 1);
296251875Speter        if (rv != APR_SUCCESS) {
297251875Speter            return rv;
298251875Speter        }
299251875Speter
300251875Speter        /* Now write the headers */
301251875Speter        arv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders,
302251875Speter                               &hdrbytes);
303251875Speter        if (arv != APR_SUCCESS) {
304251875Speter            *len = 0;
305251875Speter            return errno;
306251875Speter        }
307251875Speter        nbytes += hdrbytes;
308251875Speter
309251875Speter        /* If this was a partial write and we aren't doing timeouts,
310251875Speter         * return now with the partial byte count; this is a non-blocking
311251875Speter         * socket.
312251875Speter         */
313251875Speter        total_hdrbytes = 0;
314251875Speter        for (i = 0; i < hdtr->numheaders; i++) {
315251875Speter            total_hdrbytes += hdtr->headers[i].iov_len;
316251875Speter        }
317251875Speter        if (hdrbytes < total_hdrbytes) {
318251875Speter            *len = hdrbytes;
319251875Speter            return apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
320251875Speter        }
321251875Speter    }
322251875Speter
323251875Speter    if (sock->options & APR_INCOMPLETE_WRITE) {
324251875Speter        sock->options &= ~APR_INCOMPLETE_WRITE;
325251875Speter        goto do_select;
326251875Speter    }
327251875Speter
328251875Speter    do {
329251875Speter        rv = sendfile(sock->socketdes,    /* socket */
330251875Speter                      file->filedes, /* open file descriptor of the file to be sent */
331251875Speter                      &off,    /* where in the file to start */
332251875Speter                      *len);   /* number of bytes to send */
333251875Speter    } while (rv == -1 && errno == EINTR);
334251875Speter
335251875Speter    while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
336251875Speter                      && (sock->timeout > 0)) {
337251875Speterdo_select:
338251875Speter        arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
339251875Speter        if (arv != APR_SUCCESS) {
340251875Speter            *len = 0;
341251875Speter            return arv;
342251875Speter        }
343251875Speter        else {
344251875Speter            do {
345251875Speter                rv = sendfile(sock->socketdes,    /* socket */
346251875Speter                              file->filedes, /* open file descriptor of the file to be sent */
347251875Speter                              &off,    /* where in the file to start */
348251875Speter                              *len);    /* number of bytes to send */
349251875Speter            } while (rv == -1 && errno == EINTR);
350251875Speter        }
351251875Speter    }
352251875Speter
353251875Speter    if (rv == -1) {
354251875Speter        *len = nbytes;
355251875Speter        rv = errno;
356251875Speter        apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
357251875Speter        return rv;
358251875Speter    }
359251875Speter
360251875Speter    nbytes += rv;
361251875Speter
362251875Speter    if (rv < *len) {
363251875Speter        *len = nbytes;
364251875Speter        arv = apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
365251875Speter        if (rv > 0) {
366251875Speter
367251875Speter            /* If this was a partial write, return now with the
368251875Speter             * partial byte count;  this is a non-blocking socket.
369251875Speter             */
370251875Speter
371251875Speter            if (sock->timeout > 0) {
372251875Speter                sock->options |= APR_INCOMPLETE_WRITE;
373251875Speter            }
374251875Speter            return arv;
375251875Speter        }
376251875Speter        else {
377251875Speter            /* If the file got smaller mid-request, eventually the offset
378251875Speter             * becomes equal to the new file size and the kernel returns 0.
379251875Speter             * Make this an error so the caller knows to log something and
380251875Speter             * exit.
381251875Speter             */
382251875Speter            return APR_EOF;
383251875Speter        }
384251875Speter    }
385251875Speter
386251875Speter    /* Now write the footers */
387251875Speter    if (hdtr->numtrailers > 0) {
388251875Speter        apr_size_t trbytes;
389251875Speter        arv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers,
390251875Speter                               &trbytes);
391251875Speter        nbytes += trbytes;
392251875Speter        if (arv != APR_SUCCESS) {
393251875Speter            *len = nbytes;
394251875Speter            rv = errno;
395251875Speter            apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
396251875Speter            return rv;
397251875Speter        }
398251875Speter    }
399251875Speter
400251875Speter    apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
401251875Speter
402251875Speter    (*len) = nbytes;
403251875Speter    return rv < 0 ? errno : APR_SUCCESS;
404251875Speter}
405251875Speter
406251875Speter#elif defined(DARWIN)
407251875Speter
408251875Speter/* OS/X Release 10.5 or greater */
409251875Speterapr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file,
410251875Speter                                 apr_hdtr_t *hdtr, apr_off_t *offset,
411251875Speter                                 apr_size_t *len, apr_int32_t flags)
412251875Speter{
413251875Speter    apr_off_t nbytes = 0;
414251875Speter    apr_off_t bytes_to_send = *len;
415251875Speter    apr_off_t bytes_sent = 0;
416251875Speter    apr_status_t arv;
417251875Speter    int rv = 0;
418251875Speter
419251875Speter    /* Ignore flags for now. */
420251875Speter    flags = 0;
421251875Speter
422251875Speter    if (!hdtr) {
423251875Speter        hdtr = &no_hdtr;
424251875Speter    }
425251875Speter
426251875Speter    /* OS X can send the headers/footers as part of the system call,
427251875Speter     * but how it counts bytes isn't documented properly. We use
428251875Speter     * apr_socket_sendv() instead.
429251875Speter     */
430251875Speter     if (hdtr->numheaders > 0) {
431251875Speter        apr_size_t hbytes;
432251875Speter        int i;
433251875Speter
434251875Speter        /* Now write the headers */
435251875Speter        arv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders,
436251875Speter                               &hbytes);
437251875Speter        if (arv != APR_SUCCESS) {
438251875Speter            *len = 0;
439251875Speter            return errno;
440251875Speter        }
441251875Speter        bytes_sent = hbytes;
442251875Speter
443251875Speter        hbytes = 0;
444251875Speter        for (i = 0; i < hdtr->numheaders; i++) {
445251875Speter            hbytes += hdtr->headers[i].iov_len;
446251875Speter        }
447251875Speter        if (bytes_sent < hbytes) {
448251875Speter            *len = bytes_sent;
449251875Speter            return APR_SUCCESS;
450251875Speter        }
451251875Speter    }
452251875Speter
453251875Speter    do {
454251875Speter        if (!bytes_to_send) {
455251875Speter            break;
456251875Speter        }
457251875Speter        if (sock->options & APR_INCOMPLETE_WRITE) {
458251875Speter            apr_status_t arv;
459251875Speter            sock->options &= ~APR_INCOMPLETE_WRITE;
460251875Speter            arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
461251875Speter            if (arv != APR_SUCCESS) {
462251875Speter                *len = 0;
463251875Speter                return arv;
464251875Speter            }
465251875Speter        }
466251875Speter
467251875Speter        nbytes = bytes_to_send;
468251875Speter        rv = sendfile(file->filedes, /* file to be sent */
469251875Speter                      sock->socketdes, /* socket */
470251875Speter                      *offset,       /* where in the file to start */
471251875Speter                      &nbytes,       /* number of bytes to write/written */
472251875Speter                      NULL,          /* Headers/footers */
473251875Speter                      flags);        /* undefined, set to 0 */
474251875Speter
475251875Speter        if (rv == -1) {
476251875Speter            if (errno == EAGAIN) {
477251875Speter                if (sock->timeout > 0) {
478251875Speter                    sock->options |= APR_INCOMPLETE_WRITE;
479251875Speter                }
480251875Speter                /* BSD's sendfile can return -1/EAGAIN even if it
481251875Speter                 * sent bytes.  Sanitize the result so we get normal EAGAIN
482251875Speter                 * semantics w.r.t. bytes sent.
483251875Speter                 */
484251875Speter                if (nbytes) {
485251875Speter                    bytes_sent += nbytes;
486251875Speter                    /* normal exit for a big file & non-blocking io */
487251875Speter                    (*len) = bytes_sent;
488251875Speter                    return APR_SUCCESS;
489251875Speter                }
490251875Speter            }
491251875Speter        }
492251875Speter        else {       /* rv == 0 (or the kernel is broken) */
493251875Speter            bytes_sent += nbytes;
494251875Speter            if (nbytes == 0) {
495251875Speter                /* Most likely the file got smaller after the stat.
496251875Speter                 * Return an error so the caller can do the Right Thing.
497251875Speter                 */
498251875Speter                (*len) = bytes_sent;
499251875Speter                return APR_EOF;
500251875Speter            }
501251875Speter        }
502251875Speter    } while (rv == -1 && (errno == EINTR || errno == EAGAIN));
503251875Speter
504251875Speter    /* Now write the footers */
505251875Speter    if (hdtr->numtrailers > 0) {
506251875Speter        apr_size_t tbytes;
507251875Speter        arv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers,
508251875Speter                               &tbytes);
509251875Speter        bytes_sent += tbytes;
510251875Speter        if (arv != APR_SUCCESS) {
511251875Speter            *len = bytes_sent;
512251875Speter            rv = errno;
513251875Speter            return rv;
514251875Speter        }
515251875Speter    }
516251875Speter
517251875Speter    (*len) = bytes_sent;
518251875Speter    if (rv == -1) {
519251875Speter        return errno;
520251875Speter    }
521251875Speter    return APR_SUCCESS;
522251875Speter}
523251875Speter
524251875Speter#elif defined(__FreeBSD__) || defined(__DragonFly__)
525251875Speter
526251875Speter/* Release 3.1 or greater */
527251875Speterapr_status_t apr_socket_sendfile(apr_socket_t * sock, apr_file_t * file,
528251875Speter                                 apr_hdtr_t * hdtr, apr_off_t * offset,
529251875Speter                                 apr_size_t * len, apr_int32_t flags)
530251875Speter{
531251875Speter    off_t nbytes = 0;
532251875Speter    int rv;
533251875Speter#if defined(__FreeBSD_version) && __FreeBSD_version < 460001
534251875Speter    int i;
535251875Speter#endif
536251875Speter    struct sf_hdtr headerstruct;
537251875Speter    apr_size_t bytes_to_send = *len;
538251875Speter
539251875Speter    /* Ignore flags for now. */
540251875Speter    flags = 0;
541251875Speter
542251875Speter    if (!hdtr) {
543251875Speter        hdtr = &no_hdtr;
544251875Speter    }
545251875Speter
546251875Speter#if defined(__FreeBSD_version) && __FreeBSD_version < 460001
547251875Speter    else if (hdtr->numheaders) {
548251875Speter
549251875Speter        /* On early versions of FreeBSD sendfile, the number of bytes to send
550251875Speter         * must include the length of the headers.  Don't look at the man page
551251875Speter         * for this :(  Instead, look at the the logic in
552251875Speter         * src/sys/kern/uipc_syscalls::sendfile().
553251875Speter         *
554251875Speter         * This was fixed in the middle of 4.6-STABLE
555251875Speter         */
556251875Speter        for (i = 0; i < hdtr->numheaders; i++) {
557251875Speter            bytes_to_send += hdtr->headers[i].iov_len;
558251875Speter        }
559251875Speter    }
560251875Speter#endif
561251875Speter
562251875Speter    headerstruct.headers = hdtr->headers;
563251875Speter    headerstruct.hdr_cnt = hdtr->numheaders;
564251875Speter    headerstruct.trailers = hdtr->trailers;
565251875Speter    headerstruct.trl_cnt = hdtr->numtrailers;
566251875Speter
567251875Speter    /* FreeBSD can send the headers/footers as part of the system call */
568251875Speter    do {
569251875Speter        if (sock->options & APR_INCOMPLETE_WRITE) {
570251875Speter            apr_status_t arv;
571251875Speter            sock->options &= ~APR_INCOMPLETE_WRITE;
572251875Speter            arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
573251875Speter            if (arv != APR_SUCCESS) {
574251875Speter                *len = 0;
575251875Speter                return arv;
576251875Speter            }
577251875Speter        }
578251875Speter        if (bytes_to_send) {
579251875Speter            /* We won't dare call sendfile() if we don't have
580251875Speter             * header or file bytes to send because bytes_to_send == 0
581251875Speter             * means send the whole file.
582251875Speter             */
583251875Speter            rv = sendfile(file->filedes, /* file to be sent */
584251875Speter                          sock->socketdes, /* socket */
585251875Speter                          *offset,       /* where in the file to start */
586251875Speter                          bytes_to_send, /* number of bytes to send */
587251875Speter                          &headerstruct, /* Headers/footers */
588251875Speter                          &nbytes,       /* number of bytes written */
589251875Speter                          flags);        /* undefined, set to 0 */
590251875Speter
591251875Speter            if (rv == -1) {
592251875Speter                if (errno == EAGAIN) {
593251875Speter                    if (sock->timeout > 0) {
594251875Speter                        sock->options |= APR_INCOMPLETE_WRITE;
595251875Speter                    }
596251875Speter                    /* FreeBSD's sendfile can return -1/EAGAIN even if it
597251875Speter                     * sent bytes.  Sanitize the result so we get normal EAGAIN
598251875Speter                     * semantics w.r.t. bytes sent.
599251875Speter                     */
600251875Speter                    if (nbytes) {
601251875Speter                        /* normal exit for a big file & non-blocking io */
602251875Speter                        (*len) = nbytes;
603251875Speter                        return APR_SUCCESS;
604251875Speter                    }
605251875Speter                }
606251875Speter            }
607251875Speter            else {       /* rv == 0 (or the kernel is broken) */
608251875Speter                if (nbytes == 0) {
609251875Speter                    /* Most likely the file got smaller after the stat.
610251875Speter                     * Return an error so the caller can do the Right Thing.
611251875Speter                     */
612251875Speter                    (*len) = nbytes;
613251875Speter                    return APR_EOF;
614251875Speter                }
615251875Speter            }
616251875Speter        }
617251875Speter        else {
618251875Speter            /* just trailer bytes... use writev()
619251875Speter             */
620251875Speter            rv = writev(sock->socketdes,
621251875Speter                        hdtr->trailers,
622251875Speter                        hdtr->numtrailers);
623251875Speter            if (rv > 0) {
624251875Speter                nbytes = rv;
625251875Speter                rv = 0;
626251875Speter            }
627251875Speter            else {
628251875Speter                nbytes = 0;
629251875Speter            }
630251875Speter        }
631251875Speter        if ((rv == -1) && (errno == EAGAIN)
632251875Speter                       && (sock->timeout > 0)) {
633251875Speter            apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
634251875Speter            if (arv != APR_SUCCESS) {
635251875Speter                *len = 0;
636251875Speter                return arv;
637251875Speter            }
638251875Speter        }
639251875Speter    } while (rv == -1 && (errno == EINTR || errno == EAGAIN));
640251875Speter
641251875Speter    (*len) = nbytes;
642251875Speter    if (rv == -1) {
643251875Speter        return errno;
644251875Speter    }
645251875Speter    return APR_SUCCESS;
646251875Speter}
647251875Speter
648251875Speter#elif defined(__hpux) || defined(__hpux__)
649251875Speter
650251875Speter/* HP cc in ANSI mode defines __hpux; gcc defines __hpux__ */
651251875Speter
652251875Speter/* HP-UX Version 10.30 or greater
653251875Speter * (no worries, because we only get here if autoconfiguration found sendfile)
654251875Speter */
655251875Speter
656251875Speter/* ssize_t sendfile(int s, int fd, off_t offset, size_t nbytes,
657251875Speter *                  const struct iovec *hdtrl, int flags);
658251875Speter *
659251875Speter * nbytes is the number of bytes to send just from the file; as with FreeBSD,
660251875Speter * if nbytes == 0, the rest of the file (from offset) is sent
661251875Speter */
662251875Speter
663251875Speterapr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file,
664251875Speter                                 apr_hdtr_t *hdtr, apr_off_t *offset,
665251875Speter                                 apr_size_t *len, apr_int32_t flags)
666251875Speter{
667251875Speter    int i;
668251875Speter    apr_ssize_t rc;
669251875Speter    apr_size_t nbytes = *len, headerlen, trailerlen;
670251875Speter    struct iovec hdtrarray[2];
671251875Speter    char *headerbuf, *trailerbuf;
672251875Speter
673251875Speter#if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILE64)
674251875Speter    /* later HP-UXes have a sendfile64() */
675251875Speter#define sendfile sendfile64
676251875Speter    apr_off_t off = *offset;
677251875Speter
678251875Speter#elif APR_HAS_LARGE_FILES && SIZEOF_OFF_T == 4
679251875Speter    /* HP-UX 11.00 doesn't have a sendfile64(): fail if trying to send
680251875Speter     * past the 2Gb limit */
681251875Speter    off_t off;
682251875Speter
683251875Speter    if ((apr_int64_t)*offset + *len > INT_MAX) {
684251875Speter        return EINVAL;
685251875Speter    }
686251875Speter    off = *offset;
687251875Speter#else
688251875Speter    apr_off_t off = *offset;
689251875Speter#endif
690251875Speter
691251875Speter    if (!hdtr) {
692251875Speter        hdtr = &no_hdtr;
693251875Speter    }
694251875Speter
695251875Speter    /* Ignore flags for now. */
696251875Speter    flags = 0;
697251875Speter
698251875Speter    /* HP-UX can only send one header iovec and one footer iovec; try to
699251875Speter     * only allocate storage to combine input iovecs when we really have to
700251875Speter     */
701251875Speter
702251875Speter    switch(hdtr->numheaders) {
703251875Speter    case 0:
704251875Speter        hdtrarray[0].iov_base = NULL;
705251875Speter        hdtrarray[0].iov_len = 0;
706251875Speter        break;
707251875Speter    case 1:
708251875Speter        hdtrarray[0] = hdtr->headers[0];
709251875Speter        break;
710251875Speter    default:
711251875Speter        headerlen = 0;
712251875Speter        for (i = 0; i < hdtr->numheaders; i++) {
713251875Speter            headerlen += hdtr->headers[i].iov_len;
714251875Speter        }
715251875Speter
716251875Speter        /* XXX:  BUHHH? wow, what a memory leak! */
717251875Speter        headerbuf = hdtrarray[0].iov_base = apr_palloc(sock->pool, headerlen);
718251875Speter        hdtrarray[0].iov_len = headerlen;
719251875Speter
720251875Speter        for (i = 0; i < hdtr->numheaders; i++) {
721251875Speter            memcpy(headerbuf, hdtr->headers[i].iov_base,
722251875Speter                   hdtr->headers[i].iov_len);
723251875Speter            headerbuf += hdtr->headers[i].iov_len;
724251875Speter        }
725251875Speter    }
726251875Speter
727251875Speter    switch(hdtr->numtrailers) {
728251875Speter    case 0:
729251875Speter        hdtrarray[1].iov_base = NULL;
730251875Speter        hdtrarray[1].iov_len = 0;
731251875Speter        break;
732251875Speter    case 1:
733251875Speter        hdtrarray[1] = hdtr->trailers[0];
734251875Speter        break;
735251875Speter    default:
736251875Speter        trailerlen = 0;
737251875Speter        for (i = 0; i < hdtr->numtrailers; i++) {
738251875Speter            trailerlen += hdtr->trailers[i].iov_len;
739251875Speter        }
740251875Speter
741251875Speter        /* XXX:  BUHHH? wow, what a memory leak! */
742251875Speter        trailerbuf = hdtrarray[1].iov_base = apr_palloc(sock->pool, trailerlen);
743251875Speter        hdtrarray[1].iov_len = trailerlen;
744251875Speter
745251875Speter        for (i = 0; i < hdtr->numtrailers; i++) {
746251875Speter            memcpy(trailerbuf, hdtr->trailers[i].iov_base,
747251875Speter                   hdtr->trailers[i].iov_len);
748251875Speter            trailerbuf += hdtr->trailers[i].iov_len;
749251875Speter        }
750251875Speter    }
751251875Speter
752251875Speter    do {
753251875Speter        if (nbytes) {       /* any bytes to send from the file? */
754251875Speter            rc = sendfile(sock->socketdes,      /* socket  */
755251875Speter                          file->filedes,        /* file descriptor to send */
756251875Speter                          off,                  /* where in the file to start */
757251875Speter                          nbytes,               /* number of bytes to send from file */
758251875Speter                          hdtrarray,            /* Headers/footers */
759251875Speter                          flags);               /* undefined, set to 0 */
760251875Speter        }
761251875Speter        else {              /* we can't call sendfile() with no bytes to send from the file */
762251875Speter            rc = writev(sock->socketdes, hdtrarray, 2);
763251875Speter        }
764251875Speter    } while (rc == -1 && errno == EINTR);
765251875Speter
766251875Speter    while ((rc == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
767251875Speter                      && (sock->timeout > 0)) {
768251875Speter        apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
769251875Speter
770251875Speter        if (arv != APR_SUCCESS) {
771251875Speter            *len = 0;
772251875Speter            return arv;
773251875Speter        }
774251875Speter        else {
775251875Speter            do {
776251875Speter                if (nbytes) {
777251875Speter                    rc = sendfile(sock->socketdes,    /* socket  */
778251875Speter                                  file->filedes,      /* file descriptor to send */
779251875Speter                                  off,                /* where in the file to start */
780251875Speter                                  nbytes,             /* number of bytes to send from file */
781251875Speter                                  hdtrarray,          /* Headers/footers */
782251875Speter                                  flags);             /* undefined, set to 0 */
783251875Speter                }
784251875Speter                else {      /* we can't call sendfile() with no bytes to send from the file */
785251875Speter                    rc = writev(sock->socketdes, hdtrarray, 2);
786251875Speter                }
787251875Speter            } while (rc == -1 && errno == EINTR);
788251875Speter        }
789251875Speter    }
790251875Speter
791251875Speter    if (rc == -1) {
792251875Speter        *len = 0;
793251875Speter        return errno;
794251875Speter    }
795251875Speter
796251875Speter    /* Set len to the number of bytes written */
797251875Speter    *len = rc;
798251875Speter    return APR_SUCCESS;
799251875Speter}
800251875Speter#elif defined(_AIX) || defined(__MVS__)
801251875Speter/* AIX and OS/390 have the same send_file() interface.
802251875Speter *
803251875Speter * subtle differences:
804251875Speter *   AIX doesn't update the file ptr but OS/390 does
805251875Speter *
806251875Speter * availability (correctly determined by autoconf):
807251875Speter *
808251875Speter * AIX -  version 4.3.2 with APAR IX85388, or version 4.3.3 and above
809251875Speter * OS/390 - V2R7 and above
810251875Speter */
811251875Speterapr_status_t apr_socket_sendfile(apr_socket_t * sock, apr_file_t * file,
812251875Speter                                 apr_hdtr_t * hdtr, apr_off_t * offset,
813251875Speter                                 apr_size_t * len, apr_int32_t flags)
814251875Speter{
815251875Speter    int i, ptr, rv = 0;
816251875Speter    void * hbuf=NULL, * tbuf=NULL;
817251875Speter    apr_status_t arv;
818251875Speter    struct sf_parms parms;
819251875Speter
820251875Speter    if (!hdtr) {
821251875Speter        hdtr = &no_hdtr;
822251875Speter    }
823251875Speter
824251875Speter    /* Ignore flags for now. */
825251875Speter    flags = 0;
826251875Speter
827251875Speter    /* word to the wise: by default, AIX stores files sent by send_file()
828251875Speter     * in the network buffer cache...  there are supposedly scenarios
829251875Speter     * where the most recent copy of the file won't be sent, but I can't
830251875Speter     * recreate the potential problem, perhaps because of the way we
831251875Speter     * use send_file()...  if you suspect such a problem, try turning
832251875Speter     * on the SF_SYNC_CACHE flag
833251875Speter     */
834251875Speter
835251875Speter    /* AIX can also send the headers/footers as part of the system call */
836251875Speter    parms.header_length = 0;
837251875Speter    if (hdtr && hdtr->numheaders) {
838251875Speter        if (hdtr->numheaders == 1) {
839251875Speter            parms.header_data = hdtr->headers[0].iov_base;
840251875Speter            parms.header_length = hdtr->headers[0].iov_len;
841251875Speter        }
842251875Speter        else {
843251875Speter            for (i = 0; i < hdtr->numheaders; i++) {
844251875Speter                parms.header_length += hdtr->headers[i].iov_len;
845251875Speter            }
846251875Speter#if 0
847251875Speter            /* Keepalives make apr_palloc a bad idea */
848251875Speter            hbuf = malloc(parms.header_length);
849251875Speter#else
850251875Speter            /* but headers are small, so maybe we can hold on to the
851251875Speter             * memory for the life of the socket...
852251875Speter             */
853251875Speter            hbuf = apr_palloc(sock->pool, parms.header_length);
854251875Speter#endif
855251875Speter            ptr = 0;
856251875Speter            for (i = 0; i < hdtr->numheaders; i++) {
857251875Speter                memcpy((char *)hbuf + ptr, hdtr->headers[i].iov_base,
858251875Speter                       hdtr->headers[i].iov_len);
859251875Speter                ptr += hdtr->headers[i].iov_len;
860251875Speter            }
861251875Speter            parms.header_data = hbuf;
862251875Speter        }
863251875Speter    }
864251875Speter    else parms.header_data = NULL;
865251875Speter    parms.trailer_length = 0;
866251875Speter    if (hdtr && hdtr->numtrailers) {
867251875Speter        if (hdtr->numtrailers == 1) {
868251875Speter            parms.trailer_data = hdtr->trailers[0].iov_base;
869251875Speter            parms.trailer_length = hdtr->trailers[0].iov_len;
870251875Speter        }
871251875Speter        else {
872251875Speter            for (i = 0; i < hdtr->numtrailers; i++) {
873251875Speter                parms.trailer_length += hdtr->trailers[i].iov_len;
874251875Speter            }
875251875Speter#if 0
876251875Speter            /* Keepalives make apr_palloc a bad idea */
877251875Speter            tbuf = malloc(parms.trailer_length);
878251875Speter#else
879251875Speter            tbuf = apr_palloc(sock->pool, parms.trailer_length);
880251875Speter#endif
881251875Speter            ptr = 0;
882251875Speter            for (i = 0; i < hdtr->numtrailers; i++) {
883251875Speter                memcpy((char *)tbuf + ptr, hdtr->trailers[i].iov_base,
884251875Speter                       hdtr->trailers[i].iov_len);
885251875Speter                ptr += hdtr->trailers[i].iov_len;
886251875Speter            }
887251875Speter            parms.trailer_data = tbuf;
888251875Speter        }
889251875Speter    }
890251875Speter    else {
891251875Speter        parms.trailer_data = NULL;
892251875Speter    }
893251875Speter
894251875Speter    /* Whew! Headers and trailers set up. Now for the file data */
895251875Speter
896251875Speter    parms.file_descriptor = file->filedes;
897251875Speter    parms.file_offset = *offset;
898251875Speter    parms.file_bytes = *len;
899251875Speter
900251875Speter    /* O.K. All set up now. Let's go to town */
901251875Speter
902251875Speter    if (sock->options & APR_INCOMPLETE_WRITE) {
903251875Speter        sock->options &= ~APR_INCOMPLETE_WRITE;
904251875Speter        goto do_select;
905251875Speter    }
906251875Speter
907251875Speter    do {
908251875Speter        rv = send_file(&(sock->socketdes), /* socket */
909251875Speter                       &(parms),           /* all data */
910251875Speter                       flags);             /* flags */
911251875Speter    } while (rv == -1 && errno == EINTR);
912251875Speter
913251875Speter    while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
914251875Speter                      && (sock->timeout > 0)) {
915251875Speterdo_select:
916251875Speter        arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
917251875Speter        if (arv != APR_SUCCESS) {
918251875Speter            *len = 0;
919251875Speter            return arv;
920251875Speter        }
921251875Speter        else {
922251875Speter            do {
923251875Speter                rv = send_file(&(sock->socketdes), /* socket */
924251875Speter                               &(parms),           /* all data */
925251875Speter                               flags);             /* flags */
926251875Speter            } while (rv == -1 && errno == EINTR);
927251875Speter        }
928251875Speter    }
929251875Speter
930251875Speter    (*len) = parms.bytes_sent;
931251875Speter
932251875Speter#if 0
933251875Speter    /* Clean up after ourselves */
934251875Speter    if(hbuf) free(hbuf);
935251875Speter    if(tbuf) free(tbuf);
936251875Speter#endif
937251875Speter
938251875Speter    if (rv == -1) {
939251875Speter        return errno;
940251875Speter    }
941251875Speter
942251875Speter    if ((sock->timeout > 0)
943251875Speter          && (parms.bytes_sent
944251875Speter                < (parms.file_bytes + parms.header_length + parms.trailer_length))) {
945251875Speter        sock->options |= APR_INCOMPLETE_WRITE;
946251875Speter    }
947251875Speter
948251875Speter    return APR_SUCCESS;
949251875Speter}
950251875Speter#elif defined(__osf__) && defined (__alpha)
951251875Speter/* Tru64's sendfile implementation doesn't work, and we need to make sure that
952251875Speter * we don't use it until it is fixed.  If it is used as it is now, it will
953251875Speter * hang the machine and the only way to fix it is a reboot.
954251875Speter */
955251875Speter#elif defined(HAVE_SENDFILEV)
956251875Speter/* Solaris 8's sendfilev() interface
957251875Speter *
958251875Speter * SFV_FD_SELF refers to our memory space.
959251875Speter *
960251875Speter * Required Sparc patches (or newer):
961251875Speter * 111297-01, 108528-09, 109472-06, 109234-03, 108995-02, 111295-01, 109025-03,
962251875Speter * 108991-13
963251875Speter * Required x86 patches (or newer):
964251875Speter * 111298-01, 108529-09, 109473-06, 109235-04, 108996-02, 111296-01, 109026-04,
965251875Speter * 108992-13
966251875Speter */
967251875Speter
968251875Speter#if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILEV64)
969251875Speter#define sendfilevec_t sendfilevec64_t
970251875Speter#define sendfilev sendfilev64
971251875Speter#endif
972251875Speter
973251875Speterapr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file,
974251875Speter                                 apr_hdtr_t *hdtr, apr_off_t *offset,
975251875Speter                                 apr_size_t *len, apr_int32_t flags)
976251875Speter{
977251875Speter    apr_status_t rv, arv;
978251875Speter    apr_size_t nbytes;
979251875Speter    sendfilevec_t *sfv;
980251875Speter    int vecs, curvec, i, repeat;
981251875Speter    apr_size_t requested_len = 0;
982251875Speter
983251875Speter    if (!hdtr) {
984251875Speter        hdtr = &no_hdtr;
985251875Speter    }
986251875Speter
987251875Speter    /* Ignore flags for now. */
988251875Speter    flags = 0;
989251875Speter
990251875Speter    /* Calculate how much space we need. */
991251875Speter    vecs = hdtr->numheaders + hdtr->numtrailers + 1;
992251875Speter    sfv = apr_palloc(sock->pool, sizeof(sendfilevec_t) * vecs);
993251875Speter
994251875Speter    curvec = 0;
995251875Speter
996251875Speter    /* Add the headers */
997251875Speter    for (i = 0; i < hdtr->numheaders; i++, curvec++) {
998251875Speter        sfv[curvec].sfv_fd = SFV_FD_SELF;
999251875Speter        sfv[curvec].sfv_flag = 0;
1000251875Speter        /* Cast to unsigned long to prevent sign extension of the
1001251875Speter         * pointer value for the LFS case; see PR 39463. */
1002251875Speter        sfv[curvec].sfv_off = (unsigned long)hdtr->headers[i].iov_base;
1003251875Speter        sfv[curvec].sfv_len = hdtr->headers[i].iov_len;
1004251875Speter        requested_len += sfv[curvec].sfv_len;
1005251875Speter    }
1006251875Speter
1007251875Speter    /* If the len is 0, we skip the file. */
1008251875Speter    if (*len)
1009251875Speter    {
1010251875Speter        sfv[curvec].sfv_fd = file->filedes;
1011251875Speter        sfv[curvec].sfv_flag = 0;
1012251875Speter        sfv[curvec].sfv_off = *offset;
1013251875Speter        sfv[curvec].sfv_len = *len;
1014251875Speter        requested_len += sfv[curvec].sfv_len;
1015251875Speter
1016251875Speter        curvec++;
1017251875Speter    }
1018251875Speter    else {
1019251875Speter        vecs--;
1020251875Speter    }
1021251875Speter
1022251875Speter    /* Add the footers */
1023251875Speter    for (i = 0; i < hdtr->numtrailers; i++, curvec++) {
1024251875Speter        sfv[curvec].sfv_fd = SFV_FD_SELF;
1025251875Speter        sfv[curvec].sfv_flag = 0;
1026251875Speter        sfv[curvec].sfv_off = (unsigned long)hdtr->trailers[i].iov_base;
1027251875Speter        sfv[curvec].sfv_len = hdtr->trailers[i].iov_len;
1028251875Speter        requested_len += sfv[curvec].sfv_len;
1029251875Speter    }
1030251875Speter
1031251875Speter    /* If the last write couldn't send all the requested data,
1032251875Speter     * wait for the socket to become writable before proceeding
1033251875Speter     */
1034251875Speter    if (sock->options & APR_INCOMPLETE_WRITE) {
1035251875Speter        sock->options &= ~APR_INCOMPLETE_WRITE;
1036251875Speter        arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
1037251875Speter        if (arv != APR_SUCCESS) {
1038251875Speter            *len = 0;
1039251875Speter            return arv;
1040251875Speter        }
1041251875Speter    }
1042251875Speter
1043251875Speter    /* Actually do the sendfilev
1044251875Speter     *
1045251875Speter     * Solaris may return -1/EAGAIN even if it sent bytes on a non-block sock.
1046251875Speter     *
1047251875Speter     * If no bytes were originally sent (nbytes == 0) and we are on a TIMEOUT
1048251875Speter     * socket (which as far as the OS is concerned is a non-blocking socket),
1049251875Speter     * we want to retry after waiting for the other side to read the data (as
1050251875Speter     * determined by poll).  Once it is clear to send, we want to retry
1051251875Speter     * sending the sendfilevec_t once more.
1052251875Speter     */
1053251875Speter    arv = 0;
1054251875Speter    do {
1055251875Speter        /* Clear out the repeat */
1056251875Speter        repeat = 0;
1057251875Speter
1058251875Speter        /* socket, vecs, number of vecs, bytes written */
1059251875Speter        rv = sendfilev(sock->socketdes, sfv, vecs, &nbytes);
1060251875Speter
1061251875Speter        if (rv == -1 && errno == EAGAIN) {
1062251875Speter            if (nbytes) {
1063251875Speter                rv = 0;
1064251875Speter            }
1065251875Speter            else if (!arv && (sock->timeout > 0)) {
1066251875Speter                apr_status_t t = apr_wait_for_io_or_timeout(NULL, sock, 0);
1067251875Speter
1068251875Speter                if (t != APR_SUCCESS) {
1069251875Speter                    *len = 0;
1070251875Speter                    return t;
1071251875Speter                }
1072251875Speter
1073251875Speter                arv = 1;
1074251875Speter                repeat = 1;
1075251875Speter            }
1076251875Speter        }
1077251875Speter    } while ((rv == -1 && errno == EINTR) || repeat);
1078251875Speter
1079251875Speter    if (rv == -1) {
1080251875Speter        *len = 0;
1081251875Speter        return errno;
1082251875Speter    }
1083251875Speter
1084251875Speter    /* Update how much we sent */
1085251875Speter    *len = nbytes;
1086251875Speter
1087251875Speter    if (nbytes == 0) {
1088251875Speter        /* Most likely the file got smaller after the stat.
1089251875Speter         * Return an error so the caller can do the Right Thing.
1090251875Speter         */
1091251875Speter        return APR_EOF;
1092251875Speter    }
1093251875Speter
1094251875Speter    if ((sock->timeout > 0) && (*len < requested_len)) {
1095251875Speter        sock->options |= APR_INCOMPLETE_WRITE;
1096251875Speter    }
1097251875Speter    return APR_SUCCESS;
1098251875Speter}
1099251875Speter#else
1100251875Speter#error APR has detected sendfile on your system, but nobody has written a
1101251875Speter#error version of it for APR yet.  To get past this, either write
1102251875Speter#error apr_socket_sendfile or change APR_HAS_SENDFILE in apr.h to 0.
1103251875Speter#endif /* __linux__, __FreeBSD__, __DragonFly__, __HPUX__, _AIX, __MVS__,
1104251875Speter	  Tru64/OSF1 */
1105251875Speter
1106251875Speter#endif /* APR_HAS_SENDFILE */
1107