chiark / gitweb /
bus-proxy: bring back systemd-stdio-bridge
[elogind.git] / src / bus-proxyd / stdio-bridge.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 #include "capability.h"
47 #include "bus-control.h"
48 #include "smack-util.h"
49 #include "set.h"
50 #include "bus-xml-policy.h"
51 #include "driver.h"
52 #include "proxy.h"
53 #include "synthesize.h"
54
55 static char *arg_address = NULL;
56 static char *arg_command_line_buffer = NULL;
57 static char **arg_configuration = NULL;
58
59 static int help(void) {
60
61         printf("%s [OPTIONS...]\n\n"
62                "Connect STDIO to a given bus address.\n\n"
63                "  -h --help               Show this help\n"
64                "     --version            Show package version\n"
65                "     --configuration=PATH Configuration file or directory\n"
66                "     --machine=MACHINE    Connect to specified machine\n"
67                "     --address=ADDRESS    Connect to the bus specified by ADDRESS\n"
68                "                          (default: " DEFAULT_SYSTEM_BUS_ADDRESS ")\n",
69                program_invocation_short_name);
70
71         return 0;
72 }
73
74 static int parse_argv(int argc, char *argv[]) {
75
76         enum {
77                 ARG_VERSION = 0x100,
78                 ARG_ADDRESS,
79                 ARG_CONFIGURATION,
80                 ARG_MACHINE,
81         };
82
83         static const struct option options[] = {
84                 { "help",            no_argument,       NULL, 'h'                 },
85                 { "version",         no_argument,       NULL, ARG_VERSION         },
86                 { "address",         required_argument, NULL, ARG_ADDRESS         },
87                 { "configuration",   required_argument, NULL, ARG_CONFIGURATION   },
88                 { "machine",         required_argument, NULL, ARG_MACHINE         },
89                 {},
90         };
91
92         int c, r;
93
94         assert(argc >= 0);
95         assert(argv);
96
97         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
98
99                 switch (c) {
100
101                 case 'h':
102                         help();
103                         return 0;
104
105                 case ARG_VERSION:
106                         puts(PACKAGE_STRING);
107                         puts(SYSTEMD_FEATURES);
108                         return 0;
109
110                 case ARG_ADDRESS: {
111                         char *a;
112
113                         a = strdup(optarg);
114                         if (!a)
115                                 return log_oom();
116
117                         free(arg_address);
118                         arg_address = a;
119                         break;
120                 }
121
122                 case ARG_CONFIGURATION:
123                         r = strv_extend(&arg_configuration, optarg);
124                         if (r < 0)
125                                 return log_oom();
126                         break;
127
128                 case ARG_MACHINE: {
129                         _cleanup_free_ char *e = NULL;
130                         char *a;
131
132                         e = bus_address_escape(optarg);
133                         if (!e)
134                                 return log_oom();
135
136 #ifdef ENABLE_KDBUS
137                         a = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL);
138 #else
139                         a = strjoin("x-machine-unix:machine=", e, NULL);
140 #endif
141                         if (!a)
142                                 return log_oom();
143
144                         free(arg_address);
145                         arg_address = a;
146
147                         break;
148                 }
149
150                 case '?':
151                         return -EINVAL;
152
153                 default:
154                         assert_not_reached("Unhandled option");
155                 }
156
157         /* If the first command line argument is only "x" characters
158          * we'll write who we are talking to into it, so that "ps" is
159          * explanatory */
160         arg_command_line_buffer = argv[optind];
161         if (argc > optind + 1 || (arg_command_line_buffer && !in_charset(arg_command_line_buffer, "x"))) {
162                 log_error("Too many arguments");
163                 return -EINVAL;
164         }
165
166         if (!arg_address) {
167                 arg_address = strdup(DEFAULT_SYSTEM_BUS_ADDRESS);
168                 if (!arg_address)
169                         return log_oom();
170         }
171
172         return 1;
173 }
174
175 static int rename_service(sd_bus *a, sd_bus *b) {
176         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
177         _cleanup_free_ char *p = NULL, *name = NULL;
178         const char *comm;
179         char **cmdline;
180         uid_t uid;
181         pid_t pid;
182         int r;
183
184         assert(a);
185         assert(b);
186
187         r = sd_bus_get_owner_creds(b, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_AUGMENT, &creds);
188         if (r < 0)
189                 return r;
190
191         r = sd_bus_creds_get_uid(creds, &uid);
192         if (r < 0)
193                 return r;
194
195         r = sd_bus_creds_get_pid(creds, &pid);
196         if (r < 0)
197                 return r;
198
199         r = sd_bus_creds_get_cmdline(creds, &cmdline);
200         if (r < 0)
201                 return r;
202
203         r = sd_bus_creds_get_comm(creds, &comm);
204         if (r < 0)
205                 return r;
206
207         name = uid_to_name(uid);
208         if (!name)
209                 return -ENOMEM;
210
211         p = strv_join(cmdline, " ");
212         if (!p)
213                 return -ENOMEM;
214
215         /* The status string gets the full command line ... */
216         sd_notifyf(false,
217                    "STATUS=Processing requests from client PID "PID_FMT" (%s); UID "UID_FMT" (%s)",
218                    pid, p,
219                    uid, name);
220
221         /* ... and the argv line only the short comm */
222         if (arg_command_line_buffer) {
223                 size_t m, w;
224
225                 m = strlen(arg_command_line_buffer);
226                 w = snprintf(arg_command_line_buffer, m,
227                              "[PID "PID_FMT"/%s; UID "UID_FMT"/%s]",
228                              pid, comm,
229                              uid, name);
230
231                 if (m > w)
232                         memzero(arg_command_line_buffer + w, m - w);
233         }
234
235         log_debug("Running on behalf of PID "PID_FMT" (%s), UID "UID_FMT" (%s), %s",
236                   pid, p,
237                   uid, name,
238                   a->unique_name);
239
240         return 0;
241 }
242
243 int main(int argc, char *argv[]) {
244         _cleanup_(proxy_freep) Proxy *p = NULL;
245         int r;
246
247         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
248         log_parse_environment();
249         log_open();
250
251         r = parse_argv(argc, argv);
252         if (r <= 0)
253                 goto finish;
254
255         r = proxy_new(&p, STDIN_FILENO, STDOUT_FILENO, arg_address);
256         if (r < 0)
257                 goto finish;
258
259         r = proxy_load_policy(p, arg_configuration);
260         if (r < 0)
261                 goto finish;
262
263         r = proxy_hello_policy(p, getuid());
264         if (r < 0)
265                 goto finish;
266
267         r = rename_service(p->dest_bus, p->local_bus);
268         if (r < 0)
269                 log_debug_errno(r, "Failed to rename process: %m");
270
271         r = proxy_run(p);
272
273 finish:
274         sd_notify(false,
275                   "STOPPING=1\n"
276                   "STATUS=Shutting down.");
277
278         strv_free(arg_configuration);
279         free(arg_address);
280
281         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
282 }