chiark / gitweb /
d8caf406c7155d103be00f15e5dbddd202450884
[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
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <sys/types.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <sys/poll.h>
30 #include <stddef.h>
31 #include <getopt.h>
32
33 #include "log.h"
34 #include "util.h"
35 #include "socket-util.h"
36 #include "sd-daemon.h"
37 #include "sd-bus.h"
38 #include "bus-internal.h"
39 #include "bus-message.h"
40 #include "bus-util.h"
41 #include "build.h"
42 #include "strv.h"
43
44 #ifdef ENABLE_KDBUS
45 static const char *arg_address = "kernel:path=/dev/kdbus/0-system/bus;unix:path=/run/dbus/system_bus_socket";
46 #else
47 static const char *arg_address = "unix:path=/run/dbus/system_bus_socket";
48 #endif
49
50 static char *arg_command_line_buffer = NULL;
51
52 static int help(void) {
53
54         printf("%s [OPTIONS...]\n\n"
55                "Connect STDIO or a socket to a given bus address.\n\n"
56                "  -h --help              Show this help\n"
57                "     --version           Show package version\n"
58                "     --address=ADDRESS   Connect to bus specified by address\n",
59                program_invocation_short_name);
60
61         return 0;
62 }
63
64 static int parse_argv(int argc, char *argv[]) {
65
66         enum {
67                 ARG_VERSION = 0x100,
68                 ARG_ADDRESS,
69         };
70
71         static const struct option options[] = {
72                 { "help",       no_argument,       NULL, 'h'            },
73                 { "version",    no_argument,       NULL, ARG_VERSION    },
74                 { "address",    required_argument, NULL, ARG_ADDRESS    },
75                 { NULL,         0,                 NULL, 0              }
76         };
77
78         int c;
79
80         assert(argc >= 0);
81         assert(argv);
82
83         while ((c = getopt_long(argc, argv, "hsup:", options, NULL)) >= 0) {
84
85                 switch (c) {
86
87                 case 'h':
88                         help();
89                         return 0;
90
91                 case ARG_VERSION:
92                         puts(PACKAGE_STRING);
93                         puts(SYSTEMD_FEATURES);
94                         return 0;
95
96                 case ARG_ADDRESS:
97                         arg_address = optarg;
98                         break;
99
100                 case '?':
101                         return -EINVAL;
102
103                 default:
104                         assert_not_reached("Unhandled option");
105                 }
106         }
107
108         /* If the first command line argument is only "x" characters
109          * we'll write who we are talking to into it, so that "ps" is
110          * explanatory */
111         arg_command_line_buffer = argv[optind];
112         if (argc > optind + 1 ||
113             (arg_command_line_buffer && arg_command_line_buffer[strspn(arg_command_line_buffer, "x")] != 0)) {
114                 log_error("Too many arguments");
115                 return -EINVAL;
116         }
117
118         return 1;
119 }
120
121 int main(int argc, char *argv[]) {
122
123         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
124         _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL;
125         sd_id128_t server_id;
126         int r, in_fd, out_fd;
127         char **cmdline;
128         const char *comm;
129         bool is_unix;
130         uid_t uid;
131         pid_t pid;
132
133         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
134         log_parse_environment();
135         log_open();
136
137         r = parse_argv(argc, argv);
138         if (r <= 0)
139                 goto finish;
140
141         r = sd_listen_fds(0);
142         if (r == 0) {
143                 in_fd = STDIN_FILENO;
144                 out_fd = STDOUT_FILENO;
145         } else if (r == 1) {
146                 in_fd = SD_LISTEN_FDS_START;
147                 out_fd = SD_LISTEN_FDS_START;
148         } else {
149                 log_error("Illegal number of file descriptors passed\n");
150                 goto finish;
151         }
152
153         is_unix =
154                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
155                 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
156
157         r = sd_bus_new(&a);
158         if (r < 0) {
159                 log_error("Failed to allocate bus: %s", strerror(-r));
160                 goto finish;
161         }
162
163         r = sd_bus_set_address(a, arg_address);
164         if (r < 0) {
165                 log_error("Failed to set address to connect to: %s", strerror(-r));
166                 goto finish;
167         }
168
169         r = sd_bus_negotiate_fds(a, is_unix);
170         if (r < 0) {
171                 log_error("Failed to set FD negotiation: %s", strerror(-r));
172                 goto finish;
173         }
174
175         r = sd_bus_start(a);
176         if (r < 0) {
177                 log_error("Failed to start bus client: %s", strerror(-r));
178                 goto finish;
179         }
180
181         r = sd_bus_get_server_id(a, &server_id);
182         if (r < 0) {
183                 log_error("Failed to get server ID: %s", strerror(-r));
184                 goto finish;
185         }
186
187         r = sd_bus_new(&b);
188         if (r < 0) {
189                 log_error("Failed to allocate bus: %s", strerror(-r));
190                 goto finish;
191         }
192
193         r = sd_bus_set_fd(b, in_fd, out_fd);
194         if (r < 0) {
195                 log_error("Failed to set fds: %s", strerror(-r));
196                 goto finish;
197         }
198
199         r = sd_bus_set_server(b, 1, server_id);
200         if (r < 0) {
201                 log_error("Failed to set server mode: %s", strerror(-r));
202                 goto finish;
203         }
204
205         r = sd_bus_negotiate_fds(b, is_unix);
206         if (r < 0) {
207                 log_error("Failed to set FD negotiation: %s", strerror(-r));
208                 goto finish;
209         }
210
211         r = sd_bus_set_anonymous(b, true);
212         if (r < 0) {
213                 log_error("Failed to set anonymous authentication: %s", strerror(-r));
214                 goto finish;
215         }
216
217         r = sd_bus_start(b);
218         if (r < 0) {
219                 log_error("Failed to start bus client: %s", strerror(-r));
220                 goto finish;
221         }
222
223         if (sd_bus_get_peer_creds(b, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_COMM, &creds) >= 0 &&
224             sd_bus_creds_get_uid(creds, &uid) >= 0 &&
225             sd_bus_creds_get_pid(creds, &pid) >= 0 &&
226             sd_bus_creds_get_cmdline(creds, &cmdline) >= 0 &&
227             sd_bus_creds_get_comm(creds, &comm) >= 0) {
228                 _cleanup_free_ char *p = NULL, *name = NULL;
229
230                 name = uid_to_name(uid);
231                 if (!name) {
232                         r = log_oom();
233                         goto finish;
234                 }
235
236                 p = strv_join(cmdline, " ");
237                 if (!p) {
238                         r = log_oom();
239                         goto finish;
240                 }
241
242                 /* The status string gets the full command line ... */
243                 sd_notifyf(false,
244                            "STATUS=Processing requests from client PID %lu (%s); UID %lu (%s)",
245                            (unsigned long) pid, p,
246                            (unsigned long) uid, name);
247
248                 /* ... and the argv line only the short comm */
249                 if (arg_command_line_buffer) {
250                         size_t m, w;
251
252                         m = strlen(arg_command_line_buffer);
253                         w = snprintf(arg_command_line_buffer, m,
254                                      "[PID %lu/%s; UID %lu/%s]",
255                                      (unsigned long) pid, comm,
256                                      (unsigned long) uid, name);
257
258                         if (m > w)
259                                 memset(arg_command_line_buffer + w, 0, m - w);
260
261                 }
262         }
263
264         for (;;) {
265                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
266                 int events_a, events_b, fd;
267                 uint64_t timeout_a, timeout_b, t;
268                 struct timespec _ts, *ts;
269                 int k;
270
271                 r = sd_bus_process(a, &m);
272                 if (r < 0) {
273                         /* treat 'connection reset by peer' as clean exit condition */
274                         if (r == -ECONNRESET)
275                                 r = 0;
276                         else
277                                 log_error("Failed to process bus a: %s", strerror(-r));
278
279                         goto finish;
280                 }
281
282                 if (m) {
283                         /* We officially got EOF, let's quit */
284                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
285                                 r = 0;
286                                 goto finish;
287                         }
288
289                         k = sd_bus_send(b, m, NULL);
290                         if (k < 0) {
291                                 r = k;
292                                 log_error("Failed to send message: %s", strerror(-r));
293                                 goto finish;
294                         }
295                 }
296
297                 if (r > 0)
298                         continue;
299
300                 r = sd_bus_process(b, &m);
301                 if (r < 0) {
302                         /* treat 'connection reset by peer' as clean exit condition */
303                         if (r == -ECONNRESET)
304                                 r = 0;
305                         else
306                                 log_error("Failed to process bus b: %s", strerror(-r));
307
308                         goto finish;
309                 }
310
311                 if (m) {
312                         /* We officially got EOF, let's quit */
313                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
314                                 r = 0;
315                                 goto finish;
316                         }
317
318                         k = sd_bus_send(a, m, NULL);
319                         if (k < 0) {
320                                 r = k;
321                                 log_error("Failed to send message: %s", strerror(-r));
322                                 goto finish;
323                         }
324                 }
325
326                 if (r > 0)
327                         continue;
328
329                 fd = sd_bus_get_fd(a);
330                 if (fd < 0) {
331                         log_error("Failed to get fd: %s", strerror(-r));
332                         goto finish;
333                 }
334
335                 events_a = sd_bus_get_events(a);
336                 if (events_a < 0) {
337                         log_error("Failed to get events mask: %s", strerror(-r));
338                         goto finish;
339                 }
340
341                 r = sd_bus_get_timeout(a, &timeout_a);
342                 if (r < 0) {
343                         log_error("Failed to get timeout: %s", strerror(-r));
344                         goto finish;
345                 }
346
347                 events_b = sd_bus_get_events(b);
348                 if (events_b < 0) {
349                         log_error("Failed to get events mask: %s", strerror(-r));
350                         goto finish;
351                 }
352
353                 r = sd_bus_get_timeout(b, &timeout_b);
354                 if (r < 0) {
355                         log_error("Failed to get timeout: %s", strerror(-r));
356                         goto finish;
357                 }
358
359                 t = timeout_a;
360                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
361                         t = timeout_b;
362
363                 if (t == (uint64_t) -1)
364                         ts = NULL;
365                 else {
366                         usec_t nw;
367
368                         nw = now(CLOCK_MONOTONIC);
369                         if (t > nw)
370                                 t -= nw;
371                         else
372                                 t = 0;
373
374                         ts = timespec_store(&_ts, t);
375                 }
376
377                 {
378                         struct pollfd p[3] = {
379                                 {.fd = fd,            .events = events_a, },
380                                 {.fd = STDIN_FILENO,  .events = events_b & POLLIN, },
381                                 {.fd = STDOUT_FILENO, .events = events_b & POLLOUT, }};
382
383                         r = ppoll(p, ELEMENTSOF(p), ts, NULL);
384                 }
385                 if (r < 0) {
386                         log_error("ppoll() failed: %m");
387                         goto finish;
388                 }
389         }
390
391         r = 0;
392
393 finish:
394         sd_bus_flush(a);
395         sd_bus_flush(b);
396
397         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
398 }