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