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) {
45 struct epoll_event ev = {EPOLLIN};
48 assert(epoll_fd >= 0);
51 r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
53 log_error("Failed to add event on epoll fd:%d for fd:%d: %s",
54 epoll_fd, fd, strerror(-r));
58 static int set_nocloexec(int fd) {
61 flags = fcntl(fd, F_GETFD);
63 log_error("Querying flags for fd:%d: %m", fd);
67 if (!(flags & FD_CLOEXEC))
70 if (fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC) < 0) {
71 log_error("Settings flags for fd:%d: %m", fd);
78 static int print_socket(const char* desc, int fd) {
80 SocketAddress addr = {
81 .size = sizeof(union sockaddr_union),
86 r = getsockname(fd, &addr.sockaddr.sa, &addr.size);
88 log_warning("Failed to query socket on fd:%d: %m", fd);
92 family = socket_address_family(&addr);
96 char* _cleanup_free_ a = NULL;
97 r = socket_address_print(&addr, &a);
99 log_warning("socket_address_print(): %s", strerror(-r));
101 log_info("%s %s address %s",
103 family == AF_INET ? "IP" : "IPv6",
108 log_warning("Connection with unknown family %d", family);
114 static int open_sockets(int *epoll_fd, bool accept) {
119 n = sd_listen_fds(true);
121 log_error("Failed to read listening file descriptors from environment: %s",
125 log_info("Received %d descriptors", n);
127 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
128 log_debug("Received descriptor fd:%d", fd);
129 print_socket("Listening on", fd);
132 int r = set_nocloexec(fd);
140 STRV_FOREACH(address, arg_listen) {
141 log_info("Opening address %s", *address);
143 fd = make_socket_fd(*address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
145 log_error("Failed to open '%s': %s", *address, strerror(-fd));
152 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
154 log_error("Failed to create epoll object: %m");
158 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
159 int r = add_epoll(*epoll_fd, fd);
167 static int launch(char* name, char **argv, char **env, int fds) {
168 unsigned n_env = 0, length;
169 char **envp = NULL, **s;
170 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
171 char _cleanup_free_ *tmp = NULL;
174 length = strv_length(arg_environ);
175 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
176 envp = new(char *, length + 7);
178 STRV_FOREACH(s, arg_environ) {
182 char _cleanup_free_ *p = strappend(*s, "=");
185 envp[n_env] = strv_find_prefix(env, p);
191 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
192 envp[n_env] = strv_find_prefix(env, tocopy[i]);
197 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
198 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
201 tmp = strv_join(argv, " ");
205 log_info("Execing %s (%s)", name, tmp);
206 execvpe(name, argv, envp);
207 log_error("Failed to execp %s (%s): %m", name, tmp);
211 static int launch1(const char* child, char** argv, char **env, int fd) {
212 pid_t parent_pid, child_pid;
215 char _cleanup_free_ *tmp = NULL;
216 tmp = strv_join(argv, " ");
220 parent_pid = getpid();
224 log_error("Failed to fork: %m");
229 if (child_pid == 0) {
230 r = dup2(fd, STDIN_FILENO);
232 log_error("Failed to dup connection to stdin: %m");
236 r = dup2(fd, STDOUT_FILENO);
238 log_error("Failed to dup connection to stdout: %m");
244 log_error("Failed to close dupped connection: %m");
248 /* Make sure the child goes away when the parent dies */
249 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
252 /* Check whether our parent died before we were able
253 * to set the death signal */
254 if (getppid() != parent_pid)
258 log_error("Failed to exec child %s: %m", child);
262 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
267 static int do_accept(const char* name, char **argv, char **envp, int fd) {
268 SocketAddress addr = {
269 .size = sizeof(union sockaddr_union),
274 fd2 = accept(fd, &addr.sockaddr.sa, &addr.size);
276 log_error("Failed to accept connection on fd:%d: %m", fd);
280 print_socket("Connection from", fd2);
282 r = launch1(name, argv, envp, fd2);
286 /* SIGCHLD handler. */
287 static void sigchld_hdl(int sig, siginfo_t *t, void *data)
289 log_info("Child %d died with code %d", t->si_pid, t->si_status);
290 /* Wait for a dead child. */
291 waitpid(t->si_pid, NULL, 0);
294 static int install_chld_handler(void) {
296 struct sigaction act;
298 act.sa_flags = SA_SIGINFO;
299 act.sa_sigaction = sigchld_hdl;
301 r = sigaction(SIGCHLD, &act, 0);
303 log_error("Failed to install SIGCHLD handler: %m");
307 static int help(void) {
308 printf("%s [OPTIONS...]\n\n"
309 "Listen on sockets and launch child on connection.\n\n"
311 " -l --listen=ADDR Listen for raw connections at ADDR\n"
312 " -a --accept Spawn separate child for each connection\n"
313 " -h --help Show this help and exit\n"
314 " --version Print version string and exit\n"
316 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
317 , program_invocation_short_name
323 static int parse_argv(int argc, char *argv[]) {
328 static const struct option options[] = {
329 { "help", no_argument, NULL, 'h' },
330 { "version", no_argument, NULL, ARG_VERSION },
331 { "listen", required_argument, NULL, 'l' },
332 { "accept", no_argument, NULL, 'a' },
333 { "environment", required_argument, NULL, 'E' },
342 while ((c = getopt_long(argc, argv, "+hl:saE:", options, NULL)) >= 0)
349 puts(PACKAGE_STRING);
350 puts(SYSTEMD_FEATURES);
354 int r = strv_extend(&arg_listen, optarg);
366 int r = strv_extend(&arg_environ, optarg);
377 log_error("Unknown option code %c", c);
381 if (optind == argc) {
382 log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
383 program_invocation_short_name);
387 arg_args = argv + optind;
389 return 1 /* work to do */;
392 int main(int argc, char **argv, char **envp) {
396 log_set_max_level(LOG_DEBUG);
397 log_show_color(true);
398 log_parse_environment();
400 r = parse_argv(argc, argv);
402 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
404 r = install_chld_handler();
408 n = open_sockets(&epoll_fd, arg_accept);
413 struct epoll_event event;
415 r = epoll_wait(epoll_fd, &event, 1, -1);
420 log_error("epoll_wait() failed: %m");
424 log_info("Communication attempt on fd:%d", event.data.fd);
426 r = do_accept(argv[optind], argv + optind, envp,
434 launch(argv[optind], argv + optind, envp, n);