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 log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
73 log_info("Received %i descriptors via the environment.", n);
75 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
76 r = fd_cloexec(fd, arg_accept);
84 /* Close logging and all other descriptors */
88 for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++)
92 close_all_fds(except, 3 + n);
95 /** Note: we leak some fd's on error here. I doesn't matter
96 * much, since the program will exit immediately anyway, but
97 * would be a pain to fix.
100 STRV_FOREACH(address, arg_listen) {
102 fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
105 log_error_errno(fd, "Failed to open '%s': %m", *address);
109 assert(fd == SD_LISTEN_FDS_START + count);
116 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
118 log_error("Failed to create epoll object: %m");
122 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
123 _cleanup_free_ char *name = NULL;
125 getsockname_pretty(fd, &name);
126 log_info("Listening on %s as %i.", strna(name), fd);
128 r = add_epoll(*epoll_fd, fd);
136 static int launch(char* name, char **argv, char **env, int fds) {
138 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
139 _cleanup_strv_free_ char **envp = NULL;
140 _cleanup_free_ char *tmp = NULL;
141 unsigned n_env = 0, length;
145 length = strv_length(arg_setenv);
147 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
148 envp = new0(char *, length + 7);
152 STRV_FOREACH(s, arg_setenv) {
156 _cleanup_free_ char *p = strappend(*s, "=");
159 envp[n_env] = strv_find_prefix(env, p);
165 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
166 envp[n_env] = strv_find_prefix(env, tocopy[i]);
171 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
172 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
175 tmp = strv_join(argv, " ");
179 log_info("Execing %s (%s)", name, tmp);
180 execvpe(name, argv, envp);
181 log_error("Failed to execp %s (%s): %m", name, tmp);
186 static int launch1(const char* child, char** argv, char **env, int fd) {
187 _cleanup_free_ char *tmp = NULL;
188 pid_t parent_pid, child_pid;
191 tmp = strv_join(argv, " ");
195 parent_pid = getpid();
199 log_error("Failed to fork: %m");
204 if (child_pid == 0) {
205 r = dup2(fd, STDIN_FILENO);
207 log_error("Failed to dup connection to stdin: %m");
211 r = dup2(fd, STDOUT_FILENO);
213 log_error("Failed to dup connection to stdout: %m");
219 log_error("Failed to close dupped connection: %m");
223 /* Make sure the child goes away when the parent dies */
224 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
227 /* Check whether our parent died before we were able
228 * to set the death signal */
229 if (getppid() != parent_pid)
233 log_error("Failed to exec child %s: %m", child);
237 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
242 static int do_accept(const char* name, char **argv, char **envp, int fd) {
243 _cleanup_free_ char *local = NULL, *peer = NULL;
244 _cleanup_close_ int fd2 = -1;
246 fd2 = accept(fd, NULL, NULL);
248 log_error("Failed to accept connection on fd:%d: %m", fd);
252 getsockname_pretty(fd2, &local);
253 getpeername_pretty(fd2, &peer);
254 log_info("Connection from %s to %s", strna(peer), strna(local));
256 return launch1(name, argv, envp, fd2);
259 /* SIGCHLD handler. */
260 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
263 log_info("Child %d died with code %d", t->si_pid, t->si_status);
264 /* Wait for a dead child. */
265 waitpid(t->si_pid, NULL, 0);
268 static int install_chld_handler(void) {
270 struct sigaction act = {
271 .sa_flags = SA_SIGINFO,
272 .sa_sigaction = sigchld_hdl,
275 r = sigaction(SIGCHLD, &act, 0);
277 log_error("Failed to install SIGCHLD handler: %m");
281 static void help(void) {
282 printf("%s [OPTIONS...]\n\n"
283 "Listen on sockets and launch child on connection.\n\n"
285 " -l --listen=ADDR Listen for raw connections at ADDR\n"
286 " -a --accept Spawn separate child for each connection\n"
287 " -h --help Show this help and exit\n"
288 " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
289 " --version Print version string and exit\n"
291 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
292 , program_invocation_short_name);
295 static int parse_argv(int argc, char *argv[]) {
300 static const struct option options[] = {
301 { "help", no_argument, NULL, 'h' },
302 { "version", no_argument, NULL, ARG_VERSION },
303 { "listen", required_argument, NULL, 'l' },
304 { "accept", no_argument, NULL, 'a' },
305 { "setenv", required_argument, NULL, 'E' },
306 { "environment", required_argument, NULL, 'E' }, /* alias */
315 while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
322 puts(PACKAGE_STRING);
323 puts(SYSTEMD_FEATURES);
327 int r = strv_extend(&arg_listen, optarg);
339 int r = strv_extend(&arg_setenv, optarg);
350 assert_not_reached("Unhandled option");
353 if (optind == argc) {
354 log_error("%s: command to execute is missing.",
355 program_invocation_short_name);
359 arg_args = argv + optind;
361 return 1 /* work to do */;
364 int main(int argc, char **argv, char **envp) {
368 log_parse_environment();
371 r = parse_argv(argc, argv);
373 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
375 r = install_chld_handler();
379 n = open_sockets(&epoll_fd, arg_accept);
383 log_error("No sockets to listen on specified or passed in.");
388 struct epoll_event event;
390 r = epoll_wait(epoll_fd, &event, 1, -1);
395 log_error("epoll_wait() failed: %m");
399 log_info("Communication attempt on fd %i.", event.data.fd);
401 r = do_accept(argv[optind], argv + optind, envp,
409 launch(argv[optind], argv + optind, envp, n);