diff options
author | Lennart Poettering <lennart@poettering.net> | 2016-02-08 21:13:09 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2016-02-10 14:32:27 +0100 |
commit | eef0a274e6187d1efb8fffaf66db94b8738662a0 (patch) | |
tree | 6c5ed23ecd12ee1867aeee0879ca2c4f99ea04c5 | |
parent | 08719b64e4de1a1197074fd2e2d18b7259f27232 (diff) |
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.
-rw-r--r-- | man/systemd-activate.xml | 39 | ||||
-rw-r--r-- | src/activate/activate.c | 143 |
2 files changed, 103 insertions, 79 deletions
diff --git a/man/systemd-activate.xml b/man/systemd-activate.xml index 61a2bc8608..995e6eecce 100644 --- a/man/systemd-activate.xml +++ b/man/systemd-activate.xml @@ -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> @@ -123,6 +116,14 @@ </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> diff --git a/src/activate/activate.c b/src/activate/activate.c index 559ce33d89..0db4967edb 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -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) + r = dup2(start_fd, STDOUT_FILENO); + if (r < 0) + return log_error_errno(errno, "Failed to dup connection to stdout: %m"); + + start_fd = safe_close(start_fd); + } else { + if (start_fd != SD_LISTEN_FDS_START) { + assert(n_fds == 1); + + r = dup2(start_fd, SD_LISTEN_FDS_START); + if (r < 0) + return log_error_errno(errno, "Failed to dup connection: %m"); + + safe_close(start_fd); + start_fd = SD_LISTEN_FDS_START; + } + + if (asprintf((char**)(envp + n_env++), "LISTEN_FDS=%i", n_fds) < 0) return log_oom(); - for (i = 1; i < (unsigned) fds; i++) { - char *c; + if (asprintf((char**)(envp + n_env++), "LISTEN_PID=" PID_FMT, getpid()) < 0) + return log_oom(); - c = strjoin(e, ":", arg_fdname, NULL); - if (!c) { - free(e); + 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; } - free(e); - e = c; + envp[n_env++] = e; } - - 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; } |