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;
42 static int add_epoll(int epoll_fd, int fd) {
44 struct epoll_event ev = {EPOLLIN};
47 assert(epoll_fd >= 0);
50 r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
52 log_error("Failed to add event on epoll fd:%d for fd:%d: %s",
53 epoll_fd, fd, strerror(-r));
57 static int set_nocloexec(int fd) {
60 flags = fcntl(fd, F_GETFD);
62 log_error("Querying flags for fd:%d: %m", fd);
66 if (!(flags & FD_CLOEXEC))
69 if (fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC) < 0) {
70 log_error("Settings flags for fd:%d: %m", fd);
77 static int print_socket(const char* desc, int fd) {
79 SocketAddress addr = {
80 .size = sizeof(union sockaddr_union),
85 r = getsockname(fd, &addr.sockaddr.sa, &addr.size);
87 log_warning("Failed to query socket on fd:%d: %m", fd);
91 family = socket_address_family(&addr);
95 char* _cleanup_free_ a = NULL;
96 r = socket_address_print(&addr, &a);
98 log_warning("socket_address_print(): %s", strerror(-r));
100 log_info("%s %s address %s",
102 family == AF_INET ? "IP" : "IPv6",
107 log_warning("Connection with unknown family %d", family);
113 static int open_sockets(int *epoll_fd, bool accept) {
118 n = sd_listen_fds(true);
120 log_error("Failed to read listening file descriptors from environment: %s",
124 log_info("Received %d descriptors", n);
126 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
127 log_debug("Received descriptor fd:%d", fd);
128 print_socket("Listening on", fd);
131 int r = set_nocloexec(fd);
139 STRV_FOREACH(address, arg_listen) {
140 log_info("Opening address %s", *address);
142 fd = make_socket_fd(*address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
144 log_error("Failed to open '%s': %s", *address, strerror(-fd));
151 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
153 log_error("Failed to create epoll object: %m");
157 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
158 int r = add_epoll(*epoll_fd, fd);
166 static int launch(char* name, char **argv, char **environ, int fds) {
168 char* envp[7] = {NULL}; /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID */
169 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
170 char _cleanup_free_ *tmp = NULL;
173 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
174 envp[n_env] = strv_find_prefix(environ, tocopy[i]);
179 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
180 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
183 tmp = strv_join(argv, " ");
187 log_info("Execing %s (%s)", name, tmp);
188 execvpe(name, argv, envp);
189 log_error("Failed to execp %s (%s): %m", name, tmp);
193 static int launch1(const char* child, char** argv, char **environ, int fd) {
194 pid_t parent_pid, child_pid;
197 char _cleanup_free_ *tmp = NULL;
198 tmp = strv_join(argv, " ");
202 parent_pid = getpid();
206 log_error("Failed to fork: %m");
211 if (child_pid == 0) {
212 r = dup2(fd, STDIN_FILENO);
214 log_error("Failed to dup connection to stdin: %m");
218 r = dup2(fd, STDOUT_FILENO);
220 log_error("Failed to dup connection to stdout: %m");
226 log_error("Failed to close dupped connection: %m");
230 /* Make sure the child goes away when the parent dies */
231 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
234 /* Check whether our parent died before we were able
235 * to set the death signal */
236 if (getppid() != parent_pid)
240 log_error("Failed to exec child %s: %m", child);
244 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
249 static int do_accept(const char* name, char **argv, char **envp, int fd) {
250 SocketAddress addr = {
251 .size = sizeof(union sockaddr_union),
256 fd2 = accept(fd, &addr.sockaddr.sa, &addr.size);
258 log_error("Failed to accept connection on fd:%d: %m", fd);
262 print_socket("Connection from", fd2);
264 r = launch1(name, argv, envp, fd2);
268 /* SIGCHLD handler. */
269 static void sigchld_hdl(int sig, siginfo_t *t, void *data)
271 log_info("Child %d died with code %d", t->si_pid, t->si_status);
272 /* Wait for a dead child. */
273 waitpid(t->si_pid, NULL, 0);
276 static int install_chld_handler(void) {
278 struct sigaction act;
280 act.sa_flags = SA_SIGINFO;
281 act.sa_sigaction = sigchld_hdl;
283 r = sigaction(SIGCHLD, &act, 0);
285 log_error("Failed to install SIGCHLD handler: %m");
289 static int help(void) {
290 printf("%s [OPTIONS...]\n\n"
291 "Listen on sockets and launch child on connection.\n\n"
293 " -l --listen=ADDR Listen for raw connections at ADDR\n"
294 " -a --accept Spawn separate child for each connection\n"
295 " -h --help Show this help and exit\n"
296 " --version Print version string and exit\n"
298 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
299 , program_invocation_short_name
305 static int parse_argv(int argc, char *argv[]) {
310 static const struct option options[] = {
311 { "help", no_argument, NULL, 'h' },
312 { "version", no_argument, NULL, ARG_VERSION },
313 { "listen", required_argument, NULL, 'l' },
314 { "accept", no_argument, NULL, 'a' },
323 while ((c = getopt_long(argc, argv, "+hl:sa", options, NULL)) >= 0)
330 puts(PACKAGE_STRING);
331 puts(SYSTEMD_FEATURES);
335 int r = strv_extend(&arg_listen, optarg);
350 log_error("Unknown option code %c", c);
354 if (optind == argc) {
355 log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
356 program_invocation_short_name);
360 arg_args = argv + optind;
362 return 1 /* work to do */;
365 int main(int argc, char **argv, char **envp) {
369 log_set_max_level(LOG_DEBUG);
370 log_show_color(true);
371 log_parse_environment();
373 r = parse_argv(argc, argv);
375 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
377 r = install_chld_handler();
381 n = open_sockets(&epoll_fd, arg_accept);
386 struct epoll_event event;
388 r = epoll_wait(epoll_fd, &event, 1, -1);
393 log_error("epoll_wait() failed: %m");
397 log_info("Communication attempt on fd:%d", event.data.fd);
399 r = do_accept(argv[optind], argv + optind, envp,
407 launch(argv[optind], argv + optind, envp, n);