1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/epoll.h>
23 #include <sys/prctl.h>
24 #include <sys/socket.h>
28 #include "sd-daemon.h"
30 #include "alloc-util.h"
35 #include "process-util.h"
36 #include "signal-util.h"
37 #include "socket-util.h"
38 #include "string-util.h"
41 static char** arg_listen
= NULL
;
42 static bool arg_accept
= false;
43 static int arg_socket_type
= SOCK_STREAM
;
44 static char** arg_args
= NULL
;
45 static char** arg_setenv
= NULL
;
46 static char **arg_fdnames
= NULL
;
47 static bool arg_inetd
= false;
49 static int add_epoll(int epoll_fd
, int fd
) {
50 struct epoll_event ev
= {
55 assert(epoll_fd
>= 0);
59 r
= epoll_ctl(epoll_fd
, EPOLL_CTL_ADD
, fd
, &ev
);
61 return log_error_errno(errno
, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd
, fd
);
66 static int open_sockets(int *epoll_fd
, bool accept
) {
71 n
= sd_listen_fds(true);
73 return log_error_errno(n
, "Failed to read listening file descriptors from environment: %m");
75 log_info("Received %i descriptors via the environment.", n
);
77 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ n
; fd
++) {
78 r
= fd_cloexec(fd
, arg_accept
);
86 /* Close logging and all other descriptors */
90 for (fd
= 0; fd
< SD_LISTEN_FDS_START
+ n
; fd
++)
94 close_all_fds(except
, 3 + n
);
97 /** Note: we leak some fd's on error here. I doesn't matter
98 * much, since the program will exit immediately anyway, but
99 * would be a pain to fix.
102 STRV_FOREACH(address
, arg_listen
) {
103 fd
= make_socket_fd(LOG_DEBUG
, *address
, arg_socket_type
, (arg_accept
*SOCK_CLOEXEC
));
106 return log_error_errno(fd
, "Failed to open '%s': %m", *address
);
109 assert(fd
== SD_LISTEN_FDS_START
+ count
);
116 *epoll_fd
= epoll_create1(EPOLL_CLOEXEC
);
118 return log_error_errno(errno
, "Failed to create epoll object: %m");
120 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ count
; fd
++) {
121 _cleanup_free_
char *name
= NULL
;
123 getsockname_pretty(fd
, &name
);
124 log_info("Listening on %s as %i.", strna(name
), fd
);
126 r
= add_epoll(*epoll_fd
, fd
);
134 static int exec_process(const char* name
, char **argv
, char **env
, int start_fd
, int n_fds
) {
136 _cleanup_strv_free_
char **envp
= NULL
;
137 _cleanup_free_
char *joined
= NULL
;
138 unsigned n_env
= 0, length
;
143 if (arg_inetd
&& n_fds
!= 1) {
144 log_error("--inetd only supported for single file descriptors.");
148 length
= strv_length(arg_setenv
);
150 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
151 envp
= new0(char *, length
+ 8);
155 STRV_FOREACH(s
, arg_setenv
) {
157 if (strchr(*s
, '=')) {
166 _cleanup_free_
char *p
;
169 p
= strappend(*s
, "=");
173 n
= strv_find_prefix(env
, p
);
177 envp
[n_env
] = strdup(n
);
185 FOREACH_STRING(tocopy
, "TERM=", "PATH=", "USER=", "HOME=") {
188 n
= strv_find_prefix(env
, tocopy
);
192 envp
[n_env
] = strdup(n
);
202 r
= dup2(start_fd
, STDIN_FILENO
);
204 return log_error_errno(errno
, "Failed to dup connection to stdin: %m");
206 r
= dup2(start_fd
, STDOUT_FILENO
);
208 return log_error_errno(errno
, "Failed to dup connection to stdout: %m");
210 start_fd
= safe_close(start_fd
);
212 if (start_fd
!= SD_LISTEN_FDS_START
) {
215 r
= dup2(start_fd
, SD_LISTEN_FDS_START
);
217 return log_error_errno(errno
, "Failed to dup connection: %m");
219 safe_close(start_fd
);
220 start_fd
= SD_LISTEN_FDS_START
;
223 if (asprintf((char**)(envp
+ n_env
++), "LISTEN_FDS=%i", n_fds
) < 0)
226 if (asprintf((char**)(envp
+ n_env
++), "LISTEN_PID=" PID_FMT
, getpid_cached()) < 0)
230 _cleanup_free_
char *names
= NULL
;
235 len
= strv_length(arg_fdnames
);
237 for (i
= 1; i
< n_fds
; i
++) {
238 r
= strv_extend(&arg_fdnames
, arg_fdnames
[0]);
240 return log_error_errno(r
, "Failed to extend strv: %m");
242 else if (len
!= (unsigned) n_fds
)
243 log_warning("The number of fd names is different than number of fds: %zu vs %d",
246 names
= strv_join(arg_fdnames
, ":");
250 e
= strappend("LISTEN_FDNAMES=", names
);
258 joined
= strv_join(argv
, " ");
262 log_info("Execing %s (%s)", name
, joined
);
263 execvpe(name
, argv
, envp
);
265 return log_error_errno(errno
, "Failed to execp %s (%s): %m", name
, joined
);
268 static int fork_and_exec_process(const char* child
, char** argv
, char **env
, int fd
) {
269 _cleanup_free_
char *joined
= NULL
;
270 pid_t parent_pid
, child_pid
;
272 joined
= strv_join(argv
, " ");
276 parent_pid
= getpid_cached();
280 return log_error_errno(errno
, "Failed to fork: %m");
283 if (child_pid
== 0) {
285 (void) reset_all_signal_handlers();
286 (void) reset_signal_mask();
288 /* Make sure the child goes away when the parent dies */
289 if (prctl(PR_SET_PDEATHSIG
, SIGTERM
) < 0)
292 /* Check whether our parent died before we were able
293 * to set the death signal */
294 if (getppid() != parent_pid
)
297 exec_process(child
, argv
, env
, fd
, 1);
301 log_info("Spawned %s (%s) as PID %d", child
, joined
, child_pid
);
305 static int do_accept(const char* name
, char **argv
, char **envp
, int fd
) {
306 _cleanup_free_
char *local
= NULL
, *peer
= NULL
;
307 _cleanup_close_
int fd_accepted
= -1;
309 fd_accepted
= accept4(fd
, NULL
, NULL
, 0);
311 return log_error_errno(errno
, "Failed to accept connection on fd:%d: %m", fd
);
313 getsockname_pretty(fd_accepted
, &local
);
314 getpeername_pretty(fd_accepted
, true, &peer
);
315 log_info("Connection from %s to %s", strna(peer
), strna(local
));
317 return fork_and_exec_process(name
, argv
, envp
, fd_accepted
);
320 /* SIGCHLD handler. */
321 static void sigchld_hdl(int sig
) {
329 r
= waitid(P_ALL
, 0, &si
, WEXITED
|WNOHANG
);
332 log_error_errno(errno
, "Failed to reap children: %m");
338 log_info("Child %d died with code %d", si
.si_pid
, si
.si_status
);
342 static int install_chld_handler(void) {
343 static const struct sigaction act
= {
344 .sa_flags
= SA_NOCLDSTOP
|SA_RESTART
,
345 .sa_handler
= sigchld_hdl
,
350 r
= sigaction(SIGCHLD
, &act
, 0);
352 return log_error_errno(errno
, "Failed to install SIGCHLD handler: %m");
357 static void help(void) {
358 printf("%s [OPTIONS...]\n\n"
359 "Listen on sockets and launch child on connection.\n\n"
361 " -h --help Show this help and exit\n"
362 " --version Print version string and exit\n"
363 " -l --listen=ADDR Listen for raw connections at ADDR\n"
364 " -d --datagram Listen on datagram instead of stream socket\n"
365 " --seqpacket Listen on SOCK_SEQPACKET instead of stream socket\n"
366 " -a --accept Spawn separate child for each connection\n"
367 " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
368 " --fdname=NAME[:NAME...] Specify names for file descriptors\n"
369 " --inetd Enable inetd file descriptor passing protocol\n"
371 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
372 , program_invocation_short_name
);
375 static int parse_argv(int argc
, char *argv
[]) {
383 static const struct option options
[] = {
384 { "help", no_argument
, NULL
, 'h' },
385 { "version", no_argument
, NULL
, ARG_VERSION
},
386 { "datagram", no_argument
, NULL
, 'd' },
387 { "seqpacket", no_argument
, NULL
, ARG_SEQPACKET
},
388 { "listen", required_argument
, NULL
, 'l' },
389 { "accept", no_argument
, NULL
, 'a' },
390 { "setenv", required_argument
, NULL
, 'E' },
391 { "environment", required_argument
, NULL
, 'E' }, /* legacy alias */
392 { "fdname", required_argument
, NULL
, ARG_FDNAME
},
393 { "inetd", no_argument
, NULL
, ARG_INETD
},
402 while ((c
= getopt_long(argc
, argv
, "+hl:aE:d", options
, NULL
)) >= 0)
412 r
= strv_extend(&arg_listen
, optarg
);
419 if (arg_socket_type
== SOCK_SEQPACKET
) {
420 log_error("--datagram may not be combined with --seqpacket.");
424 arg_socket_type
= SOCK_DGRAM
;
428 if (arg_socket_type
== SOCK_DGRAM
) {
429 log_error("--seqpacket may not be combined with --datagram.");
433 arg_socket_type
= SOCK_SEQPACKET
;
441 r
= strv_extend(&arg_setenv
, optarg
);
448 _cleanup_strv_free_
char **names
;
451 names
= strv_split(optarg
, ":");
455 STRV_FOREACH(s
, names
)
456 if (!fdname_is_valid(*s
)) {
457 _cleanup_free_
char *esc
;
460 log_warning("File descriptor name \"%s\" is not valid.", esc
);
463 /* Empty optargs means one empty name */
464 r
= strv_extend_strv(&arg_fdnames
,
465 strv_isempty(names
) ? STRV_MAKE("") : names
,
468 return log_error_errno(r
, "strv_extend_strv: %m");
480 assert_not_reached("Unhandled option");
483 if (optind
== argc
) {
484 log_error("%s: command to execute is missing.",
485 program_invocation_short_name
);
489 if (arg_socket_type
== SOCK_DGRAM
&& arg_accept
) {
490 log_error("Datagram sockets do not accept connections. "
491 "The --datagram and --accept options may not be combined.");
495 arg_args
= argv
+ optind
;
497 return 1 /* work to do */;
500 int main(int argc
, char **argv
, char **envp
) {
504 log_parse_environment();
507 r
= parse_argv(argc
, argv
);
509 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;
511 r
= install_chld_handler();
515 n
= open_sockets(&epoll_fd
, arg_accept
);
519 log_error("No sockets to listen on specified or passed in.");
524 struct epoll_event event
;
526 r
= epoll_wait(epoll_fd
, &event
, 1, -1);
531 log_error_errno(errno
, "epoll_wait() failed: %m");
535 log_info("Communication attempt on fd %i.", event
.data
.fd
);
537 r
= do_accept(argv
[optind
], argv
+ optind
, envp
, event
.data
.fd
);
544 exec_process(argv
[optind
], argv
+ optind
, envp
, SD_LISTEN_FDS_START
, n
);