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