chiark / gitweb /
a425fd2facf764f281dcc1549c5cf83304304cb5
[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 #define UNIX_BUS_PATH "unix:path=/run/dbus/system_bus_socket"
45 #define KERNEL_BUS_PATH "kernel:path=/dev/kdbus/0-system/bus"
46
47 #ifdef ENABLE_KDBUS
48 #  define DEFAULT_BUS_PATH KERNEL_BUS_PATH ";" UNIX_BUS_PATH
49 #else
50 #  define DEFAULT_BUS_PATH UNIX_BUS_PATH
51 #endif
52
53 static const char *arg_address = DEFAULT_BUS_PATH;
54 static char *arg_command_line_buffer = NULL;
55
56 static int help(void) {
57
58         printf("%s [OPTIONS...]\n\n"
59                "Connect STDIO or a socket to a given bus address.\n\n"
60                "  -h --help              Show this help\n"
61                "     --version           Show package version\n"
62                "     --address=ADDRESS   Connect to the bus specified by ADDRESS\n"
63                "                         (default: " DEFAULT_BUS_PATH ")\n",
64                program_invocation_short_name);
65
66         return 0;
67 }
68
69 static int parse_argv(int argc, char *argv[]) {
70
71         enum {
72                 ARG_VERSION = 0x100,
73                 ARG_ADDRESS,
74         };
75
76         static const struct option options[] = {
77                 { "help",       no_argument,       NULL, 'h'            },
78                 { "version",    no_argument,       NULL, ARG_VERSION    },
79                 { "address",    required_argument, NULL, ARG_ADDRESS    },
80                 { NULL,         0,                 NULL, 0              }
81         };
82
83         int c;
84
85         assert(argc >= 0);
86         assert(argv);
87
88         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
89
90                 switch (c) {
91
92                 case 'h':
93                         help();
94                         return 0;
95
96                 case ARG_VERSION:
97                         puts(PACKAGE_STRING);
98                         puts(SYSTEMD_FEATURES);
99                         return 0;
100
101                 case ARG_ADDRESS:
102                         arg_address = optarg;
103                         break;
104
105                 case '?':
106                         return -EINVAL;
107
108                 default:
109                         assert_not_reached("Unhandled option");
110                 }
111         }
112
113         /* If the first command line argument is only "x" characters
114          * we'll write who we are talking to into it, so that "ps" is
115          * explanatory */
116         arg_command_line_buffer = argv[optind];
117         if (argc > optind + 1 ||
118             (arg_command_line_buffer && arg_command_line_buffer[strspn(arg_command_line_buffer, "x")] != 0)) {
119                 log_error("Too many arguments");
120                 return -EINVAL;
121         }
122
123         return 1;
124 }
125
126 static int rename_service(sd_bus *b) {
127         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
128         _cleanup_free_ char *p = NULL, *name = NULL;
129         const char *comm;
130         char **cmdline;
131         uid_t uid;
132         pid_t pid;
133         int r;
134
135         assert(b);
136
137         r = sd_bus_get_peer_creds(b, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_COMM, &creds);
138         if (r < 0)
139                 return r;
140
141         r = sd_bus_creds_get_uid(creds, &uid);
142         if (r < 0)
143                 return r;
144
145         r = sd_bus_creds_get_pid(creds, &pid);
146         if (r < 0)
147                 return r;
148
149         r = sd_bus_creds_get_cmdline(creds, &cmdline);
150         if (r < 0)
151                 return r;
152
153         r = sd_bus_creds_get_comm(creds, &comm);
154         if (r < 0)
155                 return r;
156
157         name = uid_to_name(uid);
158         if (!name)
159                 return -ENOMEM;
160
161         p = strv_join(cmdline, " ");
162         if (!p)
163                 return -ENOMEM;
164
165         /* The status string gets the full command line ... */
166         sd_notifyf(false,
167                    "STATUS=Processing requests from client PID %lu (%s); UID %lu (%s)",
168                    (unsigned long) pid, p,
169                    (unsigned long) uid, name);
170
171         /* ... and the argv line only the short comm */
172         if (arg_command_line_buffer) {
173                 size_t m, w;
174
175                 m = strlen(arg_command_line_buffer);
176                 w = snprintf(arg_command_line_buffer, m,
177                              "[PID %lu/%s; UID %lu/%s]",
178                              (unsigned long) pid, comm,
179                              (unsigned long) uid, name);
180
181                 if (m > w)
182                         memset(arg_command_line_buffer + w, 0, m - w);
183         }
184
185         return 0;
186 }
187
188 static int synthesize_name_acquired(sd_bus *a, sd_bus *b, sd_bus_message *m) {
189         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
190         const char *name, *old_owner, *new_owner;
191         int r;
192
193         assert(a);
194         assert(b);
195         assert(m);
196
197         /* If we get NameOwnerChanged for our own name, we need to
198          * synthesize NameLost/NameAcquired, since socket clients need
199          * that, even though it is obsoleted on kdbus */
200
201         if (!a->is_kernel)
202                 return 0;
203
204         if (!sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged") ||
205             !streq_ptr(m->path, "/org/freedesktop/DBus") ||
206             !streq_ptr(m->sender, "org.freedesktop.DBus"))
207                 return 0;
208
209         r = sd_bus_message_read(m, "sss", &name, &old_owner, &new_owner);
210         if (r < 0)
211                 return r;
212
213         r = sd_bus_message_rewind(m, true);
214         if (r < 0)
215                 return r;
216
217         if (streq(old_owner, a->unique_name)) {
218
219                 r = sd_bus_message_new_signal(
220                                 b,
221                                 "/org/freedesktop/DBus",
222                                 "org.freedesktop.DBus",
223                                 "NameLost",
224                                 &n);
225
226         } else if (streq(new_owner, a->unique_name)) {
227
228                 r = sd_bus_message_new_signal(
229                                 b,
230                                 "/org/freedesktop/DBus",
231                                 "org.freedesktop.DBus",
232                                 "NameAcquired",
233                                 &n);
234         } else
235                 return 0;
236
237         if (r < 0)
238                 return r;
239
240         r = sd_bus_message_append(n, "s", name);
241         if (r < 0)
242                 return r;
243
244         r = bus_message_append_sender(n, "org.freedesktop.DBus");
245         if (r < 0)
246                 return r;
247
248         r = bus_seal_synthetic_message(b, n);
249         if (r < 0)
250                 return r;
251
252         return sd_bus_send(b, n, NULL);
253 }
254
255 static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hello) {
256         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
257         bool is_hello;
258         int r;
259
260         assert(a);
261         assert(b);
262         assert(m);
263         assert(got_hello);
264
265         /* As reaction to hello we need to respond with two messages:
266          * the callback reply and the NameAcquired for the unique
267          * name, since hello is otherwise obsolete on kdbus. */
268
269         is_hello =
270                 sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "Hello") &&
271                 streq_ptr(m->destination, "org.freedesktop.DBus");
272
273         if (!is_hello) {
274
275                 if (*got_hello)
276                         return 0;
277
278                 log_error("First packet isn't hello (it's %s.%s), aborting.", m->interface, m->member);
279                 return -EIO;
280         }
281
282         if (*got_hello) {
283                 log_error("Got duplicate hello, aborting.");
284                 return -EIO;
285         }
286
287         *got_hello = true;
288
289         if (!a->is_kernel)
290                 return 0;
291
292         r = sd_bus_message_new_method_return(m, &n);
293         if (r < 0) {
294                 log_error("Failed to generate HELLO reply: %s", strerror(-r));
295                 return r;
296         }
297
298         r = sd_bus_message_append(n, "s", a->unique_name);
299         if (r < 0) {
300                 log_error("Failed to append unique name to HELLO reply: %s", strerror(-r));
301                 return r;
302         }
303
304         r = bus_message_append_sender(n, "org.freedesktop.DBus");
305         if (r < 0) {
306                 log_error("Failed to append sender to HELLO reply: %s", strerror(-r));
307                 return r;
308         }
309
310         r = bus_seal_synthetic_message(b, n);
311         if (r < 0) {
312                 log_error("Failed to seal HELLO reply: %s", strerror(-r));
313                 return r;
314         }
315
316         r = sd_bus_send(b, n, NULL);
317         if (r < 0) {
318                 log_error("Failed to send HELLO reply: %s", strerror(-r));
319                 return r;
320         }
321
322         n = sd_bus_message_unref(n);
323         r = sd_bus_message_new_signal(
324                         b,
325                         "/org/freedesktop/DBus",
326                         "org.freedesktop.DBus",
327                         "NameAcquired",
328                         &n);
329         if (r < 0) {
330                 log_error("Failed to allocate initial NameAcquired message: %s", strerror(-r));
331                 return r;
332         }
333
334         r = sd_bus_message_append(n, "s", a->unique_name);
335         if (r < 0) {
336                 log_error("Failed to append unique name to NameAcquired message: %s", strerror(-r));
337                 return r;
338         }
339
340         r = bus_message_append_sender(n, "org.freedesktop.DBus");
341         if (r < 0) {
342                 log_error("Failed to append sender to NameAcquired message: %s", strerror(-r));
343                 return r;
344         }
345
346         r = bus_seal_synthetic_message(b, n);
347         if (r < 0) {
348                 log_error("Failed to seal NameAcquired message: %s", strerror(-r));
349                 return r;
350         }
351
352         r = sd_bus_send(b, n, NULL);
353         if (r < 0) {
354                 log_error("Failed to send NameAcquired message: %s", strerror(-r));
355                 return r;
356         }
357
358         return 1;
359 }
360
361 int main(int argc, char *argv[]) {
362
363         _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL;
364         sd_id128_t server_id;
365         int r, in_fd, out_fd;
366         bool got_hello = false;
367         bool is_unix;
368
369         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
370         log_parse_environment();
371         log_open();
372
373         r = parse_argv(argc, argv);
374         if (r <= 0)
375                 goto finish;
376
377         r = sd_listen_fds(0);
378         if (r == 0) {
379                 in_fd = STDIN_FILENO;
380                 out_fd = STDOUT_FILENO;
381         } else if (r == 1) {
382                 in_fd = SD_LISTEN_FDS_START;
383                 out_fd = SD_LISTEN_FDS_START;
384         } else {
385                 log_error("Illegal number of file descriptors passed\n");
386                 goto finish;
387         }
388
389         is_unix =
390                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
391                 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
392
393         r = sd_bus_new(&a);
394         if (r < 0) {
395                 log_error("Failed to allocate bus: %s", strerror(-r));
396                 goto finish;
397         }
398
399         r = sd_bus_set_address(a, arg_address);
400         if (r < 0) {
401                 log_error("Failed to set address to connect to: %s", strerror(-r));
402                 goto finish;
403         }
404
405         r = sd_bus_negotiate_fds(a, is_unix);
406         if (r < 0) {
407                 log_error("Failed to set FD negotiation: %s", strerror(-r));
408                 goto finish;
409         }
410
411         r = sd_bus_start(a);
412         if (r < 0) {
413                 log_error("Failed to start bus client: %s", strerror(-r));
414                 goto finish;
415         }
416
417         r = sd_bus_get_server_id(a, &server_id);
418         if (r < 0) {
419                 log_error("Failed to get server ID: %s", strerror(-r));
420                 goto finish;
421         }
422
423         r = sd_bus_new(&b);
424         if (r < 0) {
425                 log_error("Failed to allocate bus: %s", strerror(-r));
426                 goto finish;
427         }
428
429         r = sd_bus_set_fd(b, in_fd, out_fd);
430         if (r < 0) {
431                 log_error("Failed to set fds: %s", strerror(-r));
432                 goto finish;
433         }
434
435         r = sd_bus_set_server(b, 1, server_id);
436         if (r < 0) {
437                 log_error("Failed to set server mode: %s", strerror(-r));
438                 goto finish;
439         }
440
441         r = sd_bus_negotiate_fds(b, is_unix);
442         if (r < 0) {
443                 log_error("Failed to set FD negotiation: %s", strerror(-r));
444                 goto finish;
445         }
446
447         r = sd_bus_set_anonymous(b, true);
448         if (r < 0) {
449                 log_error("Failed to set anonymous authentication: %s", strerror(-r));
450                 goto finish;
451         }
452
453         r = sd_bus_start(b);
454         if (r < 0) {
455                 log_error("Failed to start bus client: %s", strerror(-r));
456                 goto finish;
457         }
458
459         r = rename_service(b);
460         if (r < 0)
461                 log_debug("Failed to rename process: %s", strerror(-r));
462
463         if (a->is_kernel) {
464                 _cleanup_free_ char *match = NULL;
465                 const char *unique;
466
467                 r = sd_bus_get_unique_name(a, &unique);
468                 if (r < 0) {
469                         log_error("Failed to get unique name: %s", strerror(-r));
470                         goto finish;
471                 }
472
473                 match = strjoin("type='signal',"
474                                 "sender='org.freedesktop.DBus',"
475                                 "path='/org/freedesktop/DBus',"
476                                 "interface='org.freedesktop.DBus',"
477                                 "member='NameOwnerChanged',"
478                                 "arg1='",
479                                 unique,
480                                 "'",
481                                 NULL);
482                 if (!match) {
483                         log_oom();
484                         goto finish;
485                 }
486
487                 r = sd_bus_add_match(a, match, NULL, NULL);
488                 if (r < 0) {
489                         log_error("Failed to add match for NameLost: %s", strerror(-r));
490                         goto finish;
491                 }
492
493                 free(match);
494                 match = strjoin("type='signal',"
495                                 "sender='org.freedesktop.DBus',"
496                                 "path='/org/freedesktop/DBus',"
497                                 "interface='org.freedesktop.DBus',"
498                                 "member='NameOwnerChanged',"
499                                 "arg2='",
500                                 unique,
501                                 "'",
502                                 NULL);
503                 if (!match) {
504                         log_oom();
505                         goto finish;
506                 }
507
508                 r = sd_bus_add_match(a, match, NULL, NULL);
509                 if (r < 0) {
510                         log_error("Failed to add match for NameAcquired: %s", strerror(-r));
511                         goto finish;
512                 }
513         }
514
515         for (;;) {
516                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
517                 int events_a, events_b, fd;
518                 uint64_t timeout_a, timeout_b, t;
519                 struct timespec _ts, *ts;
520                 struct pollfd *pollfd;
521                 int k;
522
523                 if (got_hello) {
524                         r = sd_bus_process(a, &m);
525                         if (r < 0) {
526                                 /* treat 'connection reset by peer' as clean exit condition */
527                                 if (r == -ECONNRESET)
528                                         r = 0;
529                                 else
530                                         log_error("Failed to process bus a: %s", strerror(-r));
531
532                                 goto finish;
533                         }
534
535                         if (m) {
536                                 /* We officially got EOF, let's quit */
537                                 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
538                                         r = 0;
539                                         goto finish;
540                                 }
541
542                                 k = synthesize_name_acquired(a, b, m);
543                                 if (k < 0) {
544                                         r = k;
545                                         log_error("Failed to synthesize message: %s", strerror(-r));
546                                         goto finish;
547                                 }
548
549                                 k = sd_bus_send(b, m, NULL);
550                                 if (k < 0) {
551                                         r = k;
552                                         log_error("Failed to send message: %s", strerror(-r));
553                                         goto finish;
554                                 }
555                         }
556
557                         if (r > 0)
558                                 continue;
559                 }
560
561                 r = sd_bus_process(b, &m);
562                 if (r < 0) {
563                         /* treat 'connection reset by peer' as clean exit condition */
564                         if (r == -ECONNRESET)
565                                 r = 0;
566                         else
567                                 log_error("Failed to process bus b: %s", strerror(-r));
568
569                         goto finish;
570                 }
571
572                 if (m) {
573                         /* We officially got EOF, let's quit */
574                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
575                                 r = 0;
576                                 goto finish;
577                         }
578
579                         k = process_hello(a, b, m, &got_hello);
580                         if (k < 0) {
581                                 r = k;
582                                 goto finish;
583                         }
584
585                         if (k > 0)
586                                 r = k;
587                         else {
588                                 k = sd_bus_send(a, m, NULL);
589                                 if (k < 0) {
590                                         r = k;
591                                         log_error("Failed to send message: %s", strerror(-r));
592                                         goto finish;
593                                 }
594                         }
595                 }
596
597                 if (r > 0)
598                         continue;
599
600                 fd = sd_bus_get_fd(a);
601                 if (fd < 0) {
602                         log_error("Failed to get fd: %s", strerror(-r));
603                         goto finish;
604                 }
605
606                 events_a = sd_bus_get_events(a);
607                 if (events_a < 0) {
608                         log_error("Failed to get events mask: %s", strerror(-r));
609                         goto finish;
610                 }
611
612                 r = sd_bus_get_timeout(a, &timeout_a);
613                 if (r < 0) {
614                         log_error("Failed to get timeout: %s", strerror(-r));
615                         goto finish;
616                 }
617
618                 events_b = sd_bus_get_events(b);
619                 if (events_b < 0) {
620                         log_error("Failed to get events mask: %s", strerror(-r));
621                         goto finish;
622                 }
623
624                 r = sd_bus_get_timeout(b, &timeout_b);
625                 if (r < 0) {
626                         log_error("Failed to get timeout: %s", strerror(-r));
627                         goto finish;
628                 }
629
630                 t = timeout_a;
631                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
632                         t = timeout_b;
633
634                 if (t == (uint64_t) -1)
635                         ts = NULL;
636                 else {
637                         usec_t nw;
638
639                         nw = now(CLOCK_MONOTONIC);
640                         if (t > nw)
641                                 t -= nw;
642                         else
643                                 t = 0;
644
645                         ts = timespec_store(&_ts, t);
646                 }
647
648                 pollfd = (struct pollfd[3]) {
649                         {.fd = fd,     .events = events_a,           },
650                         {.fd = in_fd,  .events = events_b & POLLIN,  },
651                         {.fd = out_fd, .events = events_b & POLLOUT, }
652                 };
653
654                 r = ppoll(pollfd, 3, ts, NULL);
655                 if (r < 0) {
656                         log_error("ppoll() failed: %m");
657                         goto finish;
658                 }
659         }
660
661         r = 0;
662
663 finish:
664         sd_bus_flush(a);
665         sd_bus_flush(b);
666
667         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
668 }