chiark / gitweb /
update TODO
[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                 struct pollfd *pollfd;
270                 int k;
271
272                 r = sd_bus_process(a, &m);
273                 if (r < 0) {
274                         /* treat 'connection reset by peer' as clean exit condition */
275                         if (r == -ECONNRESET)
276                                 r = 0;
277                         else
278                                 log_error("Failed to process bus a: %s", strerror(-r));
279
280                         goto finish;
281                 }
282
283                 if (m) {
284                         /* We officially got EOF, let's quit */
285                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
286                                 r = 0;
287                                 goto finish;
288                         }
289
290                         k = sd_bus_send(b, m, NULL);
291                         if (k < 0) {
292                                 r = k;
293                                 log_error("Failed to send message: %s", strerror(-r));
294                                 goto finish;
295                         }
296                 }
297
298                 if (r > 0)
299                         continue;
300
301                 r = sd_bus_process(b, &m);
302                 if (r < 0) {
303                         /* treat 'connection reset by peer' as clean exit condition */
304                         if (r == -ECONNRESET)
305                                 r = 0;
306                         else
307                                 log_error("Failed to process bus b: %s", strerror(-r));
308
309                         goto finish;
310                 }
311
312                 if (m) {
313                         /* We officially got EOF, let's quit */
314                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
315                                 r = 0;
316                                 goto finish;
317                         }
318
319                         k = sd_bus_send(a, m, NULL);
320                         if (k < 0) {
321                                 r = k;
322                                 log_error("Failed to send message: %s", strerror(-r));
323                                 goto finish;
324                         }
325                 }
326
327                 if (r > 0)
328                         continue;
329
330                 fd = sd_bus_get_fd(a);
331                 if (fd < 0) {
332                         log_error("Failed to get fd: %s", strerror(-r));
333                         goto finish;
334                 }
335
336                 events_a = sd_bus_get_events(a);
337                 if (events_a < 0) {
338                         log_error("Failed to get events mask: %s", strerror(-r));
339                         goto finish;
340                 }
341
342                 r = sd_bus_get_timeout(a, &timeout_a);
343                 if (r < 0) {
344                         log_error("Failed to get timeout: %s", strerror(-r));
345                         goto finish;
346                 }
347
348                 events_b = sd_bus_get_events(b);
349                 if (events_b < 0) {
350                         log_error("Failed to get events mask: %s", strerror(-r));
351                         goto finish;
352                 }
353
354                 r = sd_bus_get_timeout(b, &timeout_b);
355                 if (r < 0) {
356                         log_error("Failed to get timeout: %s", strerror(-r));
357                         goto finish;
358                 }
359
360                 t = timeout_a;
361                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
362                         t = timeout_b;
363
364                 if (t == (uint64_t) -1)
365                         ts = NULL;
366                 else {
367                         usec_t nw;
368
369                         nw = now(CLOCK_MONOTONIC);
370                         if (t > nw)
371                                 t -= nw;
372                         else
373                                 t = 0;
374
375                         ts = timespec_store(&_ts, t);
376                 }
377
378                 pollfd = (struct pollfd[3]) {
379                         {.fd = fd,     .events = events_a,           },
380                         {.fd = in_fd,  .events = events_b & POLLIN,  },
381                         {.fd = out_fd, .events = events_b & POLLOUT, }
382                 };
383
384                 r = ppoll(pollfd, 3, ts, NULL);
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 }