]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/activate/activate.c
Merge pull request #2507 from evverx/fix-q-on-tmpfs
[thirdparty/systemd.git] / src / activate / activate.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <getopt.h>
23 #include <sys/epoll.h>
24 #include <sys/prctl.h>
25 #include <sys/socket.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28
29 #include "sd-daemon.h"
30
31 #include "alloc-util.h"
32 #include "fd-util.h"
33 #include "log.h"
34 #include "macro.h"
35 #include "signal-util.h"
36 #include "socket-util.h"
37 #include "string-util.h"
38 #include "strv.h"
39
40 static char** arg_listen = NULL;
41 static bool arg_accept = false;
42 static bool arg_datagram = false;
43 static char** arg_args = NULL;
44 static char** arg_setenv = NULL;
45 static const char *arg_fdname = NULL;
46
47 static int add_epoll(int epoll_fd, int fd) {
48 struct epoll_event ev = {
49 .events = EPOLLIN
50 };
51 int r;
52
53 assert(epoll_fd >= 0);
54 assert(fd >= 0);
55
56 ev.data.fd = fd;
57 r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
58 if (r < 0)
59 return log_error_errno(errno, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd);
60
61 return 0;
62 }
63
64 static int open_sockets(int *epoll_fd, bool accept) {
65 char **address;
66 int n, fd, r;
67 int count = 0;
68
69 n = sd_listen_fds(true);
70 if (n < 0)
71 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
72 if (n > 0) {
73 log_info("Received %i descriptors via the environment.", n);
74
75 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
76 r = fd_cloexec(fd, arg_accept);
77 if (r < 0)
78 return r;
79
80 count ++;
81 }
82 }
83
84 /* Close logging and all other descriptors */
85 if (arg_listen) {
86 int except[3 + n];
87
88 for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++)
89 except[fd] = fd;
90
91 log_close();
92 close_all_fds(except, 3 + n);
93 }
94
95 /** Note: we leak some fd's on error here. I doesn't matter
96 * much, since the program will exit immediately anyway, but
97 * would be a pain to fix.
98 */
99
100 STRV_FOREACH(address, arg_listen) {
101
102 if (arg_datagram)
103 fd = make_socket_fd(LOG_DEBUG, *address, SOCK_DGRAM, SOCK_CLOEXEC);
104 else
105 fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM, (arg_accept*SOCK_CLOEXEC));
106
107 if (fd < 0) {
108 log_open();
109 return log_error_errno(fd, "Failed to open '%s': %m", *address);
110 }
111
112 assert(fd == SD_LISTEN_FDS_START + count);
113 count ++;
114 }
115
116 if (arg_listen)
117 log_open();
118
119 *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
120 if (*epoll_fd < 0)
121 return log_error_errno(errno, "Failed to create epoll object: %m");
122
123 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
124 _cleanup_free_ char *name = NULL;
125
126 getsockname_pretty(fd, &name);
127 log_info("Listening on %s as %i.", strna(name), fd);
128
129 r = add_epoll(*epoll_fd, fd);
130 if (r < 0)
131 return r;
132 }
133
134 return count;
135 }
136
137 static int launch(char* name, char **argv, char **env, int fds) {
138
139 static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
140 _cleanup_strv_free_ char **envp = NULL;
141 _cleanup_free_ char *tmp = NULL;
142 unsigned n_env = 0, length;
143 char **s;
144 unsigned i;
145
146 length = strv_length(arg_setenv);
147
148 /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
149 envp = new0(char *, length + 8);
150 if (!envp)
151 return log_oom();
152
153 STRV_FOREACH(s, arg_setenv) {
154 if (strchr(*s, '=')) {
155 char *k;
156
157 k = strdup(*s);
158 if (!k)
159 return log_oom();
160
161 envp[n_env++] = k;
162 } else {
163 _cleanup_free_ char *p;
164 const char *n;
165
166 p = strappend(*s, "=");
167 if (!p)
168 return log_oom();
169
170 n = strv_find_prefix(env, p);
171 if (!n)
172 continue;
173
174 envp[n_env] = strdup(n);
175 if (!envp[n_env])
176 return log_oom();
177 }
178 }
179
180 for (i = 0; i < ELEMENTSOF(tocopy); i++) {
181 const char *n;
182
183 n = strv_find_prefix(env, tocopy[i]);
184 if (!n)
185 continue;
186
187 envp[n_env] = strdup(n);
188 if (!envp[n_env])
189 return log_oom();
190
191 n_env ++;
192 }
193
194 if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
195 (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
196 return log_oom();
197
198 if (arg_fdname) {
199 char *e;
200
201 e = strappend("LISTEN_FDNAMES=", arg_fdname);
202 if (!e)
203 return log_oom();
204
205 for (i = 1; i < (unsigned) fds; i++) {
206 char *c;
207
208 c = strjoin(e, ":", arg_fdname, NULL);
209 if (!c) {
210 free(e);
211 return log_oom();
212 }
213
214 free(e);
215 e = c;
216 }
217
218 envp[n_env++] = e;
219 }
220
221 tmp = strv_join(argv, " ");
222 if (!tmp)
223 return log_oom();
224
225 log_info("Execing %s (%s)", name, tmp);
226 execvpe(name, argv, envp);
227
228 return log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp);
229 }
230
231 static int launch1(const char* child, char** argv, char **env, int fd) {
232 _cleanup_free_ char *tmp = NULL;
233 pid_t parent_pid, child_pid;
234 int r;
235
236 tmp = strv_join(argv, " ");
237 if (!tmp)
238 return log_oom();
239
240 parent_pid = getpid();
241
242 child_pid = fork();
243 if (child_pid < 0)
244 return log_error_errno(errno, "Failed to fork: %m");
245
246 /* In the child */
247 if (child_pid == 0) {
248
249 (void) reset_all_signal_handlers();
250 (void) reset_signal_mask();
251
252 r = dup2(fd, STDIN_FILENO);
253 if (r < 0) {
254 log_error_errno(errno, "Failed to dup connection to stdin: %m");
255 _exit(EXIT_FAILURE);
256 }
257
258 r = dup2(fd, STDOUT_FILENO);
259 if (r < 0) {
260 log_error_errno(errno, "Failed to dup connection to stdout: %m");
261 _exit(EXIT_FAILURE);
262 }
263
264 r = close(fd);
265 if (r < 0) {
266 log_error_errno(errno, "Failed to close dupped connection: %m");
267 _exit(EXIT_FAILURE);
268 }
269
270 /* Make sure the child goes away when the parent dies */
271 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
272 _exit(EXIT_FAILURE);
273
274 /* Check whether our parent died before we were able
275 * to set the death signal */
276 if (getppid() != parent_pid)
277 _exit(EXIT_SUCCESS);
278
279 execvp(child, argv);
280 log_error_errno(errno, "Failed to exec child %s: %m", child);
281 _exit(EXIT_FAILURE);
282 }
283
284 log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
285
286 return 0;
287 }
288
289 static int do_accept(const char* name, char **argv, char **envp, int fd) {
290 _cleanup_free_ char *local = NULL, *peer = NULL;
291 _cleanup_close_ int fd2 = -1;
292
293 fd2 = accept(fd, NULL, NULL);
294 if (fd2 < 0) {
295 log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
296 return fd2;
297 }
298
299 getsockname_pretty(fd2, &local);
300 getpeername_pretty(fd2, true, &peer);
301 log_info("Connection from %s to %s", strna(peer), strna(local));
302
303 return launch1(name, argv, envp, fd2);
304 }
305
306 /* SIGCHLD handler. */
307 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
308 PROTECT_ERRNO;
309
310 log_info("Child %d died with code %d", t->si_pid, t->si_status);
311 /* Wait for a dead child. */
312 waitpid(t->si_pid, NULL, 0);
313 }
314
315 static int install_chld_handler(void) {
316 int r;
317 struct sigaction act = {
318 .sa_flags = SA_SIGINFO,
319 .sa_sigaction = sigchld_hdl,
320 };
321
322 r = sigaction(SIGCHLD, &act, 0);
323 if (r < 0)
324 log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
325 return r;
326 }
327
328 static void help(void) {
329 printf("%s [OPTIONS...]\n\n"
330 "Listen on sockets and launch child on connection.\n\n"
331 "Options:\n"
332 " -h --help Show this help and exit\n"
333 " --version Print version string and exit\n"
334 " -l --listen=ADDR Listen for raw connections at ADDR\n"
335 " -d --datagram Listen on datagram instead of stream socket\n"
336 " -a --accept Spawn separate child for each connection\n"
337 " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
338 "\n"
339 "Note: file descriptors from sd_listen_fds() will be passed through.\n"
340 , program_invocation_short_name);
341 }
342
343 static int parse_argv(int argc, char *argv[]) {
344 enum {
345 ARG_VERSION = 0x100,
346 ARG_FDNAME,
347 };
348
349 static const struct option options[] = {
350 { "help", no_argument, NULL, 'h' },
351 { "version", no_argument, NULL, ARG_VERSION },
352 { "datagram", no_argument, NULL, 'd' },
353 { "listen", required_argument, NULL, 'l' },
354 { "accept", no_argument, NULL, 'a' },
355 { "setenv", required_argument, NULL, 'E' },
356 { "environment", required_argument, NULL, 'E' }, /* legacy alias */
357 { "fdname", required_argument, NULL, ARG_FDNAME },
358 {}
359 };
360
361 int c, r;
362
363 assert(argc >= 0);
364 assert(argv);
365
366 while ((c = getopt_long(argc, argv, "+hl:aEd", options, NULL)) >= 0)
367 switch(c) {
368 case 'h':
369 help();
370 return 0;
371
372 case ARG_VERSION:
373 return version();
374
375 case 'l':
376 r = strv_extend(&arg_listen, optarg);
377 if (r < 0)
378 return log_oom();
379
380 break;
381
382 case 'd':
383 arg_datagram = true;
384 break;
385
386 case 'a':
387 arg_accept = true;
388 break;
389
390 case 'E':
391 r = strv_extend(&arg_setenv, optarg);
392 if (r < 0)
393 return log_oom();
394
395 break;
396
397 case ARG_FDNAME:
398 if (!fdname_is_valid(optarg)) {
399 log_error("File descriptor name %s is not valid, refusing.", optarg);
400 return -EINVAL;
401 }
402
403 arg_fdname = optarg;
404 break;
405
406 case '?':
407 return -EINVAL;
408
409 default:
410 assert_not_reached("Unhandled option");
411 }
412
413 if (optind == argc) {
414 log_error("%s: command to execute is missing.",
415 program_invocation_short_name);
416 return -EINVAL;
417 }
418
419 if (arg_datagram && arg_accept) {
420 log_error("Datagram sockets do not accept connections. "
421 "The --datagram and --accept options may not be combined.");
422 return -EINVAL;
423 }
424
425 arg_args = argv + optind;
426
427 return 1 /* work to do */;
428 }
429
430 int main(int argc, char **argv, char **envp) {
431 int r, n;
432 int epoll_fd = -1;
433
434 log_parse_environment();
435 log_open();
436
437 r = parse_argv(argc, argv);
438 if (r <= 0)
439 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
440
441 r = install_chld_handler();
442 if (r < 0)
443 return EXIT_FAILURE;
444
445 n = open_sockets(&epoll_fd, arg_accept);
446 if (n < 0)
447 return EXIT_FAILURE;
448 if (n == 0) {
449 log_error("No sockets to listen on specified or passed in.");
450 return EXIT_FAILURE;
451 }
452
453 for (;;) {
454 struct epoll_event event;
455
456 r = epoll_wait(epoll_fd, &event, 1, -1);
457 if (r < 0) {
458 if (errno == EINTR)
459 continue;
460
461 log_error_errno(errno, "epoll_wait() failed: %m");
462 return EXIT_FAILURE;
463 }
464
465 log_info("Communication attempt on fd %i.", event.data.fd);
466 if (arg_accept) {
467 r = do_accept(argv[optind], argv + optind, envp,
468 event.data.fd);
469 if (r < 0)
470 return EXIT_FAILURE;
471 } else
472 break;
473 }
474
475 launch(argv[optind], argv + optind, envp, n);
476
477 return EXIT_SUCCESS;
478 }