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 /** Note: we leak some fd's on error here. I doesn't matter
141 * much, since the program will exit immediately anyway, but
142 * would be a pain to fix.
145 STRV_FOREACH(address
, arg_listen
) {
146 log_info("Opening address %s", *address
);
148 fd
= make_socket_fd(*address
, SOCK_STREAM
| (arg_accept
*SOCK_CLOEXEC
));
150 log_error("Failed to open '%s': %s", *address
, strerror(-fd
));
157 *epoll_fd
= epoll_create1(EPOLL_CLOEXEC
);
159 log_error("Failed to create epoll object: %m");
163 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ count
; fd
++) {
164 int r
= add_epoll(*epoll_fd
, fd
);
172 static int launch(char* name
, char **argv
, char **env
, int fds
) {
173 unsigned n_env
= 0, length
;
174 _cleanup_strv_free_
char **envp
= NULL
;
176 static const char* tocopy
[] = {"TERM=", "PATH=", "USER=", "HOME="};
177 _cleanup_free_
char *tmp
= NULL
;
180 length
= strv_length(arg_environ
);
181 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
182 envp
= new(char *, length
+ 7);
184 STRV_FOREACH(s
, arg_environ
) {
188 _cleanup_free_
char *p
= strappend(*s
, "=");
191 envp
[n_env
] = strv_find_prefix(env
, p
);
197 for (i
= 0; i
< ELEMENTSOF(tocopy
); i
++) {
198 envp
[n_env
] = strv_find_prefix(env
, tocopy
[i
]);
203 if ((asprintf((char**)(envp
+ n_env
++), "LISTEN_FDS=%d", fds
) < 0) ||
204 (asprintf((char**)(envp
+ n_env
++), "LISTEN_PID=%d", getpid()) < 0))
207 tmp
= strv_join(argv
, " ");
211 log_info("Execing %s (%s)", name
, tmp
);
212 execvpe(name
, argv
, envp
);
213 log_error("Failed to execp %s (%s): %m", name
, tmp
);
217 static int launch1(const char* child
, char** argv
, char **env
, int fd
) {
218 pid_t parent_pid
, child_pid
;
221 _cleanup_free_
char *tmp
= NULL
;
222 tmp
= strv_join(argv
, " ");
226 parent_pid
= getpid();
230 log_error("Failed to fork: %m");
235 if (child_pid
== 0) {
236 r
= dup2(fd
, STDIN_FILENO
);
238 log_error("Failed to dup connection to stdin: %m");
242 r
= dup2(fd
, STDOUT_FILENO
);
244 log_error("Failed to dup connection to stdout: %m");
250 log_error("Failed to close dupped connection: %m");
254 /* Make sure the child goes away when the parent dies */
255 if (prctl(PR_SET_PDEATHSIG
, SIGTERM
) < 0)
258 /* Check whether our parent died before we were able
259 * to set the death signal */
260 if (getppid() != parent_pid
)
264 log_error("Failed to exec child %s: %m", child
);
268 log_info("Spawned %s (%s) as PID %d", child
, tmp
, child_pid
);
273 static int do_accept(const char* name
, char **argv
, char **envp
, int fd
) {
274 SocketAddress addr
= {
275 .size
= sizeof(union sockaddr_union
),
280 fd2
= accept(fd
, &addr
.sockaddr
.sa
, &addr
.size
);
282 log_error("Failed to accept connection on fd:%d: %m", fd
);
286 print_socket("Connection from", fd2
);
288 r
= launch1(name
, argv
, envp
, fd2
);
292 /* SIGCHLD handler. */
293 static void sigchld_hdl(int sig
, siginfo_t
*t
, void *data
)
295 log_info("Child %d died with code %d", t
->si_pid
, t
->si_status
);
296 /* Wait for a dead child. */
297 waitpid(t
->si_pid
, NULL
, 0);
300 static int install_chld_handler(void) {
302 struct sigaction act
;
304 act
.sa_flags
= SA_SIGINFO
;
305 act
.sa_sigaction
= sigchld_hdl
;
307 r
= sigaction(SIGCHLD
, &act
, 0);
309 log_error("Failed to install SIGCHLD handler: %m");
313 static int help(void) {
314 printf("%s [OPTIONS...]\n\n"
315 "Listen on sockets and launch child on connection.\n\n"
317 " -l --listen=ADDR Listen for raw connections at ADDR\n"
318 " -a --accept Spawn separate child for each connection\n"
319 " -h --help Show this help and exit\n"
320 " --version Print version string and exit\n"
322 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
323 , program_invocation_short_name
329 static int parse_argv(int argc
, char *argv
[]) {
334 static const struct option options
[] = {
335 { "help", no_argument
, NULL
, 'h' },
336 { "version", no_argument
, NULL
, ARG_VERSION
},
337 { "listen", required_argument
, NULL
, 'l' },
338 { "accept", no_argument
, NULL
, 'a' },
339 { "environment", required_argument
, NULL
, 'E' },
348 while ((c
= getopt_long(argc
, argv
, "+hl:saE:", options
, NULL
)) >= 0)
355 puts(PACKAGE_STRING
);
356 puts(SYSTEMD_FEATURES
);
360 int r
= strv_extend(&arg_listen
, optarg
);
372 int r
= strv_extend(&arg_environ
, optarg
);
383 log_error("Unknown option code %c", c
);
387 if (optind
== argc
) {
388 log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
389 program_invocation_short_name
);
393 arg_args
= argv
+ optind
;
395 return 1 /* work to do */;
398 int main(int argc
, char **argv
, char **envp
) {
402 log_set_max_level(LOG_DEBUG
);
403 log_show_color(true);
404 log_parse_environment();
406 r
= parse_argv(argc
, argv
);
408 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;
410 r
= install_chld_handler();
414 n
= open_sockets(&epoll_fd
, arg_accept
);
419 struct epoll_event event
;
421 r
= epoll_wait(epoll_fd
, &event
, 1, -1);
426 log_error("epoll_wait() failed: %m");
430 log_info("Communication attempt on fd:%d", event
.data
.fd
);
432 r
= do_accept(argv
[optind
], argv
+ optind
, envp
,
440 launch(argv
[optind
], argv
+ optind
, envp
, n
);