1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <getopt.h>
4 #include <sys/epoll.h>
5 #include <sys/prctl.h>
6 #include <sys/wait.h>
7 #include <unistd.h>
8 
9 #include "sd-daemon.h"
10 
11 #include "alloc-util.h"
12 #include "env-util.h"
13 #include "errno-util.h"
14 #include "escape.h"
15 #include "fd-util.h"
16 #include "log.h"
17 #include "macro.h"
18 #include "pretty-print.h"
19 #include "process-util.h"
20 #include "signal-util.h"
21 #include "socket-netlink.h"
22 #include "socket-util.h"
23 #include "string-util.h"
24 #include "strv.h"
25 #include "terminal-util.h"
26 #include "util.h"
27 
28 static char **arg_listen = NULL;
29 static bool arg_accept = false;
30 static int arg_socket_type = SOCK_STREAM;
31 static char **arg_args = NULL;
32 static char **arg_setenv = NULL;
33 static char **arg_fdnames = NULL;
34 static bool arg_inetd = false;
35 
add_epoll(int epoll_fd,int fd)36 static int add_epoll(int epoll_fd, int fd) {
37         struct epoll_event ev = {
38                 .events = EPOLLIN,
39                 .data.fd = fd,
40         };
41 
42         assert(epoll_fd >= 0);
43         assert(fd >= 0);
44 
45         if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
46                 return log_error_errno(errno, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd);
47 
48         return 0;
49 }
50 
open_sockets(int * epoll_fd,bool accept)51 static int open_sockets(int *epoll_fd, bool accept) {
52         int n, fd, r, count = 0;
53 
54         n = sd_listen_fds(true);
55         if (n < 0)
56                 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
57         if (n > 0) {
58                 log_info("Received %i descriptors via the environment.", n);
59 
60                 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
61                         r = fd_cloexec(fd, arg_accept);
62                         if (r < 0)
63                                 return r;
64 
65                         count++;
66                 }
67         }
68 
69         /* Close logging and all other descriptors */
70         if (arg_listen) {
71                 _cleanup_free_ int *except = NULL;
72                 int i;
73 
74                 except = new(int, n);
75                 if (!except)
76                         return log_oom();
77 
78                 for (i = 0; i < n; i++)
79                         except[i] = SD_LISTEN_FDS_START + i;
80 
81                 log_close();
82                 r = close_all_fds(except, n);
83                 if (r < 0)
84                         return log_error_errno(r, "Failed to close all file descriptors: %m");
85         }
86 
87         /** Note: we leak some fd's on error here. I doesn't matter
88          *  much, since the program will exit immediately anyway, but
89          *  would be a pain to fix.
90          */
91 
92         STRV_FOREACH(address, arg_listen) {
93                 fd = make_socket_fd(LOG_DEBUG, *address, arg_socket_type, (arg_accept * SOCK_CLOEXEC));
94                 if (fd < 0) {
95                         log_open();
96                         return log_error_errno(fd, "Failed to open '%s': %m", *address);
97                 }
98 
99                 assert(fd == SD_LISTEN_FDS_START + count);
100                 count++;
101         }
102 
103         if (arg_listen)
104                 log_open();
105 
106         *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
107         if (*epoll_fd < 0)
108                 return log_error_errno(errno, "Failed to create epoll object: %m");
109 
110         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
111                 _cleanup_free_ char *name = NULL;
112 
113                 getsockname_pretty(fd, &name);
114                 log_info("Listening on %s as %i.", strna(name), fd);
115 
116                 r = add_epoll(*epoll_fd, fd);
117                 if (r < 0)
118                         return r;
119         }
120 
121         return count;
122 }
123 
exec_process(const char * name,char ** argv,int start_fd,size_t n_fds)124 static int exec_process(const char *name, char **argv, int start_fd, size_t n_fds) {
125         _cleanup_strv_free_ char **envp = NULL;
126         int r;
127 
128         if (arg_inetd && n_fds != 1)
129                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
130                                        "--inetd only supported for single file descriptors.");
131 
132         FOREACH_STRING(var, "TERM", "PATH", "USER", "HOME") {
133                 const char *n;
134 
135                 n = strv_find_prefix(environ, var);
136                 if (!n)
137                         continue;
138 
139                 r = strv_extend(&envp, n);
140                 if (r < 0)
141                         return r;
142         }
143 
144         if (arg_inetd) {
145                 assert(n_fds == 1);
146 
147                 r = rearrange_stdio(start_fd, start_fd, STDERR_FILENO); /* invalidates start_fd on success + error */
148                 if (r < 0)
149                         return log_error_errno(r, "Failed to move fd to stdin+stdout: %m");
150 
151         } else {
152                 if (start_fd != SD_LISTEN_FDS_START) {
153                         assert(n_fds == 1);
154 
155                         if (dup2(start_fd, SD_LISTEN_FDS_START) < 0)
156                                 return log_error_errno(errno, "Failed to dup connection: %m");
157 
158                         safe_close(start_fd);
159                 }
160 
161                 r = strv_extendf(&envp, "LISTEN_FDS=%zu", n_fds);
162                 if (r < 0)
163                         return r;
164 
165                 r = strv_extendf(&envp, "LISTEN_PID=" PID_FMT, getpid_cached());
166                 if (r < 0)
167                         return r;
168 
169                 if (arg_fdnames) {
170                         _cleanup_free_ char *names = NULL;
171                         size_t len;
172 
173                         len = strv_length(arg_fdnames);
174                         if (len == 1)
175                                 for (size_t i = 1; i < n_fds; i++) {
176                                         r = strv_extend(&arg_fdnames, arg_fdnames[0]);
177                                         if (r < 0)
178                                                 return log_oom();
179                                 }
180                         else if (len != n_fds)
181                                 log_warning("The number of fd names is different than number of fds: %zu vs %zu", len, n_fds);
182 
183                         names = strv_join(arg_fdnames, ":");
184                         if (!names)
185                                 return log_oom();
186 
187                         char *t = strjoin("LISTEN_FDNAMES=", names);
188                         if (!t)
189                                 return log_oom();
190 
191                         r = strv_consume(&envp, t);
192                         if (r < 0)
193                                 return r;
194                 }
195         }
196 
197         STRV_FOREACH(s, arg_setenv) {
198                 r = strv_env_replace_strdup(&envp, *s);
199                 if (r < 0)
200                         return r;
201         }
202 
203         _cleanup_free_ char *joined = strv_join(argv, " ");
204         if (!joined)
205                 return log_oom();
206 
207         log_info("Execing %s (%s)", name, joined);
208         execvpe(name, argv, envp);
209 
210         return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined);
211 }
212 
fork_and_exec_process(const char * child,char ** argv,int fd)213 static int fork_and_exec_process(const char *child, char **argv, int fd) {
214         _cleanup_free_ char *joined = NULL;
215         pid_t child_pid;
216         int r;
217 
218         joined = strv_join(argv, " ");
219         if (!joined)
220                 return log_oom();
221 
222         r = safe_fork("(activate)",
223                       FORK_RESET_SIGNALS | FORK_DEATHSIG | FORK_RLIMIT_NOFILE_SAFE | FORK_LOG,
224                       &child_pid);
225         if (r < 0)
226                 return r;
227         if (r == 0) {
228                 /* In the child */
229                 exec_process(child, argv, fd, 1);
230                 _exit(EXIT_FAILURE);
231         }
232 
233         log_info("Spawned %s (%s) as PID " PID_FMT ".", child, joined, child_pid);
234         return 0;
235 }
236 
do_accept(const char * name,char ** argv,int fd)237 static int do_accept(const char *name, char **argv, int fd) {
238         _cleanup_free_ char *local = NULL, *peer = NULL;
239         _cleanup_close_ int fd_accepted = -1;
240 
241         fd_accepted = accept4(fd, NULL, NULL, 0);
242         if (fd_accepted < 0) {
243                 if (ERRNO_IS_ACCEPT_AGAIN(errno))
244                         return 0;
245 
246                 return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
247         }
248 
249         (void) getsockname_pretty(fd_accepted, &local);
250         (void) getpeername_pretty(fd_accepted, true, &peer);
251         log_info("Connection from %s to %s", strna(peer), strna(local));
252 
253         return fork_and_exec_process(name, argv, fd_accepted);
254 }
255 
256 /* SIGCHLD handler. */
sigchld_hdl(int sig)257 static void sigchld_hdl(int sig) {
258         PROTECT_ERRNO;
259 
260         for (;;) {
261                 siginfo_t si;
262                 int r;
263 
264                 si.si_pid = 0;
265                 r = waitid(P_ALL, 0, &si, WEXITED | WNOHANG);
266                 if (r < 0) {
267                         if (errno != ECHILD)
268                                 log_error_errno(errno, "Failed to reap children: %m");
269                         return;
270                 }
271                 if (si.si_pid == 0)
272                         return;
273 
274                 log_info("Child %d died with code %d", si.si_pid, si.si_status);
275         }
276 }
277 
install_chld_handler(void)278 static int install_chld_handler(void) {
279         static const struct sigaction act = {
280                 .sa_flags = SA_NOCLDSTOP | SA_RESTART,
281                 .sa_handler = sigchld_hdl,
282         };
283 
284         if (sigaction(SIGCHLD, &act, 0) < 0)
285                 return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
286 
287         return 0;
288 }
289 
help(void)290 static int help(void) {
291         _cleanup_free_ char *link = NULL;
292         int r;
293 
294         r = terminal_urlify_man("systemd-socket-activate", "1", &link);
295         if (r < 0)
296                 return log_oom();
297 
298         printf("%s [OPTIONS...]\n"
299                "\n%sListen on sockets and launch child on connection.%s\n"
300                "\nOptions:\n"
301                "  -h --help                  Show this help and exit\n"
302                "     --version               Print version string and exit\n"
303                "  -l --listen=ADDR           Listen for raw connections at ADDR\n"
304                "  -d --datagram              Listen on datagram instead of stream socket\n"
305                "     --seqpacket             Listen on SOCK_SEQPACKET instead of stream socket\n"
306                "  -a --accept                Spawn separate child for each connection\n"
307                "  -E --setenv=NAME[=VALUE]   Pass an environment variable to children\n"
308                "     --fdname=NAME[:NAME...] Specify names for file descriptors\n"
309                "     --inetd                 Enable inetd file descriptor passing protocol\n"
310                "\nNote: file descriptors from sd_listen_fds() will be passed through.\n"
311                "\nSee the %s for details.\n",
312                program_invocation_short_name,
313                ansi_highlight(),
314                ansi_normal(),
315                link);
316 
317         return 0;
318 }
319 
parse_argv(int argc,char * argv[])320 static int parse_argv(int argc, char *argv[]) {
321         enum {
322                 ARG_VERSION = 0x100,
323                 ARG_FDNAME,
324                 ARG_SEQPACKET,
325                 ARG_INETD,
326         };
327 
328         static const struct option options[] = {
329                 { "help",        no_argument,       NULL, 'h'           },
330                 { "version",     no_argument,       NULL, ARG_VERSION   },
331                 { "datagram",    no_argument,       NULL, 'd'           },
332                 { "seqpacket",   no_argument,       NULL, ARG_SEQPACKET },
333                 { "listen",      required_argument, NULL, 'l'           },
334                 { "accept",      no_argument,       NULL, 'a'           },
335                 { "setenv",      required_argument, NULL, 'E'           },
336                 { "environment", required_argument, NULL, 'E'           }, /* legacy alias */
337                 { "fdname",      required_argument, NULL, ARG_FDNAME    },
338                 { "inetd",       no_argument,       NULL, ARG_INETD     },
339                 {}
340         };
341 
342         int c, r;
343 
344         assert(argc >= 0);
345         assert(argv);
346 
347         while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0)
348                 switch (c) {
349                 case 'h':
350                         return help();
351 
352                 case ARG_VERSION:
353                         return version();
354 
355                 case 'l':
356                         r = strv_extend(&arg_listen, optarg);
357                         if (r < 0)
358                                 return log_oom();
359 
360                         break;
361 
362                 case 'd':
363                         if (arg_socket_type == SOCK_SEQPACKET)
364                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
365                                                        "--datagram may not be combined with --seqpacket.");
366 
367                         arg_socket_type = SOCK_DGRAM;
368                         break;
369 
370                 case ARG_SEQPACKET:
371                         if (arg_socket_type == SOCK_DGRAM)
372                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
373                                                        "--seqpacket may not be combined with --datagram.");
374 
375                         arg_socket_type = SOCK_SEQPACKET;
376                         break;
377 
378                 case 'a':
379                         arg_accept = true;
380                         break;
381 
382                 case 'E':
383                         r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
384                         if (r < 0)
385                                 return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
386                         break;
387 
388                 case ARG_FDNAME: {
389                         _cleanup_strv_free_ char **names = NULL;
390 
391                         names = strv_split(optarg, ":");
392                         if (!names)
393                                 return log_oom();
394 
395                         STRV_FOREACH(s, names)
396                                 if (!fdname_is_valid(*s)) {
397                                         _cleanup_free_ char *esc = NULL;
398 
399                                         esc = cescape(*s);
400                                         log_warning("File descriptor name \"%s\" is not valid.", esc);
401                                 }
402 
403                         /* Empty optargs means one empty name */
404                         r = strv_extend_strv(&arg_fdnames,
405                                              strv_isempty(names) ? STRV_MAKE("") : names,
406                                              false);
407                         if (r < 0)
408                                 return log_error_errno(r, "strv_extend_strv: %m");
409                         break;
410                 }
411 
412                 case ARG_INETD:
413                         arg_inetd = true;
414                         break;
415 
416                 case '?':
417                         return -EINVAL;
418 
419                 default:
420                         assert_not_reached();
421                 }
422 
423         if (optind == argc)
424                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
425                                        "%s: command to execute is missing.",
426                                        program_invocation_short_name);
427 
428         if (arg_socket_type == SOCK_DGRAM && arg_accept)
429                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
430                                        "Datagram sockets do not accept connections. "
431                                        "The --datagram and --accept options may not be combined.");
432 
433         arg_args = argv + optind;
434 
435         return 1 /* work to do */;
436 }
437 
main(int argc,char ** argv)438 int main(int argc, char **argv) {
439         int r, n;
440         int epoll_fd = -1;
441 
442         log_show_color(true);
443         log_parse_environment();
444         log_open();
445 
446         r = parse_argv(argc, argv);
447         if (r <= 0)
448                 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
449 
450         r = install_chld_handler();
451         if (r < 0)
452                 return EXIT_FAILURE;
453 
454         n = open_sockets(&epoll_fd, arg_accept);
455         if (n < 0)
456                 return EXIT_FAILURE;
457         if (n == 0) {
458                 log_error("No sockets to listen on specified or passed in.");
459                 return EXIT_FAILURE;
460         }
461 
462         for (;;) {
463                 struct epoll_event event;
464 
465                 if (epoll_wait(epoll_fd, &event, 1, -1) < 0) {
466                         if (errno == EINTR)
467                                 continue;
468 
469                         log_error_errno(errno, "epoll_wait() failed: %m");
470                         return EXIT_FAILURE;
471                 }
472 
473                 log_info("Communication attempt on fd %i.", event.data.fd);
474                 if (arg_accept) {
475                         r = do_accept(argv[optind], argv + optind, event.data.fd);
476                         if (r < 0)
477                                 return EXIT_FAILURE;
478                 } else
479                         break;
480         }
481 
482         exec_process(argv[optind], argv + optind, SD_LISTEN_FDS_START, (size_t) n);
483 
484         return EXIT_SUCCESS;
485 }
486