chiark / gitweb /
log: log_error() and friends add a newline after each line anyway, so avoid including...
[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         struct ucred ucred = {};
369         _cleanup_free_ char *peersec = NULL;
370
371         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
372         log_parse_environment();
373         log_open();
374
375         r = parse_argv(argc, argv);
376         if (r <= 0)
377                 goto finish;
378
379         r = sd_listen_fds(0);
380         if (r == 0) {
381                 in_fd = STDIN_FILENO;
382                 out_fd = STDOUT_FILENO;
383         } else if (r == 1) {
384                 in_fd = SD_LISTEN_FDS_START;
385                 out_fd = SD_LISTEN_FDS_START;
386         } else {
387                 log_error("Illegal number of file descriptors passed");
388                 goto finish;
389         }
390
391         is_unix =
392                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
393                 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
394
395         if (is_unix) {
396                 getpeercred(in_fd, &ucred);
397                 getpeersec(in_fd, &peersec);
398         }
399
400         r = sd_bus_new(&a);
401         if (r < 0) {
402                 log_error("Failed to allocate bus: %s", strerror(-r));
403                 goto finish;
404         }
405
406         r = sd_bus_set_address(a, arg_address);
407         if (r < 0) {
408                 log_error("Failed to set address to connect to: %s", strerror(-r));
409                 goto finish;
410         }
411
412         r = sd_bus_negotiate_fds(a, is_unix);
413         if (r < 0) {
414                 log_error("Failed to set FD negotiation: %s", strerror(-r));
415                 goto finish;
416         }
417
418         if (ucred.pid > 0) {
419                 a->fake_creds.pid = ucred.pid;
420                 a->fake_creds.uid = ucred.uid;
421                 a->fake_creds.gid = ucred.gid;
422                 a->fake_creds_valid = true;
423         }
424
425         if (peersec) {
426                 a->fake_label = peersec;
427                 peersec = NULL;
428         }
429
430         r = sd_bus_start(a);
431         if (r < 0) {
432                 log_error("Failed to start bus client: %s", strerror(-r));
433                 goto finish;
434         }
435
436         r = sd_bus_get_server_id(a, &server_id);
437         if (r < 0) {
438                 log_error("Failed to get server ID: %s", strerror(-r));
439                 goto finish;
440         }
441
442         r = sd_bus_new(&b);
443         if (r < 0) {
444                 log_error("Failed to allocate bus: %s", strerror(-r));
445                 goto finish;
446         }
447
448         r = sd_bus_set_fd(b, in_fd, out_fd);
449         if (r < 0) {
450                 log_error("Failed to set fds: %s", strerror(-r));
451                 goto finish;
452         }
453
454         r = sd_bus_set_server(b, 1, server_id);
455         if (r < 0) {
456                 log_error("Failed to set server mode: %s", strerror(-r));
457                 goto finish;
458         }
459
460         r = sd_bus_negotiate_fds(b, is_unix);
461         if (r < 0) {
462                 log_error("Failed to set FD negotiation: %s", strerror(-r));
463                 goto finish;
464         }
465
466         r = sd_bus_set_anonymous(b, true);
467         if (r < 0) {
468                 log_error("Failed to set anonymous authentication: %s", strerror(-r));
469                 goto finish;
470         }
471
472         r = sd_bus_start(b);
473         if (r < 0) {
474                 log_error("Failed to start bus client: %s", strerror(-r));
475                 goto finish;
476         }
477
478         r = rename_service(b);
479         if (r < 0)
480                 log_debug("Failed to rename process: %s", strerror(-r));
481
482         if (a->is_kernel) {
483                 _cleanup_free_ char *match = NULL;
484                 const char *unique;
485
486                 r = sd_bus_get_unique_name(a, &unique);
487                 if (r < 0) {
488                         log_error("Failed to get unique name: %s", strerror(-r));
489                         goto finish;
490                 }
491
492                 match = strjoin("type='signal',"
493                                 "sender='org.freedesktop.DBus',"
494                                 "path='/org/freedesktop/DBus',"
495                                 "interface='org.freedesktop.DBus',"
496                                 "member='NameOwnerChanged',"
497                                 "arg1='",
498                                 unique,
499                                 "'",
500                                 NULL);
501                 if (!match) {
502                         log_oom();
503                         goto finish;
504                 }
505
506                 r = sd_bus_add_match(a, match, NULL, NULL);
507                 if (r < 0) {
508                         log_error("Failed to add match for NameLost: %s", strerror(-r));
509                         goto finish;
510                 }
511
512                 free(match);
513                 match = strjoin("type='signal',"
514                                 "sender='org.freedesktop.DBus',"
515                                 "path='/org/freedesktop/DBus',"
516                                 "interface='org.freedesktop.DBus',"
517                                 "member='NameOwnerChanged',"
518                                 "arg2='",
519                                 unique,
520                                 "'",
521                                 NULL);
522                 if (!match) {
523                         log_oom();
524                         goto finish;
525                 }
526
527                 r = sd_bus_add_match(a, match, NULL, NULL);
528                 if (r < 0) {
529                         log_error("Failed to add match for NameAcquired: %s", strerror(-r));
530                         goto finish;
531                 }
532         }
533
534         for (;;) {
535                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
536                 int events_a, events_b, fd;
537                 uint64_t timeout_a, timeout_b, t;
538                 struct timespec _ts, *ts;
539                 struct pollfd *pollfd;
540                 int k;
541
542                 if (got_hello) {
543                         r = sd_bus_process(a, &m);
544                         if (r < 0) {
545                                 /* treat 'connection reset by peer' as clean exit condition */
546                                 if (r == -ECONNRESET)
547                                         r = 0;
548                                 else
549                                         log_error("Failed to process bus a: %s", strerror(-r));
550
551                                 goto finish;
552                         }
553
554                         if (m) {
555                                 /* We officially got EOF, let's quit */
556                                 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
557                                         r = 0;
558                                         goto finish;
559                                 }
560
561                                 k = synthesize_name_acquired(a, b, m);
562                                 if (k < 0) {
563                                         r = k;
564                                         log_error("Failed to synthesize message: %s", strerror(-r));
565                                         goto finish;
566                                 }
567
568                                 k = sd_bus_send(b, m, NULL);
569                                 if (k < 0) {
570                                         r = k;
571                                         log_error("Failed to send message: %s", strerror(-r));
572                                         goto finish;
573                                 }
574                         }
575
576                         if (r > 0)
577                                 continue;
578                 }
579
580                 r = sd_bus_process(b, &m);
581                 if (r < 0) {
582                         /* treat 'connection reset by peer' as clean exit condition */
583                         if (r == -ECONNRESET)
584                                 r = 0;
585                         else
586                                 log_error("Failed to process bus b: %s", strerror(-r));
587
588                         goto finish;
589                 }
590
591                 if (m) {
592                         /* We officially got EOF, let's quit */
593                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
594                                 r = 0;
595                                 goto finish;
596                         }
597
598                         k = process_hello(a, b, m, &got_hello);
599                         if (k < 0) {
600                                 r = k;
601                                 goto finish;
602                         }
603
604                         if (k > 0)
605                                 r = k;
606                         else {
607                                 k = sd_bus_send(a, m, NULL);
608                                 if (k < 0) {
609                                         r = k;
610                                         log_error("Failed to send message: %s", strerror(-r));
611                                         goto finish;
612                                 }
613                         }
614                 }
615
616                 if (r > 0)
617                         continue;
618
619                 fd = sd_bus_get_fd(a);
620                 if (fd < 0) {
621                         log_error("Failed to get fd: %s", strerror(-r));
622                         goto finish;
623                 }
624
625                 events_a = sd_bus_get_events(a);
626                 if (events_a < 0) {
627                         log_error("Failed to get events mask: %s", strerror(-r));
628                         goto finish;
629                 }
630
631                 r = sd_bus_get_timeout(a, &timeout_a);
632                 if (r < 0) {
633                         log_error("Failed to get timeout: %s", strerror(-r));
634                         goto finish;
635                 }
636
637                 events_b = sd_bus_get_events(b);
638                 if (events_b < 0) {
639                         log_error("Failed to get events mask: %s", strerror(-r));
640                         goto finish;
641                 }
642
643                 r = sd_bus_get_timeout(b, &timeout_b);
644                 if (r < 0) {
645                         log_error("Failed to get timeout: %s", strerror(-r));
646                         goto finish;
647                 }
648
649                 t = timeout_a;
650                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
651                         t = timeout_b;
652
653                 if (t == (uint64_t) -1)
654                         ts = NULL;
655                 else {
656                         usec_t nw;
657
658                         nw = now(CLOCK_MONOTONIC);
659                         if (t > nw)
660                                 t -= nw;
661                         else
662                                 t = 0;
663
664                         ts = timespec_store(&_ts, t);
665                 }
666
667                 pollfd = (struct pollfd[3]) {
668                         {.fd = fd,     .events = events_a,           },
669                         {.fd = in_fd,  .events = events_b & POLLIN,  },
670                         {.fd = out_fd, .events = events_b & POLLOUT, }
671                 };
672
673                 r = ppoll(pollfd, 3, ts, NULL);
674                 if (r < 0) {
675                         log_error("ppoll() failed: %m");
676                         goto finish;
677                 }
678         }
679
680         r = 0;
681
682 finish:
683         sd_bus_flush(a);
684         sd_bus_flush(b);
685
686         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
687 }