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/>.
23 #include <sys/epoll.h>
24 #include <sys/prctl.h>
25 #include <sys/socket.h>
29 #include "sd-daemon.h"
33 #include "signal-util.h"
34 #include "socket-util.h"
37 static char** arg_listen
= NULL
;
38 static bool arg_accept
= false;
39 static char** arg_args
= NULL
;
40 static char** arg_setenv
= NULL
;
41 static const char *arg_fdname
= 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 return log_error_errno(errno
, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd
, fd
);
60 static int open_sockets(int *epoll_fd
, bool accept
) {
65 n
= sd_listen_fds(true);
67 return log_error_errno(n
, "Failed to read listening file descriptors from environment: %m");
69 log_info("Received %i descriptors via the environment.", n
);
71 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ n
; fd
++) {
72 r
= fd_cloexec(fd
, arg_accept
);
80 /* Close logging and all other descriptors */
84 for (fd
= 0; fd
< SD_LISTEN_FDS_START
+ n
; fd
++)
88 close_all_fds(except
, 3 + n
);
91 /** Note: we leak some fd's on error here. I doesn't matter
92 * much, since the program will exit immediately anyway, but
93 * would be a pain to fix.
96 STRV_FOREACH(address
, arg_listen
) {
98 fd
= make_socket_fd(LOG_DEBUG
, *address
, SOCK_STREAM
| (arg_accept
*SOCK_CLOEXEC
));
101 return log_error_errno(fd
, "Failed to open '%s': %m", *address
);
104 assert(fd
== SD_LISTEN_FDS_START
+ count
);
111 *epoll_fd
= epoll_create1(EPOLL_CLOEXEC
);
113 return log_error_errno(errno
, "Failed to create epoll object: %m");
115 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ count
; fd
++) {
116 _cleanup_free_
char *name
= NULL
;
118 getsockname_pretty(fd
, &name
);
119 log_info("Listening on %s as %i.", strna(name
), fd
);
121 r
= add_epoll(*epoll_fd
, fd
);
129 static int launch(char* name
, char **argv
, char **env
, int fds
) {
131 static const char* tocopy
[] = {"TERM=", "PATH=", "USER=", "HOME="};
132 _cleanup_strv_free_
char **envp
= NULL
;
133 _cleanup_free_
char *tmp
= NULL
;
134 unsigned n_env
= 0, length
;
138 length
= strv_length(arg_setenv
);
140 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
141 envp
= new0(char *, length
+ 8);
145 STRV_FOREACH(s
, arg_setenv
) {
149 _cleanup_free_
char *p
;
151 p
= strappend(*s
, "=");
154 envp
[n_env
] = strv_find_prefix(env
, p
);
160 for (i
= 0; i
< ELEMENTSOF(tocopy
); i
++) {
161 envp
[n_env
] = strv_find_prefix(env
, tocopy
[i
]);
166 if ((asprintf((char**)(envp
+ n_env
++), "LISTEN_FDS=%d", fds
) < 0) ||
167 (asprintf((char**)(envp
+ n_env
++), "LISTEN_PID=%d", getpid()) < 0))
173 e
= strappend("LISTEN_FDNAMES=", arg_fdname
);
177 for (i
= 1; i
< (unsigned) fds
; i
++) {
180 c
= strjoin(e
, ":", arg_fdname
, NULL
);
193 tmp
= strv_join(argv
, " ");
197 log_info("Execing %s (%s)", name
, tmp
);
198 execvpe(name
, argv
, envp
);
200 return log_error_errno(errno
, "Failed to execp %s (%s): %m", name
, tmp
);
203 static int launch1(const char* child
, char** argv
, char **env
, int fd
) {
204 _cleanup_free_
char *tmp
= NULL
;
205 pid_t parent_pid
, child_pid
;
208 tmp
= strv_join(argv
, " ");
212 parent_pid
= getpid();
216 return log_error_errno(errno
, "Failed to fork: %m");
219 if (child_pid
== 0) {
221 (void) reset_all_signal_handlers();
222 (void) reset_signal_mask();
224 r
= dup2(fd
, STDIN_FILENO
);
226 log_error_errno(errno
, "Failed to dup connection to stdin: %m");
230 r
= dup2(fd
, STDOUT_FILENO
);
232 log_error_errno(errno
, "Failed to dup connection to stdout: %m");
238 log_error_errno(errno
, "Failed to close dupped connection: %m");
242 /* Make sure the child goes away when the parent dies */
243 if (prctl(PR_SET_PDEATHSIG
, SIGTERM
) < 0)
246 /* Check whether our parent died before we were able
247 * to set the death signal */
248 if (getppid() != parent_pid
)
252 log_error_errno(errno
, "Failed to exec child %s: %m", child
);
256 log_info("Spawned %s (%s) as PID %d", child
, tmp
, child_pid
);
261 static int do_accept(const char* name
, char **argv
, char **envp
, int fd
) {
262 _cleanup_free_
char *local
= NULL
, *peer
= NULL
;
263 _cleanup_close_
int fd2
= -1;
265 fd2
= accept(fd
, NULL
, NULL
);
267 log_error_errno(errno
, "Failed to accept connection on fd:%d: %m", fd
);
271 getsockname_pretty(fd2
, &local
);
272 getpeername_pretty(fd2
, &peer
);
273 log_info("Connection from %s to %s", strna(peer
), strna(local
));
275 return launch1(name
, argv
, envp
, fd2
);
278 /* SIGCHLD handler. */
279 static void sigchld_hdl(int sig
, siginfo_t
*t
, void *data
) {
282 log_info("Child %d died with code %d", t
->si_pid
, t
->si_status
);
283 /* Wait for a dead child. */
284 waitpid(t
->si_pid
, NULL
, 0);
287 static int install_chld_handler(void) {
289 struct sigaction act
= {
290 .sa_flags
= SA_SIGINFO
,
291 .sa_sigaction
= sigchld_hdl
,
294 r
= sigaction(SIGCHLD
, &act
, 0);
296 log_error_errno(errno
, "Failed to install SIGCHLD handler: %m");
300 static void help(void) {
301 printf("%s [OPTIONS...]\n\n"
302 "Listen on sockets and launch child on connection.\n\n"
304 " -l --listen=ADDR Listen for raw connections at ADDR\n"
305 " -a --accept Spawn separate child for each connection\n"
306 " -h --help Show this help and exit\n"
307 " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
308 " --version Print version string and exit\n"
310 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
311 , program_invocation_short_name
);
314 static int parse_argv(int argc
, char *argv
[]) {
320 static const struct option options
[] = {
321 { "help", no_argument
, NULL
, 'h' },
322 { "version", no_argument
, NULL
, ARG_VERSION
},
323 { "listen", required_argument
, NULL
, 'l' },
324 { "accept", no_argument
, NULL
, 'a' },
325 { "setenv", required_argument
, NULL
, 'E' },
326 { "environment", required_argument
, NULL
, 'E' }, /* legacy alias */
327 { "fdname", required_argument
, NULL
, ARG_FDNAME
},
336 while ((c
= getopt_long(argc
, argv
, "+hl:aE:", options
, NULL
)) >= 0)
346 r
= strv_extend(&arg_listen
, optarg
);
357 r
= strv_extend(&arg_setenv
, optarg
);
371 assert_not_reached("Unhandled option");
374 if (optind
== argc
) {
375 log_error("%s: command to execute is missing.",
376 program_invocation_short_name
);
380 arg_args
= argv
+ optind
;
382 return 1 /* work to do */;
385 int main(int argc
, char **argv
, char **envp
) {
389 log_parse_environment();
392 r
= parse_argv(argc
, argv
);
394 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;
396 r
= install_chld_handler();
400 n
= open_sockets(&epoll_fd
, arg_accept
);
404 log_error("No sockets to listen on specified or passed in.");
409 struct epoll_event event
;
411 r
= epoll_wait(epoll_fd
, &event
, 1, -1);
416 log_error_errno(errno
, "epoll_wait() failed: %m");
420 log_info("Communication attempt on fd %i.", event
.data
.fd
);
422 r
= do_accept(argv
[optind
], argv
+ optind
, envp
,
430 launch(argv
[optind
], argv
+ optind
, envp
, n
);