diff --git a/man/systemd-socket-activate.xml b/man/systemd-socket-activate.xml index 4c29fa2c66..09f0d5cad6 100644 --- a/man/systemd-socket-activate.xml +++ b/man/systemd-socket-activate.xml @@ -72,7 +72,7 @@ Launch an instance of the service program for each connection and pass the connection - socket. + socket. May not be combined with . @@ -135,6 +135,16 @@ + + + + Start the service program instantly, instead of waiting for a connection on the + socket(s). May not be combined with . + + + + + diff --git a/mkosi/mkosi.sanitizers/mkosi.postinst b/mkosi/mkosi.sanitizers/mkosi.postinst index 582a7cb99d..ee26021c32 100755 --- a/mkosi/mkosi.sanitizers/mkosi.postinst +++ b/mkosi/mkosi.sanitizers/mkosi.postinst @@ -77,6 +77,7 @@ wrap=( ps setfacl setpriv + socat sshd stat su diff --git a/src/socket-activate/socket-activate.c b/src/socket-activate/socket-activate.c index ada289ded3..a80080b9cd 100644 --- a/src/socket-activate/socket-activate.c +++ b/src/socket-activate/socket-activate.c @@ -30,6 +30,7 @@ static int arg_socket_type = SOCK_STREAM; static char **arg_setenv = NULL; static char **arg_fdnames = NULL; static bool arg_inetd = false; +static bool arg_now = false; static int add_epoll(int epoll_fd, int fd) { struct epoll_event ev = { @@ -102,9 +103,26 @@ static int open_sockets(int *ret_epoll_fd) { log_set_open_when_needed(false); } - epoll_fd = epoll_create1(EPOLL_CLOEXEC); - if (epoll_fd < 0) - return log_error_errno(errno, "Failed to create epoll object: %m"); + if (count > 1 && !arg_accept && arg_inetd) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--inetd only supported with a single file descriptor, or with --accept."); + + if (arg_fdnames && !arg_inetd) { + size_t n_fdnames = strv_length(arg_fdnames); + + if (!arg_accept && n_fdnames != (size_t) count) + log_warning("The number of fd names is different from the number of fds: %zu vs %i", + n_fdnames, count); + + if (arg_accept && n_fdnames > 1) + log_warning("More than one fd name specified with --accept."); + } + + if (!arg_now) { + epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (epoll_fd < 0) + return log_error_errno(errno, "Failed to create epoll object: %m"); + } for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) { _cleanup_free_ char *name = NULL; @@ -112,9 +130,11 @@ static int open_sockets(int *ret_epoll_fd) { getsockname_pretty(fd, &name); log_info("Listening on %s as %i.", strna(name), fd); - r = add_epoll(epoll_fd, fd); - if (r < 0) - return r; + if (epoll_fd >= 0) { + r = add_epoll(epoll_fd, fd); + if (r < 0) + return r; + } } *ret_epoll_fd = TAKE_FD(epoll_fd); @@ -129,10 +149,6 @@ static int exec_process(char * const *argv, int start_fd, size_t n_fds) { assert(start_fd >= 0); assert(n_fds > 0); - if (arg_inetd && n_fds != 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "--inetd only supported for single file descriptors."); - FOREACH_STRING(var, "TERM", "COLORTERM", "NO_COLOR", "PATH", "USER", "HOME") { const char *n; @@ -181,8 +197,6 @@ static int exec_process(char * const *argv, int start_fd, size_t n_fds) { if (r < 0) return log_oom(); } - else if (len != n_fds) - log_warning("The number of fd names is different than number of fds: %zu vs %zu", len, n_fds); names = strv_join(arg_fdnames, ":"); if (!names) @@ -314,6 +328,7 @@ static int help(void) { " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n" " --fdname=NAME[:NAME...] Specify names for file descriptors\n" " --inetd Enable inetd file descriptor passing protocol\n" + " --now Start instantly instead of waiting for connection\n" "\nNote: file descriptors from sd_listen_fds() will be passed through.\n" "\nSee the %s for details.\n", program_invocation_short_name, @@ -330,6 +345,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_FDNAME, ARG_SEQPACKET, ARG_INETD, + ARG_NOW, }; static const struct option options[] = { @@ -343,6 +359,7 @@ static int parse_argv(int argc, char *argv[]) { { "environment", required_argument, NULL, 'E' }, /* legacy alias */ { "fdname", required_argument, NULL, ARG_FDNAME }, { "inetd", no_argument, NULL, ARG_INETD }, + { "now", no_argument, NULL, ARG_NOW }, {} }; @@ -423,6 +440,10 @@ static int parse_argv(int argc, char *argv[]) { arg_inetd = true; break; + case ARG_NOW: + arg_now = true; + break; + case '?': return -EINVAL; @@ -440,6 +461,13 @@ static int parse_argv(int argc, char *argv[]) { "Datagram sockets do not accept connections. " "The --datagram and --accept options may not be combined."); + if (arg_accept && arg_now) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--now cannot be used in conjunction with --accept."); + + if (arg_fdnames && arg_inetd) + log_warning("--fdname= has no effect with --inetd present."); + return 1 /* work to do */; } @@ -481,15 +509,17 @@ static int run(int argc, char **argv) { for (;;) { struct epoll_event event; - if (epoll_wait(epoll_fd, &event, 1, -1) < 0) { - if (errno == EINTR) - continue; + if (epoll_fd >= 0) { + if (epoll_wait(epoll_fd, &event, 1, -1) < 0) { + if (errno == EINTR) + continue; - return log_error_errno(errno, "epoll_wait() failed: %m"); + return log_error_errno(errno, "epoll_wait() failed: %m"); + } + + log_info("Communication attempt on fd %i.", event.data.fd); } - log_info("Communication attempt on fd %i.", event.data.fd); - if (!arg_accept) return exec_process(exec_argv, SD_LISTEN_FDS_START, (size_t) n); diff --git a/test/units/TEST-74-AUX-UTILS.socket-activate.sh b/test/units/TEST-74-AUX-UTILS.socket-activate.sh new file mode 100755 index 0000000000..b714ae46c9 --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.socket-activate.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +CAT_PID="$(systemd-notify --fork -- systemd-socket-activate -l 1234 --accept --inetd cat)" +assert_eq "$(echo -n hello | socat - 'TCP:localhost:1234')" hello +kill "$CAT_PID" + +# Check whether socat's ACCEPT-FD is available (introduced in v1.8) +systemd-socket-activate -l 1234 --now socat ACCEPT-FD:3 PIPE & +sleep 1 +jobs >/dev/null +if kill %% &>/dev/null; then + systemd-socket-activate -l 1234 --now socat ACCEPT-FD:3 PIPE & + SOCAT_PID="$!" + + # unfortunately we need to sleep since socket-activate only sends sd_notify when --accept is passed, + # so we can't rely on that to avoid a race. + sleep 1 + + assert_in socat "$(