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