chiark / gitweb /
treewide: use log_*_errno whenever %m is in the format string
[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_errno(errno, "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                 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
70         if (n > 0) {
71                 log_info("Received %i descriptors via the environment.", n);
72
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;
77
78                         count ++;
79                 }
80         }
81
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
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
98         STRV_FOREACH(address, arg_listen) {
99
100                 fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
101                 if (fd < 0) {
102                         log_open();
103                         return log_error_errno(fd, "Failed to open '%s': %m", *address);
104                 }
105
106                 assert(fd == SD_LISTEN_FDS_START + count);
107                 count ++;
108         }
109
110         if (arg_listen)
111                 log_open();
112
113         *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
114         if (*epoll_fd < 0) {
115                 log_error_errno(errno, "Failed to create epoll object: %m");
116                 return -errno;
117         }
118
119         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
120                 _cleanup_free_ char *name = NULL;
121
122                 getsockname_pretty(fd, &name);
123                 log_info("Listening on %s as %i.", strna(name), fd);
124
125                 r = add_epoll(*epoll_fd, fd);
126                 if (r < 0)
127                         return r;
128         }
129
130         return count;
131 }
132
133 static int launch(char* name, char **argv, char **env, int fds) {
134
135         static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
136         _cleanup_strv_free_ char **envp = NULL;
137         _cleanup_free_ char *tmp = NULL;
138         unsigned n_env = 0, length;
139         char **s;
140         unsigned i;
141
142         length = strv_length(arg_setenv);
143
144         /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
145         envp = new0(char *, length + 7);
146         if (!envp)
147                 return log_oom();
148
149         STRV_FOREACH(s, arg_setenv) {
150                 if (strchr(*s, '='))
151                         envp[n_env++] = *s;
152                 else {
153                         _cleanup_free_ char *p = strappend(*s, "=");
154                         if (!p)
155                                 return log_oom();
156                         envp[n_env] = strv_find_prefix(env, p);
157                         if (envp[n_env])
158                                 n_env ++;
159                 }
160         }
161
162         for (i = 0; i < ELEMENTSOF(tocopy); i++) {
163                 envp[n_env] = strv_find_prefix(env, tocopy[i]);
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);
178         log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp);
179
180         return -errno;
181 }
182
183 static int launch1(const char* child, char** argv, char **env, int fd) {
184         _cleanup_free_ char *tmp = NULL;
185         pid_t parent_pid, child_pid;
186         int r;
187
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) {
196                 log_error_errno(errno, "Failed to fork: %m");
197                 return -errno;
198         }
199
200         /* In the child */
201         if (child_pid == 0) {
202                 r = dup2(fd, STDIN_FILENO);
203                 if (r < 0) {
204                         log_error_errno(errno, "Failed to dup connection to stdin: %m");
205                         _exit(EXIT_FAILURE);
206                 }
207
208                 r = dup2(fd, STDOUT_FILENO);
209                 if (r < 0) {
210                         log_error_errno(errno, "Failed to dup connection to stdout: %m");
211                         _exit(EXIT_FAILURE);
212                 }
213
214                 r = close(fd);
215                 if (r < 0) {
216                         log_error_errno(errno, "Failed to close dupped connection: %m");
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);
230                 log_error_errno(errno, "Failed to exec child %s: %m", child);
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
239 static int do_accept(const char* name, char **argv, char **envp, int fd) {
240         _cleanup_free_ char *local = NULL, *peer = NULL;
241         _cleanup_close_ int fd2 = -1;
242
243         fd2 = accept(fd, NULL, NULL);
244         if (fd2 < 0) {
245                 log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
246                 return fd2;
247         }
248
249         getsockname_pretty(fd2, &local);
250         getpeername_pretty(fd2, &peer);
251         log_info("Connection from %s to %s", strna(peer), strna(local));
252
253         return launch1(name, argv, envp, fd2);
254 }
255
256 /* SIGCHLD handler. */
257 static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
258         PROTECT_ERRNO;
259
260         log_info("Child %d died with code %d", t->si_pid, t->si_status);
261         /* Wait for a dead child. */
262         waitpid(t->si_pid, NULL, 0);
263 }
264
265 static int install_chld_handler(void) {
266         int r;
267         struct sigaction act = {
268                 .sa_flags = SA_SIGINFO,
269                 .sa_sigaction = sigchld_hdl,
270         };
271
272         r = sigaction(SIGCHLD, &act, 0);
273         if (r < 0)
274                 log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
275         return r;
276 }
277
278 static void help(void) {
279         printf("%s [OPTIONS...]\n\n"
280                "Listen on sockets and launch child on connection.\n\n"
281                "Options:\n"
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"
287                "\n"
288                "Note: file descriptors from sd_listen_fds() will be passed through.\n"
289                , program_invocation_short_name);
290 }
291
292 static int parse_argv(int argc, char *argv[]) {
293         enum {
294                 ARG_VERSION = 0x100,
295         };
296
297         static const struct option options[] = {
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 */
304                 {}
305         };
306
307         int c;
308
309         assert(argc >= 0);
310         assert(argv);
311
312         while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
313                 switch(c) {
314                 case 'h':
315                         help();
316                         return 0;
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
335                 case 'E': {
336                         int r = strv_extend(&arg_setenv, optarg);
337                         if (r < 0)
338                                 return r;
339
340                         break;
341                 }
342
343                 case '?':
344                         return -EINVAL;
345
346                 default:
347                         assert_not_reached("Unhandled option");
348                 }
349
350         if (optind == argc) {
351                 log_error("%s: command to execute is missing.",
352                           program_invocation_short_name);
353                 return -EINVAL;
354         }
355
356         arg_args = argv + optind;
357
358         return 1 /* work to do */;
359 }
360
361 int main(int argc, char **argv, char **envp) {
362         int r, n;
363         int epoll_fd = -1;
364
365         log_parse_environment();
366         log_open();
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;
379         if (n == 0) {
380                 log_error("No sockets to listen on specified or passed in.");
381                 return EXIT_FAILURE;
382         }
383
384         for (;;) {
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
392                         log_error_errno(errno, "epoll_wait() failed: %m");
393                         return EXIT_FAILURE;
394                 }
395
396                 log_info("Communication attempt on fd %i.", event.data.fd);
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 }