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