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/>.
23 #include <sys/epoll.h>
24 #include <sys/prctl.h>
25 #include <sys/socket.h>
29 #include "systemd/sd-daemon.h"
31 #include "socket-util.h"
37 static char** arg_listen = NULL;
38 static bool arg_accept = false;
39 static char** arg_args = NULL;
40 static char** arg_setenv = NULL;
42 static int add_epoll(int epoll_fd, int fd) {
43 struct epoll_event ev = {
48 assert(epoll_fd >= 0);
52 r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
54 return log_error_errno(errno, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd);
59 static int open_sockets(int *epoll_fd, bool accept) {
64 n = sd_listen_fds(true);
66 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
68 log_info("Received %i descriptors via the environment.", n);
70 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
71 r = fd_cloexec(fd, arg_accept);
79 /* Close logging and all other descriptors */
83 for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++)
87 close_all_fds(except, 3 + n);
90 /** Note: we leak some fd's on error here. I doesn't matter
91 * much, since the program will exit immediately anyway, but
92 * would be a pain to fix.
95 STRV_FOREACH(address, arg_listen) {
97 fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
100 return log_error_errno(fd, "Failed to open '%s': %m", *address);
103 assert(fd == SD_LISTEN_FDS_START + count);
110 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
112 return log_error_errno(errno, "Failed to create epoll object: %m");
114 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
115 _cleanup_free_ char *name = NULL;
117 getsockname_pretty(fd, &name);
118 log_info("Listening on %s as %i.", strna(name), fd);
120 r = add_epoll(*epoll_fd, fd);
128 static int launch(char* name, char **argv, char **env, int fds) {
130 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
131 _cleanup_strv_free_ char **envp = NULL;
132 _cleanup_free_ char *tmp = NULL;
133 unsigned n_env = 0, length;
137 length = strv_length(arg_setenv);
139 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
140 envp = new0(char *, length + 7);
144 STRV_FOREACH(s, arg_setenv) {
148 _cleanup_free_ char *p = strappend(*s, "=");
151 envp[n_env] = strv_find_prefix(env, p);
157 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
158 envp[n_env] = strv_find_prefix(env, tocopy[i]);
163 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
164 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
167 tmp = strv_join(argv, " ");
171 log_info("Execing %s (%s)", name, tmp);
172 execvpe(name, argv, envp);
173 log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp);
178 static int launch1(const char* child, char** argv, char **env, int fd) {
179 _cleanup_free_ char *tmp = NULL;
180 pid_t parent_pid, child_pid;
183 tmp = strv_join(argv, " ");
187 parent_pid = getpid();
191 return log_error_errno(errno, "Failed to fork: %m");
194 if (child_pid == 0) {
195 r = dup2(fd, STDIN_FILENO);
197 log_error_errno(errno, "Failed to dup connection to stdin: %m");
201 r = dup2(fd, STDOUT_FILENO);
203 log_error_errno(errno, "Failed to dup connection to stdout: %m");
209 log_error_errno(errno, "Failed to close dupped connection: %m");
213 /* Make sure the child goes away when the parent dies */
214 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
217 /* Check whether our parent died before we were able
218 * to set the death signal */
219 if (getppid() != parent_pid)
223 log_error_errno(errno, "Failed to exec child %s: %m", child);
227 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
232 static int do_accept(const char* name, char **argv, char **envp, int fd) {
233 _cleanup_free_ char *local = NULL, *peer = NULL;
234 _cleanup_close_ int fd2 = -1;
236 fd2 = accept(fd, NULL, NULL);
238 log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
242 getsockname_pretty(fd2, &local);
243 getpeername_pretty(fd2, &peer);
244 log_info("Connection from %s to %s", strna(peer), strna(local));
246 return launch1(name, argv, envp, fd2);
249 /* SIGCHLD handler. */
250 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
253 log_info("Child %d died with code %d", t->si_pid, t->si_status);
254 /* Wait for a dead child. */
255 waitpid(t->si_pid, NULL, 0);
258 static int install_chld_handler(void) {
260 struct sigaction act = {
261 .sa_flags = SA_SIGINFO,
262 .sa_sigaction = sigchld_hdl,
265 r = sigaction(SIGCHLD, &act, 0);
267 log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
271 static void help(void) {
272 printf("%s [OPTIONS...]\n\n"
273 "Listen on sockets and launch child on connection.\n\n"
275 " -l --listen=ADDR Listen for raw connections at ADDR\n"
276 " -a --accept Spawn separate child for each connection\n"
277 " -h --help Show this help and exit\n"
278 " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
279 " --version Print version string and exit\n"
281 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
282 , program_invocation_short_name);
285 static int parse_argv(int argc, char *argv[]) {
290 static const struct option options[] = {
291 { "help", no_argument, NULL, 'h' },
292 { "version", no_argument, NULL, ARG_VERSION },
293 { "listen", required_argument, NULL, 'l' },
294 { "accept", no_argument, NULL, 'a' },
295 { "setenv", required_argument, NULL, 'E' },
296 { "environment", required_argument, NULL, 'E' }, /* alias */
305 while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
312 puts(PACKAGE_STRING);
313 puts(SYSTEMD_FEATURES);
317 int r = strv_extend(&arg_listen, optarg);
329 int r = strv_extend(&arg_setenv, optarg);
340 assert_not_reached("Unhandled option");
343 if (optind == argc) {
344 log_error("%s: command to execute is missing.",
345 program_invocation_short_name);
349 arg_args = argv + optind;
351 return 1 /* work to do */;
354 int main(int argc, char **argv, char **envp) {
358 log_parse_environment();
361 r = parse_argv(argc, argv);
363 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
365 r = install_chld_handler();
369 n = open_sockets(&epoll_fd, arg_accept);
373 log_error("No sockets to listen on specified or passed in.");
378 struct epoll_event event;
380 r = epoll_wait(epoll_fd, &event, 1, -1);
385 log_error_errno(errno, "epoll_wait() failed: %m");
389 log_info("Communication attempt on fd %i.", event.data.fd);
391 r = do_accept(argv[optind], argv + optind, envp,
399 launch(argv[optind], argv + optind, envp, n);