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