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