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