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