chiark / gitweb /
bus: basic implementation of kdbus client side
[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         assert(!m->kdbus);
87
88         if (m->destination) {
89                 r = parse_unique_name(m->destination, &unique);
90                 if (r < 0)
91                         return r;
92
93                 well_known = r == 0;
94         } else
95                 well_known = false;
96
97         sz = offsetof(struct kdbus_msg, data);
98
99         /* Add in fixed header, fields header, fields header padding and payload */
100         sz += 4 * ALIGN8(offsetof(struct kdbus_msg_data, vec) + sizeof(struct kdbus_vec));
101
102         /* Add in well-known destination header */
103         if (well_known) {
104                 dl = strlen(m->destination);
105                 sz += ALIGN8(offsetof(struct kdbus_msg, data) + dl + 1);
106         }
107
108         m->kdbus = malloc0(sz);
109         if (!m->kdbus)
110                 return -ENOMEM;
111
112         m->kdbus->flags =
113                 ((m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
114                 ((m->header->flags & SD_BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
115         m->kdbus->dst_id =
116                 well_known ? 0 :
117                 m->destination ? unique : (uint64_t) -1;
118         m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS1;
119         m->kdbus->cookie = m->header->serial;
120
121         m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC;
122
123         d = m->kdbus->data;
124
125         if (well_known)
126                 append_destination(&d, m->destination, dl);
127
128         append_payload_vec(&d, m->header, sizeof(*m->header));
129
130         if (m->fields) {
131                 append_payload_vec(&d, m->fields, m->header->fields_size);
132
133                 if (m->header->fields_size % 8 != 0) {
134                         static const uint8_t padding[7] = {};
135
136                         append_payload_vec(&d, padding, 8 - (m->header->fields_size % 8));
137                 }
138         }
139
140         if (m->body)
141                 append_payload_vec(&d, m->body, m->header->body_size);
142
143         m->kdbus->size = (uint8_t*) m - (uint8_t*) m->kdbus;
144         assert(m->kdbus->size <= sz);
145
146         return 0;
147 }
148
149 int bus_kernel_take_fd(sd_bus *b) {
150         struct kdbus_cmd_hello hello = {};
151         int r;
152
153         assert(b);
154
155         r = ioctl(b->input_fd, KDBUS_CMD_HELLO, &hello);
156         if (r < 0)
157                 return -errno;
158
159         b->is_kernel = true;
160
161         r = bus_start_running(b);
162         if (r < 0)
163                 return r;
164
165         return 1;
166 }
167
168 int bus_kernel_connect(sd_bus *b) {
169         assert(b);
170         assert(b->input_fd < 0);
171         assert(b->output_fd < 0);
172         assert(b->kernel);
173
174         b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
175         if (b->input_fd)
176                 return -errno;
177
178         b->output_fd = b->input_fd;
179
180         return bus_kernel_take_fd(b);
181 }
182
183 int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
184         int r;
185
186         assert(bus);
187         assert(m);
188         assert(bus->state == BUS_RUNNING);
189
190         r = bus_message_setup_kmsg(m);
191         if (r < 0)
192                 return r;
193
194         r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
195         if (r < 0)
196                 return errno == EAGAIN ? 0 : -errno;
197
198         return 0;
199 }
200
201 static void close_kdbus_msg(struct kdbus_msg *k) {
202         struct kdbus_msg_data *d;
203
204         KDBUS_MSG_FOREACH_DATA(d, k) {
205
206                 if (d->type != KDBUS_MSG_UNIX_FDS)
207                         continue;
208
209                 close_many(d->fds, (d->size - offsetof(struct kdbus_msg_data, fds)) / sizeof(int));
210         }
211 }
212
213 static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_message **ret) {
214         sd_bus_message *m = NULL;
215         struct kdbus_msg_data *d;
216         unsigned n_payload = 0, n_fds = 0;
217         _cleanup_free_ int *fds = NULL;
218         struct bus_header *h = NULL;
219         size_t total, n_bytes = 0, idx = 0;
220         int r;
221
222         assert(bus);
223         assert(k);
224         assert(ret);
225
226         if (k->payload_type != KDBUS_PAYLOAD_DBUS1)
227                 return 0;
228
229         KDBUS_MSG_FOREACH_DATA(d, k) {
230                 size_t l;
231
232                 l = d->size - offsetof(struct kdbus_msg_data, data);
233
234                 if (d->type == KDBUS_MSG_PAYLOAD) {
235
236                         if (!h) {
237                                 if (l < sizeof(struct bus_header))
238                                         return -EBADMSG;
239
240                                 h = (struct bus_header*) d->data;
241                         }
242
243                         n_payload++;
244                         n_bytes += l;
245
246                 } else if (d->type == KDBUS_MSG_UNIX_FDS) {
247                         int *f;
248                         unsigned j;
249
250                         j = l / sizeof(int);
251                         f = realloc(fds, sizeof(int) * (n_fds + j));
252                         if (!f)
253                                 return -ENOMEM;
254
255                         fds = f;
256                         memcpy(fds + n_fds, d->fds, j);
257                         n_fds += j;
258                 }
259         }
260
261         if (!h)
262                 return -EBADMSG;
263
264         r = bus_header_size(h, &total);
265         if (r < 0)
266                 return r;
267
268         if (n_bytes != total)
269                 return -EBADMSG;
270
271         r = bus_message_from_header(h, sizeof(struct bus_header), fds, n_fds, NULL, NULL, 0, &m);
272         if (r < 0)
273                 return r;
274
275         KDBUS_MSG_FOREACH_DATA(d, k) {
276                 size_t l;
277
278                 if (d->type != KDBUS_MSG_PAYLOAD)
279                         continue;
280
281                 l = d->size - offsetof(struct kdbus_msg_data, data);
282
283                 if (idx == sizeof(struct bus_header) &&
284                     l == BUS_MESSAGE_FIELDS_SIZE(m))
285                         m->fields = d->data;
286                 else if (idx == sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) &&
287                          l == BUS_MESSAGE_BODY_SIZE(m))
288                         m->body = d->data;
289                 else {
290                         sd_bus_message_unref(m);
291                         return -EBADMSG;
292                 }
293
294                 idx += l;
295         }
296
297         r = bus_message_parse_fields(m);
298         if (r < 0) {
299                 sd_bus_message_unref(m);
300                 return r;
301         }
302
303         /* We take possession of the kmsg struct now */
304         m->kdbus = k;
305         m->free_kdbus = true;
306         m->free_fds = true;
307
308         fds = NULL;
309
310         *ret = m;
311         return 1;
312 }
313
314 int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) {
315         struct kdbus_msg *k;
316         size_t sz = 128;
317         int r;
318
319         assert(bus);
320         assert(m);
321
322         for (;;) {
323                 void *q;
324
325                 q = realloc(bus->rbuffer, sz);
326                 if (!q)
327                         return -errno;
328
329                 k = bus->rbuffer = q;
330                 k->size = sz;
331
332                 r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, bus->rbuffer);
333                 if (r >= 0)
334                         break;
335
336                 if (errno == EAGAIN)
337                         return 0;
338
339                 if (errno != -EMSGSIZE)
340                         return -errno;
341
342                 sz *= 2;
343         }
344
345         r = bus_kernel_make_message(bus, k, m);
346         if (r > 0)
347                 bus->rbuffer = NULL;
348         else
349                 close_kdbus_msg(k);
350
351         return r;
352 }
353
354 int bus_kernel_create(const char *name, char **s) {
355         struct kdbus_cmd_fname *fname;
356         size_t l;
357         int fd;
358         char *p;
359
360         assert(name);
361         assert(s);
362
363         fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
364         if (fd < 0)
365                 return -errno;
366
367         l = strlen(name);
368         fname = alloca(offsetof(struct kdbus_cmd_fname, name) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1);
369         sprintf(fname->name, "%lu-%s", (unsigned long) getuid(), name);
370         fname->size = offsetof(struct kdbus_cmd_fname, name) + strlen(fname->name) + 1;
371         fname->kernel_flags = KDBUS_CMD_FNAME_ACCESS_WORLD;
372         fname->user_flags = 0;
373
374         p = strjoin("/dev/kdbus/", fname->name, "/bus", NULL);
375         if (!p)
376                 return -ENOMEM;
377
378         if (ioctl(fd, KDBUS_CMD_BUS_MAKE, &fname) < 0) {
379                 close_nointr_nofail(fd);
380                 free(p);
381                 return -errno;
382         }
383
384         if (s)
385                 *s = p;
386
387         return fd;
388 }