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