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