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 open_sockets(int *epoll_fd
, bool accept
) {
99 n
= sd_listen_fds(true);
101 log_error("Failed to read listening file descriptors from environment: %s",
105 log_info("Received %d descriptors", n
);
107 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ n
; fd
++) {
108 log_debug("Received descriptor fd:%d", fd
);
109 print_socket("Listening on", fd
);
111 r
= fd_cloexec(fd
, arg_accept
);
118 /** Note: we leak some fd's on error here. I doesn't matter
119 * much, since the program will exit immediately anyway, but
120 * would be a pain to fix.
123 STRV_FOREACH(address
, arg_listen
) {
124 log_info("Opening address %s", *address
);
126 fd
= make_socket_fd(*address
, SOCK_STREAM
| (arg_accept
*SOCK_CLOEXEC
));
128 log_error("Failed to open '%s': %s", *address
, strerror(-fd
));
135 *epoll_fd
= epoll_create1(EPOLL_CLOEXEC
);
137 log_error("Failed to create epoll object: %m");
142 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ count
; fd
++) {
143 r
= add_epoll(*epoll_fd
, fd
);
151 static int launch(char* name
, char **argv
, char **env
, int fds
) {
152 unsigned n_env
= 0, length
;
153 _cleanup_strv_free_
char **envp
= NULL
;
155 static const char* tocopy
[] = {"TERM=", "PATH=", "USER=", "HOME="};
156 _cleanup_free_
char *tmp
= NULL
;
159 length
= strv_length(arg_environ
);
160 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
161 envp
= new(char *, length
+ 7);
163 STRV_FOREACH(s
, arg_environ
) {
167 _cleanup_free_
char *p
= strappend(*s
, "=");
170 envp
[n_env
] = strv_find_prefix(env
, p
);
176 for (i
= 0; i
< ELEMENTSOF(tocopy
); i
++) {
177 envp
[n_env
] = strv_find_prefix(env
, tocopy
[i
]);
182 if ((asprintf((char**)(envp
+ n_env
++), "LISTEN_FDS=%d", fds
) < 0) ||
183 (asprintf((char**)(envp
+ n_env
++), "LISTEN_PID=%d", getpid()) < 0))
186 tmp
= strv_join(argv
, " ");
190 log_info("Execing %s (%s)", name
, tmp
);
191 execvpe(name
, argv
, envp
);
192 log_error("Failed to execp %s (%s): %m", name
, tmp
);
196 static int launch1(const char* child
, char** argv
, char **env
, int fd
) {
197 pid_t parent_pid
, child_pid
;
200 _cleanup_free_
char *tmp
= NULL
;
201 tmp
= strv_join(argv
, " ");
205 parent_pid
= getpid();
209 log_error("Failed to fork: %m");
214 if (child_pid
== 0) {
215 r
= dup2(fd
, STDIN_FILENO
);
217 log_error("Failed to dup connection to stdin: %m");
221 r
= dup2(fd
, STDOUT_FILENO
);
223 log_error("Failed to dup connection to stdout: %m");
229 log_error("Failed to close dupped connection: %m");
233 /* Make sure the child goes away when the parent dies */
234 if (prctl(PR_SET_PDEATHSIG
, SIGTERM
) < 0)
237 /* Check whether our parent died before we were able
238 * to set the death signal */
239 if (getppid() != parent_pid
)
243 log_error("Failed to exec child %s: %m", child
);
247 log_info("Spawned %s (%s) as PID %d", child
, tmp
, child_pid
);
252 static int do_accept(const char* name
, char **argv
, char **envp
, int fd
) {
253 SocketAddress addr
= {
254 .size
= sizeof(union sockaddr_union
),
259 fd2
= accept(fd
, &addr
.sockaddr
.sa
, &addr
.size
);
261 log_error("Failed to accept connection on fd:%d: %m", fd
);
265 print_socket("Connection from", fd2
);
267 r
= launch1(name
, argv
, envp
, fd2
);
271 /* SIGCHLD handler. */
272 static void sigchld_hdl(int sig
, siginfo_t
*t
, void *data
) {
273 log_info("Child %d died with code %d", t
->si_pid
, t
->si_status
);
274 /* Wait for a dead child. */
275 waitpid(t
->si_pid
, NULL
, 0);
278 static int install_chld_handler(void) {
280 struct sigaction act
;
282 act
.sa_flags
= SA_SIGINFO
;
283 act
.sa_sigaction
= sigchld_hdl
;
285 r
= sigaction(SIGCHLD
, &act
, 0);
287 log_error("Failed to install SIGCHLD handler: %m");
291 static int help(void) {
292 printf("%s [OPTIONS...]\n\n"
293 "Listen on sockets and launch child on connection.\n\n"
295 " -l --listen=ADDR Listen for raw connections at ADDR\n"
296 " -a --accept Spawn separate child for each connection\n"
297 " -h --help Show this help and exit\n"
298 " --version Print version string and exit\n"
300 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
301 , program_invocation_short_name
307 static int parse_argv(int argc
, char *argv
[]) {
312 static const struct option options
[] = {
313 { "help", no_argument
, NULL
, 'h' },
314 { "version", no_argument
, NULL
, ARG_VERSION
},
315 { "listen", required_argument
, NULL
, 'l' },
316 { "accept", no_argument
, NULL
, 'a' },
317 { "environment", required_argument
, NULL
, 'E' },
326 while ((c
= getopt_long(argc
, argv
, "+hl:saE:", options
, NULL
)) >= 0)
332 puts(PACKAGE_STRING
);
333 puts(SYSTEMD_FEATURES
);
337 int r
= strv_extend(&arg_listen
, optarg
);
349 int r
= strv_extend(&arg_environ
, optarg
);
360 assert_not_reached("Unhandled option");
363 if (optind
== argc
) {
364 log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
365 program_invocation_short_name
);
369 arg_args
= argv
+ optind
;
371 return 1 /* work to do */;
374 int main(int argc
, char **argv
, char **envp
) {
378 log_parse_environment();
381 r
= parse_argv(argc
, argv
);
383 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;
385 r
= install_chld_handler();
389 n
= open_sockets(&epoll_fd
, arg_accept
);
394 struct epoll_event event
;
396 r
= epoll_wait(epoll_fd
, &event
, 1, -1);
401 log_error("epoll_wait() failed: %m");
405 log_info("Communication attempt on fd:%d", event
.data
.fd
);
407 r
= do_accept(argv
[optind
], argv
+ optind
, envp
,
415 launch(argv
[optind
], argv
+ optind
, envp
, n
);