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_environ = 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",
60 static int make_socket_fd(const char* address, int flags) {
64 r = socket_address_parse(&a, address);
66 log_error("Failed to parse socket: %s", strerror(-r));
70 fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT, NULL, false, false, 0755, 0644, NULL);
72 log_error("Failed to listen: %s", strerror(-r));
79 static int open_sockets(int *epoll_fd, bool accept) {
84 n = sd_listen_fds(true);
86 log_error("Failed to read listening file descriptors from environment: %s",
91 log_info("Received %i descriptors via the environment.", n);
93 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
94 r = fd_cloexec(fd, arg_accept);
102 /** Note: we leak some fd's on error here. I doesn't matter
103 * much, since the program will exit immediately anyway, but
104 * would be a pain to fix.
107 STRV_FOREACH(address, arg_listen) {
109 fd = make_socket_fd(*address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
111 log_error("Failed to open '%s': %s", *address, strerror(-fd));
115 assert(fd == SD_LISTEN_FDS_START + count);
119 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
121 log_error("Failed to create epoll object: %m");
125 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
126 _cleanup_free_ char *name = NULL;
128 getsockname_pretty(fd, &name);
129 log_info("Listening on %s as %i.", strna(name), fd);
131 r = add_epoll(*epoll_fd, fd);
139 static int launch(char* name, char **argv, char **env, int fds) {
141 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
142 _cleanup_strv_free_ char **envp = NULL;
143 _cleanup_free_ char *tmp = NULL;
144 unsigned n_env = 0, length;
148 length = strv_length(arg_environ);
150 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
151 envp = new0(char *, length + 7);
155 STRV_FOREACH(s, arg_environ) {
159 _cleanup_free_ char *p = strappend(*s, "=");
162 envp[n_env] = strv_find_prefix(env, p);
168 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
169 envp[n_env] = strv_find_prefix(env, tocopy[i]);
174 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
175 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
178 tmp = strv_join(argv, " ");
182 log_info("Execing %s (%s)", name, tmp);
183 execvpe(name, argv, envp);
184 log_error("Failed to execp %s (%s): %m", name, tmp);
189 static int launch1(const char* child, char** argv, char **env, int fd) {
190 _cleanup_free_ char *tmp = NULL;
191 pid_t parent_pid, child_pid;
194 tmp = strv_join(argv, " ");
198 parent_pid = getpid();
202 log_error("Failed to fork: %m");
207 if (child_pid == 0) {
208 r = dup2(fd, STDIN_FILENO);
210 log_error("Failed to dup connection to stdin: %m");
214 r = dup2(fd, STDOUT_FILENO);
216 log_error("Failed to dup connection to stdout: %m");
222 log_error("Failed to close dupped connection: %m");
226 /* Make sure the child goes away when the parent dies */
227 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
230 /* Check whether our parent died before we were able
231 * to set the death signal */
232 if (getppid() != parent_pid)
236 log_error("Failed to exec child %s: %m", child);
240 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
245 static int do_accept(const char* name, char **argv, char **envp, int fd) {
246 _cleanup_free_ char *local = NULL, *peer = NULL;
249 fd2 = accept(fd, NULL, NULL);
251 log_error("Failed to accept connection on fd:%d: %m", fd);
255 getsockname_pretty(fd2, &local);
256 getpeername_pretty(fd2, &peer);
257 log_info("Connection from %s to %s", strna(peer), strna(local));
259 return launch1(name, argv, envp, fd2);
262 /* SIGCHLD handler. */
263 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
266 log_info("Child %d died with code %d", t->si_pid, t->si_status);
267 /* Wait for a dead child. */
268 waitpid(t->si_pid, NULL, 0);
271 static int install_chld_handler(void) {
273 struct sigaction act;
275 act.sa_flags = SA_SIGINFO;
276 act.sa_sigaction = sigchld_hdl;
278 r = sigaction(SIGCHLD, &act, 0);
280 log_error("Failed to install SIGCHLD handler: %m");
284 static int help(void) {
285 printf("%s [OPTIONS...]\n\n"
286 "Listen on sockets and launch child on connection.\n\n"
288 " -l --listen=ADDR Listen for raw connections at ADDR\n"
289 " -a --accept Spawn separate child for each connection\n"
290 " -h --help Show this help and exit\n"
291 " -E --environment=NAME[=VALUE]\n"
292 " Pass an environment variable to children\n"
293 " --version Print version string and exit\n"
295 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
296 , program_invocation_short_name
302 static int parse_argv(int argc, char *argv[]) {
307 static const struct option options[] = {
308 { "help", no_argument, NULL, 'h' },
309 { "version", no_argument, NULL, ARG_VERSION },
310 { "listen", required_argument, NULL, 'l' },
311 { "accept", no_argument, NULL, 'a' },
312 { "environment", required_argument, NULL, 'E' },
321 while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
327 puts(PACKAGE_STRING);
328 puts(SYSTEMD_FEATURES);
332 int r = strv_extend(&arg_listen, optarg);
344 int r = strv_extend(&arg_environ, optarg);
355 assert_not_reached("Unhandled option");
358 if (optind == argc) {
359 log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
360 program_invocation_short_name);
364 arg_args = argv + optind;
366 return 1 /* work to do */;
369 int main(int argc, char **argv, char **envp) {
373 log_parse_environment();
376 r = parse_argv(argc, argv);
378 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
380 r = install_chld_handler();
384 n = open_sockets(&epoll_fd, arg_accept);
388 log_error("No sockets to listen on specified or passed in.");
393 struct epoll_event event;
395 r = epoll_wait(epoll_fd, &event, 1, -1);
400 log_error("epoll_wait() failed: %m");
404 log_info("Communication attempt on fd %i.", event.data.fd);
406 r = do_accept(argv[optind], argv + optind, envp,
414 launch(argv[optind], argv + optind, envp, n);