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) {
61 _cleanup_free_ char *p = NULL;
65 r = socket_address_parse(&a, address);
67 log_error("Failed to parse socket: %s", strerror(-r));
71 fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT, NULL, false, false, 0755, 0644, NULL);
73 log_error("Failed to listen: %s", strerror(-r));
80 static int open_sockets(int *epoll_fd, bool accept) {
85 n = sd_listen_fds(true);
87 log_error("Failed to read listening file descriptors from environment: %s",
92 log_info("Received %i descriptors via the environment.", n);
94 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
95 r = fd_cloexec(fd, arg_accept);
103 /** Note: we leak some fd's on error here. I doesn't matter
104 * much, since the program will exit immediately anyway, but
105 * would be a pain to fix.
108 STRV_FOREACH(address, arg_listen) {
110 fd = make_socket_fd(*address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
112 log_error("Failed to open '%s': %s", *address, strerror(-fd));
116 assert(fd == SD_LISTEN_FDS_START + count);
120 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
122 log_error("Failed to create epoll object: %m");
126 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
127 _cleanup_free_ char *name = NULL;
129 getsockname_pretty(fd, &name);
130 log_info("Listening on %s as %i.", strna(name), fd);
132 r = add_epoll(*epoll_fd, fd);
140 static int launch(char* name, char **argv, char **env, int fds) {
142 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
143 _cleanup_strv_free_ char **envp = NULL;
144 _cleanup_free_ char *tmp = NULL;
145 unsigned n_env = 0, length;
149 length = strv_length(arg_environ);
151 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
152 envp = new0(char *, length + 7);
156 STRV_FOREACH(s, arg_environ) {
160 _cleanup_free_ char *p = strappend(*s, "=");
163 envp[n_env] = strv_find_prefix(env, p);
169 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
170 envp[n_env] = strv_find_prefix(env, tocopy[i]);
175 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
176 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
179 tmp = strv_join(argv, " ");
183 log_info("Execing %s (%s)", name, tmp);
184 execvpe(name, argv, envp);
185 log_error("Failed to execp %s (%s): %m", name, tmp);
190 static int launch1(const char* child, char** argv, char **env, int fd) {
191 _cleanup_free_ char *tmp = NULL;
192 pid_t parent_pid, child_pid;
195 tmp = strv_join(argv, " ");
199 parent_pid = getpid();
203 log_error("Failed to fork: %m");
208 if (child_pid == 0) {
209 r = dup2(fd, STDIN_FILENO);
211 log_error("Failed to dup connection to stdin: %m");
215 r = dup2(fd, STDOUT_FILENO);
217 log_error("Failed to dup connection to stdout: %m");
223 log_error("Failed to close dupped connection: %m");
227 /* Make sure the child goes away when the parent dies */
228 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
231 /* Check whether our parent died before we were able
232 * to set the death signal */
233 if (getppid() != parent_pid)
237 log_error("Failed to exec child %s: %m", child);
241 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
246 static int do_accept(const char* name, char **argv, char **envp, int fd) {
247 _cleanup_free_ char *local = NULL, *peer = NULL;
250 fd2 = accept(fd, NULL, NULL);
252 log_error("Failed to accept connection on fd:%d: %m", fd);
256 getsockname_pretty(fd2, &local);
257 getpeername_pretty(fd2, &peer);
258 log_info("Connection from %s to %s", strna(peer), strna(local));
260 return launch1(name, argv, envp, fd2);
263 /* SIGCHLD handler. */
264 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
267 log_info("Child %d died with code %d", t->si_pid, t->si_status);
268 /* Wait for a dead child. */
269 waitpid(t->si_pid, NULL, 0);
272 static int install_chld_handler(void) {
274 struct sigaction act;
276 act.sa_flags = SA_SIGINFO;
277 act.sa_sigaction = sigchld_hdl;
279 r = sigaction(SIGCHLD, &act, 0);
281 log_error("Failed to install SIGCHLD handler: %m");
285 static int help(void) {
286 printf("%s [OPTIONS...]\n\n"
287 "Listen on sockets and launch child on connection.\n\n"
289 " -l --listen=ADDR Listen for raw connections at ADDR\n"
290 " -a --accept Spawn separate child for each connection\n"
291 " -h --help Show this help and exit\n"
292 " -E --environment=NAME[=VALUE]\n"
293 " Pass an environment variable to children\n"
294 " --version Print version string and exit\n"
296 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
297 , program_invocation_short_name
303 static int parse_argv(int argc, char *argv[]) {
308 static const struct option options[] = {
309 { "help", no_argument, NULL, 'h' },
310 { "version", no_argument, NULL, ARG_VERSION },
311 { "listen", required_argument, NULL, 'l' },
312 { "accept", no_argument, NULL, 'a' },
313 { "environment", required_argument, NULL, 'E' },
322 while ((c = getopt_long(argc, argv, "+hl:saE:", options, NULL)) >= 0)
328 puts(PACKAGE_STRING);
329 puts(SYSTEMD_FEATURES);
333 int r = strv_extend(&arg_listen, optarg);
345 int r = strv_extend(&arg_environ, optarg);
356 assert_not_reached("Unhandled option");
359 if (optind == argc) {
360 log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
361 program_invocation_short_name);
365 arg_args = argv + optind;
367 return 1 /* work to do */;
370 int main(int argc, char **argv, char **envp) {
374 log_parse_environment();
377 r = parse_argv(argc, argv);
379 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
381 r = install_chld_handler();
385 n = open_sockets(&epoll_fd, arg_accept);
389 log_error("No sockets to listen on specified or passed in.");
394 struct epoll_event event;
396 r = epoll_wait(epoll_fd, &event, 1, -1);
401 log_error("epoll_wait() failed: %m");
405 log_info("Communication attempt on fd %i.", event.data.fd);
407 r = do_accept(argv[optind], argv + optind, envp,
415 launch(argv[optind], argv + optind, envp, n);