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"
31 #include "alloc-util.h"
35 #include "signal-util.h"
36 #include "socket-util.h"
37 #include "string-util.h"
40 static char** arg_listen
= NULL
;
41 static bool arg_accept
= false;
42 static bool arg_datagram
= false;
43 static char** arg_args
= NULL
;
44 static char** arg_setenv
= NULL
;
45 static const char *arg_fdname
= NULL
;
47 static int add_epoll(int epoll_fd
, int fd
) {
48 struct epoll_event ev
= {
53 assert(epoll_fd
>= 0);
57 r
= epoll_ctl(epoll_fd
, EPOLL_CTL_ADD
, fd
, &ev
);
59 return log_error_errno(errno
, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd
, fd
);
64 static int open_sockets(int *epoll_fd
, bool accept
) {
69 n
= sd_listen_fds(true);
71 return log_error_errno(n
, "Failed to read listening file descriptors from environment: %m");
73 log_info("Received %i descriptors via the environment.", n
);
75 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ n
; fd
++) {
76 r
= fd_cloexec(fd
, arg_accept
);
84 /* Close logging and all other descriptors */
88 for (fd
= 0; fd
< SD_LISTEN_FDS_START
+ n
; fd
++)
92 close_all_fds(except
, 3 + n
);
95 /** Note: we leak some fd's on error here. I doesn't matter
96 * much, since the program will exit immediately anyway, but
97 * would be a pain to fix.
100 STRV_FOREACH(address
, arg_listen
) {
103 fd
= make_socket_fd(LOG_DEBUG
, *address
, SOCK_DGRAM
, SOCK_CLOEXEC
);
105 fd
= make_socket_fd(LOG_DEBUG
, *address
, SOCK_STREAM
, (arg_accept
*SOCK_CLOEXEC
));
109 return log_error_errno(fd
, "Failed to open '%s': %m", *address
);
112 assert(fd
== SD_LISTEN_FDS_START
+ count
);
119 *epoll_fd
= epoll_create1(EPOLL_CLOEXEC
);
121 return log_error_errno(errno
, "Failed to create epoll object: %m");
123 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ count
; fd
++) {
124 _cleanup_free_
char *name
= NULL
;
126 getsockname_pretty(fd
, &name
);
127 log_info("Listening on %s as %i.", strna(name
), fd
);
129 r
= add_epoll(*epoll_fd
, fd
);
137 static int launch(char* name
, char **argv
, char **env
, int fds
) {
139 static const char* tocopy
[] = {"TERM=", "PATH=", "USER=", "HOME="};
140 _cleanup_strv_free_
char **envp
= NULL
;
141 _cleanup_free_
char *tmp
= NULL
;
142 unsigned n_env
= 0, length
;
146 length
= strv_length(arg_setenv
);
148 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
149 envp
= new0(char *, length
+ 8);
153 STRV_FOREACH(s
, arg_setenv
) {
154 if (strchr(*s
, '=')) {
163 _cleanup_free_
char *p
;
166 p
= strappend(*s
, "=");
170 n
= strv_find_prefix(env
, p
);
174 envp
[n_env
] = strdup(n
);
180 for (i
= 0; i
< ELEMENTSOF(tocopy
); i
++) {
183 n
= strv_find_prefix(env
, tocopy
[i
]);
187 envp
[n_env
] = strdup(n
);
194 if ((asprintf((char**)(envp
+ n_env
++), "LISTEN_FDS=%d", fds
) < 0) ||
195 (asprintf((char**)(envp
+ n_env
++), "LISTEN_PID=%d", getpid()) < 0))
201 e
= strappend("LISTEN_FDNAMES=", arg_fdname
);
205 for (i
= 1; i
< (unsigned) fds
; i
++) {
208 c
= strjoin(e
, ":", arg_fdname
, NULL
);
221 tmp
= strv_join(argv
, " ");
225 log_info("Execing %s (%s)", name
, tmp
);
226 execvpe(name
, argv
, envp
);
228 return log_error_errno(errno
, "Failed to execp %s (%s): %m", name
, tmp
);
231 static int launch1(const char* child
, char** argv
, char **env
, int fd
) {
232 _cleanup_free_
char *tmp
= NULL
;
233 pid_t parent_pid
, child_pid
;
236 tmp
= strv_join(argv
, " ");
240 parent_pid
= getpid();
244 return log_error_errno(errno
, "Failed to fork: %m");
247 if (child_pid
== 0) {
249 (void) reset_all_signal_handlers();
250 (void) reset_signal_mask();
252 r
= dup2(fd
, STDIN_FILENO
);
254 log_error_errno(errno
, "Failed to dup connection to stdin: %m");
258 r
= dup2(fd
, STDOUT_FILENO
);
260 log_error_errno(errno
, "Failed to dup connection to stdout: %m");
266 log_error_errno(errno
, "Failed to close dupped connection: %m");
270 /* Make sure the child goes away when the parent dies */
271 if (prctl(PR_SET_PDEATHSIG
, SIGTERM
) < 0)
274 /* Check whether our parent died before we were able
275 * to set the death signal */
276 if (getppid() != parent_pid
)
280 log_error_errno(errno
, "Failed to exec child %s: %m", child
);
284 log_info("Spawned %s (%s) as PID %d", child
, tmp
, child_pid
);
289 static int do_accept(const char* name
, char **argv
, char **envp
, int fd
) {
290 _cleanup_free_
char *local
= NULL
, *peer
= NULL
;
291 _cleanup_close_
int fd2
= -1;
293 fd2
= accept(fd
, NULL
, NULL
);
295 log_error_errno(errno
, "Failed to accept connection on fd:%d: %m", fd
);
299 getsockname_pretty(fd2
, &local
);
300 getpeername_pretty(fd2
, true, &peer
);
301 log_info("Connection from %s to %s", strna(peer
), strna(local
));
303 return launch1(name
, argv
, envp
, fd2
);
306 /* SIGCHLD handler. */
307 static void sigchld_hdl(int sig
, siginfo_t
*t
, void *data
) {
310 log_info("Child %d died with code %d", t
->si_pid
, t
->si_status
);
311 /* Wait for a dead child. */
312 waitpid(t
->si_pid
, NULL
, 0);
315 static int install_chld_handler(void) {
317 struct sigaction act
= {
318 .sa_flags
= SA_SIGINFO
,
319 .sa_sigaction
= sigchld_hdl
,
322 r
= sigaction(SIGCHLD
, &act
, 0);
324 log_error_errno(errno
, "Failed to install SIGCHLD handler: %m");
328 static void help(void) {
329 printf("%s [OPTIONS...]\n\n"
330 "Listen on sockets and launch child on connection.\n\n"
332 " -h --help Show this help and exit\n"
333 " --version Print version string and exit\n"
334 " -l --listen=ADDR Listen for raw connections at ADDR\n"
335 " -d --datagram Listen on datagram instead of stream socket\n"
336 " -a --accept Spawn separate child for each connection\n"
337 " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
339 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
340 , program_invocation_short_name
);
343 static int parse_argv(int argc
, char *argv
[]) {
349 static const struct option options
[] = {
350 { "help", no_argument
, NULL
, 'h' },
351 { "version", no_argument
, NULL
, ARG_VERSION
},
352 { "datagram", no_argument
, NULL
, 'd' },
353 { "listen", required_argument
, NULL
, 'l' },
354 { "accept", no_argument
, NULL
, 'a' },
355 { "setenv", required_argument
, NULL
, 'E' },
356 { "environment", required_argument
, NULL
, 'E' }, /* legacy alias */
357 { "fdname", required_argument
, NULL
, ARG_FDNAME
},
366 while ((c
= getopt_long(argc
, argv
, "+hl:aEd", options
, NULL
)) >= 0)
376 r
= strv_extend(&arg_listen
, optarg
);
391 r
= strv_extend(&arg_setenv
, optarg
);
398 if (!fdname_is_valid(optarg
)) {
399 log_error("File descriptor name %s is not valid, refusing.", optarg
);
410 assert_not_reached("Unhandled option");
413 if (optind
== argc
) {
414 log_error("%s: command to execute is missing.",
415 program_invocation_short_name
);
419 if (arg_datagram
&& arg_accept
) {
420 log_error("Datagram sockets do not accept connections. "
421 "The --datagram and --accept options may not be combined.");
425 arg_args
= argv
+ optind
;
427 return 1 /* work to do */;
430 int main(int argc
, char **argv
, char **envp
) {
434 log_parse_environment();
437 r
= parse_argv(argc
, argv
);
439 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;
441 r
= install_chld_handler();
445 n
= open_sockets(&epoll_fd
, arg_accept
);
449 log_error("No sockets to listen on specified or passed in.");
454 struct epoll_event event
;
456 r
= epoll_wait(epoll_fd
, &event
, 1, -1);
461 log_error_errno(errno
, "epoll_wait() failed: %m");
465 log_info("Communication attempt on fd %i.", event
.data
.fd
);
467 r
= do_accept(argv
[optind
], argv
+ optind
, envp
,
475 launch(argv
[optind
], argv
+ optind
, envp
, n
);