]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/activate/activate.c
Merge pull request #8354 from keszybz/new-NEWS
[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 = rearrange_stdio(start_fd, start_fd, STDERR_FILENO); /* invalidates start_fd on success + error */
203 if (r < 0)
204 return log_error_errno(errno, "Failed to move fd to stdin+stdout: %m");
205
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)
219 return log_oom();
220
221 if (asprintf((char**)(envp + n_env++), "LISTEN_PID=" PID_FMT, getpid_cached()) < 0)
222 return log_oom();
223
224 if (arg_fdnames) {
225 _cleanup_free_ char *names = NULL;
226 size_t len;
227 char *e;
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);
240
241 names = strv_join(arg_fdnames, ":");
242 if (!names)
243 return log_oom();
244
245 e = strappend("LISTEN_FDNAMES=", names);
246 if (!e)
247 return log_oom();
248
249 envp[n_env++] = e;
250 }
251 }
252
253 joined = strv_join(argv, " ");
254 if (!joined)
255 return log_oom();
256
257 log_info("Execing %s (%s)", name, joined);
258 execvpe(name, argv, envp);
259
260 return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined);
261 }
262
263 static int fork_and_exec_process(const char* child, char** argv, char **env, int fd) {
264 _cleanup_free_ char *joined = NULL;
265 pid_t child_pid;
266 int r;
267
268 joined = strv_join(argv, " ");
269 if (!joined)
270 return log_oom();
271
272 r = safe_fork("(activate)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid);
273 if (r < 0)
274 return r;
275 if (r == 0) {
276 /* In the child */
277 exec_process(child, argv, env, fd, 1);
278 _exit(EXIT_FAILURE);
279 }
280
281 log_info("Spawned %s (%s) as PID " PID_FMT ".", child, joined, child_pid);
282 return 0;
283 }
284
285 static int do_accept(const char* name, char **argv, char **envp, int fd) {
286 _cleanup_free_ char *local = NULL, *peer = NULL;
287 _cleanup_close_ int fd_accepted = -1;
288
289 fd_accepted = accept4(fd, NULL, NULL, 0);
290 if (fd_accepted < 0)
291 return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
292
293 getsockname_pretty(fd_accepted, &local);
294 getpeername_pretty(fd_accepted, true, &peer);
295 log_info("Connection from %s to %s", strna(peer), strna(local));
296
297 return fork_and_exec_process(name, argv, envp, fd_accepted);
298 }
299
300 /* SIGCHLD handler. */
301 static void sigchld_hdl(int sig) {
302 PROTECT_ERRNO;
303
304 for (;;) {
305 siginfo_t si;
306 int r;
307
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 }
320 }
321
322 static int install_chld_handler(void) {
323 static const struct sigaction act = {
324 .sa_flags = SA_NOCLDSTOP|SA_RESTART,
325 .sa_handler = sigchld_hdl,
326 };
327
328 int r;
329
330 r = sigaction(SIGCHLD, &act, 0);
331 if (r < 0)
332 return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
333
334 return 0;
335 }
336
337 static void help(void) {
338 printf("%s [OPTIONS...]\n\n"
339 "Listen on sockets and launch child on connection.\n\n"
340 "Options:\n"
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"
350 "\n"
351 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
352 , program_invocation_short_name);
353 }
354
355 static int parse_argv(int argc, char *argv[]) {
356 enum {
357 ARG_VERSION = 0x100,
358 ARG_FDNAME,
359 ARG_SEQPACKET,
360 ARG_INETD,
361 };
362
363 static const struct option options[] = {
364 { "help", no_argument, NULL, 'h' },
365 { "version", no_argument, NULL, ARG_VERSION },
366 { "datagram", no_argument, NULL, 'd' },
367 { "seqpacket", no_argument, NULL, ARG_SEQPACKET },
368 { "listen", required_argument, NULL, 'l' },
369 { "accept", no_argument, NULL, 'a' },
370 { "setenv", required_argument, NULL, 'E' },
371 { "environment", required_argument, NULL, 'E' }, /* legacy alias */
372 { "fdname", required_argument, NULL, ARG_FDNAME },
373 { "inetd", no_argument, NULL, ARG_INETD },
374 {}
375 };
376
377 int c, r;
378
379 assert(argc >= 0);
380 assert(argv);
381
382 while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0)
383 switch(c) {
384 case 'h':
385 help();
386 return 0;
387
388 case ARG_VERSION:
389 return version();
390
391 case 'l':
392 r = strv_extend(&arg_listen, optarg);
393 if (r < 0)
394 return log_oom();
395
396 break;
397
398 case 'd':
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;
414 break;
415
416 case 'a':
417 arg_accept = true;
418 break;
419
420 case 'E':
421 r = strv_extend(&arg_setenv, optarg);
422 if (r < 0)
423 return log_oom();
424
425 break;
426
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;
438
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");
449 break;
450 }
451
452 case ARG_INETD:
453 arg_inetd = true;
454 break;
455
456 case '?':
457 return -EINVAL;
458
459 default:
460 assert_not_reached("Unhandled option");
461 }
462
463 if (optind == argc) {
464 log_error("%s: command to execute is missing.",
465 program_invocation_short_name);
466 return -EINVAL;
467 }
468
469 if (arg_socket_type == SOCK_DGRAM && arg_accept) {
470 log_error("Datagram sockets do not accept connections. "
471 "The --datagram and --accept options may not be combined.");
472 return -EINVAL;
473 }
474
475 arg_args = argv + optind;
476
477 return 1 /* work to do */;
478 }
479
480 int main(int argc, char **argv, char **envp) {
481 int r, n;
482 int epoll_fd = -1;
483
484 log_parse_environment();
485 log_open();
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;
498 if (n == 0) {
499 log_error("No sockets to listen on specified or passed in.");
500 return EXIT_FAILURE;
501 }
502
503 for (;;) {
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
511 log_error_errno(errno, "epoll_wait() failed: %m");
512 return EXIT_FAILURE;
513 }
514
515 log_info("Communication attempt on fd %i.", event.data.fd);
516 if (arg_accept) {
517 r = do_accept(argv[optind], argv + optind, envp, event.data.fd);
518 if (r < 0)
519 return EXIT_FAILURE;
520 } else
521 break;
522 }
523
524 exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, n);
525
526 return EXIT_SUCCESS;
527 }