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("Failed to read listening file descriptors from environment: %s",
74 log_info("Received %i descriptors via the environment.", n);
76 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
77 r = fd_cloexec(fd, arg_accept);
85 /* Close logging and all other descriptors */
89 for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++)
93 close_all_fds(except, 3 + n);
96 /** Note: we leak some fd's on error here. I doesn't matter
97 * much, since the program will exit immediately anyway, but
98 * would be a pain to fix.
101 STRV_FOREACH(address, arg_listen) {
103 fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
106 log_error("Failed to open '%s': %s", *address, strerror(-fd));
110 assert(fd == SD_LISTEN_FDS_START + count);
117 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
119 log_error("Failed to create epoll object: %m");
123 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
124 _cleanup_free_ char *name = NULL;
126 getsockname_pretty(fd, &name);
127 log_info("Listening on %s as %i.", strna(name), fd);
129 r = add_epoll(*epoll_fd, fd);
137 static int launch(char* name, char **argv, char **env, int fds) {
139 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
140 _cleanup_strv_free_ char **envp = NULL;
141 _cleanup_free_ char *tmp = NULL;
142 unsigned n_env = 0, length;
146 length = strv_length(arg_setenv);
148 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
149 envp = new0(char *, length + 7);
153 STRV_FOREACH(s, arg_setenv) {
157 _cleanup_free_ char *p = strappend(*s, "=");
160 envp[n_env] = strv_find_prefix(env, p);
166 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
167 envp[n_env] = strv_find_prefix(env, tocopy[i]);
172 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
173 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
176 tmp = strv_join(argv, " ");
180 log_info("Execing %s (%s)", name, tmp);
181 execvpe(name, argv, envp);
182 log_error("Failed to execp %s (%s): %m", name, tmp);
187 static int launch1(const char* child, char** argv, char **env, int fd) {
188 _cleanup_free_ char *tmp = NULL;
189 pid_t parent_pid, child_pid;
192 tmp = strv_join(argv, " ");
196 parent_pid = getpid();
200 log_error("Failed to fork: %m");
205 if (child_pid == 0) {
206 r = dup2(fd, STDIN_FILENO);
208 log_error("Failed to dup connection to stdin: %m");
212 r = dup2(fd, STDOUT_FILENO);
214 log_error("Failed to dup connection to stdout: %m");
220 log_error("Failed to close dupped connection: %m");
224 /* Make sure the child goes away when the parent dies */
225 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
228 /* Check whether our parent died before we were able
229 * to set the death signal */
230 if (getppid() != parent_pid)
234 log_error("Failed to exec child %s: %m", child);
238 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
243 static int do_accept(const char* name, char **argv, char **envp, int fd) {
244 _cleanup_free_ char *local = NULL, *peer = NULL;
247 fd2 = accept(fd, NULL, NULL);
249 log_error("Failed to accept connection on fd:%d: %m", fd);
253 getsockname_pretty(fd2, &local);
254 getpeername_pretty(fd2, &peer);
255 log_info("Connection from %s to %s", strna(peer), strna(local));
257 return launch1(name, argv, envp, fd2);
260 /* SIGCHLD handler. */
261 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
264 log_info("Child %d died with code %d", t->si_pid, t->si_status);
265 /* Wait for a dead child. */
266 waitpid(t->si_pid, NULL, 0);
269 static int install_chld_handler(void) {
271 struct sigaction act = {
272 .sa_flags = SA_SIGINFO,
273 .sa_sigaction = sigchld_hdl,
276 r = sigaction(SIGCHLD, &act, 0);
278 log_error("Failed to install SIGCHLD handler: %m");
282 static int help(void) {
283 printf("%s [OPTIONS...]\n\n"
284 "Listen on sockets and launch child on connection.\n\n"
286 " -l --listen=ADDR Listen for raw connections at ADDR\n"
287 " -a --accept Spawn separate child for each connection\n"
288 " -h --help Show this help and exit\n"
289 " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
290 " --version Print version string and exit\n"
292 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
293 , program_invocation_short_name
299 static int parse_argv(int argc, char *argv[]) {
304 static const struct option options[] = {
305 { "help", no_argument, NULL, 'h' },
306 { "version", no_argument, NULL, ARG_VERSION },
307 { "listen", required_argument, NULL, 'l' },
308 { "accept", no_argument, NULL, 'a' },
309 { "setenv", required_argument, NULL, 'E' },
310 { "environment", required_argument, NULL, 'E' }, /* alias */
319 while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
325 puts(PACKAGE_STRING);
326 puts(SYSTEMD_FEATURES);
330 int r = strv_extend(&arg_listen, optarg);
342 int r = strv_extend(&arg_setenv, optarg);
353 assert_not_reached("Unhandled option");
356 if (optind == argc) {
357 log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
358 program_invocation_short_name);
362 arg_args = argv + optind;
364 return 1 /* work to do */;
367 int main(int argc, char **argv, char **envp) {
371 log_parse_environment();
374 r = parse_argv(argc, argv);
376 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
378 r = install_chld_handler();
382 n = open_sockets(&epoll_fd, arg_accept);
386 log_error("No sockets to listen on specified or passed in.");
391 struct epoll_event event;
393 r = epoll_wait(epoll_fd, &event, 1, -1);
398 log_error("epoll_wait() failed: %m");
402 log_info("Communication attempt on fd %i.", event.data.fd);
404 r = do_accept(argv[optind], argv + optind, envp,
412 launch(argv[optind], argv + optind, envp, n);