]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/activate/activate.c
Merge pull request #7745 from poettering/sockaddr-size
[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 child_pid;
271 int r;
272
273 joined = strv_join(argv, " ");
274 if (!joined)
275 return log_oom();
276
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 */
282 exec_process(child, argv, env, fd, 1);
283 _exit(EXIT_FAILURE);
284 }
285
286 log_info("Spawned %s (%s) as PID " PID_FMT ".", child, joined, child_pid);
287 return 0;
288 }
289
290 static int do_accept(const char* name, char **argv, char **envp, int fd) {
291 _cleanup_free_ char *local = NULL, *peer = NULL;
292 _cleanup_close_ int fd_accepted = -1;
293
294 fd_accepted = accept4(fd, NULL, NULL, 0);
295 if (fd_accepted < 0)
296 return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
297
298 getsockname_pretty(fd_accepted, &local);
299 getpeername_pretty(fd_accepted, true, &peer);
300 log_info("Connection from %s to %s", strna(peer), strna(local));
301
302 return fork_and_exec_process(name, argv, envp, fd_accepted);
303 }
304
305 /* SIGCHLD handler. */
306 static void sigchld_hdl(int sig) {
307 PROTECT_ERRNO;
308
309 for (;;) {
310 siginfo_t si;
311 int r;
312
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 }
325 }
326
327 static int install_chld_handler(void) {
328 static const struct sigaction act = {
329 .sa_flags = SA_NOCLDSTOP|SA_RESTART,
330 .sa_handler = sigchld_hdl,
331 };
332
333 int r;
334
335 r = sigaction(SIGCHLD, &act, 0);
336 if (r < 0)
337 return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
338
339 return 0;
340 }
341
342 static void help(void) {
343 printf("%s [OPTIONS...]\n\n"
344 "Listen on sockets and launch child on connection.\n\n"
345 "Options:\n"
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"
355 "\n"
356 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
357 , program_invocation_short_name);
358 }
359
360 static int parse_argv(int argc, char *argv[]) {
361 enum {
362 ARG_VERSION = 0x100,
363 ARG_FDNAME,
364 ARG_SEQPACKET,
365 ARG_INETD,
366 };
367
368 static const struct option options[] = {
369 { "help", no_argument, NULL, 'h' },
370 { "version", no_argument, NULL, ARG_VERSION },
371 { "datagram", no_argument, NULL, 'd' },
372 { "seqpacket", no_argument, NULL, ARG_SEQPACKET },
373 { "listen", required_argument, NULL, 'l' },
374 { "accept", no_argument, NULL, 'a' },
375 { "setenv", required_argument, NULL, 'E' },
376 { "environment", required_argument, NULL, 'E' }, /* legacy alias */
377 { "fdname", required_argument, NULL, ARG_FDNAME },
378 { "inetd", no_argument, NULL, ARG_INETD },
379 {}
380 };
381
382 int c, r;
383
384 assert(argc >= 0);
385 assert(argv);
386
387 while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0)
388 switch(c) {
389 case 'h':
390 help();
391 return 0;
392
393 case ARG_VERSION:
394 return version();
395
396 case 'l':
397 r = strv_extend(&arg_listen, optarg);
398 if (r < 0)
399 return log_oom();
400
401 break;
402
403 case 'd':
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;
419 break;
420
421 case 'a':
422 arg_accept = true;
423 break;
424
425 case 'E':
426 r = strv_extend(&arg_setenv, optarg);
427 if (r < 0)
428 return log_oom();
429
430 break;
431
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;
443
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");
454 break;
455 }
456
457 case ARG_INETD:
458 arg_inetd = true;
459 break;
460
461 case '?':
462 return -EINVAL;
463
464 default:
465 assert_not_reached("Unhandled option");
466 }
467
468 if (optind == argc) {
469 log_error("%s: command to execute is missing.",
470 program_invocation_short_name);
471 return -EINVAL;
472 }
473
474 if (arg_socket_type == SOCK_DGRAM && arg_accept) {
475 log_error("Datagram sockets do not accept connections. "
476 "The --datagram and --accept options may not be combined.");
477 return -EINVAL;
478 }
479
480 arg_args = argv + optind;
481
482 return 1 /* work to do */;
483 }
484
485 int main(int argc, char **argv, char **envp) {
486 int r, n;
487 int epoll_fd = -1;
488
489 log_parse_environment();
490 log_open();
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;
503 if (n == 0) {
504 log_error("No sockets to listen on specified or passed in.");
505 return EXIT_FAILURE;
506 }
507
508 for (;;) {
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
516 log_error_errno(errno, "epoll_wait() failed: %m");
517 return EXIT_FAILURE;
518 }
519
520 log_info("Communication attempt on fd %i.", event.data.fd);
521 if (arg_accept) {
522 r = do_accept(argv[optind], argv + optind, envp, event.data.fd);
523 if (r < 0)
524 return EXIT_FAILURE;
525 } else
526 break;
527 }
528
529 exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, n);
530
531 return EXIT_SUCCESS;
532 }