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", epoll_fd, fd);
62 static int make_socket_fd(const char* address, int flags) {
66 r = socket_address_parse(&a, address);
68 log_error("Failed to parse socket: %s", strerror(-r));
72 fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT, NULL, false, false, 0755, 0644, NULL);
74 log_error("Failed to listen: %s", strerror(-r));
81 static int open_sockets(int *epoll_fd, bool accept) {
86 n = sd_listen_fds(true);
88 log_error("Failed to read listening file descriptors from environment: %s",
93 log_info("Received %i descriptors via the environment.", n);
95 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
96 r = fd_cloexec(fd, arg_accept);
104 /* Close logging and all other descriptors */
108 for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++)
112 close_all_fds(except, 3 + n);
115 /** Note: we leak some fd's on error here. I doesn't matter
116 * much, since the program will exit immediately anyway, but
117 * would be a pain to fix.
120 STRV_FOREACH(address, arg_listen) {
122 fd = make_socket_fd(*address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
125 log_error("Failed to open '%s': %s", *address, strerror(-fd));
129 assert(fd == SD_LISTEN_FDS_START + count);
136 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
138 log_error("Failed to create epoll object: %m");
142 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
143 _cleanup_free_ char *name = NULL;
145 getsockname_pretty(fd, &name);
146 log_info("Listening on %s as %i.", strna(name), fd);
148 r = add_epoll(*epoll_fd, fd);
156 static int launch(char* name, char **argv, char **env, int fds) {
158 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
159 _cleanup_strv_free_ char **envp = NULL;
160 _cleanup_free_ char *tmp = NULL;
161 unsigned n_env = 0, length;
165 length = strv_length(arg_environ);
167 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
168 envp = new0(char *, length + 7);
172 STRV_FOREACH(s, arg_environ) {
176 _cleanup_free_ char *p = strappend(*s, "=");
179 envp[n_env] = strv_find_prefix(env, p);
185 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
186 envp[n_env] = strv_find_prefix(env, tocopy[i]);
191 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
192 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
195 tmp = strv_join(argv, " ");
199 log_info("Execing %s (%s)", name, tmp);
200 execvpe(name, argv, envp);
201 log_error("Failed to execp %s (%s): %m", name, tmp);
206 static int launch1(const char* child, char** argv, char **env, int fd) {
207 _cleanup_free_ char *tmp = NULL;
208 pid_t parent_pid, child_pid;
211 tmp = strv_join(argv, " ");
215 parent_pid = getpid();
219 log_error("Failed to fork: %m");
224 if (child_pid == 0) {
225 r = dup2(fd, STDIN_FILENO);
227 log_error("Failed to dup connection to stdin: %m");
231 r = dup2(fd, STDOUT_FILENO);
233 log_error("Failed to dup connection to stdout: %m");
239 log_error("Failed to close dupped connection: %m");
243 /* Make sure the child goes away when the parent dies */
244 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
247 /* Check whether our parent died before we were able
248 * to set the death signal */
249 if (getppid() != parent_pid)
253 log_error("Failed to exec child %s: %m", child);
257 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
262 static int do_accept(const char* name, char **argv, char **envp, int fd) {
263 _cleanup_free_ char *local = NULL, *peer = NULL;
266 fd2 = accept(fd, NULL, NULL);
268 log_error("Failed to accept connection on fd:%d: %m", fd);
272 getsockname_pretty(fd2, &local);
273 getpeername_pretty(fd2, &peer);
274 log_info("Connection from %s to %s", strna(peer), strna(local));
276 return launch1(name, argv, envp, fd2);
279 /* SIGCHLD handler. */
280 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
283 log_info("Child %d died with code %d", t->si_pid, t->si_status);
284 /* Wait for a dead child. */
285 waitpid(t->si_pid, NULL, 0);
288 static int install_chld_handler(void) {
290 struct sigaction act = {
291 .sa_flags = SA_SIGINFO,
292 .sa_sigaction = sigchld_hdl,
295 r = sigaction(SIGCHLD, &act, 0);
297 log_error("Failed to install SIGCHLD handler: %m");
301 static int help(void) {
302 printf("%s [OPTIONS...]\n\n"
303 "Listen on sockets and launch child on connection.\n\n"
305 " -l --listen=ADDR Listen for raw connections at ADDR\n"
306 " -a --accept Spawn separate child for each connection\n"
307 " -h --help Show this help and exit\n"
308 " -E --environment=NAME[=VALUE]\n"
309 " Pass an environment variable to children\n"
310 " --version Print version string and exit\n"
312 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
313 , program_invocation_short_name
319 static int parse_argv(int argc, char *argv[]) {
324 static const struct option options[] = {
325 { "help", no_argument, NULL, 'h' },
326 { "version", no_argument, NULL, ARG_VERSION },
327 { "listen", required_argument, NULL, 'l' },
328 { "accept", no_argument, NULL, 'a' },
329 { "environment", required_argument, NULL, 'E' },
338 while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
344 puts(PACKAGE_STRING);
345 puts(SYSTEMD_FEATURES);
349 int r = strv_extend(&arg_listen, optarg);
361 int r = strv_extend(&arg_environ, optarg);
372 assert_not_reached("Unhandled option");
375 if (optind == argc) {
376 log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
377 program_invocation_short_name);
381 arg_args = argv + optind;
383 return 1 /* work to do */;
386 int main(int argc, char **argv, char **envp) {
390 log_parse_environment();
393 r = parse_argv(argc, argv);
395 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
397 r = install_chld_handler();
401 n = open_sockets(&epoll_fd, arg_accept);
405 log_error("No sockets to listen on specified or passed in.");
410 struct epoll_event event;
412 r = epoll_wait(epoll_fd, &event, 1, -1);
417 log_error("epoll_wait() failed: %m");
421 log_info("Communication attempt on fd %i.", event.data.fd);
423 r = do_accept(argv[optind], argv + optind, envp,
431 launch(argv[optind], argv + optind, envp, n);