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