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