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 print_socket(const char* desc, int fd) {
60 SocketAddress addr = {
61 .size = sizeof(union sockaddr_union),
66 r = getsockname(fd, &addr.sockaddr.sa, &addr.size);
68 log_warning("Failed to query socket on fd:%d: %m", fd);
72 family = socket_address_family(&addr);
76 char* _cleanup_free_ a = NULL;
77 r = socket_address_print(&addr, &a);
79 log_warning("socket_address_print(): %s", strerror(-r));
81 log_info("%s %s address %s",
83 family == AF_INET ? "IP" : "IPv6",
88 log_warning("Connection with unknown family %d", family);
94 static int make_socket_fd(const char* address, int flags) {
95 _cleanup_free_ char *p = NULL;
99 r = socket_address_parse(&a, address);
101 log_error("Failed to parse socket: %s", strerror(-r));
105 fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT, NULL, false, false, 0755, 0644, NULL);
107 log_error("Failed to listen: %s", strerror(-r));
111 r = socket_address_print(&a, &p);
113 log_error("socket_address_print(): %s", strerror(-r));
114 close_nointr_nofail(fd);
118 log_info("Listening on %s", p);
123 static int open_sockets(int *epoll_fd, bool accept) {
128 n = sd_listen_fds(true);
130 log_error("Failed to read listening file descriptors from environment: %s",
134 log_info("Received %d descriptors", n);
136 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
137 log_debug("Received descriptor fd:%d", fd);
138 print_socket("Listening on", fd);
140 r = fd_cloexec(fd, arg_accept);
147 /** Note: we leak some fd's on error here. I doesn't matter
148 * much, since the program will exit immediately anyway, but
149 * would be a pain to fix.
152 STRV_FOREACH(address, arg_listen) {
153 log_info("Opening address %s", *address);
155 fd = make_socket_fd(*address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
157 log_error("Failed to open '%s': %s", *address, strerror(-fd));
161 assert(fd == SD_LISTEN_FDS_START + count);
165 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
167 log_error("Failed to create epoll object: %m");
172 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
173 r = add_epoll(*epoll_fd, fd);
181 static int launch(char* name, char **argv, char **env, int fds) {
182 unsigned n_env = 0, length;
183 _cleanup_strv_free_ char **envp = NULL;
185 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
186 _cleanup_free_ char *tmp = NULL;
189 length = strv_length(arg_environ);
190 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
191 envp = new(char *, length + 7);
193 STRV_FOREACH(s, arg_environ) {
197 _cleanup_free_ char *p = strappend(*s, "=");
200 envp[n_env] = strv_find_prefix(env, p);
206 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
207 envp[n_env] = strv_find_prefix(env, tocopy[i]);
212 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
213 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
216 tmp = strv_join(argv, " ");
220 log_info("Execing %s (%s)", name, tmp);
221 execvpe(name, argv, envp);
222 log_error("Failed to execp %s (%s): %m", name, tmp);
226 static int launch1(const char* child, char** argv, char **env, int fd) {
227 pid_t parent_pid, child_pid;
230 _cleanup_free_ char *tmp = NULL;
231 tmp = strv_join(argv, " ");
235 parent_pid = getpid();
239 log_error("Failed to fork: %m");
244 if (child_pid == 0) {
245 r = dup2(fd, STDIN_FILENO);
247 log_error("Failed to dup connection to stdin: %m");
251 r = dup2(fd, STDOUT_FILENO);
253 log_error("Failed to dup connection to stdout: %m");
259 log_error("Failed to close dupped connection: %m");
263 /* Make sure the child goes away when the parent dies */
264 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
267 /* Check whether our parent died before we were able
268 * to set the death signal */
269 if (getppid() != parent_pid)
273 log_error("Failed to exec child %s: %m", child);
277 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
282 static int do_accept(const char* name, char **argv, char **envp, int fd) {
283 SocketAddress addr = {
284 .size = sizeof(union sockaddr_union),
289 fd2 = accept(fd, &addr.sockaddr.sa, &addr.size);
291 log_error("Failed to accept connection on fd:%d: %m", fd);
295 print_socket("Connection from", fd2);
297 r = launch1(name, argv, envp, fd2);
301 /* SIGCHLD handler. */
302 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
303 log_info("Child %d died with code %d", t->si_pid, t->si_status);
304 /* Wait for a dead child. */
305 waitpid(t->si_pid, NULL, 0);
308 static int install_chld_handler(void) {
310 struct sigaction act;
312 act.sa_flags = SA_SIGINFO;
313 act.sa_sigaction = sigchld_hdl;
315 r = sigaction(SIGCHLD, &act, 0);
317 log_error("Failed to install SIGCHLD handler: %m");
321 static int help(void) {
322 printf("%s [OPTIONS...]\n\n"
323 "Listen on sockets and launch child on connection.\n\n"
325 " -l --listen=ADDR Listen for raw connections at ADDR\n"
326 " -a --accept Spawn separate child for each connection\n"
327 " -h --help Show this help and exit\n"
328 " --version Print version string and exit\n"
330 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
331 , program_invocation_short_name
337 static int parse_argv(int argc, char *argv[]) {
342 static const struct option options[] = {
343 { "help", no_argument, NULL, 'h' },
344 { "version", no_argument, NULL, ARG_VERSION },
345 { "listen", required_argument, NULL, 'l' },
346 { "accept", no_argument, NULL, 'a' },
347 { "environment", required_argument, NULL, 'E' },
356 while ((c = getopt_long(argc, argv, "+hl:saE:", options, NULL)) >= 0)
362 puts(PACKAGE_STRING);
363 puts(SYSTEMD_FEATURES);
367 int r = strv_extend(&arg_listen, optarg);
379 int r = strv_extend(&arg_environ, optarg);
390 assert_not_reached("Unhandled option");
393 if (optind == argc) {
394 log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
395 program_invocation_short_name);
399 arg_args = argv + optind;
401 return 1 /* work to do */;
404 int main(int argc, char **argv, char **envp) {
408 log_parse_environment();
411 r = parse_argv(argc, argv);
413 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
415 r = install_chld_handler();
419 n = open_sockets(&epoll_fd, arg_accept);
424 struct epoll_event event;
426 r = epoll_wait(epoll_fd, &event, 1, -1);
431 log_error("epoll_wait() failed: %m");
435 log_info("Communication attempt on fd:%d", event.data.fd);
437 r = do_accept(argv[optind], argv + optind, envp,
445 launch(argv[optind], argv + optind, envp, n);