1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

activate: add a new switch --inetd to enable inetd-style socket activation

Previously, using --accept would enable inetd-style socket activation in addition to per-connection operation. This is
now split into two switches: --accept only switches between per-connection or single-instance operation. --inetd
switches between inetd-style or new-style fd passing.

This breaks the interface of the tool, but given that it is a debugging tool shipped in /usr/lib/systemd/ it's not
really a public interface.

This change allows testing new-style per-connection daemons.
This commit is contained in:
Lennart Poettering 2016-02-08 21:13:09 +01:00
parent 08719b64e4
commit eef0a274e6
2 changed files with 105 additions and 81 deletions

View File

@ -60,27 +60,21 @@
<refsect1>
<title>Description</title>
<para><command>systemd-activate</command> can be used to
launch a socket-activated daemon from the command line for
testing purposes. It can also be used to launch single instances
of the daemon per connection (inetd-style).
<para><command>systemd-activate</command> may be used to launch a socket-activated service binary from the command
line for testing purposes. It may also be used to launch individual instances of the service binary per connection.
</para>
<para>The daemon to launch and its options should be specified
after options intended for <command>systemd-activate</command>.
</para>
<para>If the <option>-a</option> option is given, file descriptor
of the connection will be used as the standard input and output of
the launched process. Otherwise, standard input and output will be
inherited, and sockets will be passed through file descriptors 3
and higher. Sockets passed through <varname>$LISTEN_FDS</varname>
to <command>systemd-activate</command> will be passed through to
the daemon, in the original positions. Other sockets specified
with <option>--listen</option> will use consecutive descriptors.
By default, <command>systemd-activate</command> listens on a
stream socket, use <option>--datagram</option> to listen on
a datagram socket instead (see below).
<para>If the <option>--inetd</option> option is given, the socket file descriptor will be used as the standard
input and output of the launched process. Otherwise, standard input and output will be inherited, and sockets will
be passed through file descriptors 3 and higher. Sockets passed through <varname>$LISTEN_FDS</varname> to
<command>systemd-activate</command> will be passed through to the daemon, in the original positions. Other sockets
specified with <option>--listen=</option> will use consecutive descriptors. By default,
<command>systemd-activate</command> listens on a stream socket, use <option>--datagram</option> and
<option>--seqpacket</option> to listen on datagram or sequential packet sockets instead (see below).
</para>
</refsect1>
@ -101,9 +95,8 @@
<term><option>-a</option></term>
<term><option>--accept</option></term>
<listitem><para>Launch a separate instance of daemon per
connection and pass the connection socket as standard input
and standard output.</para></listitem>
<listitem><para>Launch an instance of the service binary for each connection and pass the connection
socket.</para></listitem>
</varlistentry>
<varlistentry>
@ -122,6 +115,14 @@
<option>--datagram</option>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--inetd</option></term>
<listitem><para>Use the inetd protocol for passing file descriptors, i.e. as standard input and standard
output, instead of the new-style protocol for passing file descriptors using <varname>$LISTEN_FDS</varname>
(see above).</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-E <replaceable>VAR</replaceable><optional>=<replaceable>VALUE</replaceable></optional></option></term>
<term><option>--setenv=<replaceable>VAR</replaceable><optional>=<replaceable>VALUE</replaceable></optional></option></term>
@ -179,7 +180,7 @@
<example>
<title>Run an echo server on port 2000</title>
<programlisting>$ /usr/lib/systemd/systemd-activate -l 2000 -a cat</programlisting>
<programlisting>$ /usr/lib/systemd/systemd-activate -l 2000 --inetd -a cat</programlisting>
</example>
<example>

View File

@ -41,6 +41,7 @@ static int arg_socket_type = SOCK_STREAM;
static char** arg_args = NULL;
static char** arg_setenv = NULL;
static const char *arg_fdname = NULL;
static bool arg_inetd = false;
static int add_epoll(int epoll_fd, int fd) {
struct epoll_event ev = {
@ -127,14 +128,20 @@ static int open_sockets(int *epoll_fd, bool accept) {
return count;
}
static int launch(char* name, char **argv, char **env, int fds) {
static int exec_process(const char* name, char **argv, char **env, int start_fd, int n_fds) {
static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
_cleanup_strv_free_ char **envp = NULL;
_cleanup_free_ char *tmp = NULL;
_cleanup_free_ char *joined = NULL;
unsigned n_env = 0, length;
char **s;
const char *tocopy;
unsigned i;
char **s;
int r;
if (arg_inetd && n_fds != 1) {
log_error("--inetd only supported for single file descriptors.");
return -EINVAL;
}
length = strv_length(arg_setenv);
@ -144,6 +151,7 @@ static int launch(char* name, char **argv, char **env, int fds) {
return log_oom();
STRV_FOREACH(s, arg_setenv) {
if (strchr(*s, '=')) {
char *k;
@ -167,13 +175,15 @@ static int launch(char* name, char **argv, char **env, int fds) {
envp[n_env] = strdup(n);
if (!envp[n_env])
return log_oom();
n_env ++;
}
}
for (i = 0; i < ELEMENTSOF(tocopy); i++) {
FOREACH_STRING(tocopy, "TERM=", "PATH=", "USER=", "HOME=") {
const char *n;
n = strv_find_prefix(env, tocopy[i]);
n = strv_find_prefix(env, tocopy);
if (!n)
continue;
@ -184,50 +194,76 @@ static int launch(char* name, char **argv, char **env, int fds) {
n_env ++;
}
if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
(asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
return log_oom();
if (arg_inetd) {
assert(n_fds == 1);
if (arg_fdname) {
char *e;
r = dup2(start_fd, STDIN_FILENO);
if (r < 0)
return log_error_errno(errno, "Failed to dup connection to stdin: %m");
e = strappend("LISTEN_FDNAMES=", arg_fdname);
if (!e)
return log_oom();
r = dup2(start_fd, STDOUT_FILENO);
if (r < 0)
return log_error_errno(errno, "Failed to dup connection to stdout: %m");
for (i = 1; i < (unsigned) fds; i++) {
char *c;
start_fd = safe_close(start_fd);
} else {
if (start_fd != SD_LISTEN_FDS_START) {
assert(n_fds == 1);
c = strjoin(e, ":", arg_fdname, NULL);
if (!c) {
free(e);
return log_oom();
}
r = dup2(start_fd, SD_LISTEN_FDS_START);
if (r < 0)
return log_error_errno(errno, "Failed to dup connection: %m");
free(e);
e = c;
safe_close(start_fd);
start_fd = SD_LISTEN_FDS_START;
}
envp[n_env++] = e;
if (asprintf((char**)(envp + n_env++), "LISTEN_FDS=%i", n_fds) < 0)
return log_oom();
if (asprintf((char**)(envp + n_env++), "LISTEN_PID=" PID_FMT, getpid()) < 0)
return log_oom();
if (arg_fdname) {
char *e;
e = strappend("LISTEN_FDNAMES=", arg_fdname);
if (!e)
return log_oom();
for (i = 1; i < (unsigned) n_fds; i++) {
char *c;
c = strjoin(e, ":", arg_fdname, NULL);
if (!c) {
free(e);
return log_oom();
}
free(e);
e = c;
}
envp[n_env++] = e;
}
}
tmp = strv_join(argv, " ");
if (!tmp)
joined = strv_join(argv, " ");
if (!joined)
return log_oom();
log_info("Execing %s (%s)", name, tmp);
log_info("Execing %s (%s)", name, joined);
execvpe(name, argv, envp);
return log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp);
return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined);
}
static int launch1(const char* child, char** argv, char **env, int fd) {
_cleanup_free_ char *tmp = NULL;
static int fork_and_exec_process(const char* child, char** argv, char **env, int fd) {
_cleanup_free_ char *joined = NULL;
pid_t parent_pid, child_pid;
int r;
tmp = strv_join(argv, " ");
if (!tmp)
joined = strv_join(argv, " ");
if (!joined)
return log_oom();
parent_pid = getpid();
@ -242,24 +278,6 @@ static int launch1(const char* child, char** argv, char **env, int fd) {
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
r = dup2(fd, STDIN_FILENO);
if (r < 0) {
log_error_errno(errno, "Failed to dup connection to stdin: %m");
_exit(EXIT_FAILURE);
}
r = dup2(fd, STDOUT_FILENO);
if (r < 0) {
log_error_errno(errno, "Failed to dup connection to stdout: %m");
_exit(EXIT_FAILURE);
}
r = close(fd);
if (r < 0) {
log_error_errno(errno, "Failed to close dupped connection: %m");
_exit(EXIT_FAILURE);
}
/* Make sure the child goes away when the parent dies */
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
_exit(EXIT_FAILURE);
@ -269,29 +287,27 @@ static int launch1(const char* child, char** argv, char **env, int fd) {
if (getppid() != parent_pid)
_exit(EXIT_SUCCESS);
execvp(child, argv);
log_error_errno(errno, "Failed to exec child %s: %m", child);
exec_process(child, argv, env, fd, 1);
_exit(EXIT_FAILURE);
}
log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
log_info("Spawned %s (%s) as PID %d", child, joined, child_pid);
return 0;
}
static int do_accept(const char* name, char **argv, char **envp, int fd) {
_cleanup_free_ char *local = NULL, *peer = NULL;
_cleanup_close_ int fd2 = -1;
_cleanup_close_ int fd_accepted = -1;
fd2 = accept4(fd, NULL, NULL, 0);
if (fd2 < 0)
fd_accepted = accept4(fd, NULL, NULL, 0);
if (fd_accepted < 0)
return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
getsockname_pretty(fd2, &local);
getpeername_pretty(fd2, true, &peer);
getsockname_pretty(fd_accepted, &local);
getpeername_pretty(fd_accepted, true, &peer);
log_info("Connection from %s to %s", strna(peer), strna(local));
return launch1(name, argv, envp, fd2);
return fork_and_exec_process(name, argv, envp, fd_accepted);
}
/* SIGCHLD handler. */
@ -330,6 +346,7 @@ static void help(void) {
" --seqpacket Listen on SOCK_SEQPACKET instead of stream socket\n"
" -a --accept Spawn separate child for each connection\n"
" -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
" --inetd Enable inetd file descriptor passing protocol\n"
"\n"
"Note: file descriptors from sd_listen_fds() will be passed through.\n"
, program_invocation_short_name);
@ -340,6 +357,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERSION = 0x100,
ARG_FDNAME,
ARG_SEQPACKET,
ARG_INETD,
};
static const struct option options[] = {
@ -352,6 +370,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "setenv", required_argument, NULL, 'E' },
{ "environment", required_argument, NULL, 'E' }, /* legacy alias */
{ "fdname", required_argument, NULL, ARG_FDNAME },
{ "inetd", no_argument, NULL, ARG_INETD },
{}
};
@ -414,6 +433,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_fdname = optarg;
break;
case ARG_INETD:
arg_inetd = true;
break;
case '?':
return -EINVAL;
@ -482,7 +505,7 @@ int main(int argc, char **argv, char **envp) {
break;
}
launch(argv[optind], argv + optind, envp, n);
exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, n);
return EXIT_SUCCESS;
}