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