1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/epoll.h>
25 #include <sys/prctl.h>
26 #include <sys/socket.h>
30 #include "systemd/sd-daemon.h"
32 #include "socket-util.h"
38 static char** arg_listen = NULL;
39 static bool arg_accept = false;
40 static char** arg_args = NULL;
41 static char** arg_setenv = NULL;
43 static int add_epoll(int epoll_fd, int fd) {
44 struct epoll_event ev = {
49 assert(epoll_fd >= 0);
53 r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
55 log_error("Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd);
62 static int open_sockets(int *epoll_fd, bool accept) {
67 n = sd_listen_fds(true);
69 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
71 log_info("Received %i descriptors via the environment.", n);
73 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
74 r = fd_cloexec(fd, arg_accept);
82 /* Close logging and all other descriptors */
86 for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++)
90 close_all_fds(except, 3 + n);
93 /** Note: we leak some fd's on error here. I doesn't matter
94 * much, since the program will exit immediately anyway, but
95 * would be a pain to fix.
98 STRV_FOREACH(address, arg_listen) {
100 fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
103 return log_error_errno(fd, "Failed to open '%s': %m", *address);
106 assert(fd == SD_LISTEN_FDS_START + count);
113 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
115 log_error("Failed to create epoll object: %m");
119 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
120 _cleanup_free_ char *name = NULL;
122 getsockname_pretty(fd, &name);
123 log_info("Listening on %s as %i.", strna(name), fd);
125 r = add_epoll(*epoll_fd, fd);
133 static int launch(char* name, char **argv, char **env, int fds) {
135 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
136 _cleanup_strv_free_ char **envp = NULL;
137 _cleanup_free_ char *tmp = NULL;
138 unsigned n_env = 0, length;
142 length = strv_length(arg_setenv);
144 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
145 envp = new0(char *, length + 7);
149 STRV_FOREACH(s, arg_setenv) {
153 _cleanup_free_ char *p = strappend(*s, "=");
156 envp[n_env] = strv_find_prefix(env, p);
162 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
163 envp[n_env] = strv_find_prefix(env, tocopy[i]);
168 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
169 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
172 tmp = strv_join(argv, " ");
176 log_info("Execing %s (%s)", name, tmp);
177 execvpe(name, argv, envp);
178 log_error("Failed to execp %s (%s): %m", name, tmp);
183 static int launch1(const char* child, char** argv, char **env, int fd) {
184 _cleanup_free_ char *tmp = NULL;
185 pid_t parent_pid, child_pid;
188 tmp = strv_join(argv, " ");
192 parent_pid = getpid();
196 log_error("Failed to fork: %m");
201 if (child_pid == 0) {
202 r = dup2(fd, STDIN_FILENO);
204 log_error("Failed to dup connection to stdin: %m");
208 r = dup2(fd, STDOUT_FILENO);
210 log_error("Failed to dup connection to stdout: %m");
216 log_error("Failed to close dupped connection: %m");
220 /* Make sure the child goes away when the parent dies */
221 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
224 /* Check whether our parent died before we were able
225 * to set the death signal */
226 if (getppid() != parent_pid)
230 log_error("Failed to exec child %s: %m", child);
234 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
239 static int do_accept(const char* name, char **argv, char **envp, int fd) {
240 _cleanup_free_ char *local = NULL, *peer = NULL;
241 _cleanup_close_ int fd2 = -1;
243 fd2 = accept(fd, NULL, NULL);
245 log_error("Failed to accept connection on fd:%d: %m", fd);
249 getsockname_pretty(fd2, &local);
250 getpeername_pretty(fd2, &peer);
251 log_info("Connection from %s to %s", strna(peer), strna(local));
253 return launch1(name, argv, envp, fd2);
256 /* SIGCHLD handler. */
257 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
260 log_info("Child %d died with code %d", t->si_pid, t->si_status);
261 /* Wait for a dead child. */
262 waitpid(t->si_pid, NULL, 0);
265 static int install_chld_handler(void) {
267 struct sigaction act = {
268 .sa_flags = SA_SIGINFO,
269 .sa_sigaction = sigchld_hdl,
272 r = sigaction(SIGCHLD, &act, 0);
274 log_error("Failed to install SIGCHLD handler: %m");
278 static void help(void) {
279 printf("%s [OPTIONS...]\n\n"
280 "Listen on sockets and launch child on connection.\n\n"
282 " -l --listen=ADDR Listen for raw connections at ADDR\n"
283 " -a --accept Spawn separate child for each connection\n"
284 " -h --help Show this help and exit\n"
285 " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
286 " --version Print version string and exit\n"
288 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
289 , program_invocation_short_name);
292 static int parse_argv(int argc, char *argv[]) {
297 static const struct option options[] = {
298 { "help", no_argument, NULL, 'h' },
299 { "version", no_argument, NULL, ARG_VERSION },
300 { "listen", required_argument, NULL, 'l' },
301 { "accept", no_argument, NULL, 'a' },
302 { "setenv", required_argument, NULL, 'E' },
303 { "environment", required_argument, NULL, 'E' }, /* alias */
312 while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
319 puts(PACKAGE_STRING);
320 puts(SYSTEMD_FEATURES);
324 int r = strv_extend(&arg_listen, optarg);
336 int r = strv_extend(&arg_setenv, optarg);
347 assert_not_reached("Unhandled option");
350 if (optind == argc) {
351 log_error("%s: command to execute is missing.",
352 program_invocation_short_name);
356 arg_args = argv + optind;
358 return 1 /* work to do */;
361 int main(int argc, char **argv, char **envp) {
365 log_parse_environment();
368 r = parse_argv(argc, argv);
370 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
372 r = install_chld_handler();
376 n = open_sockets(&epoll_fd, arg_accept);
380 log_error("No sockets to listen on specified or passed in.");
385 struct epoll_event event;
387 r = epoll_wait(epoll_fd, &event, 1, -1);
392 log_error("epoll_wait() failed: %m");
396 log_info("Communication attempt on fd %i.", event.data.fd);
398 r = do_accept(argv[optind], argv + optind, envp,
406 launch(argv[optind], argv + optind, envp, n);