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