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