]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/bus-proxyd/bus-proxyd.c
Merge pull request #908 from richardmaw-codethink/nspawn-path-escapes-v3
[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 <sys/socket.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <sys/prctl.h>
30 #include <stddef.h>
31 #include <getopt.h>
32 #include <pthread.h>
33
34 #include "log.h"
35 #include "util.h"
36 #include "sd-daemon.h"
37 #include "bus-internal.h"
38 #include "build.h"
39 #include "strv.h"
40 #include "def.h"
41 #include "capability.h"
42 #include "bus-xml-policy.h"
43 #include "proxy.h"
44 #include "formats-util.h"
45
46 static char *arg_address = NULL;
47 static char **arg_configuration = NULL;
48
49 typedef struct {
50 int fd;
51 SharedPolicy *policy;
52 uid_t bus_uid;
53 } ClientContext;
54
55 static ClientContext *client_context_free(ClientContext *c) {
56 if (!c)
57 return NULL;
58
59 safe_close(c->fd);
60 free(c);
61
62 return NULL;
63 }
64
65 DEFINE_TRIVIAL_CLEANUP_FUNC(ClientContext*, client_context_free);
66
67 static int client_context_new(ClientContext **out) {
68 _cleanup_(client_context_freep) ClientContext *c = NULL;
69
70 c = new0(ClientContext, 1);
71 if (!c)
72 return -ENOMEM;
73
74 c->fd = -1;
75
76 *out = c;
77 c = NULL;
78 return 0;
79 }
80
81 static void *run_client(void *userdata) {
82 _cleanup_(client_context_freep) ClientContext *c = userdata;
83 _cleanup_(proxy_freep) Proxy *p = NULL;
84 char comm[16];
85 int r;
86
87 r = proxy_new(&p, c->fd, c->fd, arg_address);
88 if (r < 0)
89 goto exit;
90
91 c->fd = -1;
92
93 /* set comm to "p$PIDu$UID" and suffix with '*' if truncated */
94 r = snprintf(comm, sizeof(comm), "p" PID_FMT "u" UID_FMT, p->local_creds.pid, p->local_creds.uid);
95 if (r >= (ssize_t)sizeof(comm))
96 comm[sizeof(comm) - 2] = '*';
97 (void) prctl(PR_SET_NAME, comm);
98
99 r = proxy_set_policy(p, c->policy, arg_configuration);
100 if (r < 0)
101 goto exit;
102
103 r = proxy_hello_policy(p, c->bus_uid);
104 if (r < 0)
105 goto exit;
106
107 r = proxy_run(p);
108
109 exit:
110 return NULL;
111 }
112
113 static int loop_clients(int accept_fd, uid_t bus_uid) {
114 _cleanup_(shared_policy_freep) SharedPolicy *sp = NULL;
115 pthread_attr_t attr;
116 int r;
117
118 r = pthread_attr_init(&attr);
119 if (r < 0) {
120 return log_error_errno(errno, "Cannot initialize pthread attributes: %m");
121 }
122
123 r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
124 if (r < 0) {
125 r = log_error_errno(errno, "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_error("Cannot spawn thread: %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: %s)\n",
181 program_invocation_short_name,
182 is_kdbus_available() ? KERNEL_SYSTEM_BUS_ADDRESS : UNIX_SYSTEM_BUS_ADDRESS);
183
184 return 0;
185 }
186
187 static int parse_argv(int argc, char *argv[]) {
188
189 enum {
190 ARG_VERSION = 0x100,
191 ARG_ADDRESS,
192 ARG_CONFIGURATION,
193 ARG_MACHINE,
194 };
195
196 static const struct option options[] = {
197 { "help", no_argument, NULL, 'h' },
198 { "version", no_argument, NULL, ARG_VERSION },
199 { "address", required_argument, NULL, ARG_ADDRESS },
200 { "configuration", required_argument, NULL, ARG_CONFIGURATION },
201 { "machine", required_argument, NULL, ARG_MACHINE },
202 {},
203 };
204
205 int c, r;
206
207 assert(argc >= 0);
208 assert(argv);
209
210 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
211
212 switch (c) {
213
214 case 'h':
215 help();
216 return 0;
217
218 case ARG_VERSION:
219 puts(PACKAGE_STRING);
220 puts(SYSTEMD_FEATURES);
221 return 0;
222
223 case ARG_ADDRESS:
224 r = free_and_strdup(&arg_address, optarg);
225 if (r < 0)
226 return log_oom();
227 break;
228
229 case ARG_CONFIGURATION:
230 r = strv_extend(&arg_configuration, optarg);
231 if (r < 0)
232 return log_oom();
233 break;
234
235 case ARG_MACHINE: {
236 _cleanup_free_ char *e = NULL;
237 char *a;
238
239 e = bus_address_escape(optarg);
240 if (!e)
241 return log_oom();
242
243 a = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL);
244 if (!a)
245 return log_oom();
246
247 free(arg_address);
248 arg_address = a;
249
250 break;
251 }
252
253 case '?':
254 return -EINVAL;
255
256 default:
257 assert_not_reached("Unhandled option");
258 }
259
260 if (argc > optind) {
261 log_error("Too many arguments");
262 return -EINVAL;
263 }
264
265 if (!arg_address) {
266 arg_address = strdup(is_kdbus_available() ? KERNEL_SYSTEM_BUS_ADDRESS : UNIX_SYSTEM_BUS_ADDRESS);
267 if (!arg_address)
268 return log_oom();
269 }
270
271 return 1;
272 }
273
274 int main(int argc, char *argv[]) {
275 int r, accept_fd;
276 uid_t uid, bus_uid;
277 gid_t gid;
278
279 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
280 log_parse_environment();
281 log_open();
282
283 bus_uid = getuid();
284
285 if (geteuid() == 0) {
286 const char *user = "systemd-bus-proxy";
287
288 r = get_user_creds(&user, &uid, &gid, NULL, NULL);
289 if (r < 0) {
290 log_error_errno(r, "Cannot resolve user name %s: %m", user);
291 goto finish;
292 }
293
294 r = drop_privileges(uid, gid, 1ULL << CAP_IPC_OWNER);
295 if (r < 0) {
296 log_error_errno(r, "Cannot drop privileges: %m");
297 goto finish;
298 }
299 }
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
313 r = fd_nonblock(accept_fd, false);
314 if (r < 0) {
315 log_error_errno(r, "Cannot mark accept-fd non-blocking: %m");
316 goto finish;
317 }
318
319 r = loop_clients(accept_fd, bus_uid);
320
321 finish:
322 sd_notify(false,
323 "STOPPING=1\n"
324 "STATUS=Shutting down.");
325
326 strv_free(arg_configuration);
327 free(arg_address);
328
329 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
330 }