summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Strauss <gstrauss@gluelogic.com>2017-10-29 22:36:49 -0400
committerGlenn Strauss <gstrauss@gluelogic.com>2017-11-02 00:41:53 -0400
commit142971a80c47e319b1362bf46b85536391dce756 (patch)
tree2b2f080e35017e1feeb1b4ea9ecb570a77b1a2c9
parent9287c87dcd0ca584c735bafabbdce7fdcfae3e03 (diff)
downloadlighttpd-git-142971a80c47e319b1362bf46b85536391dce756.tar.gz
[core] consolidate backend network write handlers
network_write.[ch] isolates various write, writev, sendfile wrappers
-rw-r--r--src/CMakeLists.txt7
-rw-r--r--src/Makefile.am8
-rw-r--r--src/SConscript7
-rw-r--r--src/base.h2
-rw-r--r--src/connections.c2
-rw-r--r--src/gw_backend.c3
-rw-r--r--src/meson.build7
-rw-r--r--src/network.c76
-rw-r--r--src/network_backends.h119
-rw-r--r--src/network_darwin_sendfile.c82
-rw-r--r--src/network_freebsd_sendfile.c81
-rw-r--r--src/network_linux_sendfile.c78
-rw-r--r--src/network_solaris_sendfilev.c92
-rw-r--r--src/network_write.c814
-rw-r--r--src/network_write.h9
-rw-r--r--src/network_write_mmap.c210
-rw-r--r--src/network_write_no_mmap.c84
-rw-r--r--src/network_writev.c121
-rw-r--r--src/server.c44
19 files changed, 743 insertions, 1103 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2e7c5948..f285f836 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -590,14 +590,7 @@ add_executable(lighttpd
connections.c
inet_ntop_cache.c
network.c
- network_darwin_sendfile.c
- network_freebsd_sendfile.c
- network_linux_sendfile.c
- network_solaris_sendfilev.c
network_write.c
- network_write_mmap.c
- network_write_no_mmap.c
- network_writev.c
configfile.c
configparser.c
request.c
diff --git a/src/Makefile.am b/src/Makefile.am
index c16fc449..ca9e1710 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -82,11 +82,7 @@ common_src=base64.c buffer.c log.c \
src = server.c response.c connections.c \
inet_ntop_cache.c \
network.c \
- network_write.c network_linux_sendfile.c \
- network_write_mmap.c network_write_no_mmap.c \
- network_freebsd_sendfile.c network_writev.c \
- network_solaris_sendfilev.c \
- network_darwin_sendfile.c \
+ network_write.c \
configfile.c configparser.c proc_open.c
lib_LTLIBRARIES =
@@ -386,7 +382,7 @@ hdr = server.h base64.h buffer.h network.h log.h keyvalue.h \
fdevent.h gw_backend.h connections.h base.h base_decls.h stat_cache.h \
plugin.h \
etag.h joblist.h array.h vector.h crc32.h \
- network_backends.h configfile.h \
+ network_write.h configfile.h \
mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \
configparser.h mod_ssi_exprparser.h \
rand.h \
diff --git a/src/SConscript b/src/SConscript
index d2bfa7d7..c2ec0f54 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -77,12 +77,7 @@ common_src = Split("base64.c buffer.c log.c \
src = Split("server.c response.c connections.c \
inet_ntop_cache.c \
network.c \
- network_writev.c \
- network_write_mmap.c network_write_no_mmap.c \
- network_write.c network_linux_sendfile.c \
- network_freebsd_sendfile.c \
- network_solaris_sendfilev.c \
- network_darwin_sendfile.c \
+ network_write.c \
configfile.c configparser.c request.c proc_open.c")
lemon = env.Program('lemon', 'lemon.c', LIBS = GatherLibs(env))
diff --git a/src/base.h b/src/base.h
index cef4e1cd..51736b27 100644
--- a/src/base.h
+++ b/src/base.h
@@ -574,7 +574,7 @@ struct server {
int event_handler;
- int (* network_backend_write)(struct server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes);
+ int (* network_backend_write)(struct server *srv, int fd, chunkqueue *cq, off_t max_bytes);
handler_t (* request_env)(struct server *srv, connection *con);
uid_t uid;
diff --git a/src/connections.c b/src/connections.c
index bf271bb4..985667e9 100644
--- a/src/connections.c
+++ b/src/connections.c
@@ -1058,7 +1058,7 @@ static int connection_read_cq(server *srv, connection *con, chunkqueue *cq, off_
static int connection_write_cq(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) {
- return srv->network_backend_write(srv, con, con->fd, cq, max_bytes);
+ return srv->network_backend_write(srv, con->fd, cq, max_bytes);
}
diff --git a/src/gw_backend.c b/src/gw_backend.c
index c24332b6..91f15c5d 100644
--- a/src/gw_backend.c
+++ b/src/gw_backend.c
@@ -1816,7 +1816,6 @@ static handler_t gw_write_request(server *srv, gw_handler_ctx *hctx) {
/* fall through */
case GW_STATE_WRITE:
if (!chunkqueue_is_empty(hctx->wb)) {
- connection *con = hctx->remote_conn;
int ret;
#if 0
if (hctx->conf.debug > 1) {
@@ -1825,7 +1824,7 @@ static handler_t gw_write_request(server *srv, gw_handler_ctx *hctx) {
"), size =", chunkqueue_length(hctx->wb));
}
#endif
- ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb,
+ ret = srv->network_backend_write(srv, hctx->fd, hctx->wb,
MAX_WRITE_LIMIT);
chunkqueue_remove_finished_chunks(hctx->wb);
diff --git a/src/meson.build b/src/meson.build
index 08a4c8b0..8f696aa9 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -571,14 +571,7 @@ main_src = [
'configfile.c',
'connections.c',
'inet_ntop_cache.c',
- 'network_darwin_sendfile.c',
- 'network_freebsd_sendfile.c',
- 'network_linux_sendfile.c',
- 'network_solaris_sendfilev.c',
- 'network_write_mmap.c',
- 'network_write_no_mmap.c',
'network_write.c',
- 'network_writev.c',
'network.c',
'proc_open.c',
'request.c',
diff --git a/src/network.c b/src/network.c
index 51dce3cc..17dc3f79 100644
--- a/src/network.c
+++ b/src/network.c
@@ -8,7 +8,7 @@
#include "configfile.h"
#include "sock_addr.h"
-#include "network_backends.h"
+#include "network_write.h"
#include "sys-socket.h"
#include <sys/types.h>
@@ -385,81 +385,9 @@ int network_close(server *srv) {
return 0;
}
-typedef enum {
- NETWORK_BACKEND_UNSET,
- NETWORK_BACKEND_WRITE,
- NETWORK_BACKEND_WRITEV,
- NETWORK_BACKEND_SENDFILE,
-} network_backend_t;
-
int network_init(server *srv, int stdin_fd) {
size_t i;
- network_backend_t backend;
-
- struct nb_map {
- network_backend_t nb;
- const char *name;
- } network_backends[] = {
- /* lowest id wins */
-#if defined USE_SENDFILE
- { NETWORK_BACKEND_SENDFILE, "sendfile" },
-#endif
-#if defined USE_LINUX_SENDFILE
- { NETWORK_BACKEND_SENDFILE, "linux-sendfile" },
-#endif
-#if defined USE_FREEBSD_SENDFILE
- { NETWORK_BACKEND_SENDFILE, "freebsd-sendfile" },
-#endif
-#if defined USE_SOLARIS_SENDFILEV
- { NETWORK_BACKEND_SENDFILE, "solaris-sendfilev" },
-#endif
-#if defined USE_WRITEV
- { NETWORK_BACKEND_WRITEV, "writev" },
-#endif
- { NETWORK_BACKEND_WRITE, "write" },
- { NETWORK_BACKEND_UNSET, NULL }
- };
-
- /* get a useful default */
- backend = network_backends[0].nb;
-
- /* match name against known types */
- if (!buffer_string_is_empty(srv->srvconf.network_backend)) {
- for (i = 0; network_backends[i].name; i++) {
- /**/
- if (buffer_is_equal_string(srv->srvconf.network_backend, network_backends[i].name, strlen(network_backends[i].name))) {
- backend = network_backends[i].nb;
- break;
- }
- }
- if (NULL == network_backends[i].name) {
- /* we don't know it */
-
- log_error_write(srv, __FILE__, __LINE__, "sb",
- "server.network-backend has a unknown value:",
- srv->srvconf.network_backend);
-
- return -1;
- }
- }
-
- switch(backend) {
- case NETWORK_BACKEND_WRITE:
- srv->network_backend_write = network_write_chunkqueue_write;
- break;
-#if defined(USE_WRITEV)
- case NETWORK_BACKEND_WRITEV:
- srv->network_backend_write = network_write_chunkqueue_writev;
- break;
-#endif
-#if defined(USE_SENDFILE)
- case NETWORK_BACKEND_SENDFILE:
- srv->network_backend_write = network_write_chunkqueue_sendfile;
- break;
-#endif
- default:
- return -1;
- }
+ if (0 != network_write_init(srv)) return -1;
{
int rc;
diff --git a/src/network_backends.h b/src/network_backends.h
deleted file mode 100644
index db03a589..00000000
--- a/src/network_backends.h
+++ /dev/null
@@ -1,119 +0,0 @@
-#ifndef _NETWORK_BACKENDS_H_
-#define _NETWORK_BACKENDS_H_
-#include "first.h"
-
-#include "settings.h"
-
-#include <sys/types.h>
-
-/* on linux 2.4.x you get either sendfile or LFS */
-#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE && (!defined _LARGEFILE_SOURCE || defined HAVE_SENDFILE64) && defined(__linux__) && !defined HAVE_SENDFILE_BROKEN
-# ifdef USE_SENDFILE
-# error "can't have more than one sendfile implementation"
-# endif
-# define USE_SENDFILE "linux-sendfile"
-# define USE_LINUX_SENDFILE
-#endif
-
-#if defined HAVE_SENDFILE && (defined(__FreeBSD__) || defined(__DragonFly__))
-# ifdef USE_SENDFILE
-# error "can't have more than one sendfile implementation"
-# endif
-# define USE_SENDFILE "freebsd-sendfile"
-# define USE_FREEBSD_SENDFILE
-#endif
-
-#if defined HAVE_SENDFILE && defined(__APPLE__)
-# ifdef USE_SENDFILE
-# error "can't have more than one sendfile implementation"
-# endif
-# define USE_SENDFILE "darwin-sendfile"
-# define USE_DARWIN_SENDFILE
-#endif
-
-#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILEV && defined(__sun)
-# ifdef USE_SENDFILE
-# error "can't have more than one sendfile implementation"
-# endif
-# define USE_SENDFILE "solaris-sendfilev"
-# define USE_SOLARIS_SENDFILEV
-#endif
-
-/* not supported so far
-#if defined HAVE_SEND_FILE && defined(__aix)
-# ifdef USE_SENDFILE
-# error "can't have more than one sendfile implementation"
-# endif
-# define USE_SENDFILE "aix-sendfile"
-# define USE_AIX_SENDFILE
-#endif
-*/
-
-#if defined HAVE_SYS_UIO_H && defined HAVE_WRITEV
-# define USE_WRITEV
-#endif
-
-#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP
-# define USE_MMAP
-#endif
-
-#include "base.h"
-
-/* return values:
- * >= 0 : no error
- * -1 : error (on our side)
- * -2 : remote close
- */
-
-int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes);
-
-#if defined(USE_WRITEV)
-int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); /* fallback to write */
-#endif
-
-#if defined(USE_SENDFILE)
-int network_write_chunkqueue_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); /* fallback to write */
-#endif
-
-/* write next chunk(s); finished chunks are removed afterwards after successful writes.
- * return values: similar as backends (0 succes, -1 error, -2 remote close, -3 try again later (EINTR/EAGAIN)) */
-/* next chunk must be MEM_CHUNK. use write()/send() */
-int network_write_mem_chunk(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes);
-
-#if defined(USE_WRITEV)
-/* next chunk must be MEM_CHUNK. send multiple mem chunks using writev() */
-int network_writev_mem_chunks(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes);
-#else
-/* fallback to write()/send() */
-static inline int network_writev_mem_chunks(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
- return network_write_mem_chunk(srv, con, fd, cq, p_max_bytes);
-}
-#endif
-
-/* next chunk must be FILE_CHUNK. use temporary buffer (srv->tmp_buf) to read into, then write()/send() it */
-int network_write_file_chunk_no_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes);
-
-off_t mmap_align_offset(off_t start);
-#if defined(USE_MMAP)
-/* next chunk must be FILE_CHUNK. send mmap()ed file with write() */
-int network_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes);
-#else
-/* fallback to no_mmap */
-static inline int network_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
- return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes);
-}
-#endif
-
-#if defined(USE_SENDFILE)
-int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes);
-#else
-/* fallback to mmap */
-static inline int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
- return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes);
-}
-#endif
-
-/* next chunk must be FILE_CHUNK. return values: 0 success (=> -1 != cq->first->file.fd), -1 error */
-int network_open_file_chunk(server *srv, connection *con, chunkqueue *cq);
-
-#endif
diff --git a/src/network_darwin_sendfile.c b/src/network_darwin_sendfile.c
deleted file mode 100644
index 9b4ba6cf..00000000
--- a/src/network_darwin_sendfile.c
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "first.h"
-
-#include "network_backends.h"
-
-#if defined(USE_DARWIN_SENDFILE)
-
-#include "log.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-
-#include <errno.h>
-#include <string.h>
-
-int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
- chunk* const c = cq->first;
- off_t offset, written = 0;
- off_t toSend;
- int r;
-
- force_assert(NULL != c);
- force_assert(FILE_CHUNK == c->type);
- force_assert(c->offset >= 0 && c->offset <= c->file.length);
-
- offset = c->file.start + c->offset;
- toSend = c->file.length - c->offset;
- if (toSend > *p_max_bytes) toSend = *p_max_bytes;
-
- if (0 == toSend) {
- chunkqueue_remove_finished_chunks(cq);
- return 0;
- }
-
- if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1;
-
- /* Darwin sendfile() */
- written = toSend;
- if (-1 == (r = sendfile(c->file.fd, fd, offset, &written, NULL, 0))) {
- switch(errno) {
- case EAGAIN:
- case EINTR:
- /* for EAGAIN/EINTR written still contains the sent bytes */
- break; /* try again later */
- case EPIPE:
- case ENOTCONN:
- return -2;
- case EINVAL:
- case ENOSYS:
- #if defined(ENOTSUP) \
- && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP)
- case ENOTSUP:
- #endif
- #ifdef EOPNOTSUPP
- case EOPNOTSUPP:
- #endif
- #ifdef ESOCKTNOSUPPORT
- case ESOCKTNOSUPPORT:
- #endif
- #ifdef EAFNOSUPPORT
- case EAFNOSUPPORT:
- #endif
- #ifdef USE_MMAP
- return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes);
- #else
- return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes);
- #endif
- default:
- log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno);
- return -1;
- }
- }
-
- if (written >= 0) {
- chunkqueue_mark_written(cq, written);
- *p_max_bytes -= written;
- }
-
- return (r >= 0 && written == toSend) ? 0 : -3;
-}
-
-#endif /* USE_DARWIN_SENDFILE */
diff --git a/src/network_freebsd_sendfile.c b/src/network_freebsd_sendfile.c
deleted file mode 100644
index 11da4e85..00000000
--- a/src/network_freebsd_sendfile.c
+++ /dev/null
@@ -1,81 +0,0 @@
-#include "first.h"
-
-#include "network_backends.h"
-
-#if defined(USE_FREEBSD_SENDFILE)
-
-#include "log.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-
-#include <errno.h>
-#include <string.h>
-
-int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
- chunk* const c = cq->first;
- off_t offset, written = 0;
- off_t toSend;
- int r;
-
- force_assert(NULL != c);
- force_assert(FILE_CHUNK == c->type);
- force_assert(c->offset >= 0 && c->offset <= c->file.length);
-
- offset = c->file.start + c->offset;
- toSend = c->file.length - c->offset;
- if (toSend > *p_max_bytes) toSend = *p_max_bytes;
-
- if (0 == toSend) {
- chunkqueue_remove_finished_chunks(cq);
- return 0;
- }
-
- if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1;
-
- /* FreeBSD sendfile() */
- if (-1 == (r = sendfile(c->file.fd, fd, offset, toSend, NULL, &written, 0))) {
- switch(errno) {
- case EAGAIN:
- case EINTR:
- /* for EAGAIN/EINTR written still contains the sent bytes */
- break; /* try again later */
- case EPIPE:
- case ENOTCONN:
- return -2;
- case EINVAL:
- case ENOSYS:
- #if defined(ENOTSUP) \
- && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP)
- case ENOTSUP:
- #endif
- #ifdef EOPNOTSUPP
- case EOPNOTSUPP:
- #endif
- #ifdef ESOCKTNOSUPPORT
- case ESOCKTNOSUPPORT:
- #endif
- #ifdef EAFNOSUPPORT
- case EAFNOSUPPORT:
- #endif
- #ifdef USE_MMAP
- return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes);
- #else
- return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes);
- #endif
- default:
- log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno);
- return -1;
- }
- }
-
- if (written >= 0) {
- chunkqueue_mark_written(cq, written);
- *p_max_bytes -= written;
- }
-
- return (r >= 0 && written == toSend) ? 0 : -3;
-}
-
-#endif /* USE_FREEBSD_SENDFILE */
diff --git a/src/network_linux_sendfile.c b/src/network_linux_sendfile.c
deleted file mode 100644
index a15ef252..00000000
--- a/src/network_linux_sendfile.c
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "first.h"
-
-#include "network_backends.h"
-
-#if defined(USE_LINUX_SENDFILE)
-
-#include "log.h"
-
-#include <sys/sendfile.h>
-
-#include <errno.h>
-#include <string.h>
-
-int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
- chunk* const c = cq->first;
- ssize_t r;
- off_t offset;
- off_t toSend;
-
- force_assert(NULL != c);
- force_assert(FILE_CHUNK == c->type);
- force_assert(c->offset >= 0 && c->offset <= c->file.length);
-
- offset = c->file.start + c->offset;
- toSend = c->file.length - c->offset;
- if (toSend > *p_max_bytes) toSend = *p_max_bytes;
-
- if (0 == toSend) {
- chunkqueue_remove_finished_chunks(cq);
- return 0;
- }
-
- if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1;
-
- if (-1 == (r = sendfile(fd, c->file.fd, &offset, toSend))) {
- switch (errno) {
- case EAGAIN:
- case EINTR:
- break;
- case EPIPE:
- case ECONNRESET:
- return -2;
- case EINVAL:
- case ENOSYS:
- #if defined(ENOTSUP) \
- && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP)
- case ENOTSUP:
- #endif
- #ifdef EOPNOTSUPP
- case EOPNOTSUPP:
- #endif
- #ifdef ESOCKTNOSUPPORT
- case ESOCKTNOSUPPORT:
- #endif
- #ifdef EAFNOSUPPORT
- case EAFNOSUPPORT:
- #endif
- #ifdef USE_MMAP
- return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes);
- #else
- return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes);
- #endif
- default:
- log_error_write(srv, __FILE__, __LINE__, "ssd",
- "sendfile failed:", strerror(errno), fd);
- return -1;
- }
- }
-
- if (r >= 0) {
- chunkqueue_mark_written(cq, r);
- *p_max_bytes -= r;
- }
-
- return (r > 0 && r == toSend) ? 0 : -3;
-}
-
-#endif /* USE_LINUX_SENDFILE */
diff --git a/src/network_solaris_sendfilev.c b/src/network_solaris_sendfilev.c
deleted file mode 100644
index d0e5007a..00000000
--- a/src/network_solaris_sendfilev.c
+++ /dev/null
@@ -1,92 +0,0 @@
-#include "first.h"
-
-#include "network_backends.h"
-
-#if defined(USE_SOLARIS_SENDFILEV)
-
-#include "log.h"
-
-#include <sys/sendfile.h>
-
-#include <errno.h>
-#include <string.h>
-
-/**
- * a very simple sendfilev() interface for solaris which can be optimised a lot more
- * as solaris sendfilev() supports 'sending everythin in one syscall()'
- */
-
-int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
- chunk* const c = cq->first;
- off_t offset;
- off_t toSend;
- size_t written = 0;
- int r;
- sendfilevec_t fvec;
-
- force_assert(NULL != c);
- force_assert(FILE_CHUNK == c->type);
- force_assert(c->offset >= 0 && c->offset <= c->file.length);
-
- offset = c->file.start + c->offset;
- toSend = c->file.length - c->offset;
- if (toSend > *p_max_bytes) toSend = *p_max_bytes;
-
- if (0 == toSend) {
- chunkqueue_remove_finished_chunks(cq);
- return 0;
- }
-
- if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1;
-
- fvec.sfv_fd = c->file.fd;
- fvec.sfv_flag = 0;
- fvec.sfv_off = offset;
- fvec.sfv_len = toSend;
-
- /* Solaris sendfilev() */
-
- if (-1 == (r = sendfilev(fd, &fvec, 1, &written))) {
- switch(errno) {
- case EAGAIN:
- case EINTR:
- /* for EAGAIN/EINTR written still contains the sent bytes */
- break; /* try again later */
- case EPIPE:
- case ENOTCONN:
- return -2;
- case EINVAL:
- case ENOSYS:
- #if defined(ENOTSUP) \
- && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP)
- case ENOTSUP:
- #endif
- #ifdef EOPNOTSUPP
- case EOPNOTSUPP:
- #endif
- #ifdef ESOCKTNOSUPPORT
- case ESOCKTNOSUPPORT:
- #endif
- #ifdef EAFNOSUPPORT
- case EAFNOSUPPORT:
- #endif
- #ifdef USE_MMAP
- return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes);
- #else
- return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes);
- #endif
- default:
- log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno);
- return -1;
- }
- }
-
- if (written >= 0) {
- chunkqueue_mark_written(cq, written);
- *p_max_bytes -= written;
- }
-
- return (r >= 0 && (off_t) written == toSend) ? 0 : -3;
-}
-
-#endif /* USE_SOLARIS_SENDFILEV */
diff --git a/src/network_write.c b/src/network_write.c
index a346e937..240e9b59 100644
--- a/src/network_write.c
+++ b/src/network_write.c
@@ -1,115 +1,737 @@
#include "first.h"
-#include "network_backends.h"
+#include "network_write.h"
-#include "network.h"
+#include "base.h"
#include "log.h"
+#include <sys/types.h>
#include "sys-socket.h"
-#include <unistd.h>
-
#include <errno.h>
#include <string.h>
+#include <unistd.h>
+
+
+/* on linux 2.4.x you get either sendfile or LFS */
+#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE \
+ && (!defined _LARGEFILE_SOURCE || defined HAVE_SENDFILE64) \
+ && defined(__linux__) && !defined HAVE_SENDFILE_BROKEN
+# ifdef NETWORK_WRITE_USE_SENDFILE
+# error "can't have more than one sendfile implementation"
+# endif
+# define NETWORK_WRITE_USE_SENDFILE "linux-sendfile"
+# define NETWORK_WRITE_USE_LINUX_SENDFILE
+#endif
+
+#if defined HAVE_SENDFILE && (defined(__FreeBSD__) || defined(__DragonFly__))
+# ifdef NETWORK_WRITE_USE_SENDFILE
+# error "can't have more than one sendfile implementation"
+# endif
+# define NETWORK_WRITE_USE_SENDFILE "freebsd-sendfile"
+# define NETWORK_WRITE_USE_FREEBSD_SENDFILE
+#endif
+
+#if defined HAVE_SENDFILE && defined(__APPLE__)
+# ifdef NETWORK_WRITE_USE_SENDFILE
+# error "can't have more than one sendfile implementation"
+# endif
+# define NETWORK_WRITE_USE_SENDFILE "darwin-sendfile"
+# define NETWORK_WRITE_USE_DARWIN_SENDFILE
+#endif
+
+#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILEV && defined(__sun)
+# ifdef NETWORK_WRITE_USE_SENDFILE
+# error "can't have more than one sendfile implementation"
+# endif
+# define NETWORK_WRITE_USE_SENDFILE "solaris-sendfilev"
+# define NETWORK_WRITE_USE_SOLARIS_SENDFILEV
+#endif
+
+/* not supported so far
+#if defined HAVE_SEND_FILE && defined(__aix)
+# ifdef NETWORK_WRITE_USE_SENDFILE
+# error "can't have more than one sendfile implementation"
+# endif
+# define NETWORK_WRITE_USE_SENDFILE "aix-sendfile"
+# define NETWORK_WRITE_USE_AIX_SENDFILE
+#endif
+*/
+
+#if defined HAVE_SYS_UIO_H && defined HAVE_WRITEV
+# define NETWORK_WRITE_USE_WRITEV
+#endif
+
+#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP
+# define NETWORK_WRITE_USE_MMAP
+#endif
+
+
+static int network_write_error(server *srv, int fd) {
+ #if defined(__WIN32)
+ int lastError = WSAGetLastError();
+ switch (lastError) {
+ case WSAEINTR:
+ case WSAEWOULDBLOCK:
+ return -3;
+ case WSAECONNRESET:
+ case WSAETIMEDOUT:
+ case WSAECONNABORTED:
+ return -2;
+ default:
+ log_error_write(srv, __FILE__, __LINE__, "sdd",
+ "send failed: ", lastError, fd);
+ return -1;
+ }
+ #else /* __WIN32 */
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ return -3;
+ case EPIPE:
+ case ECONNRESET:
+ return -2;
+ default:
+ log_error_write(srv, __FILE__, __LINE__, "ssd",
+ "write failed:", strerror(errno), fd);
+ return -1;
+ }
+ #endif /* __WIN32 */
+}
+
+inline
+static ssize_t network_write_data_len(int fd, const char *data, off_t len) {
+ #if defined(__WIN32)
+ return send(fd, data, len, 0);
+ #else /* __WIN32 */
+ return write(fd, data, len);
+ #endif /* __WIN32 */
+}
+
+
+
+
+/* write next chunk(s); finished chunks are removed afterwards after successful writes.
+ * return values: similar as backends (0 succes, -1 error, -2 remote close, -3 try again later (EINTR/EAGAIN)) */
+/* next chunk must be MEM_CHUNK. use write()/send() */
+static int network_write_mem_chunk(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) {
+ chunk* const c = cq->first;
+ ssize_t wr;
+ off_t c_len = (off_t)buffer_string_length(c->mem);
+ force_assert(c->offset >= 0 && c->offset <= c_len);
+ c_len -= c->offset;
+ if (c_len > *p_max_bytes) c_len = *p_max_bytes;
+
+ if (0 == c_len) {
+ chunkqueue_remove_finished_chunks(cq);
+ return 0;
+ }
+
+ wr = network_write_data_len(fd, c->mem->ptr + c->offset, c_len);
+ if (wr >= 0) {
+ *p_max_bytes -= wr;
+ chunkqueue_mark_written(cq, wr);
+ return (wr > 0 && wr == c_len) ? 0 : -3;
+ } else {
+ return network_write_error(srv, fd);
+ }
+}
+
+
+
+
+#if !defined(NETWORK_WRITE_USE_MMAP)
+
+static int network_write_file_chunk_no_mmap(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) {
+ chunk* const c = cq->first;
+ off_t offset, toSend;
+ ssize_t wr;
+
+ force_assert(c->offset >= 0 && c->offset <= c->file.length);
+
+ offset = c->file.start + c->offset;
+ toSend = c->file.length - c->offset;
+ if (toSend > *p_max_bytes) toSend = *p_max_bytes;
+
+ if (0 == toSend) {
+ chunkqueue_remove_finished_chunks(cq);
+ return 0;
+ }
+
+ if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1;
+
+ if (toSend > 64*1024) toSend = 64*1024; /* max read 64kb in one step */
+ buffer_string_prepare_copy(srv->tmp_buf, toSend);
+
+ if (-1 == lseek(c->file.fd, offset, SEEK_SET)) {
+ log_error_write(srv, __FILE__, __LINE__, "ss","lseek:",strerror(errno));
+ return -1;
+ }
+ if (-1 == (toSend = read(c->file.fd, srv->tmp_buf->ptr, toSend))) {
+ log_error_write(srv, __FILE__, __LINE__, "ss","read:",strerror(errno));
+ return -1;
+ }
+
+ wr = network_write_data_len(fd, srv->tmp_buf->ptr, toSend);
+ if (wr >= 0) {
+ *p_max_bytes -= wr;
+ chunkqueue_mark_written(cq, wr);
+ return (wr > 0 && wr == toSend) ? 0 : -3;
+ } else {
+ return network_write_error(srv, fd);
+ }
+}
+
+#endif
+
+
+
+
+#if defined(NETWORK_WRITE_USE_MMAP)
+
+#include "sys-mmap.h"
+
+#include <setjmp.h>
+#include <signal.h>
+
+#define MMAP_CHUNK_SIZE (512*1024)
+
+static off_t mmap_align_offset(off_t start) {
+ static long pagesize = 0;
+ if (0 == pagesize) {
+ pagesize = sysconf(_SC_PAGESIZE);
+ force_assert(pagesize < MMAP_CHUNK_SIZE);
+ }
+ force_assert(start >= (start % pagesize));
+ return start - (start % pagesize);
+}
+
+static volatile int sigbus_jmp_valid;
+static sigjmp_buf sigbus_jmp;
+
+static void sigbus_handler(int sig) {
+ UNUSED(sig);
+ if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1);
+ log_failed_assert(__FILE__, __LINE__, "SIGBUS");
+}
+
+/* next chunk must be FILE_CHUNK. send mmap()ed file with write() */
+static int network_write_file_chunk_mmap(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) {
+ chunk* const c = cq->first;
+ off_t offset, toSend, file_end;
+ ssize_t r;
+ size_t mmap_offset, mmap_avail;
+ const char *data;
+
+ force_assert(c->offset >= 0 && c->offset <= c->file.length);
+
+ offset = c->file.start + c->offset;
+ toSend = c->file.length - c->offset;
+ if (toSend > *p_max_bytes) toSend = *p_max_bytes;
+ file_end = c->file.start + c->file.length; /*file end offset in this chunk*/
+
+ if (0 == toSend) {
+ chunkqueue_remove_finished_chunks(cq);
+ return 0;
+ }
+
+ if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1;
+
+ /* mmap buffer if offset is outside old mmap area or not mapped at all */
+ if (MAP_FAILED == c->file.mmap.start
+ || offset < c->file.mmap.offset
+ || offset >= (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
+
+ if (MAP_FAILED != c->file.mmap.start) {
+ munmap(c->file.mmap.start, c->file.mmap.length);
+ c->file.mmap.start = MAP_FAILED;
+ }
+
+ /* Optimizations for the future:
+ *
+ * adaptive mem-mapping
+ * the problem:
+ * we mmap() the whole file. If someone has alot large files and
+ * 32-bit machine the virtual address area will be unrun and we
+ * will have a failing mmap() call.
+ * solution:
+ * only mmap 16M in one chunk and move the window as soon as we have
+ * finished the first 8M
+ *
+ * read-ahead buffering
+ * the problem:
+ * sending out several large files in parallel trashes read-ahead
+ * of the kernel leading to long wait-for-seek times.
+ * solutions: (increasing complexity)
+ * 1. use madvise
+ * 2. use a internal read-ahead buffer in the chunk-structure
+ * 3. use non-blocking IO for file-transfers
+ * */
+
+ c->file.mmap.offset = mmap_align_offset(offset);
+
+ /* all mmap()ed areas are MMAP_CHUNK_SIZE
+ * except the last which might be smaller */
+ c->file.mmap.length = MMAP_CHUNK_SIZE;
+ if (c->file.mmap.offset > file_end - (off_t)c->file.mmap.length) {
+ c->file.mmap.length = file_end - c->file.mmap.offset;
+ }
+
+ c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ,
+ MAP_SHARED, c->file.fd, c->file.mmap.offset);
+ if (MAP_FAILED == c->file.mmap.start) {
+ log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:",
+ strerror(errno), c->file.name, c->file.fd, c->file.mmap.offset, (off_t) c->file.mmap.length);
+ return -1;
+ }
+
+ #if defined(HAVE_MADVISE)
+ /* don't advise files < 64Kb */
+ if (c->file.mmap.length > (64*1024)) {
+ /* darwin 7 is returning EINVAL all the time and I don't know how to
+ * detect this at runtime.
+ *
+ * ignore the return value for now */
+ madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED);
+ }
+ #endif
+ }
+
+ force_assert(offset >= c->file.mmap.offset);
+ mmap_offset = offset - c->file.mmap.offset;
+ force_assert(c->file.mmap.length > mmap_offset);
+ mmap_avail = c->file.mmap.length - mmap_offset;
+ if (toSend > (off_t) mmap_avail) toSend = mmap_avail;
+
+ data = c->file.mmap.start + mmap_offset;
+
+ /* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */
+ if (0 == sigsetjmp(sigbus_jmp, 1)) {
+ signal(SIGBUS, sigbus_handler);
+
+ sigbus_jmp_valid = 1;
+ r = network_write_data_len(fd, data, toSend);
+ sigbus_jmp_valid = 0;
+ } else {
+ sigbus_jmp_valid = 0;
+
+ log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:",
+ c->file.name, c->file.fd);
+
+ munmap(c->file.mmap.start, c->file.mmap.length);
+ c->file.mmap.start = MAP_FAILED;
+ return -1;
+ }
-int network_write_mem_chunk(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
- chunk* const c = cq->first;
- off_t c_len;
- ssize_t r;
- UNUSED(con);
-
- force_assert(NULL != c);
- force_assert(MEM_CHUNK == c->type);
- force_assert(c->offset >= 0 && c->offset <= (off_t)buffer_string_length(c->mem));
-
- c_len = buffer_string_length(c->mem) - c->offset;
- if (c_len > *p_max_bytes) c_len = *p_max_bytes;
-
- if (0 == c_len) {
- chunkqueue_remove_finished_chunks(cq);
- return 0;
- }
-
-#if defined(__WIN32)
- if ((r = send(fd, c->mem->ptr + c->offset, c_len, 0)) < 0) {
- int lastError = WSAGetLastError();
- switch (lastError) {
- case WSAEINTR:
- case WSAEWOULDBLOCK:
- break;
- case WSAECONNRESET:
- case WSAETIMEDOUT:
- case WSAECONNABORTED:
- return -2;
- default:
- log_error_write(srv, __FILE__, __LINE__, "sdd",
- "send failed: ", lastError, fd);
- return -1;
- }
- }
-#else /* __WIN32 */
- if ((r = write(fd, c->mem->ptr + c->offset, c_len)) < 0) {
- switch (errno) {
- case EAGAIN:
- case EINTR:
- break;
- case EPIPE:
- case ECONNRESET:
- return -2;
- default:
- log_error_write(srv, __FILE__, __LINE__, "ssd",
- "write failed:", strerror(errno), fd);
- return -1;
- }
- }
-#endif /* __WIN32 */
-
- if (r >= 0) {
- *p_max_bytes -= r;
- chunkqueue_mark_written(cq, r);
- }
-
- return (r > 0 && r == c_len) ? 0 : -3;
+ if (r >= 0) {
+ *p_max_bytes -= r;
+ chunkqueue_mark_written(cq, r);
+ return (r > 0 && r == toSend) ? 0 : -3;
+ } else {
+ return network_write_error(srv, fd);
+ }
}
-int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) {
- while (max_bytes > 0 && NULL != cq->first) {
- int r = -1;
+#endif /* NETWORK_WRITE_USE_MMAP */
- switch (cq->first->type) {
- case MEM_CHUNK:
- r = network_write_mem_chunk(srv, con, fd, cq, &max_bytes);
- break;
- case FILE_CHUNK:
- r = network_write_file_chunk_mmap(srv, con, fd, cq, &max_bytes);
- break;
- }
- if (-3 == r) return 0;
- if (0 != r) return r;
- }
- return 0;
+
+#if defined(NETWORK_WRITE_USE_WRITEV)
+
+#if defined(HAVE_SYS_UIO_H)
+# include <sys/uio.h>
+#endif
+
+#if defined(UIO_MAXIOV)
+# define SYS_MAX_CHUNKS UIO_MAXIOV
+#elif defined(IOV_MAX)
+/* new name for UIO_MAXIOV since IEEE Std 1003.1-2001 */
+# define SYS_MAX_CHUNKS IOV_MAX
+#elif defined(_XOPEN_IOV_MAX)
+/* minimum value for sysconf(_SC_IOV_MAX); posix requires this to be at least 16, which is good enough - no need to call sysconf() */
+# define SYS_MAX_CHUNKS _XOPEN_IOV_MAX
+#else
+# error neither UIO_MAXIOV nor IOV_MAX nor _XOPEN_IOV_MAX are defined
+#endif
+
+/* allocate iovec[MAX_CHUNKS] on stack, so pick a sane limit:
+ * - each entry will use 1 pointer + 1 size_t
+ * - 32 chunks -> 256 / 512 bytes (32-bit/64-bit pointers)
+ */
+#define STACK_MAX_ALLOC_CHUNKS 32
+#if SYS_MAX_CHUNKS > STACK_MAX_ALLOC_CHUNKS
+# define MAX_CHUNKS STACK_MAX_ALLOC_CHUNKS
+#else
+# define MAX_CHUNKS SYS_MAX_CHUNKS
+#endif
+
+/* next chunk must be MEM_CHUNK. send multiple mem chunks using writev() */
+static int network_writev_mem_chunks(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) {
+ struct iovec chunks[MAX_CHUNKS];
+ size_t num_chunks = 0;
+ off_t max_bytes = *p_max_bytes;
+ off_t toSend = 0;
+ ssize_t r;
+
+ for (const chunk *c = cq->first;
+ NULL != c && MEM_CHUNK == c->type
+ && num_chunks < MAX_CHUNKS && toSend < max_bytes;
+ c = c->next) {
+ size_t c_len = buffer_string_length(c->mem);
+ force_assert(c->offset >= 0 && c->offset <= (off_t)c_len);
+ c_len -= c->offset;
+ if (c_len > 0) {
+ toSend += c_len;
+
+ chunks[num_chunks].iov_base = c->mem->ptr + c->offset;
+ chunks[num_chunks].iov_len = c_len;
+
+ ++num_chunks;
+ }
+ }
+
+ if (0 == num_chunks) {
+ chunkqueue_remove_finished_chunks(cq);
+ return 0;
+ }
+
+ r = writev(fd, chunks, num_chunks);
+
+ if (r < 0) switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ break;
+ case EPIPE:
+ case ECONNRESET:
+ return -2;
+ default:
+ log_error_write(srv, __FILE__, __LINE__, "ssd",
+ "writev failed:", strerror(errno), fd);
+ return -1;
+ }
+
+ if (r >= 0) {
+ *p_max_bytes -= r;
+ chunkqueue_mark_written(cq, r);
+ }
+
+ return (r > 0 && r == toSend) ? 0 : -3;
}
-#if defined(USE_SENDFILE)
-int network_write_chunkqueue_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) {
- while (max_bytes > 0 && NULL != cq->first) {
- int r = -1;
-
- switch (cq->first->type) {
- case MEM_CHUNK:
- r = network_writev_mem_chunks(srv, con, fd, cq, &max_bytes);
- break;
- case FILE_CHUNK:
- r = network_write_file_chunk_sendfile(srv, con, fd, cq, &max_bytes);
- break;
- }
-
- if (-3 == r) return 0;
- if (0 != r) return r;
- }
-
- return 0;
+#endif /* NETWORK_WRITE_USE_WRITEV */
+
+
+
+
+#if defined(NETWORK_WRITE_USE_SENDFILE)
+
+#if defined(NETWORK_WRITE_USE_LINUX_SENDFILE) \
+ || defined(NETWORK_WRITE_USE_SOLARIS_SENDFILEV)
+#include <sys/sendfile.h>
+#endif
+
+#if defined(NETWORK_WRITE_USE_FREEBSD_SENDFILE) \
+ || defined(NETWORK_WRITE_USE_DARWIN_SENDFILE)
+#include <sys/uio.h>
+#endif
+
+static int network_write_file_chunk_sendfile(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) {
+ chunk * const c = cq->first;
+ ssize_t r;
+ off_t offset;
+ off_t toSend;
+ off_t written = 0;
+
+ force_assert(c->offset >= 0 && c->offset <= c->file.length);
+
+ offset = c->file.start + c->offset;
+ toSend = c->file.length - c->offset;
+ if (toSend > *p_max_bytes) toSend = *p_max_bytes;
+
+ if (0 == toSend) {
+ chunkqueue_remove_finished_chunks(cq);
+ return 0;
+ }
+
+ if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1;
+
+ /* Darwin, FreeBSD, and Solaris variants support iovecs and could
+ * be optimized to send more than just file in single syscall */
+
+ #if defined(NETWORK_WRITE_USE_LINUX_SENDFILE)
+
+ r = sendfile(fd, c->file.fd, &offset, toSend);
+ if (r > 0) written = (off_t)r;
+
+ #elif defined(NETWORK_WRITE_USE_DARWIN_SENDFILE)
+
+ written = toSend;
+ r = sendfile(c->file.fd, fd, offset, &written, NULL, 0);
+ /* (for EAGAIN/EINTR written still contains the sent bytes) */
+
+ #elif defined(NETWORK_WRITE_USE_FREEBSD_SENDFILE)
+
+ r = sendfile(c->file.fd, fd, offset, toSend, NULL, &written, 0);
+ /* (for EAGAIN/EINTR written still contains the sent bytes) */
+
+ #elif defined(NETWORK_WRITE_USE_SOLARIS_SENDFILEV)
+ {
+ sendfilevec_t fvec;
+ fvec.sfv_fd = c->file.fd;
+ fvec.sfv_flag = 0;
+ fvec.sfv_off = offset;
+ fvec.sfv_len = toSend;
+
+ /* Solaris sendfilev() */
+ r = sendfilev(fd, &fvec, 1, (size_t *)&written);
+ /* (for EAGAIN/EINTR written still contains the sent bytes) */
+ }
+ #else
+
+ r = -1;
+ errno = ENOSYS;
+
+ #endif
+
+ if (-1 == r) {
+ switch(errno) {
+ case EAGAIN:
+ case EINTR:
+ break; /* try again later */
+ case EPIPE:
+ case ECONNRESET:
+ case ENOTCONN:
+ return -2;
+ case EINVAL:
+ case ENOSYS:
+ #if defined(ENOTSUP) && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP)
+ case ENOTSUP:
+ #endif
+ #ifdef EOPNOTSUPP
+ case EOPNOTSUPP:
+ #endif
+ #ifdef ESOCKTNOSUPPORT
+ case ESOCKTNOSUPPORT:
+ #endif
+ #ifdef EAFNOSUPPORT
+ case EAFNOSUPPORT:
+ #endif
+ #ifdef NETWORK_WRITE_USE_MMAP
+ return network_write_file_chunk_mmap(srv, fd, cq, p_max_bytes);
+ #else
+ return network_write_file_chunk_no_mmap(srv, fd, cq, p_max_bytes);
+ #endif
+ default:
+ log_error_write(srv, __FILE__, __LINE__, "ssdSd",
+ "sendfile():", strerror(errno), errno, "fd:", fd);
+ return -1;
+ }
+ }
+
+ if (written >= 0) { /*(always true)*/
+ chunkqueue_mark_written(cq, written);
+ *p_max_bytes -= written;
+ }
+
+ return (r >= 0 && written == toSend) ? 0 : -3;
}
+
#endif
+
+
+
+
+/* return values:
+ * >= 0 : no error
+ * -1 : error (on our side)
+ * -2 : remote close
+ */
+
+static int network_write_chunkqueue_write(server *srv, int fd, chunkqueue *cq, off_t max_bytes) {
+ while (max_bytes > 0 && NULL != cq->first) {
+ int r = -1;
+
+ switch (cq->first->type) {
+ case MEM_CHUNK:
+ r = network_write_mem_chunk(srv, fd, cq, &max_bytes);
+ break;
+ case FILE_CHUNK:
+ #ifdef NETWORK_WRITE_USE_MMAP
+ r = network_write_file_chunk_mmap(srv, fd, cq, &max_bytes);
+ #else
+ r = network_write_file_chunk_no_mmap(srv, fd, cq, &max_bytes);
+ #endif
+ break;
+ }
+
+ if (-3 == r) return 0;
+ if (0 != r) return r;
+ }
+
+ return 0;
+}
+
+#if defined(NETWORK_WRITE_USE_WRITEV)
+static int network_write_chunkqueue_writev(server *srv, int fd, chunkqueue *cq, off_t max_bytes) {
+ while (max_bytes > 0 && NULL != cq->first) {
+ int r = -1;
+
+ switch (cq->first->type) {
+ case MEM_CHUNK:
+ #if defined(NETWORK_WRITE_USE_WRITEV)
+ r = network_writev_mem_chunks(srv, fd, cq, &max_bytes);
+ #else
+ r = network_write_mem_chunk(srv, fd, cq, &max_bytes);
+ #endif
+ break;
+ case FILE_CHUNK:
+ #ifdef NETWORK_WRITE_USE_MMAP
+ r = network_write_file_chunk_mmap(srv, fd, cq, &max_bytes);
+ #else
+ r = network_write_file_chunk_no_mmap(srv, fd, cq, &max_bytes);
+ #endif
+ break;
+ }
+
+ if (-3 == r) return 0;
+ if (0 != r) return r;
+ }
+
+ return 0;
+}
+#endif
+
+#if defined(NETWORK_WRITE_USE_SENDFILE)
+static int network_write_chunkqueue_sendfile(server *srv, int fd, chunkqueue *cq, off_t max_bytes) {
+ while (max_bytes > 0 && NULL != cq->first) {
+ int r = -1;
+
+ switch (cq->first->type) {
+ case MEM_CHUNK:
+ #if defined(NETWORK_WRITE_USE_WRITEV)
+ r = network_writev_mem_chunks(srv, fd, cq, &max_bytes);
+ #else
+ r = network_write_mem_chunk(srv, fd, cq, &max_bytes);
+ #endif
+ break;
+ case FILE_CHUNK:
+ #if defined(NETWORK_WRITE_USE_SENDFILE)
+ r = network_write_file_chunk_sendfile(srv, fd, cq, &max_bytes);
+ #elif defined(NETWORK_WRITE_USE_MMAP)
+ r = network_write_file_chunk_mmap(srv, fd, cq, &max_bytes);
+ #else
+ r = network_write_file_chunk_no_mmap(srv, fd, cq, &max_bytes);
+ #endif
+ break;
+ }
+
+ if (-3 == r) return 0;
+ if (0 != r) return r;
+ }
+
+ return 0;
+}
+#endif
+
+int network_write_init(server *srv) {
+ typedef enum {
+ NETWORK_BACKEND_UNSET,
+ NETWORK_BACKEND_WRITE,
+ NETWORK_BACKEND_WRITEV,
+ NETWORK_BACKEND_SENDFILE,
+ } network_backend_t;
+
+ network_backend_t backend;
+
+ struct nb_map {
+ network_backend_t nb;
+ const char *name;
+ } network_backends[] = {
+ /* lowest id wins */
+ { NETWORK_BACKEND_SENDFILE, "sendfile" },
+ { NETWORK_BACKEND_SENDFILE, "linux-sendfile" },
+ { NETWORK_BACKEND_SENDFILE, "freebsd-sendfile" },
+ { NETWORK_BACKEND_SENDFILE, "solaris-sendfilev" },
+ { NETWORK_BACKEND_WRITEV, "writev" },
+ { NETWORK_BACKEND_WRITE, "write" },
+ { NETWORK_BACKEND_UNSET, NULL }
+ };
+
+ /* get a useful default */
+ backend = network_backends[0].nb;
+
+ /* match name against known types */
+ if (!buffer_string_is_empty(srv->srvconf.network_backend)) {
+ const char *name;
+ for (size_t i = 0; NULL != (name = network_backends[i].name); ++i) {
+ if (0 == strcmp(srv->srvconf.network_backend->ptr, name)) {
+ backend = network_backends[i].nb;
+ break;
+ }
+ }
+ if (NULL == name) {
+ log_error_write(srv, __FILE__, __LINE__, "sb",
+ "server.network-backend has an unknown value:",
+ srv->srvconf.network_backend);
+ return -1;
+ }
+ }
+
+ switch(backend) {
+ case NETWORK_BACKEND_SENDFILE:
+ #if defined(NETWORK_WRITE_USE_SENDFILE)
+ srv->network_backend_write = network_write_chunkqueue_sendfile;
+ break;
+ #endif
+ case NETWORK_BACKEND_WRITEV:
+ #if defined(NETWORK_WRITE_USE_WRITEV)
+ srv->network_backend_write = network_write_chunkqueue_writev;
+ break;
+ #endif
+ case NETWORK_BACKEND_WRITE:
+ srv->network_backend_write = network_write_chunkqueue_write;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+const char * network_write_show_handlers(void) {
+ return
+ "\nNetwork handler:\n\n"
+ #if defined NETWORK_WRITE_USE_LINUX_SENDFILE
+ "\t+ linux-sendfile\n"
+ #else
+ "\t- linux-sendfile\n"
+ #endif
+ #if defined NETWORK_WRITE_USE_FREEBSD_SENDFILE
+ "\t+ freebsd-sendfile\n"
+ #else
+ "\t- freebsd-sendfile\n"
+ #endif
+ #if defined NETWORK_WRITE_USE_DARWIN_SENDFILE
+ "\t+ darwin-sendfile\n"
+ #else
+ "\t- darwin-sendfile\n"
+ #endif
+ #if defined NETWORK_WRITE_USE_SOLARIS_SENDFILEV
+ "\t+ solaris-sendfilev\n"
+ #else
+ "\t- solaris-sendfilev\n"
+ #endif
+ #if defined NETWORK_WRITE_USE_WRITEV
+ "\t+ writev\n"
+ #else
+ "\t- writev\n"
+ #endif
+ "\t+ write\n"
+ #ifdef NETWORK_WRITE_USE_MMAP
+ "\t+ mmap support\n"
+ #else
+ "\t- mmap support\n"
+ #endif
+ ;
+}
diff --git a/src/network_write.h b/src/network_write.h
new file mode 100644
index 00000000..a80cbcf8
--- /dev/null
+++ b/src/network_write.h
@@ -0,0 +1,9 @@
+#ifndef INCLUDED_NETWORK_WRITE_H
+#define INCLUDED_NETWORK_WRITE_H
+#include "first.h"
+#include "base_decls.h"
+
+int network_write_init(server *srv);
+const char * network_write_show_handlers(void);
+
+#endif
diff --git a/src/network_write_mmap.c b/src/network_write_mmap.c
deleted file mode 100644
index d46c1adf..00000000
--- a/src/network_write_mmap.c
+++ /dev/null
@@ -1,210 +0,0 @@
-#include "first.h"
-
-#include "network_backends.h"
-
-#include "log.h"
-#include "sys-mmap.h"
-
-#include <setjmp.h>
-#include <signal.h>
-#include <unistd.h>
-
-#include <errno.h>
-#include <string.h>
-
-#define MMAP_CHUNK_SIZE (512*1024)
-
-off_t mmap_align_offset(off_t start) {
- static long pagesize = 0;
- if (0 == pagesize) {
- pagesize = sysconf(_SC_PAGESIZE);
- force_assert(pagesize < MMAP_CHUNK_SIZE);
- }
- force_assert(start >= (start % pagesize));
- return start - (start % pagesize);
-}
-
-#if defined(USE_MMAP)
-
-static volatile int sigbus_jmp_valid;
-static sigjmp_buf sigbus_jmp;
-
-static void sigbus_handler(int sig) {
- UNUSED(sig);
- if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1);
- log_failed_assert(__FILE__, __LINE__, "SIGBUS");
-}
-
-#if 0
-/* read mmap()ed data into local buffer */
-#define LOCAL_BUFFERING 1
-#endif
-
-int network_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
- chunk* const c = cq->first;
- off_t offset, toSend, file_end;
- ssize_t r;
- size_t mmap_offset, mmap_avail;
- const char *data;
- UNUSED(con);
-
- force_assert(NULL != c);
- force_assert(FILE_CHUNK == c->type);
- force_assert(c->offset >= 0 && c->offset <= c->file.length);
-
- offset = c->file.start + c->offset;
- toSend = c->file.length - c->offset;
- if (toSend > *p_max_bytes) toSend = *p_max_bytes;
- file_end = c->file.start + c->file.length; /* offset to file end in this chunk */
-
- if (0 == toSend) {
- chunkqueue_remove_finished_chunks(cq);
- return 0;
- }
-
- if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1;
-
- /* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */
- if (0 != sigsetjmp(sigbus_jmp, 1)) {
- sigbus_jmp_valid = 0;
-
- log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:",
- c->file.name, c->file.fd);
-
- munmap(c->file.mmap.start, c->file.mmap.length);
- c->file.mmap.start = MAP_FAILED;
-#ifdef LOCAL_BUFFERING
- buffer_reset(c->mem);
-#endif
-
- return -1;
- }
-
- signal(SIGBUS, sigbus_handler);
-
- /* mmap the buffer if offset is outside old mmap area or not mapped at all */
- if (MAP_FAILED == c->file.mmap.start
- || offset < c->file.mmap.offset
- || offset >= (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
-
- if (MAP_FAILED != c->file.mmap.start) {
- munmap(c->file.mmap.start, c->file.mmap.length);
- c->file.mmap.start = MAP_FAILED;
- }
-
- /* Optimizations for the future:
- *
- * adaptive mem-mapping
- * the problem:
- * we mmap() the whole file. If someone has alot large files and 32bit
- * machine the virtual address area will be unrun and we will have a failing
- * mmap() call.
- * solution:
- * only mmap 16M in one chunk and move the window as soon as we have finished
- * the first 8M
- *
- * read-ahead buffering
- * the problem:
- * sending out several large files in parallel trashes the read-ahead of the
- * kernel leading to long wait-for-seek times.
- * solutions: (increasing complexity)
- * 1. use madvise
- * 2. use a internal read-ahead buffer in the chunk-structure
- * 3. use non-blocking IO for file-transfers
- * */
-
- c->file.mmap.offset = mmap_align_offset(offset);
-
- /* all mmap()ed areas are MMAP_CHUNK_SIZE except the last which might be smaller */
- c->file.mmap.length = MMAP_CHUNK_SIZE;
- if (c->file.mmap.offset > file_end - (off_t)c->file.mmap.length) {
- c->file.mmap.length = file_end - c->file.mmap.offset;
- }
-
- if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) {
- log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:",
- strerror(errno), c->file.name, c->file.fd, c->file.mmap.offset, (off_t) c->file.mmap.length);
- return -1;
- }
-
-#if defined(LOCAL_BUFFERING)
- sigbus_jmp_valid = 1;
- buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length);
- sigbus_jmp_valid = 0;
-#else
-# if defined(HAVE_MADVISE)
- /* don't advise files < 64Kb */
- if (c->file.mmap.length > (64*1024)) {
- /* darwin 7 is returning EINVAL all the time and I don't know how to
- * detect this at runtime.
- *
- * ignore the return value for now */
- madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED);
- }
-# endif
-#endif
- }
-
- force_assert(offset >= c->file.mmap.offset);
- mmap_offset = offset - c->file.mmap.offset;
- force_assert(c->file.mmap.length > mmap_offset);
- mmap_avail = c->file.mmap.length - mmap_offset;
- if (toSend > (off_t) mmap_avail) toSend = mmap_avail;
-
-#if defined(LOCAL_BUFFERING)
- data = c->mem->ptr + mmap_offset;
-#else
- data = c->file.mmap.start + mmap_offset;
-#endif
-
- sigbus_jmp_valid = 1;
-#if defined(__WIN32)
- r = send(fd, data, toSend, 0);
-#else /* __WIN32 */
- r = write(fd, data, toSend);
-#endif /* __WIN32 */
- sigbus_jmp_valid = 0;
-
-#if defined(__WIN32)
- if (r < 0) {
- int lastError = WSAGetLastError();
- switch (lastError) {
- case WSAEINTR:
- case WSAEWOULDBLOCK:
- break;
- case WSAECONNRESET:
- case WSAETIMEDOUT:
- case WSAECONNABORTED:
- return -2;
- default:
- log_error_write(srv, __FILE__, __LINE__, "sdd",
- "send failed: ", lastError, fd);
- return -1;
- }
- }
-#else /* __WIN32 */
- if (r < 0) {
- switch (errno) {
- case EAGAIN:
- case EINTR:
- break;
- case EPIPE:
- case ECONNRESET:
- return -2;
- default:
- log_error_write(srv, __FILE__, __LINE__, "ssd",
- "write failed:", strerror(errno), fd);
- return -1;
- }
- }
-#endif /* __WIN32 */
-
- if (r >= 0) {
- *p_max_bytes -= r;
- chunkqueue_mark_written(cq, r);
- }
-
- return (r > 0 && r == toSend) ? 0 : -3;
-}
-
-#endif /* USE_MMAP */
diff --git a/src/network_write_no_mmap.c b/src/network_write_no_mmap.c
deleted file mode 100644
index 2268cc93..00000000
--- a/src/network_write_no_mmap.c
+++ /dev/null
@@ -1,84 +0,0 @@
-#include "first.h"
-
-#include "network_backends.h"
-#include "log.h"
-#include "sys-socket.h"
-
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-
-int network_write_file_chunk_no_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
- chunk* const c = cq->first;
- off_t offset, toSend;
- ssize_t r;
- UNUSED(con);
-
- force_assert(NULL != c);
- force_assert(FILE_CHUNK == c->type);
- force_assert(c->offset >= 0 && c->offset <= c->file.length);
-
- offset = c->file.start + c->offset;
- toSend = c->file.length - c->offset;
- if (toSend > 64*1024) toSend = 64*1024; /* max read 64kb in one step */
- if (toSend > *p_max_bytes) toSend = *p_max_bytes;
-
- if (0 == toSend) {
- chunkqueue_remove_finished_chunks(cq);
- return 0;
- }
-
- if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1;
-
- buffer_string_prepare_copy(srv->tmp_buf, toSend);
-
- if (-1 == lseek(c->file.fd, offset, SEEK_SET)) {
- log_error_write(srv, __FILE__, __LINE__, "ss", "lseek: ", strerror(errno));
- return -1;
- }
- if (-1 == (toSend = read(c->file.fd, srv->tmp_buf->ptr, toSend))) {
- log_error_write(srv, __FILE__, __LINE__, "ss", "read: ", strerror(errno));
- return -1;
- }
-
-#if defined(__WIN32)
- if ((r = send(fd, srv->tmp_buf->ptr, toSend, 0)) < 0) {
- int lastError = WSAGetLastError();
- switch (lastError) {
- case WSAEINTR:
- case WSAEWOULDBLOCK:
- break;
- case WSAECONNRESET:
- case WSAETIMEDOUT:
- case WSAECONNABORTED:
- return -2;
- default:
- log_error_write(srv, __FILE__, __LINE__, "sdd",
- "send failed: ", lastError, fd);
- return -1;
- }
- }
-#else /* __WIN32 */
- if ((r = write(fd, srv->tmp_buf->ptr, toSend)) < 0) {
- switch (errno) {
- case EAGAIN:
- case EINTR:
- break;
- case EPIPE:
- case ECONNRESET:
- return -2;
- default:
- log_error_write(srv, __FILE__, __LINE__, "ssd",
- "write failed:", strerror(errno), fd);
- return -1;
- }
- }
-#endif /* __WIN32 */
-
- if (r >= 0) {
- *p_max_bytes -= r;
- chunkqueue_mark_written(cq, r);
- }
-
- return (r > 0 && r == toSend) ? 0 : -3;
-}
diff --git a/src/network_writev.c b/src/network_writev.c
deleted file mode 100644
index 28edf589..00000000
--- a/src/network_writev.c
+++ /dev/null
@@ -1,121 +0,0 @@
-#include "first.h"
-
-#include "network_backends.h"
-
-#if defined(USE_WRITEV)
-
-#include "network.h"
-#include "log.h"
-
-#if defined(HAVE_SYS_UIO_H)
-# include <sys/uio.h>
-#endif
-
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-
-#if defined(UIO_MAXIOV)
-# define SYS_MAX_CHUNKS UIO_MAXIOV
-#elif defined(IOV_MAX)
-/* new name for UIO_MAXIOV since IEEE Std 1003.1-2001 */
-# define SYS_MAX_CHUNKS IOV_MAX
-#elif defined(_XOPEN_IOV_MAX)
-/* minimum value for sysconf(_SC_IOV_MAX); posix requires this to be at least 16, which is good enough - no need to call sysconf() */
-# define SYS_MAX_CHUNKS _XOPEN_IOV_MAX
-#else
-# error neither UIO_MAXIOV nor IOV_MAX nor _XOPEN_IOV_MAX are defined
-#endif
-
-/* allocate iovec[MAX_CHUNKS] on stack, so pick a sane limit:
- * - each entry will use 1 pointer + 1 size_t
- * - 32 chunks -> 256 / 512 bytes (32-bit/64-bit pointers)
- */
-#define STACK_MAX_ALLOC_CHUNKS 32
-#if SYS_MAX_CHUNKS > STACK_MAX_ALLOC_CHUNKS
-# define MAX_CHUNKS STACK_MAX_ALLOC_CHUNKS
-#else
-# define MAX_CHUNKS SYS_MAX_CHUNKS
-#endif
-
-int network_writev_mem_chunks(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
- struct iovec chunks[MAX_CHUNKS];
- size_t num_chunks;
- off_t max_bytes = *p_max_bytes;
- off_t toSend;
- ssize_t r;
- UNUSED(con);
-
- force_assert(NULL != cq->first);
- force_assert(MEM_CHUNK == cq->first->type);
-
- {
- chunk const *c;
-
- toSend = 0;
- num_chunks = 0;
- for (c = cq->first; NULL != c && MEM_CHUNK == c->type && num_chunks < MAX_CHUNKS && toSend < max_bytes; c = c->next) {
- size_t c_len;
-
- force_assert(c->offset >= 0 && c->offset <= (off_t)buffer_string_length(c->mem));
- c_len = buffer_string_length(c->mem) - c->offset;
- if (c_len > 0) {
- toSend += c_len;
-
- chunks[num_chunks].iov_base = c->mem->ptr + c->offset;
- chunks[num_chunks].iov_len = c_len;
-
- ++num_chunks;
- }
- }
- }
-
- if (0 == num_chunks) {
- chunkqueue_remove_finished_chunks(cq);
- return 0;
- }
-
- r = writev(fd, chunks, num_chunks);
-
- if (r < 0) switch (errno) {
- case EAGAIN:
- case EINTR:
- break;
- case EPIPE:
- case ECONNRESET:
- return -2;
- default:
- log_error_write(srv, __FILE__, __LINE__, "ssd",
- "writev failed:", strerror(errno), fd);
- return -1;
- }
-
- if (r >= 0) {
- *p_max_bytes -= r;
- chunkqueue_mark_written(cq, r);
- }
-
- return (r > 0 && r == toSend) ? 0 : -3;
-}
-
-#endif /* USE_WRITEV */
-
-int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) {
- while (max_bytes > 0 && NULL != cq->first) {
- int r = -1;
-
- switch (cq->first->type) {
- case MEM_CHUNK:
- r = network_writev_mem_chunks(srv, con, fd, cq, &max_bytes);
- break;
- case FILE_CHUNK:
- r = network_write_file_chunk_mmap(srv, con, fd, cq, &max_bytes);
- break;
- }
-
- if (-3 == r) return 0;
- if (0 != r) return r;
- }
-
- return 0;
-}
diff --git a/src/server.c b/src/server.c
index 4cd8699c..5bbee542 100644
--- a/src/server.c
+++ b/src/server.c
@@ -18,7 +18,7 @@
#include "stat_cache.h"
#include "plugin.h"
#include "joblist.h"
-#include "network_backends.h"
+#include "network_write.h"
#ifdef HAVE_VERSIONSTAMP_H
# include "versionstamp.h"
@@ -524,7 +524,8 @@ static void show_version (void) {
}
static void show_features (void) {
- const char features[] = ""
+ static const char event_handlers[] = ""
+ "\nEvent Handlers:\n\n"
#ifdef USE_SELECT
"\t+ select (generic)\n"
#else
@@ -560,38 +561,9 @@ static void show_features (void) {
#else
"\t- libev (generic)\n"
#endif
- "\nNetwork handler:\n\n"
-#if defined USE_LINUX_SENDFILE
- "\t+ linux-sendfile\n"
-#else
- "\t- linux-sendfile\n"
-#endif
-#if defined USE_FREEBSD_SENDFILE
- "\t+ freebsd-sendfile\n"
-#else
- "\t- freebsd-sendfile\n"
-#endif
-#if defined USE_DARWIN_SENDFILE
- "\t+ darwin-sendfile\n"
-#else
- "\t- darwin-sendfile\n"
-#endif
-#if defined USE_SOLARIS_SENDFILEV
- "\t+ solaris-sendfilev\n"
-#else
- "\t- solaris-sendfilev\n"
-#endif
-#if defined USE_WRITEV
- "\t+ writev\n"
-#else
- "\t- writev\n"
-#endif
- "\t+ write\n"
-#ifdef USE_MMAP
- "\t+ mmap support\n"
-#else
- "\t- mmap support\n"
-#endif
+ ;
+
+ static const char features[] =
"\nFeatures:\n\n"
#ifdef HAVE_IPV6
"\t+ IPv6 support\n"
@@ -678,9 +650,9 @@ static void show_features (void) {
#else
"\t- GDBM support\n"
#endif
- "\n";
+ ;
show_version();
- printf("\nEvent Handlers:\n\n%s", features);
+ printf("%s%s%s\n", event_handlers, network_write_show_handlers(), features);
}
static void show_help (void) {