chiark / gitweb /
bus-proxyd: handle org.freedesktob.DBus calls natively
[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
10   systemd is free software; you can redistribute it and/or modify it
11   under the terms of the GNU Lesser General Public License as published by
12   the Free Software Foundation; either version 2.1 of the License, or
13   (at your option) any later version.
14
15   systemd is distributed in the hope that it will be useful, but
16   WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18   Lesser General Public License for more details.
19
20   You should have received a copy of the GNU Lesser General Public License
21   along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 ***/
23
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <sys/types.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <sys/poll.h>
32 #include <stddef.h>
33 #include <getopt.h>
34
35 #include "log.h"
36 #include "util.h"
37 #include "socket-util.h"
38 #include "sd-daemon.h"
39 #include "sd-bus.h"
40 #include "bus-internal.h"
41 #include "bus-message.h"
42 #include "bus-util.h"
43 #include "build.h"
44 #include "strv.h"
45 #include "def.h"
46
47 static const char *arg_address = DEFAULT_SYSTEM_BUS_PATH;
48 static char *arg_command_line_buffer = NULL;
49
50 static int help(void) {
51
52         printf("%s [OPTIONS...]\n\n"
53                "Connect STDIO or a socket to a given bus address.\n\n"
54                "  -h --help              Show this help\n"
55                "     --version           Show package version\n"
56                "     --address=ADDRESS   Connect to the bus specified by ADDRESS\n"
57                "                         (default: " DEFAULT_SYSTEM_BUS_PATH ")\n",
58                program_invocation_short_name);
59
60         return 0;
61 }
62
63 static int parse_argv(int argc, char *argv[]) {
64
65         enum {
66                 ARG_VERSION = 0x100,
67                 ARG_ADDRESS,
68         };
69
70         static const struct option options[] = {
71                 { "help",       no_argument,       NULL, 'h'            },
72                 { "version",    no_argument,       NULL, ARG_VERSION    },
73                 { "address",    required_argument, NULL, ARG_ADDRESS    },
74                 { NULL,         0,                 NULL, 0              }
75         };
76
77         int c;
78
79         assert(argc >= 0);
80         assert(argv);
81
82         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
83
84                 switch (c) {
85
86                 case 'h':
87                         help();
88                         return 0;
89
90                 case ARG_VERSION:
91                         puts(PACKAGE_STRING);
92                         puts(SYSTEMD_FEATURES);
93                         return 0;
94
95                 case ARG_ADDRESS:
96                         arg_address = optarg;
97                         break;
98
99                 case '?':
100                         return -EINVAL;
101
102                 default:
103                         assert_not_reached("Unhandled option");
104                 }
105         }
106
107         /* If the first command line argument is only "x" characters
108          * we'll write who we are talking to into it, so that "ps" is
109          * explanatory */
110         arg_command_line_buffer = argv[optind];
111         if (argc > optind + 1 ||
112             (arg_command_line_buffer && arg_command_line_buffer[strspn(arg_command_line_buffer, "x")] != 0)) {
113                 log_error("Too many arguments");
114                 return -EINVAL;
115         }
116
117         return 1;
118 }
119
120 static int rename_service(sd_bus *a, sd_bus *b) {
121         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
122         _cleanup_free_ char *p = NULL, *name = NULL;
123         const char *comm;
124         char **cmdline;
125         uid_t uid;
126         pid_t pid;
127         int r;
128
129         assert(a);
130         assert(b);
131
132         r = sd_bus_get_peer_creds(b, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_COMM, &creds);
133         if (r < 0)
134                 return r;
135
136         r = sd_bus_creds_get_uid(creds, &uid);
137         if (r < 0)
138                 return r;
139
140         r = sd_bus_creds_get_pid(creds, &pid);
141         if (r < 0)
142                 return r;
143
144         r = sd_bus_creds_get_cmdline(creds, &cmdline);
145         if (r < 0)
146                 return r;
147
148         r = sd_bus_creds_get_comm(creds, &comm);
149         if (r < 0)
150                 return r;
151
152         name = uid_to_name(uid);
153         if (!name)
154                 return -ENOMEM;
155
156         p = strv_join(cmdline, " ");
157         if (!p)
158                 return -ENOMEM;
159
160         /* The status string gets the full command line ... */
161         sd_notifyf(false,
162                    "STATUS=Processing requests from client PID "PID_FMT" (%s); UID "UID_FMT" (%s)",
163                    pid, p,
164                    uid, name);
165
166         /* ... and the argv line only the short comm */
167         if (arg_command_line_buffer) {
168                 size_t m, w;
169
170                 m = strlen(arg_command_line_buffer);
171                 w = snprintf(arg_command_line_buffer, m,
172                              "[PID "PID_FMT"/%s; UID "UID_FMT"/%s]",
173                              pid, comm,
174                              uid, name);
175
176                 if (m > w)
177                         memzero(arg_command_line_buffer + w, m - w);
178         }
179
180         log_debug("Running on behalf of PID "PID_FMT" (%s), UID "UID_FMT" (%s), %s",
181                   pid, p,
182                   uid, name,
183                   a->unique_name);
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                                 &n,
222                                 "/org/freedesktop/DBus",
223                                 "org.freedesktop.DBus",
224                                 "NameLost");
225
226         } else if (streq(new_owner, a->unique_name)) {
227
228                 r = sd_bus_message_new_signal(
229                                 b,
230                                 &n,
231                                 "/org/freedesktop/DBus",
232                                 "org.freedesktop.DBus",
233                                 "NameAcquired");
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_policy(sd_bus *a, sd_bus *b, sd_bus_message *m) {
256         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
257         int r;
258
259         assert(a);
260         assert(b);
261         assert(m);
262
263         if (!sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "GetAll"))
264                 return 0;
265
266         if (!streq_ptr(m->path, "/org/gnome/DisplayManager/Slave"))
267                 return 0;
268
269         r = sd_bus_message_new_method_errorf(m, &n, SD_BUS_ERROR_ACCESS_DENIED, "gdm, you are stupid");
270         if (r < 0)
271                 return r;
272
273         r = bus_message_append_sender(n, "org.freedesktop.DBus");
274         if (r < 0) {
275                 log_error("Failed to append sender to gdm reply: %s", strerror(-r));
276                 return r;
277         }
278
279         r = bus_seal_synthetic_message(b, n);
280         if (r < 0) {
281                 log_error("Failed to seal gdm reply: %s", strerror(-r));
282                 return r;
283         }
284
285         r = sd_bus_send(b, n, NULL);
286         if (r < 0) {
287                 log_error("Failed to send gdm reply: %s", strerror(-r));
288                 return r;
289         }
290
291         return 1;
292 }
293
294 static int synthetic_driver_send(sd_bus *b, sd_bus_message *m) {
295         int r;
296
297         assert(b);
298         assert(m);
299
300         r = bus_message_append_sender(m, "org.freedesktop.DBus");
301         if (r < 0)
302                 return r;
303
304         r = bus_seal_synthetic_message(b, m);
305         if (r < 0)
306                 return r;
307
308         return sd_bus_send(b, m, NULL);
309 }
310
311 static int synthetic_reply_method_error(sd_bus_message *call, const sd_bus_error *e) {
312         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
313         int r;
314
315         if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
316                 return 0;
317
318         r = sd_bus_message_new_method_error(call, &m, e);
319         if (r < 0)
320                 return r;
321
322         return synthetic_driver_send(call->bus, m);
323 }
324
325 static int synthetic_reply_method_errno(sd_bus_message *call, int error, const sd_bus_error *p) {
326
327         _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
328
329         if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
330                 return 0;
331
332         if (sd_bus_error_is_set(p))
333                 return synthetic_reply_method_error(call, p);
334
335         sd_bus_error_set_errno(&berror, error);
336
337         return synthetic_reply_method_error(call, &berror);
338 }
339
340 static int synthetic_reply_method_return(sd_bus_message *call, const char *types, ...) {
341         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
342         int r;
343
344         if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
345                 return 0;
346
347         r = sd_bus_message_new_method_return(call, &m);
348         if (r < 0)
349                 return r;
350
351         if (!isempty(types)) {
352                 va_list ap;
353
354                 va_start(ap, types);
355                 r = bus_message_append_ap(m, types, ap);
356                 va_end(ap);
357                 if (r < 0)
358                         return r;
359         }
360
361         return synthetic_driver_send(call->bus, m);
362 }
363
364 static int synthetic_reply_return_strv(sd_bus_message *call, char **l) {
365         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
366         int r;
367
368         r = sd_bus_message_new_method_return(call, &m);
369         if (r < 0)
370                 return synthetic_reply_method_errno(call, r, NULL);
371
372         r = sd_bus_message_append_strv(m, l);
373         if (r < 0)
374                 return synthetic_reply_method_errno(call, r, NULL);
375
376         return synthetic_driver_send(call->bus, m);
377 }
378
379 static int get_creds_by_name(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) {
380         _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
381         int r;
382
383         assert(bus);
384         assert(name);
385         assert(_creds);
386
387         assert_return(service_name_is_valid(name), -EINVAL);
388
389         r = sd_bus_get_owner(bus, name, mask, &c);
390         if (r == -ESRCH || r == -ENXIO)
391                 return sd_bus_error_setf(error, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Name %s is currently not owned by anyone.", name);
392         if (r < 0)
393                 return r;
394
395         if ((c->mask & mask) != mask)
396                 return -ENOTSUP;
397
398         *_creds = c;
399         c = NULL;
400
401         return 0;
402 }
403
404 static int get_creds_by_message(sd_bus *bus, sd_bus_message *m, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) {
405         const char *name;
406         int r;
407
408         assert(bus);
409         assert(m);
410         assert(_creds);
411
412         r = sd_bus_message_read(m, "s", &name);
413         if (r < 0)
414                 return r;
415
416         return get_creds_by_name(bus, name, mask, _creds, error);
417 }
418
419 static int peer_is_privileged(sd_bus *bus, sd_bus_message *m) {
420         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
421         uid_t uid;
422         int r;
423
424         r = get_creds_by_message(bus, m, SD_BUS_CREDS_UID, &creds, NULL);
425         if (r < 0)
426                 return r;
427
428         r = sd_bus_creds_get_uid(creds, &uid);
429         if (r < 0)
430                 return r;
431
432         r = sd_bus_creds_has_effective_cap(creds, CAP_SYS_ADMIN);
433         if (r > 0)
434                 return true;
435
436         if (uid == getuid())
437                 return true;
438
439         return false;
440 }
441
442
443 static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) {
444         int r;
445
446         assert(a);
447         assert(b);
448         assert(m);
449
450         if (!streq_ptr(sd_bus_message_get_destination(m), "org.freedesktop.DBus"))
451                 return 0;
452
453         if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
454                 if (0 && !isempty(sd_bus_message_get_signature(m, true))) {
455                         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
456
457                         r = sd_bus_error_setf(&error, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
458
459                         return synthetic_reply_method_errno(m, r, &error);
460                 }
461
462                 return synthetic_reply_method_return(m, "s",
463                         "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
464                           "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
465                         "<node>\n"
466                         " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
467                         "  <method name=\"Introspect\">\n"
468                         "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
469                         "  </method>\n"
470                         " </interface>\n"
471                         " <interface name=\"org.freedesktop.DBus\">\n"
472                         "  <method name=\"AddMatch\">\n"
473                         "   <arg type=\"s\" direction=\"in\"/>\n"
474                         "  </method>\n"
475                         "  <method name=\"RemoveMatch\">\n"
476                         "   <arg type=\"s\" direction=\"in\"/>\n"
477                         "  </method>\n"
478                         "  <method name=\"GetConnectionSELinuxSecurityContext\">\n"
479                         "   <arg type=\"s\" direction=\"in\"/>\n"
480                         "   <arg type=\"ay\" direction=\"out\"/>\n"
481                         "  </method>\n"
482                         "  <method name=\"GetConnectionUnixProcessID\">\n"
483                         "   <arg type=\"s\" direction=\"in\"/>\n"
484                         "   <arg type=\"u\" direction=\"out\"/>\n"
485                         "  </method>\n"
486                         "  <method name=\"GetConnectionUnixUser\">\n"
487                         "   <arg type=\"s\" direction=\"in\"/>\n"
488                         "   <arg type=\"u\" direction=\"out\"/>\n"
489                         "  </method>\n"
490                         "  <method name=\"GetId\">\n"
491                         "   <arg type=\"s\" direction=\"out\"/>\n"
492                         "  </method>\n"
493                         "  <method name=\"GetNameOwner\">\n"
494                         "   <arg type=\"s\" direction=\"in\"/>\n"
495                         "   <arg type=\"s\" direction=\"out\"/>\n"
496                         "  </method>\n"
497                         "  <method name=\"Hello\">\n"
498                         "   <arg type=\"s\" direction=\"out\"/>\n"
499                         "  </method>\n"
500                         "  <method name=\"ListActivatableNames\">\n"
501                         "   <arg type=\"as\" direction=\"out\"/>\n"
502                         "  </method>\n"
503                         "  <method name=\"ListNames\">\n"
504                         "   <arg type=\"as\" direction=\"out\"/>\n"
505                         "  </method>\n"
506                         "  <method name=\"ListQueuedOwners\">\n"
507                         "   <arg type=\"s\" direction=\"in\"/>\n"
508                         "   <arg type=\"as\" direction=\"out\"/>\n"
509                         "  </method>\n"
510                         "  <method name=\"NameHasOwner\">\n"
511                         "   <arg type=\"s\" direction=\"in\"/>\n"
512                         "   <arg type=\"b\" direction=\"out\"/>\n"
513                         "  </method>\n"
514                         "  <method name=\"ReleaseName\">\n"
515                         "   <arg type=\"s\" direction=\"in\"/>\n"
516                         "   <arg type=\"u\" direction=\"out\"/>\n"
517                         "  </method>\n"
518                         "  <method name=\"ReloadConfig\">\n"
519                         "  </method>\n"
520                         "  <method name=\"RequestName\">\n"
521                         "   <arg type=\"s\" direction=\"in\"/>\n"
522                         "   <arg type=\"u\" direction=\"in\"/>\n"
523                         "   <arg type=\"u\" direction=\"out\"/>\n"
524                         "  </method>\n"
525                         "  <method name=\"StartServiceByName\">\n"
526                         "   <arg type=\"s\" direction=\"in\"/>\n"
527                         "   <arg type=\"u\" direction=\"in\"/>\n"
528                         "   <arg type=\"u\" direction=\"out\"/>\n"
529                         "  </method>\n"
530                         "  <method name=\"UpdateActivationEnvironment\">\n"
531                         "   <arg type=\"a{ss}\" direction=\"in\"/>\n"
532                         "  </method>\n"
533                         "  <signal name=\"NameAcquired\">\n"
534                         "   <arg type=\"s\"/>\n"
535                         "  </signal>\n"
536                         "  <signal name=\"NameLost\">\n"
537                         "   <arg type=\"s\"/>\n"
538                         "  </signal>\n"
539                         "  <signal name=\"NameOwnerChanged\">\n"
540                         "   <arg type=\"s\"/>\n"
541                         "   <arg type=\"s\"/>\n"
542                         "   <arg type=\"s\"/>\n"
543                         "  </signal>\n"
544                         " </interface>\n"
545                         "</node>\n");
546
547         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "AddMatch")) {
548                 const char *match;
549
550                 r = sd_bus_message_read(m, "s", &match);
551                 if (r < 0)
552                         return synthetic_reply_method_errno(m, r, NULL);
553
554                 r = sd_bus_add_match(a, match, NULL, NULL);
555                 if (r < 0)
556                         return synthetic_reply_method_errno(m, r, NULL);
557
558                 return synthetic_reply_method_return(m, NULL);
559
560         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "RemoveMatch")) {
561                 const char *match;
562
563                 r = sd_bus_message_read(m, "s", &match);
564                 if (r < 0)
565                         return synthetic_reply_method_errno(m, r, NULL);
566
567                 r = sd_bus_remove_match(a, match, NULL, NULL);
568                 if (r < 0)
569                         return synthetic_reply_method_errno(m, r, NULL);
570
571                 return synthetic_reply_method_return(m, NULL);
572
573         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionSELinuxSecurityContext")) {
574                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
575
576                 r = get_creds_by_message(a, m, SD_BUS_CREDS_SELINUX_CONTEXT, &creds, NULL);
577                 if (r < 0)
578                         return synthetic_reply_method_errno(m, r, NULL);
579
580                 return synthetic_reply_method_return(m, "y", creds->label, strlen(creds->label));
581
582         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixProcessID")) {
583                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
584
585                 r = get_creds_by_message(a, m, SD_BUS_CREDS_PID, &creds, NULL);
586                 if (r < 0)
587                         return synthetic_reply_method_errno(m, r, NULL);
588
589                 return synthetic_reply_method_return(m, "u", (uint32_t) creds->pid);
590
591         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixUser")) {
592                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
593
594                 r = get_creds_by_message(a, m, SD_BUS_CREDS_UID, &creds, NULL);
595                 if (r < 0)
596                         return synthetic_reply_method_errno(m, r, NULL);
597
598                 return synthetic_reply_method_return(m, "u", (uint32_t) creds->uid);
599
600         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetId")) {
601                 sd_id128_t server_id;
602                 char buf[SD_ID128_STRING_MAX];
603
604                 r = sd_bus_get_server_id(a, &server_id);
605                 if (r < 0)
606                         return synthetic_reply_method_errno(m, r, NULL);
607
608                 return synthetic_reply_method_return(m, "s", sd_id128_to_string(server_id, buf));
609
610         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetNameOwner")) {
611                 const char *name;
612                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
613                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
614
615                 r = sd_bus_message_read(m, "s", &name);
616                 if (r < 0)
617                         return synthetic_reply_method_errno(m, r, NULL);
618
619                 if (streq(name, "org.freedesktop.DBus"))
620                         return synthetic_reply_method_return(m, "s", "org.freedesktop.DBus");
621
622                 r = get_creds_by_name(a, name, SD_BUS_CREDS_UNIQUE_NAME, &creds, &error);
623                 if (r < 0)
624                         return synthetic_reply_method_errno(m, r, &error);
625
626                 return synthetic_reply_method_return(m, "s", creds->unique_name);
627
628         /* "Hello" is handled in process_hello() */
629
630         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListActivatableNames")) {
631                 _cleanup_strv_free_ char **names = NULL;
632
633                 r = sd_bus_list_names(a, NULL, &names);
634                 if (r < 0)
635                         return synthetic_reply_method_errno(m, r, NULL);
636
637                 /* Let's sort the names list to make it stable */
638                 strv_sort(names);
639
640                 return synthetic_reply_return_strv(m, names);
641
642         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListNames")) {
643                 _cleanup_strv_free_ char **names = NULL;
644
645                 r = sd_bus_list_names(a, &names, NULL);
646                 if (r < 0)
647                         return synthetic_reply_method_errno(m, r, NULL);
648
649                 r = strv_extend(&names, "org.freedesktop.DBus");
650                 if (r < 0)
651                         return synthetic_reply_method_errno(m, r, NULL);
652
653                 /* Let's sort the names list to make it stable */
654                 strv_sort(names);
655
656                 return synthetic_reply_return_strv(m, names);
657
658         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListQueuedOwners")) {
659                 struct kdbus_cmd_name_list cmd = {};
660                 struct kdbus_name_list *name_list;
661                 struct kdbus_cmd_name *name;
662                 _cleanup_strv_free_ char **owners = NULL;
663                 char *arg0;
664                 int err = 0;
665
666                 r = sd_bus_message_read(m, "s", &arg0);
667                 if (r < 0)
668                         return synthetic_reply_method_errno(m, r, NULL);
669
670                 if (service_name_is_valid(arg0) < 0)
671                         return synthetic_reply_method_errno(m, -EINVAL, NULL);
672
673                 cmd.flags = KDBUS_NAME_LIST_QUEUED;
674                 r = ioctl(a->input_fd, KDBUS_CMD_NAME_LIST, &cmd);
675                 if (r < 0)
676                         return synthetic_reply_method_errno(m, -errno, NULL);
677
678                 name_list = (struct kdbus_name_list *) ((uint8_t *) a->kdbus_buffer + cmd.offset);
679
680                 KDBUS_ITEM_FOREACH(name, name_list, names) {
681                         char *n;
682
683                         if (name->size <= sizeof(*name))
684                                 continue;
685
686                         if (!streq(name->name, arg0))
687                                 continue;
688
689                         if (asprintf(&n, ":1.%llu", (unsigned long long) name->owner_id) < 0) {
690                                 err  = -ENOMEM;
691                                 break;
692                         }
693
694                         r = strv_consume(&owners, n);
695                         if (r < 0) {
696                                 err = r;
697                                 break;
698                         }
699                 }
700
701                 r = ioctl(a->input_fd, KDBUS_CMD_FREE, &cmd.offset);
702                 if (r < 0)
703                         return synthetic_reply_method_errno(m, r, NULL);
704
705                 if (err > 0)
706                         return synthetic_reply_method_errno(m, err, NULL);
707
708                 return synthetic_reply_return_strv(m, owners);
709
710         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "NameHasOwner")) {
711                 const char *name;
712
713                 r = sd_bus_message_read(m, "s", &name);
714                 if (r < 0)
715                         return synthetic_reply_method_errno(m, r, NULL);
716
717                 if (service_name_is_valid(name) < 0)
718                         return synthetic_reply_method_errno(m, -EINVAL, NULL);
719
720                 if (streq(name, "org.freedesktop.DBus"))
721                         return synthetic_reply_method_return(m, "b", true);
722
723                 r = sd_bus_get_owner(a, name, 0, NULL);
724                 if (r < 0 && r != -ESRCH && r != -ENXIO)
725                         return synthetic_reply_method_errno(m, r, NULL);
726
727                 return synthetic_reply_method_return(m, "b", r >= 0);
728
729         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ReleaseName")) {
730                 const char *name;
731
732                 r = sd_bus_message_read(m, "s", &name);
733                 if (r < 0)
734                         return synthetic_reply_method_errno(m, r, NULL);
735
736                 if (service_name_is_valid(name) < 0)
737                         return synthetic_reply_method_errno(m, -EINVAL, NULL);
738
739                 r = sd_bus_release_name(a, name);
740                 if (r < 0) {
741                         if (r == -ESRCH)
742                                 synthetic_reply_method_return(m, "u", BUS_NAME_NON_EXISTENT);
743                         if (r == -EADDRINUSE)
744                                 synthetic_reply_method_return(m, "u", BUS_NAME_NOT_OWNER);
745                         return synthetic_reply_method_errno(m, r, NULL);
746                 }
747
748                 return synthetic_reply_method_return(m, "u", BUS_NAME_RELEASED);
749
750         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ReloadConfig")) {
751                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
752
753                 r = sd_bus_error_setf(&error, SD_BUS_ERROR_NOT_SUPPORTED, "%s() is not supported", sd_bus_message_get_member(m));
754
755                 return synthetic_reply_method_errno(m, r, &error);
756
757         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "RequestName")) {
758                 const char *name;
759                 uint32_t flags;
760
761                 r = sd_bus_message_read(m, "su", &name, &flags);
762                 if (r < 0)
763                         return synthetic_reply_method_errno(m, r, NULL);
764
765                 if (service_name_is_valid(name) < 0)
766                         return synthetic_reply_method_errno(m, -EINVAL, NULL);
767                 if ((flags & ~(BUS_NAME_ALLOW_REPLACEMENT|BUS_NAME_REPLACE_EXISTING|BUS_NAME_DO_NOT_QUEUE)) != 0)
768                         return synthetic_reply_method_errno(m, -EINVAL, NULL);
769
770                 r = sd_bus_request_name(a, name, flags);
771                 if (r < 0) {
772                         if (r == -EEXIST)
773                                 return synthetic_reply_method_return(m, "u", BUS_NAME_EXISTS);
774                         if (r == -EALREADY)
775                                 return synthetic_reply_method_return(m, "u", BUS_NAME_ALREADY_OWNER);
776                         return synthetic_reply_method_errno(m, r, NULL);
777                 }
778
779                 if (r == 0)
780                         return synthetic_reply_method_return(m, "u", BUS_NAME_IN_QUEUE);
781
782                 return synthetic_reply_method_return(m, "u", BUS_NAME_PRIMARY_OWNER);
783
784         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "StartServiceByName")) {
785                 _cleanup_bus_message_unref_ sd_bus_message *msg = NULL;
786                 const char *name;
787                 uint32_t flags;
788
789                 r = sd_bus_message_read(m, "su", &name, &flags);
790                 if (r < 0)
791                         return synthetic_reply_method_errno(m, r, NULL);
792
793                 if (service_name_is_valid(name) < 0)
794                         return synthetic_reply_method_errno(m, -EINVAL, NULL);
795                 if (flags != 0)
796                         return synthetic_reply_method_errno(m, -EINVAL, NULL);
797
798                 r = sd_bus_get_owner(a, name, 0, NULL);
799                 if (r >= 0 || streq(name, "org.freedesktop.DBus"))
800                         return synthetic_reply_method_return(m, "u", BUS_START_REPLY_ALREADY_RUNNING);
801                 if (r != -ESRCH)
802                         return synthetic_reply_method_errno(m, r, NULL);
803
804                 r = sd_bus_message_new_method_call(
805                                 a,
806                                 &msg,
807                                 name,
808                                 "/",
809                                 "org.freedesktop.DBus.Peer",
810                                 "Ping");
811                 if (r < 0)
812                         return synthetic_reply_method_errno(m, r, NULL);
813
814                 r = sd_bus_send(a, msg, NULL);
815                 if (r < 0)
816                         return synthetic_reply_method_errno(m, r, NULL);
817
818                 return synthetic_reply_method_return(m, "u", BUS_START_REPLY_SUCCESS);
819
820         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "UpdateActivationEnvironment")) {
821                 _cleanup_bus_message_unref_ sd_bus_message *msg = NULL;
822                 _cleanup_strv_free_ char **args = NULL;
823
824                 if (!peer_is_privileged(a, m))
825                         return synthetic_reply_method_errno(m, -EPERM, NULL);
826
827                 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{ss}");
828                 if (r < 0)
829                         return synthetic_reply_method_errno(m, r, NULL);
830
831                 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "ss")) > 0) {
832                         _cleanup_free_ char *s = NULL;
833                         const char *key;
834                         const char *value;
835
836                         r = sd_bus_message_read(m, "ss", &key, &value);
837                         if (r < 0)
838                                 return synthetic_reply_method_errno(m, r, NULL);
839
840                         s = strjoin(key, "=", value, NULL);
841                         if (!s)
842                                 return synthetic_reply_method_errno(m, -ENOMEM, NULL);
843
844                         r  = strv_extend(&args, s);
845                         if (r < 0)
846                                 return synthetic_reply_method_errno(m, r, NULL);
847
848                         r = sd_bus_message_exit_container(m);
849                         if (r < 0)
850                                 return synthetic_reply_method_errno(m, r, NULL);
851                 }
852
853                 r = sd_bus_message_exit_container(m);
854                 if (r < 0)
855                         return synthetic_reply_method_errno(m, r, NULL);
856
857                 if (!args)
858                         return synthetic_reply_method_errno(m, -EINVAL, NULL);
859
860                 r = sd_bus_message_new_method_call(
861                                 a,
862                                 &msg,
863                                 "org.freedesktop.systemd1",
864                                 "/org/freedesktop/systemd1",
865                                 "org.freedesktop.systemd1.Manager",
866                                 "SetEnvironment");
867                 if (r < 0)
868                         return synthetic_reply_method_errno(m, r, NULL);
869
870                 r = sd_bus_message_append_strv(msg, args);
871                 if (r < 0)
872                         return synthetic_reply_method_errno(m, r, NULL);
873
874                 r = sd_bus_call(a, msg, 0, NULL, NULL);
875                 if (r < 0)
876                         return synthetic_reply_method_errno(m, r, NULL);
877
878                return synthetic_reply_method_return(m, NULL);
879
880         } else {
881                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
882
883                 r = sd_bus_error_setf(&error, SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method '%s'.", m->member);
884
885                 return synthetic_reply_method_errno(m, r, &error);
886         }
887 }
888
889 static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hello) {
890         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
891         bool is_hello;
892         int r;
893
894         assert(a);
895         assert(b);
896         assert(m);
897         assert(got_hello);
898
899         /* As reaction to hello we need to respond with two messages:
900          * the callback reply and the NameAcquired for the unique
901          * name, since hello is otherwise obsolete on kdbus. */
902
903         is_hello =
904                 sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "Hello") &&
905                 streq_ptr(m->destination, "org.freedesktop.DBus");
906
907         if (!is_hello) {
908
909                 if (*got_hello)
910                         return 0;
911
912                 log_error("First packet isn't hello (it's %s.%s), aborting.", m->interface, m->member);
913                 return -EIO;
914         }
915
916         if (*got_hello) {
917                 log_error("Got duplicate hello, aborting.");
918                 return -EIO;
919         }
920
921         *got_hello = true;
922
923         if (!a->is_kernel)
924                 return 0;
925
926         r = sd_bus_message_new_method_return(m, &n);
927         if (r < 0) {
928                 log_error("Failed to generate HELLO reply: %s", strerror(-r));
929                 return r;
930         }
931
932         r = sd_bus_message_append(n, "s", a->unique_name);
933         if (r < 0) {
934                 log_error("Failed to append unique name to HELLO reply: %s", strerror(-r));
935                 return r;
936         }
937
938         r = bus_message_append_sender(n, "org.freedesktop.DBus");
939         if (r < 0) {
940                 log_error("Failed to append sender to HELLO reply: %s", strerror(-r));
941                 return r;
942         }
943
944         r = bus_seal_synthetic_message(b, n);
945         if (r < 0) {
946                 log_error("Failed to seal HELLO reply: %s", strerror(-r));
947                 return r;
948         }
949
950         r = sd_bus_send(b, n, NULL);
951         if (r < 0) {
952                 log_error("Failed to send HELLO reply: %s", strerror(-r));
953                 return r;
954         }
955
956         n = sd_bus_message_unref(n);
957         r = sd_bus_message_new_signal(
958                         b,
959                         &n,
960                         "/org/freedesktop/DBus",
961                         "org.freedesktop.DBus",
962                         "NameAcquired");
963         if (r < 0) {
964                 log_error("Failed to allocate initial NameAcquired message: %s", strerror(-r));
965                 return r;
966         }
967
968         r = sd_bus_message_append(n, "s", a->unique_name);
969         if (r < 0) {
970                 log_error("Failed to append unique name to NameAcquired message: %s", strerror(-r));
971                 return r;
972         }
973
974         r = bus_message_append_sender(n, "org.freedesktop.DBus");
975         if (r < 0) {
976                 log_error("Failed to append sender to NameAcquired message: %s", strerror(-r));
977                 return r;
978         }
979
980         r = bus_seal_synthetic_message(b, n);
981         if (r < 0) {
982                 log_error("Failed to seal NameAcquired message: %s", strerror(-r));
983                 return r;
984         }
985
986         r = sd_bus_send(b, n, NULL);
987         if (r < 0) {
988                 log_error("Failed to send NameAcquired message: %s", strerror(-r));
989                 return r;
990         }
991
992         return 1;
993 }
994
995 static int patch_sender(sd_bus *a, sd_bus_message *m) {
996         char **well_known = NULL;
997         sd_bus_creds *c;
998         int r;
999
1000         assert(a);
1001         assert(m);
1002
1003         if (!a->is_kernel)
1004                 return 0;
1005
1006         /* We will change the sender of messages from the bus driver
1007          * so that they originate from the bus driver. This is a
1008          * speciality originating from dbus1, where the bus driver did
1009          * not have a unique id, but only the well-known name. */
1010
1011         c = sd_bus_message_get_creds(m);
1012         if (!c)
1013                 return 0;
1014
1015         r = sd_bus_creds_get_well_known_names(c, &well_known);
1016         if (r < 0)
1017                 return r;
1018
1019         if (strv_contains(well_known, "org.freedesktop.DBus"))
1020                 m->sender = "org.freedesktop.DBus";
1021
1022         return 0;
1023 }
1024
1025 int main(int argc, char *argv[]) {
1026
1027         _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL;
1028         sd_id128_t server_id;
1029         int r, in_fd, out_fd;
1030         bool got_hello = false;
1031         bool is_unix;
1032         struct ucred ucred = {};
1033         _cleanup_free_ char *peersec = NULL;
1034
1035         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
1036         log_parse_environment();
1037         log_open();
1038
1039         r = parse_argv(argc, argv);
1040         if (r <= 0)
1041                 goto finish;
1042
1043         r = sd_listen_fds(0);
1044         if (r == 0) {
1045                 in_fd = STDIN_FILENO;
1046                 out_fd = STDOUT_FILENO;
1047         } else if (r == 1) {
1048                 in_fd = SD_LISTEN_FDS_START;
1049                 out_fd = SD_LISTEN_FDS_START;
1050         } else {
1051                 log_error("Illegal number of file descriptors passed");
1052                 goto finish;
1053         }
1054
1055         is_unix =
1056                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
1057                 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
1058
1059         if (is_unix) {
1060                 getpeercred(in_fd, &ucred);
1061                 getpeersec(in_fd, &peersec);
1062         }
1063
1064         r = sd_bus_new(&a);
1065         if (r < 0) {
1066                 log_error("Failed to allocate bus: %s", strerror(-r));
1067                 goto finish;
1068         }
1069
1070         r = sd_bus_set_name(a, "sd-proxy");
1071         if (r < 0) {
1072                 log_error("Failed to set bus name: %s", strerror(-r));
1073                 goto finish;
1074         }
1075
1076         r = sd_bus_set_address(a, arg_address);
1077         if (r < 0) {
1078                 log_error("Failed to set address to connect to: %s", strerror(-r));
1079                 goto finish;
1080         }
1081
1082         r = sd_bus_negotiate_fds(a, is_unix);
1083         if (r < 0) {
1084                 log_error("Failed to set FD negotiation: %s", strerror(-r));
1085                 goto finish;
1086         }
1087
1088         if (ucred.pid > 0) {
1089                 a->fake_creds.pid = ucred.pid;
1090                 a->fake_creds.uid = ucred.uid;
1091                 a->fake_creds.gid = ucred.gid;
1092                 a->fake_creds_valid = true;
1093         }
1094
1095         if (peersec) {
1096                 a->fake_label = peersec;
1097                 peersec = NULL;
1098         }
1099
1100         a->manual_peer_interface = true;
1101
1102         r = sd_bus_start(a);
1103         if (r < 0) {
1104                 log_error("Failed to start bus client: %s", strerror(-r));
1105                 goto finish;
1106         }
1107
1108         r = sd_bus_get_server_id(a, &server_id);
1109         if (r < 0) {
1110                 log_error("Failed to get server ID: %s", strerror(-r));
1111                 goto finish;
1112         }
1113
1114         r = sd_bus_new(&b);
1115         if (r < 0) {
1116                 log_error("Failed to allocate bus: %s", strerror(-r));
1117                 goto finish;
1118         }
1119
1120         r = sd_bus_set_fd(b, in_fd, out_fd);
1121         if (r < 0) {
1122                 log_error("Failed to set fds: %s", strerror(-r));
1123                 goto finish;
1124         }
1125
1126         r = sd_bus_set_server(b, 1, server_id);
1127         if (r < 0) {
1128                 log_error("Failed to set server mode: %s", strerror(-r));
1129                 goto finish;
1130         }
1131
1132         r = sd_bus_negotiate_fds(b, is_unix);
1133         if (r < 0) {
1134                 log_error("Failed to set FD negotiation: %s", strerror(-r));
1135                 goto finish;
1136         }
1137
1138         r = sd_bus_set_anonymous(b, true);
1139         if (r < 0) {
1140                 log_error("Failed to set anonymous authentication: %s", strerror(-r));
1141                 goto finish;
1142         }
1143
1144         b->manual_peer_interface = true;
1145
1146         r = sd_bus_start(b);
1147         if (r < 0) {
1148                 log_error("Failed to start bus client: %s", strerror(-r));
1149                 goto finish;
1150         }
1151
1152         r = rename_service(a, b);
1153         if (r < 0)
1154                 log_debug("Failed to rename process: %s", strerror(-r));
1155
1156         if (a->is_kernel) {
1157                 _cleanup_free_ char *match = NULL;
1158                 const char *unique;
1159
1160                 r = sd_bus_get_unique_name(a, &unique);
1161                 if (r < 0) {
1162                         log_error("Failed to get unique name: %s", strerror(-r));
1163                         goto finish;
1164                 }
1165
1166                 match = strjoin("type='signal',"
1167                                 "sender='org.freedesktop.DBus',"
1168                                 "path='/org/freedesktop/DBus',"
1169                                 "interface='org.freedesktop.DBus',"
1170                                 "member='NameOwnerChanged',"
1171                                 "arg1='",
1172                                 unique,
1173                                 "'",
1174                                 NULL);
1175                 if (!match) {
1176                         log_oom();
1177                         goto finish;
1178                 }
1179
1180                 r = sd_bus_add_match(a, match, NULL, NULL);
1181                 if (r < 0) {
1182                         log_error("Failed to add match for NameLost: %s", strerror(-r));
1183                         goto finish;
1184                 }
1185
1186                 free(match);
1187                 match = strjoin("type='signal',"
1188                                 "sender='org.freedesktop.DBus',"
1189                                 "path='/org/freedesktop/DBus',"
1190                                 "interface='org.freedesktop.DBus',"
1191                                 "member='NameOwnerChanged',"
1192                                 "arg2='",
1193                                 unique,
1194                                 "'",
1195                                 NULL);
1196                 if (!match) {
1197                         log_oom();
1198                         goto finish;
1199                 }
1200
1201                 r = sd_bus_add_match(a, match, NULL, NULL);
1202                 if (r < 0) {
1203                         log_error("Failed to add match for NameAcquired: %s", strerror(-r));
1204                         goto finish;
1205                 }
1206         }
1207
1208         for (;;) {
1209                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1210                 int events_a, events_b, fd;
1211                 uint64_t timeout_a, timeout_b, t;
1212                 struct timespec _ts, *ts;
1213                 struct pollfd *pollfd;
1214                 int k;
1215
1216                 if (got_hello) {
1217                         r = sd_bus_process(a, &m);
1218                         if (r < 0) {
1219                                 /* treat 'connection reset by peer' as clean exit condition */
1220                                 if (r == -ECONNRESET)
1221                                         r = 0;
1222                                 else
1223                                         log_error("Failed to process bus a: %s", strerror(-r));
1224
1225                                 goto finish;
1226                         }
1227
1228                         if (m) {
1229                                 /* We officially got EOF, let's quit */
1230                                 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
1231                                         r = 0;
1232                                         goto finish;
1233                                 }
1234
1235                                 k = synthesize_name_acquired(a, b, m);
1236                                 if (k < 0) {
1237                                         r = k;
1238                                         log_error("Failed to synthesize message: %s", strerror(-r));
1239                                         goto finish;
1240                                 }
1241
1242                                 patch_sender(a, m);
1243
1244                                 k = sd_bus_send(b, m, NULL);
1245                                 if (k < 0) {
1246                                         if (k == -ECONNRESET)
1247                                                 r = 0;
1248                                         else {
1249                                                 r = k;
1250                                                 log_error("Failed to send message: %s", strerror(-r));
1251                                         }
1252
1253                                         goto finish;
1254                                 }
1255                         }
1256
1257                         if (r > 0)
1258                                 continue;
1259                 }
1260
1261                 r = sd_bus_process(b, &m);
1262                 if (r < 0) {
1263                         /* treat 'connection reset by peer' as clean exit condition */
1264                         if (r == -ECONNRESET)
1265                                 r = 0;
1266                         else
1267                                 log_error("Failed to process bus b: %s", strerror(-r));
1268
1269                         goto finish;
1270                 }
1271
1272                 if (m) {
1273                         /* We officially got EOF, let's quit */
1274                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
1275                                 r = 0;
1276                                 goto finish;
1277                         }
1278
1279                         k = process_hello(a, b, m, &got_hello);
1280                         if (k < 0) {
1281                                 r = k;
1282                                 log_error("Failed to process HELLO: %s", strerror(-r));
1283                                 goto finish;
1284                         }
1285
1286                         if (k > 0)
1287                                 r = k;
1288                         else {
1289                                 k = process_policy(a, b, m);
1290                                 if (k < 0) {
1291                                         r = k;
1292                                         log_error("Failed to process policy: %s", strerror(-r));
1293                                         goto finish;
1294                                 }
1295
1296                                 k = process_driver(a, b, m);
1297                                 if (k < 0) {
1298                                         r = k;
1299                                         log_error("Failed to process driver calls: %s", strerror(-r));
1300                                         goto finish;
1301                                 }
1302
1303                                 if (k > 0)
1304                                         r = k;
1305                                 else {
1306                                         k = sd_bus_send(a, m, NULL);
1307                                         if (k < 0) {
1308                                                 if (r == -ECONNRESET)
1309                                                         r = 0;
1310                                                 else {
1311                                                         r = k;
1312                                                         log_error("Failed to send message: %s", strerror(-r));
1313                                                 }
1314
1315                                                 goto finish;
1316                                         }
1317                                 }
1318                         }
1319                 }
1320
1321                 if (r > 0)
1322                         continue;
1323
1324                 fd = sd_bus_get_fd(a);
1325                 if (fd < 0) {
1326                         log_error("Failed to get fd: %s", strerror(-r));
1327                         goto finish;
1328                 }
1329
1330                 events_a = sd_bus_get_events(a);
1331                 if (events_a < 0) {
1332                         log_error("Failed to get events mask: %s", strerror(-r));
1333                         goto finish;
1334                 }
1335
1336                 r = sd_bus_get_timeout(a, &timeout_a);
1337                 if (r < 0) {
1338                         log_error("Failed to get timeout: %s", strerror(-r));
1339                         goto finish;
1340                 }
1341
1342                 events_b = sd_bus_get_events(b);
1343                 if (events_b < 0) {
1344                         log_error("Failed to get events mask: %s", strerror(-r));
1345                         goto finish;
1346                 }
1347
1348                 r = sd_bus_get_timeout(b, &timeout_b);
1349                 if (r < 0) {
1350                         log_error("Failed to get timeout: %s", strerror(-r));
1351                         goto finish;
1352                 }
1353
1354                 t = timeout_a;
1355                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
1356                         t = timeout_b;
1357
1358                 if (t == (uint64_t) -1)
1359                         ts = NULL;
1360                 else {
1361                         usec_t nw;
1362
1363                         nw = now(CLOCK_MONOTONIC);
1364                         if (t > nw)
1365                                 t -= nw;
1366                         else
1367                                 t = 0;
1368
1369                         ts = timespec_store(&_ts, t);
1370                 }
1371
1372                 pollfd = (struct pollfd[3]) {
1373                         {.fd = fd,     .events = events_a,           },
1374                         {.fd = in_fd,  .events = events_b & POLLIN,  },
1375                         {.fd = out_fd, .events = events_b & POLLOUT, }
1376                 };
1377
1378                 r = ppoll(pollfd, 3, ts, NULL);
1379                 if (r < 0) {
1380                         log_error("ppoll() failed: %m");
1381                         goto finish;
1382                 }
1383         }
1384
1385 finish:
1386         sd_bus_flush(a);
1387         sd_bus_flush(b);
1388
1389         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1390 }