]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/activate/activate.c
Merge pull request #8345 from sourcejedi/logind_restart_is_sorely_lacking_in_testing
[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
2b33ab09 202 r = rearrange_stdio(start_fd, start_fd, STDERR_FILENO); /* invalidates start_fd on success + error */
eef0a274 203 if (r < 0)
2b33ab09 204 return log_error_errno(errno, "Failed to move fd to stdin+stdout: %m");
8dd4c05b 205
eef0a274
LP
206 } else {
207 if (start_fd != SD_LISTEN_FDS_START) {
208 assert(n_fds == 1);
209
210 r = dup2(start_fd, SD_LISTEN_FDS_START);
211 if (r < 0)
212 return log_error_errno(errno, "Failed to dup connection: %m");
213
214 safe_close(start_fd);
215 start_fd = SD_LISTEN_FDS_START;
216 }
217
218 if (asprintf((char**)(envp + n_env++), "LISTEN_FDS=%i", n_fds) < 0)
8dd4c05b
LP
219 return log_oom();
220
df0ff127 221 if (asprintf((char**)(envp + n_env++), "LISTEN_PID=" PID_FMT, getpid_cached()) < 0)
eef0a274 222 return log_oom();
8dd4c05b 223
cf98937c
ZJS
224 if (arg_fdnames) {
225 _cleanup_free_ char *names = NULL;
226 size_t len;
eef0a274 227 char *e;
cf98937c
ZJS
228 int i;
229
230 len = strv_length(arg_fdnames);
231 if (len == 1)
232 for (i = 1; i < n_fds; i++) {
233 r = strv_extend(&arg_fdnames, arg_fdnames[0]);
234 if (r < 0)
235 return log_error_errno(r, "Failed to extend strv: %m");
236 }
237 else if (len != (unsigned) n_fds)
238 log_warning("The number of fd names is different than number of fds: %zu vs %d",
239 len, n_fds);
eef0a274 240
cf98937c
ZJS
241 names = strv_join(arg_fdnames, ":");
242 if (!names)
8dd4c05b 243 return log_oom();
eef0a274 244
cf98937c
ZJS
245 e = strappend("LISTEN_FDNAMES=", names);
246 if (!e)
247 return log_oom();
8dd4c05b 248
eef0a274 249 envp[n_env++] = e;
8dd4c05b 250 }
8dd4c05b
LP
251 }
252
eef0a274
LP
253 joined = strv_join(argv, " ");
254 if (!joined)
2ca0435b
ZJS
255 return log_oom();
256
eef0a274 257 log_info("Execing %s (%s)", name, joined);
2ca0435b 258 execvpe(name, argv, envp);
30374ebe 259
eef0a274 260 return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined);
2ca0435b
ZJS
261}
262
eef0a274
LP
263static int fork_and_exec_process(const char* child, char** argv, char **env, int fd) {
264 _cleanup_free_ char *joined = NULL;
4c253ed1
LP
265 pid_t child_pid;
266 int r;
2ca0435b 267
eef0a274
LP
268 joined = strv_join(argv, " ");
269 if (!joined)
2ca0435b
ZJS
270 return log_oom();
271
b6e1fff1 272 r = safe_fork("(activate)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid);
4c253ed1 273 if (r < 0)
b6e1fff1 274 return r;
4c253ed1
LP
275 if (r == 0) {
276 /* In the child */
eef0a274 277 exec_process(child, argv, env, fd, 1);
2ca0435b
ZJS
278 _exit(EXIT_FAILURE);
279 }
280
4c253ed1 281 log_info("Spawned %s (%s) as PID " PID_FMT ".", child, joined, child_pid);
2ca0435b
ZJS
282 return 0;
283}
284
285static int do_accept(const char* name, char **argv, char **envp, int fd) {
30374ebe 286 _cleanup_free_ char *local = NULL, *peer = NULL;
eef0a274 287 _cleanup_close_ int fd_accepted = -1;
2ca0435b 288
eef0a274
LP
289 fd_accepted = accept4(fd, NULL, NULL, 0);
290 if (fd_accepted < 0)
08719b64 291 return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
2ca0435b 292
eef0a274
LP
293 getsockname_pretty(fd_accepted, &local);
294 getpeername_pretty(fd_accepted, true, &peer);
30374ebe 295 log_info("Connection from %s to %s", strna(peer), strna(local));
2ca0435b 296
eef0a274 297 return fork_and_exec_process(name, argv, envp, fd_accepted);
2ca0435b
ZJS
298}
299
300/* SIGCHLD handler. */
5488e52d 301static void sigchld_hdl(int sig) {
9d458c09
LP
302 PROTECT_ERRNO;
303
5488e52d
EV
304 for (;;) {
305 siginfo_t si;
306 int r;
08719b64 307
5488e52d
EV
308 si.si_pid = 0;
309 r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG);
310 if (r < 0) {
311 if (errno != ECHILD)
312 log_error_errno(errno, "Failed to reap children: %m");
313 return;
314 }
315 if (si.si_pid == 0)
316 return;
317
318 log_info("Child %d died with code %d", si.si_pid, si.si_status);
319 }
2ca0435b
ZJS
320}
321
322static int install_chld_handler(void) {
08719b64 323 static const struct sigaction act = {
e28c7cd0 324 .sa_flags = SA_NOCLDSTOP|SA_RESTART,
5488e52d 325 .sa_handler = sigchld_hdl,
c0997164 326 };
2ca0435b 327
08719b64
LP
328 int r;
329
2ca0435b
ZJS
330 r = sigaction(SIGCHLD, &act, 0);
331 if (r < 0)
08719b64
LP
332 return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
333
334 return 0;
2ca0435b
ZJS
335}
336
601185b4 337static void help(void) {
2ca0435b
ZJS
338 printf("%s [OPTIONS...]\n\n"
339 "Listen on sockets and launch child on connection.\n\n"
340 "Options:\n"
cf98937c
ZJS
341 " -h --help Show this help and exit\n"
342 " --version Print version string and exit\n"
343 " -l --listen=ADDR Listen for raw connections at ADDR\n"
344 " -d --datagram Listen on datagram instead of stream socket\n"
345 " --seqpacket Listen on SOCK_SEQPACKET instead of stream socket\n"
346 " -a --accept Spawn separate child for each connection\n"
347 " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
348 " --fdname=NAME[:NAME...] Specify names for file descriptors\n"
349 " --inetd Enable inetd file descriptor passing protocol\n"
2ca0435b
ZJS
350 "\n"
351 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
601185b4 352 , program_invocation_short_name);
2ca0435b
ZJS
353}
354
355static int parse_argv(int argc, char *argv[]) {
356 enum {
357 ARG_VERSION = 0x100,
8dd4c05b 358 ARG_FDNAME,
d31e430f 359 ARG_SEQPACKET,
eef0a274 360 ARG_INETD,
2ca0435b
ZJS
361 };
362
363 static const struct option options[] = {
892213bf
ZJS
364 { "help", no_argument, NULL, 'h' },
365 { "version", no_argument, NULL, ARG_VERSION },
7b7afdfc 366 { "datagram", no_argument, NULL, 'd' },
d31e430f 367 { "seqpacket", no_argument, NULL, ARG_SEQPACKET },
892213bf
ZJS
368 { "listen", required_argument, NULL, 'l' },
369 { "accept", no_argument, NULL, 'a' },
370 { "setenv", required_argument, NULL, 'E' },
8dd4c05b
LP
371 { "environment", required_argument, NULL, 'E' }, /* legacy alias */
372 { "fdname", required_argument, NULL, ARG_FDNAME },
eef0a274 373 { "inetd", no_argument, NULL, ARG_INETD },
eb9da376 374 {}
2ca0435b
ZJS
375 };
376
8dd4c05b 377 int c, r;
2ca0435b
ZJS
378
379 assert(argc >= 0);
380 assert(argv);
381
b722348d 382 while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0)
2ca0435b
ZJS
383 switch(c) {
384 case 'h':
601185b4
ZJS
385 help();
386 return 0;
2ca0435b
ZJS
387
388 case ARG_VERSION:
3f6fd1ba 389 return version();
2ca0435b 390
8dd4c05b
LP
391 case 'l':
392 r = strv_extend(&arg_listen, optarg);
2ca0435b 393 if (r < 0)
8dd4c05b 394 return log_oom();
2ca0435b
ZJS
395
396 break;
2ca0435b 397
7b7afdfc 398 case 'd':
d31e430f
LP
399 if (arg_socket_type == SOCK_SEQPACKET) {
400 log_error("--datagram may not be combined with --seqpacket.");
401 return -EINVAL;
402 }
403
404 arg_socket_type = SOCK_DGRAM;
405 break;
406
407 case ARG_SEQPACKET:
408 if (arg_socket_type == SOCK_DGRAM) {
409 log_error("--seqpacket may not be combined with --datagram.");
410 return -EINVAL;
411 }
412
413 arg_socket_type = SOCK_SEQPACKET;
7b7afdfc
SS
414 break;
415
2ca0435b
ZJS
416 case 'a':
417 arg_accept = true;
418 break;
419
8dd4c05b
LP
420 case 'E':
421 r = strv_extend(&arg_setenv, optarg);
5e65c93a 422 if (r < 0)
8dd4c05b 423 return log_oom();
5e65c93a
ZJS
424
425 break;
8dd4c05b 426
cf98937c
ZJS
427 case ARG_FDNAME: {
428 _cleanup_strv_free_ char **names;
429 char **s;
430
431 names = strv_split(optarg, ":");
432 if (!names)
433 return log_oom();
434
435 STRV_FOREACH(s, names)
436 if (!fdname_is_valid(*s)) {
437 _cleanup_free_ char *esc;
163c76c9 438
cf98937c
ZJS
439 esc = cescape(*s);
440 log_warning("File descriptor name \"%s\" is not valid.", esc);
441 }
442
443 /* Empty optargs means one empty name */
444 r = strv_extend_strv(&arg_fdnames,
445 strv_isempty(names) ? STRV_MAKE("") : names,
446 false);
447 if (r < 0)
448 return log_error_errno(r, "strv_extend_strv: %m");
8dd4c05b 449 break;
cf98937c 450 }
5e65c93a 451
eef0a274
LP
452 case ARG_INETD:
453 arg_inetd = true;
454 break;
455
2ca0435b
ZJS
456 case '?':
457 return -EINVAL;
458
459 default:
eb9da376 460 assert_not_reached("Unhandled option");
2ca0435b
ZJS
461 }
462
463 if (optind == argc) {
601185b4 464 log_error("%s: command to execute is missing.",
2ca0435b
ZJS
465 program_invocation_short_name);
466 return -EINVAL;
467 }
468
d31e430f 469 if (arg_socket_type == SOCK_DGRAM && arg_accept) {
7b7afdfc
SS
470 log_error("Datagram sockets do not accept connections. "
471 "The --datagram and --accept options may not be combined.");
472 return -EINVAL;
473 }
474
2ca0435b
ZJS
475 arg_args = argv + optind;
476
477 return 1 /* work to do */;
478}
479
480int main(int argc, char **argv, char **envp) {
481 int r, n;
482 int epoll_fd = -1;
483
2ca0435b 484 log_parse_environment();
eceb8483 485 log_open();
2ca0435b
ZJS
486
487 r = parse_argv(argc, argv);
488 if (r <= 0)
489 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
490
491 r = install_chld_handler();
492 if (r < 0)
493 return EXIT_FAILURE;
494
495 n = open_sockets(&epoll_fd, arg_accept);
496 if (n < 0)
497 return EXIT_FAILURE;
2c408cb6
LP
498 if (n == 0) {
499 log_error("No sockets to listen on specified or passed in.");
500 return EXIT_FAILURE;
501 }
2ca0435b 502
eceb8483 503 for (;;) {
2ca0435b
ZJS
504 struct epoll_event event;
505
506 r = epoll_wait(epoll_fd, &event, 1, -1);
507 if (r < 0) {
508 if (errno == EINTR)
509 continue;
510
56f64d95 511 log_error_errno(errno, "epoll_wait() failed: %m");
2ca0435b
ZJS
512 return EXIT_FAILURE;
513 }
514
2c408cb6 515 log_info("Communication attempt on fd %i.", event.data.fd);
2ca0435b 516 if (arg_accept) {
d31e430f 517 r = do_accept(argv[optind], argv + optind, envp, event.data.fd);
2ca0435b
ZJS
518 if (r < 0)
519 return EXIT_FAILURE;
520 } else
521 break;
522 }
523
eef0a274 524 exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, n);
2ca0435b
ZJS
525
526 return EXIT_SUCCESS;
527}