chiark / gitweb /
bus: process AddMatch/RemoveMatch driver call in proxy
[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 #include "def.h"
44
45 static const char *arg_address = DEFAULT_SYSTEM_BUS_PATH;
46 static char *arg_command_line_buffer = NULL;
47
48 static int help(void) {
49
50         printf("%s [OPTIONS...]\n\n"
51                "Connect STDIO or a socket to a given bus address.\n\n"
52                "  -h --help              Show this help\n"
53                "     --version           Show package version\n"
54                "     --address=ADDRESS   Connect to the bus specified by ADDRESS\n"
55                "                         (default: " DEFAULT_SYSTEM_BUS_PATH ")\n",
56                program_invocation_short_name);
57
58         return 0;
59 }
60
61 static int parse_argv(int argc, char *argv[]) {
62
63         enum {
64                 ARG_VERSION = 0x100,
65                 ARG_ADDRESS,
66         };
67
68         static const struct option options[] = {
69                 { "help",       no_argument,       NULL, 'h'            },
70                 { "version",    no_argument,       NULL, ARG_VERSION    },
71                 { "address",    required_argument, NULL, ARG_ADDRESS    },
72                 { NULL,         0,                 NULL, 0              }
73         };
74
75         int c;
76
77         assert(argc >= 0);
78         assert(argv);
79
80         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
81
82                 switch (c) {
83
84                 case 'h':
85                         help();
86                         return 0;
87
88                 case ARG_VERSION:
89                         puts(PACKAGE_STRING);
90                         puts(SYSTEMD_FEATURES);
91                         return 0;
92
93                 case ARG_ADDRESS:
94                         arg_address = optarg;
95                         break;
96
97                 case '?':
98                         return -EINVAL;
99
100                 default:
101                         assert_not_reached("Unhandled option");
102                 }
103         }
104
105         /* If the first command line argument is only "x" characters
106          * we'll write who we are talking to into it, so that "ps" is
107          * explanatory */
108         arg_command_line_buffer = argv[optind];
109         if (argc > optind + 1 ||
110             (arg_command_line_buffer && arg_command_line_buffer[strspn(arg_command_line_buffer, "x")] != 0)) {
111                 log_error("Too many arguments");
112                 return -EINVAL;
113         }
114
115         return 1;
116 }
117
118 static int rename_service(sd_bus *a, sd_bus *b) {
119         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
120         _cleanup_free_ char *p = NULL, *name = NULL;
121         const char *comm;
122         char **cmdline;
123         uid_t uid;
124         pid_t pid;
125         int r;
126
127         assert(a);
128         assert(b);
129
130         r = sd_bus_get_peer_creds(b, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_COMM, &creds);
131         if (r < 0)
132                 return r;
133
134         r = sd_bus_creds_get_uid(creds, &uid);
135         if (r < 0)
136                 return r;
137
138         r = sd_bus_creds_get_pid(creds, &pid);
139         if (r < 0)
140                 return r;
141
142         r = sd_bus_creds_get_cmdline(creds, &cmdline);
143         if (r < 0)
144                 return r;
145
146         r = sd_bus_creds_get_comm(creds, &comm);
147         if (r < 0)
148                 return r;
149
150         name = uid_to_name(uid);
151         if (!name)
152                 return -ENOMEM;
153
154         p = strv_join(cmdline, " ");
155         if (!p)
156                 return -ENOMEM;
157
158         /* The status string gets the full command line ... */
159         sd_notifyf(false,
160                    "STATUS=Processing requests from client PID "PID_FMT" (%s); UID "UID_FMT" (%s)",
161                    pid, p,
162                    uid, name);
163
164         /* ... and the argv line only the short comm */
165         if (arg_command_line_buffer) {
166                 size_t m, w;
167
168                 m = strlen(arg_command_line_buffer);
169                 w = snprintf(arg_command_line_buffer, m,
170                              "[PID "PID_FMT"/%s; UID "UID_FMT"/%s]",
171                              pid, comm,
172                              uid, name);
173
174                 if (m > w)
175                         memzero(arg_command_line_buffer + w, m - w);
176         }
177
178         log_debug("Running on behalf of PID "PID_FMT" (%s), UID "UID_FMT" (%s), %s",
179                   pid, p,
180                   uid, name,
181                   a->unique_name);
182                 ;
183         return 0;
184 }
185
186 static int synthesize_name_acquired(sd_bus *a, sd_bus *b, sd_bus_message *m) {
187         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
188         const char *name, *old_owner, *new_owner;
189         int r;
190
191         assert(a);
192         assert(b);
193         assert(m);
194
195         /* If we get NameOwnerChanged for our own name, we need to
196          * synthesize NameLost/NameAcquired, since socket clients need
197          * that, even though it is obsoleted on kdbus */
198
199         if (!a->is_kernel)
200                 return 0;
201
202         if (!sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged") ||
203             !streq_ptr(m->path, "/org/freedesktop/DBus") ||
204             !streq_ptr(m->sender, "org.freedesktop.DBus"))
205                 return 0;
206
207         r = sd_bus_message_read(m, "sss", &name, &old_owner, &new_owner);
208         if (r < 0)
209                 return r;
210
211         r = sd_bus_message_rewind(m, true);
212         if (r < 0)
213                 return r;
214
215         if (streq(old_owner, a->unique_name)) {
216
217                 r = sd_bus_message_new_signal(
218                                 b,
219                                 &n,
220                                 "/org/freedesktop/DBus",
221                                 "org.freedesktop.DBus",
222                                 "NameLost");
223
224         } else if (streq(new_owner, a->unique_name)) {
225
226                 r = sd_bus_message_new_signal(
227                                 b,
228                                 &n,
229                                 "/org/freedesktop/DBus",
230                                 "org.freedesktop.DBus",
231                                 "NameAcquired");
232         } else
233                 return 0;
234
235         if (r < 0)
236                 return r;
237
238         r = sd_bus_message_append(n, "s", name);
239         if (r < 0)
240                 return r;
241
242         r = bus_message_append_sender(n, "org.freedesktop.DBus");
243         if (r < 0)
244                 return r;
245
246         r = bus_seal_synthetic_message(b, n);
247         if (r < 0)
248                 return r;
249
250         return sd_bus_send(b, n, NULL);
251 }
252
253 static int process_policy(sd_bus *a, sd_bus *b, sd_bus_message *m) {
254         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
255         int r;
256
257         assert(a);
258         assert(b);
259         assert(m);
260
261         if (!sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "GetAll"))
262                 return 0;
263
264         if (!streq_ptr(m->path, "/org/gnome/DisplayManager/Slave"))
265                 return 0;
266
267         r = sd_bus_message_new_method_errorf(m, &n, SD_BUS_ERROR_ACCESS_DENIED, "gdm, you are stupid");
268         if (r < 0)
269                 return r;
270
271         r = bus_message_append_sender(n, "org.freedesktop.DBus");
272         if (r < 0) {
273                 log_error("Failed to append sender to gdm reply: %s", strerror(-r));
274                 return r;
275         }
276
277         r = bus_seal_synthetic_message(b, n);
278         if (r < 0) {
279                 log_error("Failed to seal gdm reply: %s", strerror(-r));
280                 return r;
281         }
282
283         r = sd_bus_send(b, n, NULL);
284         if (r < 0) {
285                 log_error("Failed to send gdm reply: %s", strerror(-r));
286                 return r;
287         }
288
289         return 1;
290 }
291
292 static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) {
293         int r;
294
295         assert(a);
296         assert(b);
297         assert(m);
298
299         if (!streq_ptr(sd_bus_message_get_destination(m), "org.freedesktop.DBus"))
300                 return 0;
301
302         if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "AddMatch")) {
303                 const char *match;
304
305                 r = sd_bus_message_read(m, "s", &match);
306                 if (r < 0)
307                         return r;
308
309                 r = sd_bus_add_match(a, match, NULL, NULL);
310                 if (r < 0)
311                         return r;
312
313                 return sd_bus_reply_method_return(m, NULL);
314
315         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "RemoveMatch")) {
316                 const char *match;
317
318                 r = sd_bus_message_read(m, "s", &match);
319                 if (r < 0)
320                         return r;
321
322                 r = sd_bus_remove_match(a, match, NULL, NULL);
323                 if (r < 0)
324                         return r;
325
326                 return sd_bus_reply_method_return(m, NULL);
327         } else
328                 return 0;
329
330         return r;
331 }
332
333 static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hello) {
334         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
335         bool is_hello;
336         int r;
337
338         assert(a);
339         assert(b);
340         assert(m);
341         assert(got_hello);
342
343         /* As reaction to hello we need to respond with two messages:
344          * the callback reply and the NameAcquired for the unique
345          * name, since hello is otherwise obsolete on kdbus. */
346
347         is_hello =
348                 sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "Hello") &&
349                 streq_ptr(m->destination, "org.freedesktop.DBus");
350
351         if (!is_hello) {
352
353                 if (*got_hello)
354                         return 0;
355
356                 log_error("First packet isn't hello (it's %s.%s), aborting.", m->interface, m->member);
357                 return -EIO;
358         }
359
360         if (*got_hello) {
361                 log_error("Got duplicate hello, aborting.");
362                 return -EIO;
363         }
364
365         *got_hello = true;
366
367         if (!a->is_kernel)
368                 return 0;
369
370         r = sd_bus_message_new_method_return(m, &n);
371         if (r < 0) {
372                 log_error("Failed to generate HELLO reply: %s", strerror(-r));
373                 return r;
374         }
375
376         r = sd_bus_message_append(n, "s", a->unique_name);
377         if (r < 0) {
378                 log_error("Failed to append unique name to HELLO reply: %s", strerror(-r));
379                 return r;
380         }
381
382         r = bus_message_append_sender(n, "org.freedesktop.DBus");
383         if (r < 0) {
384                 log_error("Failed to append sender to HELLO reply: %s", strerror(-r));
385                 return r;
386         }
387
388         r = bus_seal_synthetic_message(b, n);
389         if (r < 0) {
390                 log_error("Failed to seal HELLO reply: %s", strerror(-r));
391                 return r;
392         }
393
394         r = sd_bus_send(b, n, NULL);
395         if (r < 0) {
396                 log_error("Failed to send HELLO reply: %s", strerror(-r));
397                 return r;
398         }
399
400         n = sd_bus_message_unref(n);
401         r = sd_bus_message_new_signal(
402                         b,
403                         &n,
404                         "/org/freedesktop/DBus",
405                         "org.freedesktop.DBus",
406                         "NameAcquired");
407         if (r < 0) {
408                 log_error("Failed to allocate initial NameAcquired message: %s", strerror(-r));
409                 return r;
410         }
411
412         r = sd_bus_message_append(n, "s", a->unique_name);
413         if (r < 0) {
414                 log_error("Failed to append unique name to NameAcquired message: %s", strerror(-r));
415                 return r;
416         }
417
418         r = bus_message_append_sender(n, "org.freedesktop.DBus");
419         if (r < 0) {
420                 log_error("Failed to append sender to NameAcquired message: %s", strerror(-r));
421                 return r;
422         }
423
424         r = bus_seal_synthetic_message(b, n);
425         if (r < 0) {
426                 log_error("Failed to seal NameAcquired message: %s", strerror(-r));
427                 return r;
428         }
429
430         r = sd_bus_send(b, n, NULL);
431         if (r < 0) {
432                 log_error("Failed to send NameAcquired message: %s", strerror(-r));
433                 return r;
434         }
435
436         return 1;
437 }
438
439 static int patch_sender(sd_bus *a, sd_bus_message *m) {
440         char **well_known = NULL;
441         sd_bus_creds *c;
442         int r;
443
444         assert(a);
445         assert(m);
446
447         if (!a->is_kernel)
448                 return 0;
449
450         /* We will change the sender of messages from the bus driver
451          * so that they originate from the bus driver. This is a
452          * speciality originating from dbus1, where the bus driver did
453          * not have a unique id, but only the well-known name. */
454
455         c = sd_bus_message_get_creds(m);
456         if (!c)
457                 return 0;
458
459         r = sd_bus_creds_get_well_known_names(c, &well_known);
460         if (r < 0)
461                 return r;
462
463         if (strv_contains(well_known, "org.freedesktop.DBus"))
464                 m->sender = "org.freedesktop.DBus";
465
466         return 0;
467 }
468
469 int main(int argc, char *argv[]) {
470
471         _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL;
472         sd_id128_t server_id;
473         int r, in_fd, out_fd;
474         bool got_hello = false;
475         bool is_unix;
476         struct ucred ucred = {};
477         _cleanup_free_ char *peersec = NULL;
478
479         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
480         log_parse_environment();
481         log_open();
482
483         r = parse_argv(argc, argv);
484         if (r <= 0)
485                 goto finish;
486
487         r = sd_listen_fds(0);
488         if (r == 0) {
489                 in_fd = STDIN_FILENO;
490                 out_fd = STDOUT_FILENO;
491         } else if (r == 1) {
492                 in_fd = SD_LISTEN_FDS_START;
493                 out_fd = SD_LISTEN_FDS_START;
494         } else {
495                 log_error("Illegal number of file descriptors passed");
496                 goto finish;
497         }
498
499         is_unix =
500                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
501                 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
502
503         if (is_unix) {
504                 getpeercred(in_fd, &ucred);
505                 getpeersec(in_fd, &peersec);
506         }
507
508         r = sd_bus_new(&a);
509         if (r < 0) {
510                 log_error("Failed to allocate bus: %s", strerror(-r));
511                 goto finish;
512         }
513
514         r = sd_bus_set_name(a, "sd-proxy");
515         if (r < 0) {
516                 log_error("Failed to set bus name: %s", strerror(-r));
517                 goto finish;
518         }
519
520         r = sd_bus_set_address(a, arg_address);
521         if (r < 0) {
522                 log_error("Failed to set address to connect to: %s", strerror(-r));
523                 goto finish;
524         }
525
526         r = sd_bus_negotiate_fds(a, is_unix);
527         if (r < 0) {
528                 log_error("Failed to set FD negotiation: %s", strerror(-r));
529                 goto finish;
530         }
531
532         if (ucred.pid > 0) {
533                 a->fake_creds.pid = ucred.pid;
534                 a->fake_creds.uid = ucred.uid;
535                 a->fake_creds.gid = ucred.gid;
536                 a->fake_creds_valid = true;
537         }
538
539         if (peersec) {
540                 a->fake_label = peersec;
541                 peersec = NULL;
542         }
543
544         a->manual_peer_interface = true;
545
546         r = sd_bus_start(a);
547         if (r < 0) {
548                 log_error("Failed to start bus client: %s", strerror(-r));
549                 goto finish;
550         }
551
552         r = sd_bus_get_server_id(a, &server_id);
553         if (r < 0) {
554                 log_error("Failed to get server ID: %s", strerror(-r));
555                 goto finish;
556         }
557
558         r = sd_bus_new(&b);
559         if (r < 0) {
560                 log_error("Failed to allocate bus: %s", strerror(-r));
561                 goto finish;
562         }
563
564         r = sd_bus_set_fd(b, in_fd, out_fd);
565         if (r < 0) {
566                 log_error("Failed to set fds: %s", strerror(-r));
567                 goto finish;
568         }
569
570         r = sd_bus_set_server(b, 1, server_id);
571         if (r < 0) {
572                 log_error("Failed to set server mode: %s", strerror(-r));
573                 goto finish;
574         }
575
576         r = sd_bus_negotiate_fds(b, is_unix);
577         if (r < 0) {
578                 log_error("Failed to set FD negotiation: %s", strerror(-r));
579                 goto finish;
580         }
581
582         r = sd_bus_set_anonymous(b, true);
583         if (r < 0) {
584                 log_error("Failed to set anonymous authentication: %s", strerror(-r));
585                 goto finish;
586         }
587
588         b->manual_peer_interface = true;
589
590         r = sd_bus_start(b);
591         if (r < 0) {
592                 log_error("Failed to start bus client: %s", strerror(-r));
593                 goto finish;
594         }
595
596         r = rename_service(a, b);
597         if (r < 0)
598                 log_debug("Failed to rename process: %s", strerror(-r));
599
600         if (a->is_kernel) {
601                 _cleanup_free_ char *match = NULL;
602                 const char *unique;
603
604                 r = sd_bus_get_unique_name(a, &unique);
605                 if (r < 0) {
606                         log_error("Failed to get unique name: %s", strerror(-r));
607                         goto finish;
608                 }
609
610                 match = strjoin("type='signal',"
611                                 "sender='org.freedesktop.DBus',"
612                                 "path='/org/freedesktop/DBus',"
613                                 "interface='org.freedesktop.DBus',"
614                                 "member='NameOwnerChanged',"
615                                 "arg1='",
616                                 unique,
617                                 "'",
618                                 NULL);
619                 if (!match) {
620                         log_oom();
621                         goto finish;
622                 }
623
624                 r = sd_bus_add_match(a, match, NULL, NULL);
625                 if (r < 0) {
626                         log_error("Failed to add match for NameLost: %s", strerror(-r));
627                         goto finish;
628                 }
629
630                 free(match);
631                 match = strjoin("type='signal',"
632                                 "sender='org.freedesktop.DBus',"
633                                 "path='/org/freedesktop/DBus',"
634                                 "interface='org.freedesktop.DBus',"
635                                 "member='NameOwnerChanged',"
636                                 "arg2='",
637                                 unique,
638                                 "'",
639                                 NULL);
640                 if (!match) {
641                         log_oom();
642                         goto finish;
643                 }
644
645                 r = sd_bus_add_match(a, match, NULL, NULL);
646                 if (r < 0) {
647                         log_error("Failed to add match for NameAcquired: %s", strerror(-r));
648                         goto finish;
649                 }
650         }
651
652         for (;;) {
653                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
654                 int events_a, events_b, fd;
655                 uint64_t timeout_a, timeout_b, t;
656                 struct timespec _ts, *ts;
657                 struct pollfd *pollfd;
658                 int k;
659
660                 if (got_hello) {
661                         r = sd_bus_process(a, &m);
662                         if (r < 0) {
663                                 /* treat 'connection reset by peer' as clean exit condition */
664                                 if (r == -ECONNRESET)
665                                         r = 0;
666                                 else
667                                         log_error("Failed to process bus a: %s", strerror(-r));
668
669                                 goto finish;
670                         }
671
672                         if (m) {
673                                 /* We officially got EOF, let's quit */
674                                 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
675                                         r = 0;
676                                         goto finish;
677                                 }
678
679                                 k = synthesize_name_acquired(a, b, m);
680                                 if (k < 0) {
681                                         r = k;
682                                         log_error("Failed to synthesize message: %s", strerror(-r));
683                                         goto finish;
684                                 }
685
686                                 patch_sender(a, m);
687
688                                 k = sd_bus_send(b, m, NULL);
689                                 if (k < 0) {
690                                         if (k == -ECONNRESET)
691                                                 r = 0;
692                                         else {
693                                                 r = k;
694                                                 log_error("Failed to send message: %s", strerror(-r));
695                                         }
696
697                                         goto finish;
698                                 }
699                         }
700
701                         if (r > 0)
702                                 continue;
703                 }
704
705                 r = sd_bus_process(b, &m);
706                 if (r < 0) {
707                         /* treat 'connection reset by peer' as clean exit condition */
708                         if (r == -ECONNRESET)
709                                 r = 0;
710                         else
711                                 log_error("Failed to process bus b: %s", strerror(-r));
712
713                         goto finish;
714                 }
715
716                 if (m) {
717                         /* We officially got EOF, let's quit */
718                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
719                                 r = 0;
720                                 goto finish;
721                         }
722
723                         k = process_hello(a, b, m, &got_hello);
724                         if (k < 0) {
725                                 r = k;
726                                 log_error("Failed to process HELLO: %s", strerror(-r));
727                                 goto finish;
728                         }
729
730                         if (k > 0)
731                                 r = k;
732                         else {
733                                 k = process_policy(a, b, m);
734                                 if (k < 0) {
735                                         r = k;
736                                         log_error("Failed to process policy: %s", strerror(-r));
737                                         goto finish;
738                                 }
739
740                                 k = process_driver(a, b, m);
741                                 if (k < 0) {
742                                         r = k;
743                                         log_error("Failed to process driver calls: %s", strerror(-r));
744                                         goto finish;
745                                 }
746
747                                 if (k > 0)
748                                         r = k;
749                                 else {
750                                         k = sd_bus_send(a, m, NULL);
751                                         if (k < 0) {
752                                                 if (r == -ECONNRESET)
753                                                         r = 0;
754                                                 else {
755                                                         r = k;
756                                                         log_error("Failed to send message: %s", strerror(-r));
757                                                 }
758
759                                                 goto finish;
760                                         }
761                                 }
762                         }
763                 }
764
765                 if (r > 0)
766                         continue;
767
768                 fd = sd_bus_get_fd(a);
769                 if (fd < 0) {
770                         log_error("Failed to get fd: %s", strerror(-r));
771                         goto finish;
772                 }
773
774                 events_a = sd_bus_get_events(a);
775                 if (events_a < 0) {
776                         log_error("Failed to get events mask: %s", strerror(-r));
777                         goto finish;
778                 }
779
780                 r = sd_bus_get_timeout(a, &timeout_a);
781                 if (r < 0) {
782                         log_error("Failed to get timeout: %s", strerror(-r));
783                         goto finish;
784                 }
785
786                 events_b = sd_bus_get_events(b);
787                 if (events_b < 0) {
788                         log_error("Failed to get events mask: %s", strerror(-r));
789                         goto finish;
790                 }
791
792                 r = sd_bus_get_timeout(b, &timeout_b);
793                 if (r < 0) {
794                         log_error("Failed to get timeout: %s", strerror(-r));
795                         goto finish;
796                 }
797
798                 t = timeout_a;
799                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
800                         t = timeout_b;
801
802                 if (t == (uint64_t) -1)
803                         ts = NULL;
804                 else {
805                         usec_t nw;
806
807                         nw = now(CLOCK_MONOTONIC);
808                         if (t > nw)
809                                 t -= nw;
810                         else
811                                 t = 0;
812
813                         ts = timespec_store(&_ts, t);
814                 }
815
816                 pollfd = (struct pollfd[3]) {
817                         {.fd = fd,     .events = events_a,           },
818                         {.fd = in_fd,  .events = events_b & POLLIN,  },
819                         {.fd = out_fd, .events = events_b & POLLOUT, }
820                 };
821
822                 r = ppoll(pollfd, 3, ts, NULL);
823                 if (r < 0) {
824                         log_error("ppoll() failed: %m");
825                         goto finish;
826                 }
827         }
828
829 finish:
830         sd_bus_flush(a);
831         sd_bus_flush(b);
832
833         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
834 }