]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/activate/activate.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / activate / activate.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
21 #include <getopt.h>
22 #include <sys/epoll.h>
23 #include <sys/prctl.h>
24 #include <sys/socket.h>
25 #include <sys/wait.h>
26 #include <unistd.h>
27
28 #include "sd-daemon.h"
29
30 #include "alloc-util.h"
31 #include "escape.h"
32 #include "fd-util.h"
33 #include "log.h"
34 #include "macro.h"
35 #include "process-util.h"
36 #include "signal-util.h"
37 #include "socket-util.h"
38 #include "string-util.h"
39 #include "strv.h"
40
41 static char** arg_listen = NULL;
42 static bool arg_accept = false;
43 static int arg_socket_type = SOCK_STREAM;
44 static char** arg_args = NULL;
45 static char** arg_setenv = NULL;
46 static char **arg_fdnames = NULL;
47 static bool arg_inetd = false;
48
49 static int add_epoll(int epoll_fd, int fd) {
50 struct epoll_event ev = {
51 .events = EPOLLIN
52 };
53 int r;
54
55 assert(epoll_fd >= 0);
56 assert(fd >= 0);
57
58 ev.data.fd = fd;
59 r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
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);
62
63 return 0;
64 }
65
66 static int open_sockets(int *epoll_fd, bool accept) {
67 char **address;
68 int n, fd, r;
69 int count = 0;
70
71 n = sd_listen_fds(true);
72 if (n < 0)
73 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
74 if (n > 0) {
75 log_info("Received %i descriptors via the environment.", n);
76
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;
81
82 count++;
83 }
84 }
85
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
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
102 STRV_FOREACH(address, arg_listen) {
103 fd = make_socket_fd(LOG_DEBUG, *address, arg_socket_type, (arg_accept*SOCK_CLOEXEC));
104 if (fd < 0) {
105 log_open();
106 return log_error_errno(fd, "Failed to open '%s': %m", *address);
107 }
108
109 assert(fd == SD_LISTEN_FDS_START + count);
110 count++;
111 }
112
113 if (arg_listen)
114 log_open();
115
116 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
117 if (*epoll_fd < 0)
118 return log_error_errno(errno, "Failed to create epoll object: %m");
119
120 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
121 _cleanup_free_ char *name = NULL;
122
123 getsockname_pretty(fd, &name);
124 log_info("Listening on %s as %i.", strna(name), fd);
125
126 r = add_epoll(*epoll_fd, fd);
127 if (r < 0)
128 return r;
129 }
130
131 return count;
132 }
133
134 static int exec_process(const char* name, char **argv, char **env, int start_fd, int n_fds) {
135
136 _cleanup_strv_free_ char **envp = NULL;
137 _cleanup_free_ char *joined = NULL;
138 unsigned n_env = 0, length;
139 const char *tocopy;
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 }
147
148 length = strv_length(arg_setenv);
149
150 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
151 envp = new0(char *, length + 8);
152 if (!envp)
153 return log_oom();
154
155 STRV_FOREACH(s, arg_setenv) {
156
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 {
166 _cleanup_free_ char *p;
167 const char *n;
168
169 p = strappend(*s, "=");
170 if (!p)
171 return log_oom();
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();
180
181 n_env++;
182 }
183 }
184
185 FOREACH_STRING(tocopy, "TERM=", "PATH=", "USER=", "HOME=") {
186 const char *n;
187
188 n = strv_find_prefix(env, tocopy);
189 if (!n)
190 continue;
191
192 envp[n_env] = strdup(n);
193 if (!envp[n_env])
194 return log_oom();
195
196 n_env++;
197 }
198
199 if (arg_inetd) {
200 assert(n_fds == 1);
201
202 r = dup2(start_fd, STDIN_FILENO);
203 if (r < 0)
204 return log_error_errno(errno, "Failed to dup connection to stdin: %m");
205
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)
224 return log_oom();
225
226 if (asprintf((char**)(envp + n_env++), "LISTEN_PID=" PID_FMT, getpid_cached()) < 0)
227 return log_oom();
228
229 if (arg_fdnames) {
230 _cleanup_free_ char *names = NULL;
231 size_t len;
232 char *e;
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);
245
246 names = strv_join(arg_fdnames, ":");
247 if (!names)
248 return log_oom();
249
250 e = strappend("LISTEN_FDNAMES=", names);
251 if (!e)
252 return log_oom();
253
254 envp[n_env++] = e;
255 }
256 }
257
258 joined = strv_join(argv, " ");
259 if (!joined)
260 return log_oom();
261
262 log_info("Execing %s (%s)", name, joined);
263 execvpe(name, argv, envp);
264
265 return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined);
266 }
267
268 static int fork_and_exec_process(const char* child, char** argv, char **env, int fd) {
269 _cleanup_free_ char *joined = NULL;
270 pid_t parent_pid, child_pid;
271
272 joined = strv_join(argv, " ");
273 if (!joined)
274 return log_oom();
275
276 parent_pid = getpid_cached();
277
278 child_pid = fork();
279 if (child_pid < 0)
280 return log_error_errno(errno, "Failed to fork: %m");
281
282 /* In the child */
283 if (child_pid == 0) {
284
285 (void) reset_all_signal_handlers();
286 (void) reset_signal_mask();
287
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
297 exec_process(child, argv, env, fd, 1);
298 _exit(EXIT_FAILURE);
299 }
300
301 log_info("Spawned %s (%s) as PID %d", child, joined, child_pid);
302 return 0;
303 }
304
305 static int do_accept(const char* name, char **argv, char **envp, int fd) {
306 _cleanup_free_ char *local = NULL, *peer = NULL;
307 _cleanup_close_ int fd_accepted = -1;
308
309 fd_accepted = accept4(fd, NULL, NULL, 0);
310 if (fd_accepted < 0)
311 return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
312
313 getsockname_pretty(fd_accepted, &local);
314 getpeername_pretty(fd_accepted, true, &peer);
315 log_info("Connection from %s to %s", strna(peer), strna(local));
316
317 return fork_and_exec_process(name, argv, envp, fd_accepted);
318 }
319
320 /* SIGCHLD handler. */
321 static void sigchld_hdl(int sig) {
322 PROTECT_ERRNO;
323
324 for (;;) {
325 siginfo_t si;
326 int r;
327
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 }
340 }
341
342 static int install_chld_handler(void) {
343 static const struct sigaction act = {
344 .sa_flags = SA_NOCLDSTOP|SA_RESTART,
345 .sa_handler = sigchld_hdl,
346 };
347
348 int r;
349
350 r = sigaction(SIGCHLD, &act, 0);
351 if (r < 0)
352 return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
353
354 return 0;
355 }
356
357 static void help(void) {
358 printf("%s [OPTIONS...]\n\n"
359 "Listen on sockets and launch child on connection.\n\n"
360 "Options:\n"
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"
370 "\n"
371 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
372 , program_invocation_short_name);
373 }
374
375 static int parse_argv(int argc, char *argv[]) {
376 enum {
377 ARG_VERSION = 0x100,
378 ARG_FDNAME,
379 ARG_SEQPACKET,
380 ARG_INETD,
381 };
382
383 static const struct option options[] = {
384 { "help", no_argument, NULL, 'h' },
385 { "version", no_argument, NULL, ARG_VERSION },
386 { "datagram", no_argument, NULL, 'd' },
387 { "seqpacket", no_argument, NULL, ARG_SEQPACKET },
388 { "listen", required_argument, NULL, 'l' },
389 { "accept", no_argument, NULL, 'a' },
390 { "setenv", required_argument, NULL, 'E' },
391 { "environment", required_argument, NULL, 'E' }, /* legacy alias */
392 { "fdname", required_argument, NULL, ARG_FDNAME },
393 { "inetd", no_argument, NULL, ARG_INETD },
394 {}
395 };
396
397 int c, r;
398
399 assert(argc >= 0);
400 assert(argv);
401
402 while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0)
403 switch(c) {
404 case 'h':
405 help();
406 return 0;
407
408 case ARG_VERSION:
409 return version();
410
411 case 'l':
412 r = strv_extend(&arg_listen, optarg);
413 if (r < 0)
414 return log_oom();
415
416 break;
417
418 case 'd':
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;
434 break;
435
436 case 'a':
437 arg_accept = true;
438 break;
439
440 case 'E':
441 r = strv_extend(&arg_setenv, optarg);
442 if (r < 0)
443 return log_oom();
444
445 break;
446
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;
458
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");
469 break;
470 }
471
472 case ARG_INETD:
473 arg_inetd = true;
474 break;
475
476 case '?':
477 return -EINVAL;
478
479 default:
480 assert_not_reached("Unhandled option");
481 }
482
483 if (optind == argc) {
484 log_error("%s: command to execute is missing.",
485 program_invocation_short_name);
486 return -EINVAL;
487 }
488
489 if (arg_socket_type == SOCK_DGRAM && arg_accept) {
490 log_error("Datagram sockets do not accept connections. "
491 "The --datagram and --accept options may not be combined.");
492 return -EINVAL;
493 }
494
495 arg_args = argv + optind;
496
497 return 1 /* work to do */;
498 }
499
500 int main(int argc, char **argv, char **envp) {
501 int r, n;
502 int epoll_fd = -1;
503
504 log_parse_environment();
505 log_open();
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;
518 if (n == 0) {
519 log_error("No sockets to listen on specified or passed in.");
520 return EXIT_FAILURE;
521 }
522
523 for (;;) {
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
531 log_error_errno(errno, "epoll_wait() failed: %m");
532 return EXIT_FAILURE;
533 }
534
535 log_info("Communication attempt on fd %i.", event.data.fd);
536 if (arg_accept) {
537 r = do_accept(argv[optind], argv + optind, envp, event.data.fd);
538 if (r < 0)
539 return EXIT_FAILURE;
540 } else
541 break;
542 }
543
544 exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, n);
545
546 return EXIT_SUCCESS;
547 }