1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <getopt.h>
5 #include <stddef.h>
6 #include <string.h>
7 #include <unistd.h>
8 
9 #include "sd-bus.h"
10 #include "sd-daemon.h"
11 
12 #include "alloc-util.h"
13 #include "bus-internal.h"
14 #include "bus-util.h"
15 #include "errno-util.h"
16 #include "io-util.h"
17 #include "log.h"
18 #include "main-func.h"
19 #include "util.h"
20 #include "version.h"
21 
22 #define DEFAULT_BUS_PATH "unix:path=/run/dbus/system_bus_socket"
23 
24 static const char *arg_bus_path = DEFAULT_BUS_PATH;
25 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
26 static bool arg_user = false;
27 
help(void)28 static int help(void) {
29         printf("%s [OPTIONS...]\n\n"
30                "Forward messages between a pipe or socket and a D-Bus bus.\n\n"
31                "  -h --help              Show this help\n"
32                "     --version           Show package version\n"
33                "  -p --bus-path=PATH     Path to the bus address (default: %s)\n"
34                "     --system            Connect to system bus\n"
35                "     --user              Connect to user bus\n"
36                "  -M --machine=CONTAINER Name of local container to connect to\n",
37                program_invocation_short_name, DEFAULT_BUS_PATH);
38 
39         return 0;
40 }
41 
parse_argv(int argc,char * argv[])42 static int parse_argv(int argc, char *argv[]) {
43         enum {
44                 ARG_VERSION = 0x100,
45                 ARG_MACHINE,
46                 ARG_USER,
47                 ARG_SYSTEM,
48         };
49 
50         static const struct option options[] = {
51                 { "help",            no_argument,       NULL, 'h'         },
52                 { "version",         no_argument,       NULL, ARG_VERSION },
53                 { "bus-path",        required_argument, NULL, 'p'         },
54                 { "user",            no_argument,       NULL, ARG_USER    },
55                 { "system",          no_argument,       NULL, ARG_SYSTEM  },
56                 { "machine",         required_argument, NULL, 'M'         },
57                 {},
58         };
59 
60         int c;
61 
62         assert(argc >= 0);
63         assert(argv);
64 
65         while ((c = getopt_long(argc, argv, "hp:M:", options, NULL)) >= 0)
66 
67                 switch (c) {
68 
69                 case 'h':
70                         return help();
71 
72                 case ARG_VERSION:
73                         return version();
74 
75                 case ARG_USER:
76                         arg_user = true;
77                         break;
78 
79                 case ARG_SYSTEM:
80                         arg_user = false;
81                         break;
82 
83                 case 'p':
84                         arg_bus_path = optarg;
85                         break;
86 
87                 case 'M':
88                         arg_bus_path = optarg;
89                         arg_transport = BUS_TRANSPORT_MACHINE;
90                         break;
91 
92                 case '?':
93                         return -EINVAL;
94 
95                 default:
96                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
97                                                "Unknown option code %c", c);
98                 }
99 
100         return 1;
101 }
102 
run(int argc,char * argv[])103 static int run(int argc, char *argv[]) {
104         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *a = NULL, *b = NULL;
105         sd_id128_t server_id;
106         bool is_unix;
107         int r, in_fd, out_fd;
108 
109         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
110         log_parse_environment();
111         log_open();
112 
113         r = parse_argv(argc, argv);
114         if (r <= 0)
115                 return r;
116 
117         r = sd_listen_fds(0);
118         if (r == 0) {
119                 in_fd = STDIN_FILENO;
120                 out_fd = STDOUT_FILENO;
121         } else if (r == 1) {
122                 in_fd = SD_LISTEN_FDS_START;
123                 out_fd = SD_LISTEN_FDS_START;
124         } else
125                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "More than one file descriptor was passed.");
126 
127         is_unix =
128                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
129                 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
130 
131         r = sd_bus_new(&a);
132         if (r < 0)
133                 return log_error_errno(r, "Failed to allocate bus: %m");
134 
135         if (arg_transport == BUS_TRANSPORT_MACHINE)
136                 r = bus_set_address_machine(a, arg_user, arg_bus_path);
137         else
138                 r = sd_bus_set_address(a, arg_bus_path);
139         if (r < 0)
140                 return log_error_errno(r, "Failed to set address to connect to: %m");
141 
142         r = sd_bus_negotiate_fds(a, is_unix);
143         if (r < 0)
144                 return log_error_errno(r, "Failed to set FD negotiation: %m");
145 
146         r = sd_bus_start(a);
147         if (r < 0)
148                 return log_error_errno(r, "Failed to start bus client: %m");
149 
150         r = sd_bus_get_bus_id(a, &server_id);
151         if (r < 0)
152                 return log_error_errno(r, "Failed to get server ID: %m");
153 
154         r = sd_bus_new(&b);
155         if (r < 0)
156                 return log_error_errno(r, "Failed to allocate bus: %m");
157 
158         r = sd_bus_set_fd(b, in_fd, out_fd);
159         if (r < 0)
160                 return log_error_errno(r, "Failed to set fds: %m");
161 
162         r = sd_bus_set_server(b, 1, server_id);
163         if (r < 0)
164                 return log_error_errno(r, "Failed to set server mode: %m");
165 
166         r = sd_bus_negotiate_fds(b, is_unix);
167         if (r < 0)
168                 return log_error_errno(r, "Failed to set FD negotiation: %m");
169 
170         r = sd_bus_set_anonymous(b, true);
171         if (r < 0)
172                 return log_error_errno(r, "Failed to set anonymous authentication: %m");
173 
174         r = sd_bus_start(b);
175         if (r < 0)
176                 return log_error_errno(r, "Failed to start bus client: %m");
177 
178         for (;;) {
179                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
180                 int events_a, events_b, fd;
181                 usec_t timeout_a, timeout_b, t;
182 
183                 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
184 
185                 r = sd_bus_process(a, &m);
186                 if (r < 0)
187                         return log_error_errno(r, "Failed to process bus a: %m");
188 
189                 if (m) {
190                         r = sd_bus_send(b, m, NULL);
191                         if (r < 0)
192                                 return log_error_errno(r, "Failed to send message: %m");
193                 }
194 
195                 if (r > 0)
196                         continue;
197 
198                 r = sd_bus_process(b, &m);
199                 if (r < 0) {
200                         /* treat 'connection reset by peer' as clean exit condition */
201                         if (ERRNO_IS_DISCONNECT(r))
202                                 return 0;
203 
204                         return log_error_errno(r, "Failed to process bus: %m");
205                 }
206 
207                 if (m) {
208                         r = sd_bus_send(a, m, NULL);
209                         if (r < 0)
210                                 return log_error_errno(r, "Failed to send message: %m");
211                 }
212 
213                 if (r > 0)
214                         continue;
215 
216                 fd = sd_bus_get_fd(a);
217                 if (fd < 0)
218                         return log_error_errno(fd, "Failed to get fd: %m");
219 
220                 events_a = sd_bus_get_events(a);
221                 if (events_a < 0)
222                         return log_error_errno(events_a, "Failed to get events mask: %m");
223 
224                 r = sd_bus_get_timeout(a, &timeout_a);
225                 if (r < 0)
226                         return log_error_errno(r, "Failed to get timeout: %m");
227 
228                 events_b = sd_bus_get_events(b);
229                 if (events_b < 0)
230                         return log_error_errno(events_b, "Failed to get events mask: %m");
231 
232                 r = sd_bus_get_timeout(b, &timeout_b);
233                 if (r < 0)
234                         return log_error_errno(r, "Failed to get timeout: %m");
235 
236                 t = usec_sub_unsigned(MIN(timeout_a, timeout_b), now(CLOCK_MONOTONIC));
237 
238                 struct pollfd p[3] = {
239                         { .fd = fd,            .events = events_a           },
240                         { .fd = STDIN_FILENO,  .events = events_b & POLLIN  },
241                         { .fd = STDOUT_FILENO, .events = events_b & POLLOUT },
242                 };
243 
244                 r = ppoll_usec(p, ELEMENTSOF(p), t);
245                 if (r < 0)
246                         return log_error_errno(r, "ppoll() failed: %m");
247         }
248 
249         return 0;
250 }
251 
252 DEFINE_MAIN_FUNCTION(run);
253