chiark / gitweb /
bus: make kdbus work so that we can exchange real messages
[elogind.git] / src / libsystemd-bus / bus-kernel.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 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 <fcntl.h>
23
24 #include "util.h"
25
26 #include "bus-internal.h"
27 #include "bus-message.h"
28 #include "bus-kernel.h"
29
30 #define KDBUS_MSG_FOREACH_DATA(d, k)                                    \
31         for ((d) = (k)->data;                                           \
32              (uint8_t*) (d) < (uint8_t*) (k) + (k)->size;               \
33              (d) = (struct kdbus_msg_data*) ((uint8_t*) (d) + ALIGN8((d)->size)))
34
35
36
37 static int parse_unique_name(const char *s, uint64_t *id) {
38         int r;
39
40         assert(s);
41         assert(id);
42
43         if (!startswith(s, ":1."))
44                 return 0;
45
46         r = safe_atou64(s + 3, id);
47         if (r < 0)
48                 return r;
49
50         return 1;
51 }
52
53 static void append_payload_vec(struct kdbus_msg_data **d, const void *p, size_t sz) {
54         assert(d);
55         assert(p);
56         assert(sz > 0);
57
58         (*d)->size = offsetof(struct kdbus_msg_data, vec) + sizeof(struct kdbus_vec);
59         (*d)->type = KDBUS_MSG_PAYLOAD_VEC;
60         (*d)->vec.address = (uint64_t) p;
61         (*d)->vec.size = sz;
62
63         *d = (struct kdbus_msg_data*) ((uint8_t*) *d + ALIGN8((*d)->size));
64 }
65
66 static void append_destination(struct kdbus_msg_data **d, const char *s, size_t length) {
67         assert(d);
68         assert(d);
69
70         (*d)->size = offsetof(struct kdbus_msg_data, data) + length + 1;
71         (*d)->type = KDBUS_MSG_DST_NAME;
72         memcpy((*d)->data, s, length + 1);
73
74         *d = (struct kdbus_msg_data*) ((uint8_t*) *d + ALIGN8((*d)->size));
75 }
76
77 static int bus_message_setup_kmsg(sd_bus_message *m) {
78         struct kdbus_msg_data *d;
79         bool well_known;
80         uint64_t unique;
81         size_t sz, dl;
82         int r;
83
84         assert(m);
85         assert(m->sealed);
86
87         if (m->kdbus)
88                 return 0;
89
90         if (m->destination) {
91                 r = parse_unique_name(m->destination, &unique);
92                 if (r < 0)
93                         return r;
94
95                 well_known = r == 0;
96         } else
97                 well_known = false;
98
99         sz = offsetof(struct kdbus_msg, data);
100
101         /* Add in fixed header, fields header, fields header padding and payload */
102         sz += 4 * ALIGN8(offsetof(struct kdbus_msg_data, vec) + sizeof(struct kdbus_vec));
103
104         /* Add in well-known destination header */
105         if (well_known) {
106                 dl = strlen(m->destination);
107                 sz += ALIGN8(offsetof(struct kdbus_msg, data) + dl + 1);
108         }
109
110         m->kdbus = malloc0(sz);
111         if (!m->kdbus)
112                 return -ENOMEM;
113
114         m->kdbus->flags =
115                 ((m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
116                 ((m->header->flags & SD_BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
117         m->kdbus->dst_id =
118                 well_known ? 0 :
119                 m->destination ? unique : (uint64_t) -1;
120         m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS1;
121         m->kdbus->cookie = m->header->serial;
122
123         m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC;
124
125         d = m->kdbus->data;
126
127         if (well_known)
128                 append_destination(&d, m->destination, dl);
129
130         append_payload_vec(&d, m->header, sizeof(*m->header));
131
132         if (m->fields) {
133                 append_payload_vec(&d, m->fields, m->header->fields_size);
134
135                 if (m->header->fields_size % 8 != 0) {
136                         static const uint8_t padding[7] = {};
137
138                         append_payload_vec(&d, padding, 8 - (m->header->fields_size % 8));
139                 }
140         }
141
142         if (m->body)
143                 append_payload_vec(&d, m->body, m->header->body_size);
144
145         m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus;
146         assert(m->kdbus->size <= sz);
147
148         m->free_kdbus = true;
149
150         return 0;
151 }
152
153 int bus_kernel_take_fd(sd_bus *b) {
154         struct kdbus_cmd_hello hello = {};
155         int r;
156
157         assert(b);
158
159         r = ioctl(b->input_fd, KDBUS_CMD_HELLO, &hello);
160         if (r < 0)
161                 return -errno;
162
163         if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello.id) < 0)
164                 return -ENOMEM;
165
166         b->is_kernel = true;
167
168         r = bus_start_running(b);
169         if (r < 0)
170                 return r;
171
172         return 1;
173 }
174
175 int bus_kernel_connect(sd_bus *b) {
176         assert(b);
177         assert(b->input_fd < 0);
178         assert(b->output_fd < 0);
179         assert(b->kernel);
180
181         b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
182         if (b->input_fd < 0)
183                 return -errno;
184
185         b->output_fd = b->input_fd;
186
187         return bus_kernel_take_fd(b);
188 }
189
190 int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
191         int r;
192
193         assert(bus);
194         assert(m);
195         assert(bus->state == BUS_RUNNING);
196
197         r = bus_message_setup_kmsg(m);
198         if (r < 0)
199                 return r;
200
201         r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
202         if (r < 0)
203                 return errno == EAGAIN ? 0 : -errno;
204
205         return 0;
206 }
207
208 static void close_kdbus_msg(struct kdbus_msg *k) {
209         struct kdbus_msg_data *d;
210
211         KDBUS_MSG_FOREACH_DATA(d, k) {
212
213                 if (d->type != KDBUS_MSG_UNIX_FDS)
214                         continue;
215
216                 close_many(d->fds, (d->size - offsetof(struct kdbus_msg_data, fds)) / sizeof(int));
217         }
218 }
219
220 static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_message **ret) {
221         sd_bus_message *m = NULL;
222         struct kdbus_msg_data *d;
223         unsigned n_payload = 0, n_fds = 0;
224         _cleanup_free_ int *fds = NULL;
225         struct bus_header *h = NULL;
226         size_t total, n_bytes = 0, idx = 0;
227         int r;
228
229         assert(bus);
230         assert(k);
231         assert(ret);
232
233         if (k->payload_type != KDBUS_PAYLOAD_DBUS1)
234                 return 0;
235
236         KDBUS_MSG_FOREACH_DATA(d, k) {
237                 size_t l;
238
239                 l = d->size - offsetof(struct kdbus_msg_data, data);
240
241                 if (d->type == KDBUS_MSG_PAYLOAD) {
242
243                         if (!h) {
244                                 if (l < sizeof(struct bus_header))
245                                         return -EBADMSG;
246
247                                 h = (struct bus_header*) d->data;
248                         }
249
250                         n_payload++;
251                         n_bytes += l;
252
253                 } else if (d->type == KDBUS_MSG_UNIX_FDS) {
254                         int *f;
255                         unsigned j;
256
257                         j = l / sizeof(int);
258                         f = realloc(fds, sizeof(int) * (n_fds + j));
259                         if (!f)
260                                 return -ENOMEM;
261
262                         fds = f;
263                         memcpy(fds + n_fds, d->fds, j);
264                         n_fds += j;
265                 }
266         }
267
268         if (!h)
269                 return -EBADMSG;
270
271         r = bus_header_size(h, &total);
272         if (r < 0)
273                 return r;
274
275         if (n_bytes != total)
276                 return -EBADMSG;
277
278         r = bus_message_from_header(h, sizeof(struct bus_header), fds, n_fds, NULL, NULL, 0, &m);
279         if (r < 0)
280                 return r;
281
282         KDBUS_MSG_FOREACH_DATA(d, k) {
283                 size_t l;
284
285                 if (d->type != KDBUS_MSG_PAYLOAD)
286                         continue;
287
288                 l = d->size - offsetof(struct kdbus_msg_data, data);
289                 if (idx == sizeof(struct bus_header) &&
290                     l == BUS_MESSAGE_FIELDS_SIZE(m))
291                         m->fields = d->data;
292                 else if (idx == sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) &&
293                          l == BUS_MESSAGE_BODY_SIZE(m))
294                         m->body = d->data;
295                 else if (!(idx == 0 && l == sizeof(struct bus_header)) &&
296                          !(idx == sizeof(struct bus_header) + BUS_MESSAGE_FIELDS_SIZE(m))) {
297                         sd_bus_message_unref(m);
298                         return -EBADMSG;
299                 }
300
301                 idx += l;
302         }
303
304         r = bus_message_parse_fields(m);
305         if (r < 0) {
306                 sd_bus_message_unref(m);
307                 return r;
308         }
309
310         /* We take possession of the kmsg struct now */
311         m->kdbus = k;
312         m->free_kdbus = true;
313         m->free_fds = true;
314
315         fds = NULL;
316
317         *ret = m;
318         return 1;
319 }
320
321 int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) {
322         struct kdbus_msg *k;
323         size_t sz = 128;
324         int r;
325
326         assert(bus);
327         assert(m);
328
329         for (;;) {
330                 void *q;
331
332                 q = realloc(bus->rbuffer, sz);
333                 if (!q)
334                         return -errno;
335
336                 k = bus->rbuffer = q;
337                 k->size = sz;
338
339                 r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, bus->rbuffer);
340                 if (r >= 0)
341                         break;
342
343                 if (errno == EAGAIN)
344                         return 0;
345
346                 if (errno != ENOBUFS)
347                         return -errno;
348
349                 sz *= 2;
350         }
351
352         r = bus_kernel_make_message(bus, k, m);
353         if (r > 0)
354                 bus->rbuffer = NULL;
355         else
356                 close_kdbus_msg(k);
357
358         return r;
359 }
360
361 int bus_kernel_create(const char *name, char **s) {
362         struct kdbus_cmd_fname *fname;
363         size_t l;
364         int fd;
365         char *p;
366
367         assert(name);
368         assert(s);
369
370         fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
371         if (fd < 0)
372                 return -errno;
373
374         l = strlen(name);
375         fname = alloca(offsetof(struct kdbus_cmd_fname, name) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1);
376         sprintf(fname->name, "%lu-%s", (unsigned long) getuid(), name);
377         fname->size = offsetof(struct kdbus_cmd_fname, name) + strlen(fname->name) + 1;
378         fname->kernel_flags = KDBUS_CMD_FNAME_ACCESS_WORLD;
379         fname->user_flags = 0;
380
381         p = strjoin("/dev/kdbus/", fname->name, "/bus", NULL);
382         if (!p)
383                 return -ENOMEM;
384
385         if (ioctl(fd, KDBUS_CMD_BUS_MAKE, fname) < 0) {
386                 close_nointr_nofail(fd);
387                 free(p);
388                 return -errno;
389         }
390
391         if (s)
392                 *s = p;
393
394         return fd;
395 }