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