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