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