chiark / gitweb /
83379d1430408f6807f53a90edb28b5a38174a61
[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         struct kdbus_creds *creds = NULL;
228         int r;
229
230         assert(bus);
231         assert(k);
232         assert(ret);
233
234         if (k->payload_type != KDBUS_PAYLOAD_DBUS1)
235                 return 0;
236
237         KDBUS_MSG_FOREACH_DATA(d, k) {
238                 size_t l;
239
240                 l = d->size - offsetof(struct kdbus_msg_data, data);
241
242                 if (d->type == KDBUS_MSG_PAYLOAD) {
243
244                         if (!h) {
245                                 if (l < sizeof(struct bus_header))
246                                         return -EBADMSG;
247
248                                 h = (struct bus_header*) d->data;
249                         }
250
251                         n_payload++;
252                         n_bytes += l;
253
254                 } else if (d->type == KDBUS_MSG_UNIX_FDS) {
255                         int *f;
256                         unsigned j;
257
258                         j = l / sizeof(int);
259                         f = realloc(fds, sizeof(int) * (n_fds + j));
260                         if (!f)
261                                 return -ENOMEM;
262
263                         fds = f;
264                         memcpy(fds + n_fds, d->fds, j);
265                         n_fds += j;
266
267                 } else if (d->type == KDBUS_MSG_SRC_CREDS)
268                         creds = &d->creds;
269         }
270
271         if (!h)
272                 return -EBADMSG;
273
274         r = bus_header_size(h, &total);
275         if (r < 0)
276                 return r;
277
278         if (n_bytes != total)
279                 return -EBADMSG;
280
281         r = bus_message_from_header(h, sizeof(struct bus_header), fds, n_fds, NULL, NULL, 0, &m);
282         if (r < 0)
283                 return r;
284
285         KDBUS_MSG_FOREACH_DATA(d, k) {
286                 size_t l;
287
288                 if (d->type != KDBUS_MSG_PAYLOAD)
289                         continue;
290
291                 l = d->size - offsetof(struct kdbus_msg_data, data);
292                 if (idx == sizeof(struct bus_header) &&
293                     l == BUS_MESSAGE_FIELDS_SIZE(m))
294                         m->fields = d->data;
295                 else if (idx == sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) &&
296                          l == BUS_MESSAGE_BODY_SIZE(m))
297                         m->body = d->data;
298                 else if (!(idx == 0 && l == sizeof(struct bus_header)) &&
299                          !(idx == sizeof(struct bus_header) + BUS_MESSAGE_FIELDS_SIZE(m))) {
300                         sd_bus_message_unref(m);
301                         return -EBADMSG;
302                 }
303
304                 idx += l;
305         }
306
307         if (creds) {
308                 m->uid = creds->uid;
309                 m->gid = creds->gid;
310                 m->pid = creds->pid;
311                 m->tid = creds->tid;
312                 m->uid_valid = m->gid_valid = true;
313         }
314
315         r = bus_message_parse_fields(m);
316         if (r < 0) {
317                 sd_bus_message_unref(m);
318                 return r;
319         }
320
321         /* We take possession of the kmsg struct now */
322         m->kdbus = k;
323         m->free_kdbus = true;
324         m->free_fds = true;
325
326         fds = NULL;
327
328         *ret = m;
329         return 1;
330 }
331
332 int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) {
333         struct kdbus_msg *k;
334         size_t sz = 128;
335         int r;
336
337         assert(bus);
338         assert(m);
339
340         for (;;) {
341                 void *q;
342
343                 q = realloc(bus->rbuffer, sz);
344                 if (!q)
345                         return -errno;
346
347                 k = bus->rbuffer = q;
348                 k->size = sz;
349
350                 r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, bus->rbuffer);
351                 if (r >= 0)
352                         break;
353
354                 if (errno == EAGAIN)
355                         return 0;
356
357                 if (errno != ENOBUFS)
358                         return -errno;
359
360                 sz *= 2;
361         }
362
363         r = bus_kernel_make_message(bus, k, m);
364         if (r > 0)
365                 bus->rbuffer = NULL;
366         else
367                 close_kdbus_msg(k);
368
369         return r;
370 }
371
372 int bus_kernel_create(const char *name, char **s) {
373         struct kdbus_cmd_fname *fname;
374         size_t l;
375         int fd;
376         char *p;
377
378         assert(name);
379         assert(s);
380
381         fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
382         if (fd < 0)
383                 return -errno;
384
385         l = strlen(name);
386         fname = alloca(offsetof(struct kdbus_cmd_fname, name) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1);
387         sprintf(fname->name, "%lu-%s", (unsigned long) getuid(), name);
388         fname->size = offsetof(struct kdbus_cmd_fname, name) + strlen(fname->name) + 1;
389         fname->kernel_flags = KDBUS_CMD_FNAME_ACCESS_WORLD;
390         fname->user_flags = 0;
391
392         p = strjoin("/dev/kdbus/", fname->name, "/bus", NULL);
393         if (!p)
394                 return -ENOMEM;
395
396         if (ioctl(fd, KDBUS_CMD_BUS_MAKE, fname) < 0) {
397                 close_nointr_nofail(fd);
398                 free(p);
399                 return -errno;
400         }
401
402         if (s)
403                 *s = p;
404
405         return fd;
406 }