summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Strauss <gstrauss@gluelogic.com>2016-04-29 00:11:45 -0400
committerGlenn Strauss <gstrauss@gluelogic.com>2016-05-02 15:29:33 -0400
commit1812f5541a1fffb720180f958cb36ae7a8fcab3d (patch)
treed726670828163c616ca316785e8042995900d604
parent6c35e38fe112eb75bf993e258a293f3b2386bc91 (diff)
downloadlighttpd-git-1812f5541a1fffb720180f958cb36ae7a8fcab3d.tar.gz
[core] lighttpd -1 handles single request on stdin socket (fixes #1584)
(e.g. when called from xinetd) Note: lighttpd is designed as a high performance, long-running server, not a one-shot executable. This one-shot mode of operation has not been tuned for performance. lighttpd server start-up and initialization aims for correctness, not speed. If using this one-shot mode as part of fork and exec from xinetd, then performance is already not of high concern. x-ref: "support for xinetd" https://redmine.lighttpd.net/issues/1584
-rw-r--r--doc/lighttpd.83
-rw-r--r--src/connections.c8
-rw-r--r--src/connections.h1
-rw-r--r--src/network.c10
-rw-r--r--src/server.c132
5 files changed, 151 insertions, 3 deletions
diff --git a/doc/lighttpd.8 b/doc/lighttpd.8
index 5c4211b2..e2eb10c6 100644
--- a/doc/lighttpd.8
+++ b/doc/lighttpd.8
@@ -42,6 +42,9 @@ Do not daemonize (go into background). The default is to daemonize.
\fB\-i\ \fP \fIsecs\fP
Trigger daemon graceful shutdown after \fIsecs\fP of inactivity.
.TP 8
+\fB\-1\fP
+Process single (one) request on stdin socket, then exit.
+.TP 8
\fB\-v\fP
Show version and exit.
.TP 8
diff --git a/src/connections.c b/src/connections.c
index d9aaaebf..7d5072b0 100644
--- a/src/connections.c
+++ b/src/connections.c
@@ -894,6 +894,11 @@ connection *connection_accept(server *srv, server_socket *srv_socket) {
}
return NULL;
} else {
+ return connection_accepted(srv, srv_socket, &cnt_addr, cnt);
+ }
+}
+
+connection *connection_accepted(server *srv, server_socket *srv_socket, sock_addr *cnt_addr, int cnt) {
connection *con;
srv->cur_fds++;
@@ -917,7 +922,7 @@ connection *connection_accept(server *srv, server_socket *srv_socket) {
connection_set_state(srv, con, CON_STATE_REQUEST_START);
con->connection_start = srv->cur_ts;
- con->dst_addr = cnt_addr;
+ con->dst_addr = *cnt_addr;
buffer_copy_string(con->dst_addr_buf, inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
con->srv_socket = srv_socket;
@@ -947,7 +952,6 @@ connection *connection_accept(server *srv, server_socket *srv_socket) {
}
#endif
return con;
- }
}
diff --git a/src/connections.h b/src/connections.h
index 92cdc618..7a42b955 100644
--- a/src/connections.h
+++ b/src/connections.h
@@ -10,6 +10,7 @@ int connection_reset(server *srv, connection *con);
void connections_free(server *srv);
connection * connection_accept(server *srv, server_socket *srv_sock);
+connection * connection_accepted(server *srv, server_socket *srv_socket, sock_addr *cnt_addr, int cnt);
int connection_set_state(server *srv, connection *con, connection_state_t state);
const char * connection_get_state(connection_state_t state);
diff --git a/src/network.c b/src/network.c
index 2c5c8fb5..5b64cdc0 100644
--- a/src/network.c
+++ b/src/network.c
@@ -334,6 +334,13 @@ static int network_server_init(server *srv, buffer *host_token, specific_config
goto error_free_socket;
}
+ if (srv->sockets_disabled) { /* lighttpd -1 (one-shot mode) */
+#ifdef USE_OPENSSL
+ if (s->ssl_enabled) srv_socket->ssl_ctx = s->ssl_ctx;
+#endif
+ goto srv_sockets_append;
+ }
+
#ifdef HAVE_SYS_UN_H
if (AF_UNIX == srv_socket->addr.plain.sa_family) {
/* check if the socket exists and try to connect to it. */
@@ -455,6 +462,7 @@ static int network_server_init(server *srv, buffer *host_token, specific_config
#endif
}
+srv_sockets_append:
srv_socket->is_ssl = s->ssl_enabled;
if (srv->srv_sockets.size == 0) {
@@ -1026,6 +1034,8 @@ int network_register_fdevents(server *srv) {
return -1;
}
+ if (srv->sockets_disabled) return 0; /* lighttpd -1 (one-shot mode) */
+
/* register fdevents after reset */
for (i = 0; i < srv->srv_sockets.used; i++) {
server_socket *srv_socket = srv->srv_sockets.ptr[i];
diff --git a/src/server.c b/src/server.c
index 42e0ef04..1119efd0 100644
--- a/src/server.c
+++ b/src/server.c
@@ -424,6 +424,110 @@ static void remove_pid_file(server *srv, int *pid_fd) {
}
}
+
+static server_socket * server_oneshot_getsock(server *srv, sock_addr *cnt_addr) {
+ server_socket *srv_socket, *srv_socket_wild = NULL;
+ size_t i;
+ for (i = 0; i < srv->srv_sockets.used; ++i) {
+ srv_socket = srv->srv_sockets.ptr[i];
+ if (cnt_addr->plain.sa_family != srv_socket->addr.plain.sa_family) continue;
+ switch (cnt_addr->plain.sa_family) {
+ case AF_INET:
+ if (srv_socket->addr.ipv4.sin_port != cnt_addr->ipv4.sin_port) continue;
+ if (srv_socket->addr.ipv4.sin_addr.s_addr == cnt_addr->ipv4.sin_addr.s_addr) {
+ return srv_socket;
+ }
+ if (srv_socket->addr.ipv4.sin_addr.s_addr == htonl(INADDR_ANY)) {
+ srv_socket_wild = srv_socket;
+ }
+ continue;
+ #ifdef HAVE_IPV6
+ case AF_INET6:
+ if (srv_socket->addr.ipv6.sin6_port != cnt_addr->ipv6.sin6_port) continue;
+ if (0 == memcmp(&srv_socket->addr.ipv6.sin6_addr, &cnt_addr->ipv6.sin6_addr, sizeof(struct in6_addr))) {
+ return srv_socket;
+ }
+ if (0 == memcmp(&srv_socket->addr.ipv6.sin6_addr, &in6addr_any, sizeof(struct in6_addr))) {
+ srv_socket_wild = srv_socket;
+ }
+ continue;
+ #endif
+ #ifdef HAVE_SYS_UN_H
+ case AF_UNIX:
+ if (0 == strcmp(srv_socket->addr.un.sun_path, cnt_addr->un.sun_path)) {
+ return srv_socket;
+ }
+ continue;
+ #endif
+ default: continue;
+ }
+ }
+
+ if (NULL != srv_socket_wild) {
+ return srv_socket_wild;
+ } else if (srv->srv_sockets.used) {
+ return srv->srv_sockets.ptr[0];
+ } else {
+ log_error_write(srv, __FILE__, __LINE__, "s", "no sockets configured");
+ return NULL;
+ }
+}
+
+
+static int server_oneshot_init(server *srv, int fd) {
+ /* Note: does not work with netcat due to requirement that fd be socket.
+ * STDOUT_FILENO was not saved earlier in startup, and that is to where
+ * netcat expects output to be sent. Since lighttpd expects connections
+ * to be sockets, con->fd is where output is sent; separate fds are not
+ * stored for input and output, but netcat has different fds for stdin
+ * and * stdout. To support netcat, would additionally need to avoid
+ * S_ISSOCK(), getsockname(), and getpeername() below, reconstructing
+ * addresses from environment variables:
+ * NCAT_LOCAL_ADDR NCAT_LOCAL_PORT
+ * NCAT_REMOTE_ADDR NCAT_REMOTE_PORT
+ * NCAT_PROTO
+ */
+ connection *con;
+ server_socket *srv_socket;
+ sock_addr cnt_addr;
+ socklen_t cnt_len;
+ struct stat st;
+
+ if (0 != fstat(fd, &st)) {
+ log_error_write(srv, __FILE__, __LINE__, "ss", "fstat:", strerror(errno));
+ return 0;
+ }
+
+ if (!S_ISSOCK(st.st_mode)) {
+ /* require that fd is a socket
+ * (modules might expect STDIN_FILENO and STDOUT_FILENO opened to /dev/null) */
+ log_error_write(srv, __FILE__, __LINE__, "s", "lighttpd -1 stdin is not a socket");
+ return 0;
+ }
+
+ cnt_len = sizeof(cnt_addr);
+ if (0 != getsockname(fd, (struct sockaddr *)&cnt_addr, &cnt_len)) {
+ log_error_write(srv, __FILE__, __LINE__, "ss", "getsockname:", strerror(errno));
+ return 0;
+ }
+
+ srv_socket = server_oneshot_getsock(srv, &cnt_addr);
+ if (NULL == srv_socket) return 0;
+
+ cnt_len = sizeof(cnt_addr);
+ if (0 != getpeername(fd, (struct sockaddr *)&cnt_addr, &cnt_len)) {
+ log_error_write(srv, __FILE__, __LINE__, "ss", "getpeername:", strerror(errno));
+ return 0;
+ }
+
+ con = connection_accepted(srv, srv_socket, &cnt_addr, fd);
+ if (NULL == con) return 0;
+
+ connection_state_machine(srv, con);
+ return 1;
+}
+
+
static void show_version (void) {
#ifdef USE_OPENSSL
# define TEXT_SSL " (ssl)"
@@ -600,6 +704,7 @@ static void show_help (void) {
" -f <name> filename of the config-file\n" \
" -m <name> module directory (default: "LIBRARY_DIR")\n" \
" -i <secs> graceful shutdown after <secs> of inactivity\n" \
+" -1 process single (one) request on stdin socket, then exit\n" \
" -p print the parsed config-file in internal form, and exit\n" \
" -t test the config-file, and exit\n" \
" -D don't go to background (default: go to background)\n" \
@@ -633,6 +738,7 @@ int main (int argc, char **argv) {
#ifdef HAVE_FORK
int parent_pipe_fd = -1;
#endif
+ int oneshot_fd = 0;
#ifdef USE_ALARM
struct itimerval interval;
@@ -662,7 +768,7 @@ int main (int argc, char **argv) {
srv->srvconf.dont_daemonize = 0;
srv->srvconf.preflight_check = 0;
- while(-1 != (o = getopt(argc, argv, "f:m:i:hvVDpt"))) {
+ while(-1 != (o = getopt(argc, argv, "f:m:i:hvVD1pt"))) {
switch(o) {
case 'f':
if (srv->config_storage) {
@@ -694,6 +800,7 @@ int main (int argc, char **argv) {
}
case 'p': print_config = 1; break;
case 't': ++test_config; break;
+ case '1': oneshot_fd = dup(STDIN_FILENO); break;
case 'D': srv->srvconf.dont_daemonize = 1; break;
case 'v': show_version(); server_free(srv); return 0;
case 'V': show_features(); server_free(srv); return 0;
@@ -740,6 +847,24 @@ int main (int argc, char **argv) {
return 0;
}
+ if (oneshot_fd) {
+ if (oneshot_fd <= STDERR_FILENO) {
+ log_error_write(srv, __FILE__, __LINE__, "s",
+ "Invalid fds at startup with lighttpd -1");
+ server_free(srv);
+ return -1;
+ }
+ graceful_shutdown = 1;
+ srv->sockets_disabled = 1;
+ srv->srvconf.dont_daemonize = 1;
+ buffer_reset(srv->srvconf.pid_file);
+ if (srv->srvconf.max_worker) {
+ srv->srvconf.max_worker = 0;
+ log_error_write(srv, __FILE__, __LINE__, "s",
+ "server one-shot command line option disables server.max-worker config file option.");
+ }
+ }
+
/* close stdin and stdout, as they are not needed */
openDevNull(STDIN_FILENO);
openDevNull(STDOUT_FILENO);
@@ -1344,12 +1469,17 @@ int main (int argc, char **argv) {
for (i = 0; i < srv->srv_sockets.used; i++) {
server_socket *srv_socket = srv->srv_sockets.ptr[i];
+ if (srv->sockets_disabled) continue; /* lighttpd -1 (one-shot mode) */
if (-1 == fdevent_fcntl_set(srv->ev, srv_socket->fd)) {
log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed:", strerror(errno));
return -1;
}
}
+ if (oneshot_fd && !server_oneshot_init(srv, oneshot_fd)) {
+ close(oneshot_fd);
+ }
+
/* main-loop */
while (!srv_shutdown) {
int n;