]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/activate/activate.c
Add SPDX license identifiers to source files under the LGPL
[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;
2ca0435b 270 pid_t parent_pid, child_pid;
2ca0435b 271
eef0a274
LP
272 joined = strv_join(argv, " ");
273 if (!joined)
2ca0435b
ZJS
274 return log_oom();
275
df0ff127 276 parent_pid = getpid_cached();
2ca0435b
ZJS
277
278 child_pid = fork();
4a62c710
MS
279 if (child_pid < 0)
280 return log_error_errno(errno, "Failed to fork: %m");
2ca0435b
ZJS
281
282 /* In the child */
283 if (child_pid == 0) {
ce30c8dc
LP
284
285 (void) reset_all_signal_handlers();
286 (void) reset_signal_mask();
287
2ca0435b
ZJS
288 /* Make sure the child goes away when the parent dies */
289 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
290 _exit(EXIT_FAILURE);
291
292 /* Check whether our parent died before we were able
293 * to set the death signal */
294 if (getppid() != parent_pid)
295 _exit(EXIT_SUCCESS);
296
eef0a274 297 exec_process(child, argv, env, fd, 1);
2ca0435b
ZJS
298 _exit(EXIT_FAILURE);
299 }
300
eef0a274 301 log_info("Spawned %s (%s) as PID %d", child, joined, child_pid);
2ca0435b
ZJS
302 return 0;
303}
304
305static int do_accept(const char* name, char **argv, char **envp, int fd) {
30374ebe 306 _cleanup_free_ char *local = NULL, *peer = NULL;
eef0a274 307 _cleanup_close_ int fd_accepted = -1;
2ca0435b 308
eef0a274
LP
309 fd_accepted = accept4(fd, NULL, NULL, 0);
310 if (fd_accepted < 0)
08719b64 311 return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
2ca0435b 312
eef0a274
LP
313 getsockname_pretty(fd_accepted, &local);
314 getpeername_pretty(fd_accepted, true, &peer);
30374ebe 315 log_info("Connection from %s to %s", strna(peer), strna(local));
2ca0435b 316
eef0a274 317 return fork_and_exec_process(name, argv, envp, fd_accepted);
2ca0435b
ZJS
318}
319
320/* SIGCHLD handler. */
5488e52d 321static void sigchld_hdl(int sig) {
9d458c09
LP
322 PROTECT_ERRNO;
323
5488e52d
EV
324 for (;;) {
325 siginfo_t si;
326 int r;
08719b64 327
5488e52d
EV
328 si.si_pid = 0;
329 r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG);
330 if (r < 0) {
331 if (errno != ECHILD)
332 log_error_errno(errno, "Failed to reap children: %m");
333 return;
334 }
335 if (si.si_pid == 0)
336 return;
337
338 log_info("Child %d died with code %d", si.si_pid, si.si_status);
339 }
2ca0435b
ZJS
340}
341
342static int install_chld_handler(void) {
08719b64 343 static const struct sigaction act = {
e28c7cd0 344 .sa_flags = SA_NOCLDSTOP|SA_RESTART,
5488e52d 345 .sa_handler = sigchld_hdl,
c0997164 346 };
2ca0435b 347
08719b64
LP
348 int r;
349
2ca0435b
ZJS
350 r = sigaction(SIGCHLD, &act, 0);
351 if (r < 0)
08719b64
LP
352 return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
353
354 return 0;
2ca0435b
ZJS
355}
356
601185b4 357static void help(void) {
2ca0435b
ZJS
358 printf("%s [OPTIONS...]\n\n"
359 "Listen on sockets and launch child on connection.\n\n"
360 "Options:\n"
cf98937c
ZJS
361 " -h --help Show this help and exit\n"
362 " --version Print version string and exit\n"
363 " -l --listen=ADDR Listen for raw connections at ADDR\n"
364 " -d --datagram Listen on datagram instead of stream socket\n"
365 " --seqpacket Listen on SOCK_SEQPACKET instead of stream socket\n"
366 " -a --accept Spawn separate child for each connection\n"
367 " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
368 " --fdname=NAME[:NAME...] Specify names for file descriptors\n"
369 " --inetd Enable inetd file descriptor passing protocol\n"
2ca0435b
ZJS
370 "\n"
371 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
601185b4 372 , program_invocation_short_name);
2ca0435b
ZJS
373}
374
375static int parse_argv(int argc, char *argv[]) {
376 enum {
377 ARG_VERSION = 0x100,
8dd4c05b 378 ARG_FDNAME,
d31e430f 379 ARG_SEQPACKET,
eef0a274 380 ARG_INETD,
2ca0435b
ZJS
381 };
382
383 static const struct option options[] = {
892213bf
ZJS
384 { "help", no_argument, NULL, 'h' },
385 { "version", no_argument, NULL, ARG_VERSION },
7b7afdfc 386 { "datagram", no_argument, NULL, 'd' },
d31e430f 387 { "seqpacket", no_argument, NULL, ARG_SEQPACKET },
892213bf
ZJS
388 { "listen", required_argument, NULL, 'l' },
389 { "accept", no_argument, NULL, 'a' },
390 { "setenv", required_argument, NULL, 'E' },
8dd4c05b
LP
391 { "environment", required_argument, NULL, 'E' }, /* legacy alias */
392 { "fdname", required_argument, NULL, ARG_FDNAME },
eef0a274 393 { "inetd", no_argument, NULL, ARG_INETD },
eb9da376 394 {}
2ca0435b
ZJS
395 };
396
8dd4c05b 397 int c, r;
2ca0435b
ZJS
398
399 assert(argc >= 0);
400 assert(argv);
401
b722348d 402 while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0)
2ca0435b
ZJS
403 switch(c) {
404 case 'h':
601185b4
ZJS
405 help();
406 return 0;
2ca0435b
ZJS
407
408 case ARG_VERSION:
3f6fd1ba 409 return version();
2ca0435b 410
8dd4c05b
LP
411 case 'l':
412 r = strv_extend(&arg_listen, optarg);
2ca0435b 413 if (r < 0)
8dd4c05b 414 return log_oom();
2ca0435b
ZJS
415
416 break;
2ca0435b 417
7b7afdfc 418 case 'd':
d31e430f
LP
419 if (arg_socket_type == SOCK_SEQPACKET) {
420 log_error("--datagram may not be combined with --seqpacket.");
421 return -EINVAL;
422 }
423
424 arg_socket_type = SOCK_DGRAM;
425 break;
426
427 case ARG_SEQPACKET:
428 if (arg_socket_type == SOCK_DGRAM) {
429 log_error("--seqpacket may not be combined with --datagram.");
430 return -EINVAL;
431 }
432
433 arg_socket_type = SOCK_SEQPACKET;
7b7afdfc
SS
434 break;
435
2ca0435b
ZJS
436 case 'a':
437 arg_accept = true;
438 break;
439
8dd4c05b
LP
440 case 'E':
441 r = strv_extend(&arg_setenv, optarg);
5e65c93a 442 if (r < 0)
8dd4c05b 443 return log_oom();
5e65c93a
ZJS
444
445 break;
8dd4c05b 446
cf98937c
ZJS
447 case ARG_FDNAME: {
448 _cleanup_strv_free_ char **names;
449 char **s;
450
451 names = strv_split(optarg, ":");
452 if (!names)
453 return log_oom();
454
455 STRV_FOREACH(s, names)
456 if (!fdname_is_valid(*s)) {
457 _cleanup_free_ char *esc;
163c76c9 458
cf98937c
ZJS
459 esc = cescape(*s);
460 log_warning("File descriptor name \"%s\" is not valid.", esc);
461 }
462
463 /* Empty optargs means one empty name */
464 r = strv_extend_strv(&arg_fdnames,
465 strv_isempty(names) ? STRV_MAKE("") : names,
466 false);
467 if (r < 0)
468 return log_error_errno(r, "strv_extend_strv: %m");
8dd4c05b 469 break;
cf98937c 470 }
5e65c93a 471
eef0a274
LP
472 case ARG_INETD:
473 arg_inetd = true;
474 break;
475
2ca0435b
ZJS
476 case '?':
477 return -EINVAL;
478
479 default:
eb9da376 480 assert_not_reached("Unhandled option");
2ca0435b
ZJS
481 }
482
483 if (optind == argc) {
601185b4 484 log_error("%s: command to execute is missing.",
2ca0435b
ZJS
485 program_invocation_short_name);
486 return -EINVAL;
487 }
488
d31e430f 489 if (arg_socket_type == SOCK_DGRAM && arg_accept) {
7b7afdfc
SS
490 log_error("Datagram sockets do not accept connections. "
491 "The --datagram and --accept options may not be combined.");
492 return -EINVAL;
493 }
494
2ca0435b
ZJS
495 arg_args = argv + optind;
496
497 return 1 /* work to do */;
498}
499
500int main(int argc, char **argv, char **envp) {
501 int r, n;
502 int epoll_fd = -1;
503
2ca0435b 504 log_parse_environment();
eceb8483 505 log_open();
2ca0435b
ZJS
506
507 r = parse_argv(argc, argv);
508 if (r <= 0)
509 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
510
511 r = install_chld_handler();
512 if (r < 0)
513 return EXIT_FAILURE;
514
515 n = open_sockets(&epoll_fd, arg_accept);
516 if (n < 0)
517 return EXIT_FAILURE;
2c408cb6
LP
518 if (n == 0) {
519 log_error("No sockets to listen on specified or passed in.");
520 return EXIT_FAILURE;
521 }
2ca0435b 522
eceb8483 523 for (;;) {
2ca0435b
ZJS
524 struct epoll_event event;
525
526 r = epoll_wait(epoll_fd, &event, 1, -1);
527 if (r < 0) {
528 if (errno == EINTR)
529 continue;
530
56f64d95 531 log_error_errno(errno, "epoll_wait() failed: %m");
2ca0435b
ZJS
532 return EXIT_FAILURE;
533 }
534
2c408cb6 535 log_info("Communication attempt on fd %i.", event.data.fd);
2ca0435b 536 if (arg_accept) {
d31e430f 537 r = do_accept(argv[optind], argv + optind, envp, event.data.fd);
2ca0435b
ZJS
538 if (r < 0)
539 return EXIT_FAILURE;
540 } else
541 break;
542 }
543
eef0a274 544 exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, n);
2ca0435b
ZJS
545
546 return EXIT_SUCCESS;
547}