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 "$(