chiark / gitweb /
bus-proxy: turn into multi-threaded daemon
[elogind.git] / src / bus-proxyd / bus-proxyd.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7   Copyright 2013 Daniel Mack
8   Copyright 2014 Kay Sievers
9   Copyright 2015 David Herrmann
10
11   systemd is free software; you can redistribute it and/or modify it
12   under the terms of the GNU Lesser General Public License as published by
13   the Free Software Foundation; either version 2.1 of the License, or
14   (at your option) any later version.
15
16   systemd is distributed in the hope that it will be useful, but
17   WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19   Lesser General Public License for more details.
20
21   You should have received a copy of the GNU Lesser General Public License
22   along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 ***/
24
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 #include <sys/types.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <sys/poll.h>
33 #include <stddef.h>
34 #include <getopt.h>
35 #include <pthread.h>
36
37 #include "log.h"
38 #include "util.h"
39 #include "hashmap.h"
40 #include "socket-util.h"
41 #include "sd-daemon.h"
42 #include "sd-bus.h"
43 #include "bus-internal.h"
44 #include "bus-message.h"
45 #include "bus-util.h"
46 #include "build.h"
47 #include "strv.h"
48 #include "def.h"
49 #include "capability.h"
50 #include "bus-control.h"
51 #include "smack-util.h"
52 #include "set.h"
53 #include "bus-xml-policy.h"
54 #include "driver.h"
55 #include "proxy.h"
56 #include "synthesize.h"
57
58 static char *arg_address = NULL;
59 static char **arg_configuration = NULL;
60
61 typedef struct {
62         int fd;
63 } ClientContext;
64
65 static ClientContext *client_context_free(ClientContext *c) {
66         if (!c)
67                 return NULL;
68
69         close(c->fd);
70         free(c);
71
72         return NULL;
73 }
74
75 DEFINE_TRIVIAL_CLEANUP_FUNC(ClientContext*, client_context_free);
76
77 static int client_context_new(ClientContext **out, int fd) {
78         _cleanup_(client_context_freep) ClientContext *c = NULL;
79
80         c = new0(ClientContext, 1);
81         if (!c)
82                 return log_oom();
83
84         c->fd = fd;
85
86         *out = c;
87         c = NULL;
88         return 0;
89 }
90
91 static void *run_client(void *userdata) {
92         _cleanup_(client_context_freep) ClientContext *c = userdata;
93         _cleanup_(proxy_freep) Proxy *p = NULL;
94         int r;
95
96         r = proxy_new(&p, c->fd, c->fd, arg_address);
97         if (r < 0)
98                 goto exit;
99
100         r = proxy_load_policy(p, arg_configuration);
101         if (r < 0)
102                 goto exit;
103
104         r = proxy_hello_policy(p, getuid());
105         if (r < 0)
106                 goto exit;
107
108         r = proxy_run(p);
109
110 exit:
111         return NULL;
112 }
113
114 static int loop_clients(int accept_fd) {
115         pthread_attr_t attr;
116         int r;
117
118         r = pthread_attr_init(&attr);
119         if (r < 0) {
120                 r = log_error_errno(errno, "Cannot initialize pthread attributes: %m");
121                 goto exit;
122         }
123
124         r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
125         if (r < 0) {
126                 r = log_error_errno(errno, "Cannot mark pthread attributes as detached: %m");
127                 goto exit_attr;
128         }
129
130         for (;;) {
131                 ClientContext *c;
132                 pthread_t tid;
133                 int fd;
134
135                 fd = accept4(accept_fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
136                 if (fd < 0) {
137                         if (errno == EAGAIN || errno == EINTR)
138                                 continue;
139
140                         r = log_error_errno(errno, "accept4() failed: %m");
141                         break;
142                 }
143
144                 r = client_context_new(&c, fd);
145                 if (r < 0) {
146                         log_oom();
147                         close(fd);
148                         continue;
149                 }
150
151                 r = pthread_create(&tid, &attr, run_client, c);
152                 if (r < 0) {
153                         log_error("Cannot spawn thread: %m");
154                         client_context_free(c);
155                         continue;
156                 }
157         }
158
159 exit_attr:
160         pthread_attr_destroy(&attr);
161 exit:
162         return r;
163 }
164
165 static int help(void) {
166
167         printf("%s [OPTIONS...]\n\n"
168                "DBus proxy server.\n\n"
169                "  -h --help               Show this help\n"
170                "     --version            Show package version\n"
171                "     --configuration=PATH Configuration file or directory\n"
172                "     --machine=MACHINE    Connect to specified machine\n"
173                "     --address=ADDRESS    Connect to the bus specified by ADDRESS\n"
174                "                          (default: " DEFAULT_SYSTEM_BUS_ADDRESS ")\n",
175                program_invocation_short_name);
176
177         return 0;
178 }
179
180 static int parse_argv(int argc, char *argv[]) {
181
182         enum {
183                 ARG_VERSION = 0x100,
184                 ARG_ADDRESS,
185                 ARG_CONFIGURATION,
186                 ARG_MACHINE,
187         };
188
189         static const struct option options[] = {
190                 { "help",            no_argument,       NULL, 'h'                 },
191                 { "version",         no_argument,       NULL, ARG_VERSION         },
192                 { "address",         required_argument, NULL, ARG_ADDRESS         },
193                 { "configuration",   required_argument, NULL, ARG_CONFIGURATION   },
194                 { "machine",         required_argument, NULL, ARG_MACHINE         },
195                 {},
196         };
197
198         int c, r;
199
200         assert(argc >= 0);
201         assert(argv);
202
203         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
204
205                 switch (c) {
206
207                 case 'h':
208                         help();
209                         return 0;
210
211                 case ARG_VERSION:
212                         puts(PACKAGE_STRING);
213                         puts(SYSTEMD_FEATURES);
214                         return 0;
215
216                 case ARG_ADDRESS: {
217                         char *a;
218
219                         a = strdup(optarg);
220                         if (!a)
221                                 return log_oom();
222
223                         free(arg_address);
224                         arg_address = a;
225                         break;
226                 }
227
228                 case ARG_CONFIGURATION:
229                         r = strv_extend(&arg_configuration, optarg);
230                         if (r < 0)
231                                 return log_oom();
232                         break;
233
234                 case ARG_MACHINE: {
235                         _cleanup_free_ char *e = NULL;
236                         char *a;
237
238                         e = bus_address_escape(optarg);
239                         if (!e)
240                                 return log_oom();
241
242 #ifdef ENABLE_KDBUS
243                         a = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL);
244 #else
245                         a = strjoin("x-machine-unix:machine=", e, NULL);
246 #endif
247                         if (!a)
248                                 return log_oom();
249
250                         free(arg_address);
251                         arg_address = a;
252
253                         break;
254                 }
255
256                 case '?':
257                         return -EINVAL;
258
259                 default:
260                         assert_not_reached("Unhandled option");
261                 }
262
263         if (argc > optind) {
264                 log_error("Too many arguments");
265                 return -EINVAL;
266         }
267
268         if (!arg_address) {
269                 arg_address = strdup(DEFAULT_SYSTEM_BUS_ADDRESS);
270                 if (!arg_address)
271                         return log_oom();
272         }
273
274         return 1;
275 }
276
277 int main(int argc, char *argv[]) {
278         int r, accept_fd;
279
280         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
281         log_parse_environment();
282         log_open();
283
284         r = parse_argv(argc, argv);
285         if (r <= 0)
286                 goto finish;
287
288         r = sd_listen_fds(0);
289         if (r != 1) {
290                 log_error("Illegal number of file descriptors passed");
291                 goto finish;
292         }
293
294         accept_fd = SD_LISTEN_FDS_START;
295         r = fd_nonblock(accept_fd, false);
296         if (r < 0) {
297                 log_error_errno(r, "Cannot mark accept-fd non-blocking: %m");
298                 goto finish;
299         }
300
301         r = loop_clients(accept_fd);
302
303 finish:
304         sd_notify(false,
305                   "STOPPING=1\n"
306                   "STATUS=Shutting down.");
307
308         strv_free(arg_configuration);
309         free(arg_address);
310
311         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
312 }