chiark / gitweb /
treewide: more log_*_errno() conversions, multiline calls
[elogind.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_setenv = NULL;
42
43 static int add_epoll(int epoll_fd, int fd) {
44         struct epoll_event ev = {
45                 .events = EPOLLIN
46         };
47         int r;
48
49         assert(epoll_fd >= 0);
50         assert(fd >= 0);
51
52         ev.data.fd = fd;
53         r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
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;
60 }
61
62 static int open_sockets(int *epoll_fd, bool accept) {
63         char **address;
64         int n, fd, r;
65         int count = 0;
66
67         n = sd_listen_fds(true);
68         if (n < 0) {
69                 log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
70                 return n;
71         }
72         if (n > 0) {
73                 log_info("Received %i descriptors via the environment.", n);
74
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;
79
80                         count ++;
81                 }
82         }
83
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
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
100         STRV_FOREACH(address, arg_listen) {
101
102                 fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
103                 if (fd < 0) {
104                         log_open();
105                         log_error_errno(fd, "Failed to open '%s': %m", *address);
106                         return fd;
107                 }
108
109                 assert(fd == SD_LISTEN_FDS_START + count);
110                 count ++;
111         }
112
113         if (arg_listen)
114                 log_open();
115
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++) {
123                 _cleanup_free_ char *name = NULL;
124
125                 getsockname_pretty(fd, &name);
126                 log_info("Listening on %s as %i.", strna(name), fd);
127
128                 r = add_epoll(*epoll_fd, fd);
129                 if (r < 0)
130                         return r;
131         }
132
133         return count;
134 }
135
136 static int launch(char* name, char **argv, char **env, int fds) {
137
138         static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
139         _cleanup_strv_free_ char **envp = NULL;
140         _cleanup_free_ char *tmp = NULL;
141         unsigned n_env = 0, length;
142         char **s;
143         unsigned i;
144
145         length = strv_length(arg_setenv);
146
147         /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
148         envp = new0(char *, length + 7);
149         if (!envp)
150                 return log_oom();
151
152         STRV_FOREACH(s, arg_setenv) {
153                 if (strchr(*s, '='))
154                         envp[n_env++] = *s;
155                 else {
156                         _cleanup_free_ char *p = strappend(*s, "=");
157                         if (!p)
158                                 return log_oom();
159                         envp[n_env] = strv_find_prefix(env, p);
160                         if (envp[n_env])
161                                 n_env ++;
162                 }
163         }
164
165         for (i = 0; i < ELEMENTSOF(tocopy); i++) {
166                 envp[n_env] = strv_find_prefix(env, tocopy[i]);
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);
182
183         return -errno;
184 }
185
186 static int launch1(const char* child, char** argv, char **env, int fd) {
187         _cleanup_free_ char *tmp = NULL;
188         pid_t parent_pid, child_pid;
189         int r;
190
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
242 static int do_accept(const char* name, char **argv, char **envp, int fd) {
243         _cleanup_free_ char *local = NULL, *peer = NULL;
244         _cleanup_close_ int fd2 = -1;
245
246         fd2 = accept(fd, NULL, NULL);
247         if (fd2 < 0) {
248                 log_error("Failed to accept connection on fd:%d: %m", fd);
249                 return fd2;
250         }
251
252         getsockname_pretty(fd2, &local);
253         getpeername_pretty(fd2, &peer);
254         log_info("Connection from %s to %s", strna(peer), strna(local));
255
256         return launch1(name, argv, envp, fd2);
257 }
258
259 /* SIGCHLD handler. */
260 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
261         PROTECT_ERRNO;
262
263         log_info("Child %d died with code %d", t->si_pid, t->si_status);
264         /* Wait for a dead child. */
265         waitpid(t->si_pid, NULL, 0);
266 }
267
268 static int install_chld_handler(void) {
269         int r;
270         struct sigaction act = {
271                 .sa_flags = SA_SIGINFO,
272                 .sa_sigaction = sigchld_hdl,
273         };
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
281 static void help(void) {
282         printf("%s [OPTIONS...]\n\n"
283                "Listen on sockets and launch child on connection.\n\n"
284                "Options:\n"
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"
290                "\n"
291                "Note: file descriptors from sd_listen_fds() will be passed through.\n"
292                , program_invocation_short_name);
293 }
294
295 static int parse_argv(int argc, char *argv[]) {
296         enum {
297                 ARG_VERSION = 0x100,
298         };
299
300         static const struct option options[] = {
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 */
307                 {}
308         };
309
310         int c;
311
312         assert(argc >= 0);
313         assert(argv);
314
315         while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
316                 switch(c) {
317                 case 'h':
318                         help();
319                         return 0;
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
338                 case 'E': {
339                         int r = strv_extend(&arg_setenv, optarg);
340                         if (r < 0)
341                                 return r;
342
343                         break;
344                 }
345
346                 case '?':
347                         return -EINVAL;
348
349                 default:
350                         assert_not_reached("Unhandled option");
351                 }
352
353         if (optind == argc) {
354                 log_error("%s: command to execute is missing.",
355                           program_invocation_short_name);
356                 return -EINVAL;
357         }
358
359         arg_args = argv + optind;
360
361         return 1 /* work to do */;
362 }
363
364 int main(int argc, char **argv, char **envp) {
365         int r, n;
366         int epoll_fd = -1;
367
368         log_parse_environment();
369         log_open();
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;
382         if (n == 0) {
383                 log_error("No sockets to listen on specified or passed in.");
384                 return EXIT_FAILURE;
385         }
386
387         for (;;) {
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
399                 log_info("Communication attempt on fd %i.", event.data.fd);
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 }