diff --git a/src/base.h b/src/base.h index 46a82983..b3c508c8 100644 --- a/src/base.h +++ b/src/base.h @@ -347,6 +347,7 @@ typedef struct { buffer *syslog_facility; unsigned short compat_module_load; + unsigned short systemd_socket_activation; } server_config; typedef struct server_socket { @@ -459,6 +460,8 @@ struct server { uid_t uid; gid_t gid; pid_t pid; + + server_socket_array srv_sockets_inherited; }; diff --git a/src/configfile.c b/src/configfile.c index ce6b9716..f3f69fc1 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -277,6 +277,7 @@ static int config_insert(server *srv) { { "server.syslog-facility", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 80 */ { "server.socket-perms", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 81 */ { "server.http-parseopts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_SERVER }, /* 82 */ + { "server.systemd-socket-activation", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 83 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; @@ -321,6 +322,7 @@ static int config_insert(server *srv) { cv[80].destination = srv->srvconf.syslog_facility; http_parseopts = array_init(); cv[82].destination = http_parseopts; + cv[83].destination = &(srv->srvconf.systemd_socket_activation); srv->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); diff --git a/src/network.c b/src/network.c index 919a7326..016d0d5b 100644 --- a/src/network.c +++ b/src/network.c @@ -219,6 +219,17 @@ static int network_server_init(server *srv, buffer *host_token, size_t sidx, int return 0; } + if (srv->srvconf.systemd_socket_activation) { + for (size_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { + if (0 != memcmp(&srv->srv_sockets_inherited.ptr[i]->addr, &srv_socket->addr, addr_len)) continue; + if ((unsigned short)~0u == srv->srv_sockets_inherited.ptr[i]->sidx) { + srv->srv_sockets_inherited.ptr[i]->sidx = sidx; + } + stdin_fd = srv->srv_sockets_inherited.ptr[i]->fd; + break; + } + } + if (-1 != stdin_fd) { srv_socket->fd = stdin_fd; if (-1 == fdevent_fcntl_set_nb_cloexec(srv->ev, stdin_fd)) { @@ -367,12 +378,65 @@ int network_close(server *srv) { srv->srv_sockets.used = 0; srv->srv_sockets.size = 0; + for (i = 0; i < srv->srv_sockets_inherited.used; i++) { + server_socket *srv_socket = srv->srv_sockets_inherited.ptr[i]; + if (srv_socket->fd != -1 && srv_socket->sidx != (unsigned short)~0u) { + close(srv_socket->fd); + } + + buffer_free(srv_socket->srv_token); + + free(srv_socket); + } + + free(srv->srv_sockets_inherited.ptr); + srv->srv_sockets_inherited.ptr = NULL; + srv->srv_sockets_inherited.used = 0; + srv->srv_sockets_inherited.size = 0; + return 0; } -int network_init(server *srv, int stdin_fd) { - size_t i; +static int network_socket_activation_nfds(server *srv, int nfds) { + buffer *host = buffer_init(); + socklen_t addr_len; + sock_addr addr; + int rc = 0; + nfds += 3; /* #define SD_LISTEN_FDS_START 3 */ + for (int fd = 3; fd < nfds; ++fd) { + addr_len = sizeof(sock_addr); + if (-1 == (rc = getsockname(fd, (struct sockaddr *)&addr, &addr_len))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "socket activation getsockname()", strerror(errno)); + break; + } + network_host_normalize_addr_str(host, &addr); + rc = network_server_init(srv, host, 0, fd); + if (0 != rc) break; + srv->srv_sockets.ptr[srv->srv_sockets.used-1]->sidx = (unsigned short)~0u; + } + buffer_free(host); + memcpy(&srv->srv_sockets_inherited, &srv->srv_sockets, sizeof(server_socket_array)); + memset(&srv->srv_sockets, 0, sizeof(server_socket_array)); + return rc; +} +static int network_socket_activation_from_env(server *srv) { + char *listen_pid = getenv("LISTEN_PID"); + char *listen_fds = getenv("LISTEN_FDS"); + pid_t lpid = listen_pid ? (pid_t)strtoul(listen_pid,NULL,10) : 0; + int nfds = listen_fds ? atoi(listen_fds) : 0; + int rc = (lpid == getpid() && nfds > 0) + ? network_socket_activation_nfds(srv, nfds) + : 0; + unsetenv("LISTEN_PID"); + unsetenv("LISTEN_FDS"); + unsetenv("LISTEN_FDNAMES"); + /*(upon graceful restart, unsetenv will result in no-op above)*/ + return rc; +} + +int network_init(server *srv, int stdin_fd) { #ifdef __WIN32 WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); @@ -384,7 +448,19 @@ int network_init(server *srv, int stdin_fd) { if (0 != network_write_init(srv)) return -1; - { + if (srv->srvconf.systemd_socket_activation) { + for (size_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { + srv->srv_sockets_inherited.ptr[i]->sidx = (unsigned short)~0u; + } + if (0 != network_socket_activation_from_env(srv)) return -1; + if (0 == srv->srv_sockets_inherited.used) { + srv->srvconf.systemd_socket_activation = 0; + } + } + + /* process srv->srvconf.bindhost + * (skip if systemd socket activation is enabled and bindhost is empty; do not additionally listen on "*") */ + if (!srv->srvconf.systemd_socket_activation || !buffer_string_is_empty(srv->srvconf.bindhost)) { int rc; buffer *b = buffer_init(); buffer_copy_buffer(b, srv->srvconf.bindhost); @@ -401,7 +477,7 @@ int network_init(server *srv, int stdin_fd) { } /* check for $SERVER["socket"] */ - for (i = 1; i < srv->config_context->used; i++) { + for (size_t i = 1; i < srv->config_context->used; ++i) { data_config *dc = (data_config *)srv->config_context->data[i]; /* not our stage */ @@ -422,6 +498,19 @@ int network_init(server *srv, int stdin_fd) { if (0 != network_server_init(srv, dc->string, i, -1)) return -1; } + if (srv->srvconf.systemd_socket_activation) { + /* activate any inherited sockets not explicitly listed in config file */ + server_socket *srv_socket; + for (size_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { + if ((unsigned short)~0u != srv->srv_sockets_inherited.ptr[i]->sidx) continue; + srv->srv_sockets_inherited.ptr[i]->sidx = 0; + srv_socket = calloc(1, sizeof(server_socket)); + force_assert(NULL != srv_socket); + memcpy(srv_socket, srv->srv_sockets_inherited.ptr[i], sizeof(server_socket)); + network_srv_sockets_append(srv, srv_socket); + } + } + return 0; } diff --git a/src/server.c b/src/server.c index cbaac0ff..1be67c30 100644 --- a/src/server.c +++ b/src/server.c @@ -87,6 +87,7 @@ static int oneshot_fd = 0; static volatile int pid_fd = -2; static server_socket_array graceful_sockets; +static server_socket_array inherited_sockets; static volatile sig_atomic_t graceful_restart = 0; static volatile sig_atomic_t graceful_shutdown = 0; static volatile sig_atomic_t srv_shutdown = 0; @@ -301,6 +302,7 @@ static server *server_init(void) { srv->srvconf.loadavg[1] = 0.0; srv->srvconf.loadavg[2] = 0.0; srv->srvconf.compat_module_load = 1; + srv->srvconf.systemd_socket_activation = 0; /* use syslog */ srv->errorlog_fd = STDERR_FILENO; @@ -856,11 +858,15 @@ static int log_error_close(server *srv) { static void server_sockets_save (server *srv) { /* graceful_restart */ memcpy(&graceful_sockets, &srv->srv_sockets, sizeof(server_socket_array)); memset(&srv->srv_sockets, 0, sizeof(server_socket_array)); + memcpy(&inherited_sockets, &srv->srv_sockets_inherited, sizeof(server_socket_array)); + memset(&srv->srv_sockets_inherited, 0, sizeof(server_socket_array)); } static void server_sockets_restore (server *srv) { /* graceful_restart */ memcpy(&srv->srv_sockets, &graceful_sockets, sizeof(server_socket_array)); memset(&graceful_sockets, 0, sizeof(server_socket_array)); + memcpy(&srv->srv_sockets_inherited, &inherited_sockets, sizeof(server_socket_array)); + memset(&inherited_sockets, 0, sizeof(server_socket_array)); } static int server_sockets_set_nb_cloexec (server *srv) { @@ -1003,6 +1009,7 @@ static int server_main (server * const srv, int argc, char **argv) { /*graceful_restart = 0;*//*(reset below to avoid further daemonizing)*/ /*(intentionally preserved)*/ /*memset(graceful_sockets, 0, sizeof(graceful_sockets));*/ + /*memset(inherited_sockets, 0, sizeof(inherited_sockets));*/ /*pid_fd = -1;*/ srv->srvconf.port = 0;