[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
This commit is contained in:
Glenn Strauss 2016-04-29 00:11:45 -04:00
parent 6c35e38fe1
commit 1812f5541a
5 changed files with 151 additions and 3 deletions

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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];

View File

@ -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;