chiark / gitweb /
72e11467bda144dc12d43a24c6d70bda1567e2de
[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 <sys/prctl.h>
34 #include <stddef.h>
35 #include <getopt.h>
36 #include <pthread.h>
37
38 #include "log.h"
39 #include "util.h"
40 #include "hashmap.h"
41 #include "socket-util.h"
42 #include "sd-daemon.h"
43 #include "sd-bus.h"
44 #include "bus-internal.h"
45 #include "bus-message.h"
46 #include "bus-util.h"
47 #include "build.h"
48 #include "strv.h"
49 #include "def.h"
50 #include "capability.h"
51 #include "bus-control.h"
52 #include "smack-util.h"
53 #include "set.h"
54 #include "bus-xml-policy.h"
55 #include "driver.h"
56 #include "proxy.h"
57 #include "synthesize.h"
58
59 static char *arg_address = NULL;
60 static char **arg_configuration = NULL;
61
62 typedef struct {
63         int fd;
64         SharedPolicy *policy;
65 } ClientContext;
66
67 static ClientContext *client_context_free(ClientContext *c) {
68         if (!c)
69                 return NULL;
70
71         close(c->fd);
72         free(c);
73
74         return NULL;
75 }
76
77 DEFINE_TRIVIAL_CLEANUP_FUNC(ClientContext*, client_context_free);
78
79 static int client_context_new(ClientContext **out) {
80         _cleanup_(client_context_freep) ClientContext *c = NULL;
81
82         c = new0(ClientContext, 1);
83         if (!c)
84                 return log_oom();
85
86         c->fd = -1;
87
88         *out = c;
89         c = NULL;
90         return 0;
91 }
92
93 static void *run_client(void *userdata) {
94         _cleanup_(client_context_freep) ClientContext *c = userdata;
95         _cleanup_(proxy_freep) Proxy *p = NULL;
96         char comm[16];
97         int r;
98
99         r = proxy_new(&p, c->fd, c->fd, arg_address);
100         if (r < 0)
101                 goto exit;
102
103         /* set comm to "p$PIDu$UID" and suffix with '*' if truncated */
104         r = snprintf(comm, sizeof(comm), "p" PID_FMT "u" UID_FMT, p->local_creds.pid, p->local_creds.uid);
105         if (r >= (ssize_t)sizeof(comm))
106                 comm[sizeof(comm) - 2] = '*';
107         (void) prctl(PR_SET_NAME, comm);
108
109         r = proxy_set_policy(p, c->policy, arg_configuration);
110         if (r < 0)
111                 goto exit;
112
113         r = proxy_hello_policy(p, getuid());
114         if (r < 0)
115                 goto exit;
116
117         r = proxy_run(p);
118
119 exit:
120         return NULL;
121 }
122
123 static int loop_clients(int accept_fd) {
124         _cleanup_(shared_policy_freep) SharedPolicy *sp = NULL;
125         pthread_attr_t attr;
126         int r;
127
128         r = pthread_attr_init(&attr);
129         if (r < 0) {
130                 r = log_error_errno(errno, "Cannot initialize pthread attributes: %m");
131                 goto exit;
132         }
133
134         r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
135         if (r < 0) {
136                 r = log_error_errno(errno, "Cannot mark pthread attributes as detached: %m");
137                 goto exit_attr;
138         }
139
140         r = shared_policy_new(&sp);
141         if (r < 0)
142                 goto exit_attr;
143
144         for (;;) {
145                 ClientContext *c;
146                 pthread_t tid;
147                 int fd;
148
149                 fd = accept4(accept_fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
150                 if (fd < 0) {
151                         if (errno == EAGAIN || errno == EINTR)
152                                 continue;
153
154                         r = log_error_errno(errno, "accept4() failed: %m");
155                         break;
156                 }
157
158                 r = client_context_new(&c);
159                 if (r < 0) {
160                         log_oom();
161                         close(fd);
162                         continue;
163                 }
164
165                 c->fd = fd;
166                 c->policy = sp;
167
168                 r = pthread_create(&tid, &attr, run_client, c);
169                 if (r < 0) {
170                         log_error("Cannot spawn thread: %m");
171                         client_context_free(c);
172                         continue;
173                 }
174         }
175
176 exit_attr:
177         pthread_attr_destroy(&attr);
178 exit:
179         return r;
180 }
181
182 static int help(void) {
183
184         printf("%s [OPTIONS...]\n\n"
185                "DBus proxy server.\n\n"
186                "  -h --help               Show this help\n"
187                "     --version            Show package version\n"
188                "     --configuration=PATH Configuration file or directory\n"
189                "     --machine=MACHINE    Connect to specified machine\n"
190                "     --address=ADDRESS    Connect to the bus specified by ADDRESS\n"
191                "                          (default: " DEFAULT_SYSTEM_BUS_ADDRESS ")\n",
192                program_invocation_short_name);
193
194         return 0;
195 }
196
197 static int parse_argv(int argc, char *argv[]) {
198
199         enum {
200                 ARG_VERSION = 0x100,
201                 ARG_ADDRESS,
202                 ARG_CONFIGURATION,
203                 ARG_MACHINE,
204         };
205
206         static const struct option options[] = {
207                 { "help",            no_argument,       NULL, 'h'                 },
208                 { "version",         no_argument,       NULL, ARG_VERSION         },
209                 { "address",         required_argument, NULL, ARG_ADDRESS         },
210                 { "configuration",   required_argument, NULL, ARG_CONFIGURATION   },
211                 { "machine",         required_argument, NULL, ARG_MACHINE         },
212                 {},
213         };
214
215         int c, r;
216
217         assert(argc >= 0);
218         assert(argv);
219
220         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
221
222                 switch (c) {
223
224                 case 'h':
225                         help();
226                         return 0;
227
228                 case ARG_VERSION:
229                         puts(PACKAGE_STRING);
230                         puts(SYSTEMD_FEATURES);
231                         return 0;
232
233                 case ARG_ADDRESS: {
234                         char *a;
235
236                         a = strdup(optarg);
237                         if (!a)
238                                 return log_oom();
239
240                         free(arg_address);
241                         arg_address = a;
242                         break;
243                 }
244
245                 case ARG_CONFIGURATION:
246                         r = strv_extend(&arg_configuration, optarg);
247                         if (r < 0)
248                                 return log_oom();
249                         break;
250
251                 case ARG_MACHINE: {
252                         _cleanup_free_ char *e = NULL;
253                         char *a;
254
255                         e = bus_address_escape(optarg);
256                         if (!e)
257                                 return log_oom();
258
259 #ifdef ENABLE_KDBUS
260                         a = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL);
261 #else
262                         a = strjoin("x-machine-unix:machine=", e, NULL);
263 #endif
264                         if (!a)
265                                 return log_oom();
266
267                         free(arg_address);
268                         arg_address = a;
269
270                         break;
271                 }
272
273                 case '?':
274                         return -EINVAL;
275
276                 default:
277                         assert_not_reached("Unhandled option");
278                 }
279
280         if (argc > optind) {
281                 log_error("Too many arguments");
282                 return -EINVAL;
283         }
284
285         if (!arg_address) {
286                 arg_address = strdup(DEFAULT_SYSTEM_BUS_ADDRESS);
287                 if (!arg_address)
288                         return log_oom();
289         }
290
291         return 1;
292 }
293
294 int main(int argc, char *argv[]) {
295         int r, accept_fd;
296
297         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
298         log_parse_environment();
299         log_open();
300
301         r = parse_argv(argc, argv);
302         if (r <= 0)
303                 goto finish;
304
305         r = sd_listen_fds(0);
306         if (r != 1) {
307                 log_error("Illegal number of file descriptors passed");
308                 goto finish;
309         }
310
311         accept_fd = SD_LISTEN_FDS_START;
312         r = fd_nonblock(accept_fd, false);
313         if (r < 0) {
314                 log_error_errno(r, "Cannot mark accept-fd non-blocking: %m");
315                 goto finish;
316         }
317
318         r = loop_clients(accept_fd);
319
320 finish:
321         sd_notify(false,
322                   "STOPPING=1\n"
323                   "STATUS=Shutting down.");
324
325         strv_free(arg_configuration);
326         free(arg_address);
327
328         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
329 }