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