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 /* Close logging and all other descriptors */
106 for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++)
110 close_all_fds(except, 3 + n);
113 /** Note: we leak some fd's on error here. I doesn't matter
114 * much, since the program will exit immediately anyway, but
115 * would be a pain to fix.
118 STRV_FOREACH(address, arg_listen) {
120 fd = make_socket_fd(*address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
123 log_error("Failed to open '%s': %s", *address, strerror(-fd));
127 assert(fd == SD_LISTEN_FDS_START + count);
134 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
136 log_error("Failed to create epoll object: %m");
140 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
141 _cleanup_free_ char *name = NULL;
143 getsockname_pretty(fd, &name);
144 log_info("Listening on %s as %i.", strna(name), fd);
146 r = add_epoll(*epoll_fd, fd);
154 static int launch(char* name, char **argv, char **env, int fds) {
156 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
157 _cleanup_strv_free_ char **envp = NULL;
158 _cleanup_free_ char *tmp = NULL;
159 unsigned n_env = 0, length;
163 length = strv_length(arg_environ);
165 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
166 envp = new0(char *, length + 7);
170 STRV_FOREACH(s, arg_environ) {
174 _cleanup_free_ char *p = strappend(*s, "=");
177 envp[n_env] = strv_find_prefix(env, p);
183 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
184 envp[n_env] = strv_find_prefix(env, tocopy[i]);
189 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
190 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
193 tmp = strv_join(argv, " ");
197 log_info("Execing %s (%s)", name, tmp);
198 execvpe(name, argv, envp);
199 log_error("Failed to execp %s (%s): %m", name, tmp);
204 static int launch1(const char* child, char** argv, char **env, int fd) {
205 _cleanup_free_ char *tmp = NULL;
206 pid_t parent_pid, child_pid;
209 tmp = strv_join(argv, " ");
213 parent_pid = getpid();
217 log_error("Failed to fork: %m");
222 if (child_pid == 0) {
223 r = dup2(fd, STDIN_FILENO);
225 log_error("Failed to dup connection to stdin: %m");
229 r = dup2(fd, STDOUT_FILENO);
231 log_error("Failed to dup connection to stdout: %m");
237 log_error("Failed to close dupped connection: %m");
241 /* Make sure the child goes away when the parent dies */
242 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
245 /* Check whether our parent died before we were able
246 * to set the death signal */
247 if (getppid() != parent_pid)
251 log_error("Failed to exec child %s: %m", child);
255 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
260 static int do_accept(const char* name, char **argv, char **envp, int fd) {
261 _cleanup_free_ char *local = NULL, *peer = NULL;
264 fd2 = accept(fd, NULL, NULL);
266 log_error("Failed to accept connection on fd:%d: %m", fd);
270 getsockname_pretty(fd2, &local);
271 getpeername_pretty(fd2, &peer);
272 log_info("Connection from %s to %s", strna(peer), strna(local));
274 return launch1(name, argv, envp, fd2);
277 /* SIGCHLD handler. */
278 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
281 log_info("Child %d died with code %d", t->si_pid, t->si_status);
282 /* Wait for a dead child. */
283 waitpid(t->si_pid, NULL, 0);
286 static int install_chld_handler(void) {
288 struct sigaction act = {
289 .sa_flags = SA_SIGINFO,
290 .sa_sigaction = sigchld_hdl,
293 r = sigaction(SIGCHLD, &act, 0);
295 log_error("Failed to install SIGCHLD handler: %m");
299 static int help(void) {
300 printf("%s [OPTIONS...]\n\n"
301 "Listen on sockets and launch child on connection.\n\n"
303 " -l --listen=ADDR Listen for raw connections at ADDR\n"
304 " -a --accept Spawn separate child for each connection\n"
305 " -h --help Show this help and exit\n"
306 " -E --environment=NAME[=VALUE]\n"
307 " Pass an environment variable to children\n"
308 " --version Print version string and exit\n"
310 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
311 , program_invocation_short_name
317 static int parse_argv(int argc, char *argv[]) {
322 static const struct option options[] = {
323 { "help", no_argument, NULL, 'h' },
324 { "version", no_argument, NULL, ARG_VERSION },
325 { "listen", required_argument, NULL, 'l' },
326 { "accept", no_argument, NULL, 'a' },
327 { "environment", required_argument, NULL, 'E' },
336 while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
342 puts(PACKAGE_STRING);
343 puts(SYSTEMD_FEATURES);
347 int r = strv_extend(&arg_listen, optarg);
359 int r = strv_extend(&arg_environ, optarg);
370 assert_not_reached("Unhandled option");
373 if (optind == argc) {
374 log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
375 program_invocation_short_name);
379 arg_args = argv + optind;
381 return 1 /* work to do */;
384 int main(int argc, char **argv, char **envp) {
388 log_parse_environment();
391 r = parse_argv(argc, argv);
393 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
395 r = install_chld_handler();
399 n = open_sockets(&epoll_fd, arg_accept);
403 log_error("No sockets to listen on specified or passed in.");
408 struct epoll_event event;
410 r = epoll_wait(epoll_fd, &event, 1, -1);
415 log_error("epoll_wait() failed: %m");
419 log_info("Communication attempt on fd %i.", event.data.fd);
421 r = do_accept(argv[optind], argv + optind, envp,
429 launch(argv[optind], argv + optind, envp, n);