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 log_error_errno(fd, "Failed to open '%s': %m", *address);
107 assert(fd == SD_LISTEN_FDS_START + count);
114 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
116 log_error("Failed to create epoll object: %m");
120 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
121 _cleanup_free_ char *name = NULL;
123 getsockname_pretty(fd, &name);
124 log_info("Listening on %s as %i.", strna(name), fd);
126 r = add_epoll(*epoll_fd, fd);
134 static int launch(char* name, char **argv, char **env, int fds) {
136 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
137 _cleanup_strv_free_ char **envp = NULL;
138 _cleanup_free_ char *tmp = NULL;
139 unsigned n_env = 0, length;
143 length = strv_length(arg_setenv);
145 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
146 envp = new0(char *, length + 7);
150 STRV_FOREACH(s, arg_setenv) {
154 _cleanup_free_ char *p = strappend(*s, "=");
157 envp[n_env] = strv_find_prefix(env, p);
163 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
164 envp[n_env] = strv_find_prefix(env, tocopy[i]);
169 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
170 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
173 tmp = strv_join(argv, " ");
177 log_info("Execing %s (%s)", name, tmp);
178 execvpe(name, argv, envp);
179 log_error("Failed to execp %s (%s): %m", name, tmp);
184 static int launch1(const char* child, char** argv, char **env, int fd) {
185 _cleanup_free_ char *tmp = NULL;
186 pid_t parent_pid, child_pid;
189 tmp = strv_join(argv, " ");
193 parent_pid = getpid();
197 log_error("Failed to fork: %m");
202 if (child_pid == 0) {
203 r = dup2(fd, STDIN_FILENO);
205 log_error("Failed to dup connection to stdin: %m");
209 r = dup2(fd, STDOUT_FILENO);
211 log_error("Failed to dup connection to stdout: %m");
217 log_error("Failed to close dupped connection: %m");
221 /* Make sure the child goes away when the parent dies */
222 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
225 /* Check whether our parent died before we were able
226 * to set the death signal */
227 if (getppid() != parent_pid)
231 log_error("Failed to exec child %s: %m", child);
235 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
240 static int do_accept(const char* name, char **argv, char **envp, int fd) {
241 _cleanup_free_ char *local = NULL, *peer = NULL;
242 _cleanup_close_ int fd2 = -1;
244 fd2 = accept(fd, NULL, NULL);
246 log_error("Failed to accept connection on fd:%d: %m", fd);
250 getsockname_pretty(fd2, &local);
251 getpeername_pretty(fd2, &peer);
252 log_info("Connection from %s to %s", strna(peer), strna(local));
254 return launch1(name, argv, envp, fd2);
257 /* SIGCHLD handler. */
258 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
261 log_info("Child %d died with code %d", t->si_pid, t->si_status);
262 /* Wait for a dead child. */
263 waitpid(t->si_pid, NULL, 0);
266 static int install_chld_handler(void) {
268 struct sigaction act = {
269 .sa_flags = SA_SIGINFO,
270 .sa_sigaction = sigchld_hdl,
273 r = sigaction(SIGCHLD, &act, 0);
275 log_error("Failed to install SIGCHLD handler: %m");
279 static void help(void) {
280 printf("%s [OPTIONS...]\n\n"
281 "Listen on sockets and launch child on connection.\n\n"
283 " -l --listen=ADDR Listen for raw connections at ADDR\n"
284 " -a --accept Spawn separate child for each connection\n"
285 " -h --help Show this help and exit\n"
286 " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
287 " --version Print version string and exit\n"
289 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
290 , program_invocation_short_name);
293 static int parse_argv(int argc, char *argv[]) {
298 static const struct option options[] = {
299 { "help", no_argument, NULL, 'h' },
300 { "version", no_argument, NULL, ARG_VERSION },
301 { "listen", required_argument, NULL, 'l' },
302 { "accept", no_argument, NULL, 'a' },
303 { "setenv", required_argument, NULL, 'E' },
304 { "environment", required_argument, NULL, 'E' }, /* alias */
313 while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
320 puts(PACKAGE_STRING);
321 puts(SYSTEMD_FEATURES);
325 int r = strv_extend(&arg_listen, optarg);
337 int r = strv_extend(&arg_setenv, optarg);
348 assert_not_reached("Unhandled option");
351 if (optind == argc) {
352 log_error("%s: command to execute is missing.",
353 program_invocation_short_name);
357 arg_args = argv + optind;
359 return 1 /* work to do */;
362 int main(int argc, char **argv, char **envp) {
366 log_parse_environment();
369 r = parse_argv(argc, argv);
371 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
373 r = install_chld_handler();
377 n = open_sockets(&epoll_fd, arg_accept);
381 log_error("No sockets to listen on specified or passed in.");
386 struct epoll_event event;
388 r = epoll_wait(epoll_fd, &event, 1, -1);
393 log_error("epoll_wait() failed: %m");
397 log_info("Communication attempt on fd %i.", event.data.fd);
399 r = do_accept(argv[optind], argv + optind, envp,
407 launch(argv[optind], argv + optind, envp, n);