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