]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-bus/bus-kernel.c
bus: add sd_bus_message_append_string_space() for zero-copy string appending
[thirdparty/systemd.git] / src / libsystemd-bus / bus-kernel.c
CommitLineData
6629161f
LP
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
7211f918
LP
22#ifdef HAVE_VALGRIND_MEMCHECK_H
23#include <valgrind/memcheck.h>
24#endif
25
6629161f 26#include <fcntl.h>
c556fe79 27#include <malloc.h>
6629161f
LP
28
29#include "util.h"
30
31#include "bus-internal.h"
32#include "bus-message.h"
33#include "bus-kernel.h"
a56f19c4 34#include "bus-bloom.h"
6629161f 35
6133cee2
KS
36#define KDBUS_ITEM_NEXT(item) \
37 (typeof(item))(((uint8_t *)item) + ALIGN8((item)->size))
9097fe29 38
6133cee2
KS
39#define KDBUS_ITEM_FOREACH(item, head) \
40 for (item = (head)->items; \
41 (uint8_t *)(item) < (uint8_t *)(head) + (head)->size; \
42 item = KDBUS_ITEM_NEXT(item))
6629161f 43
febfd508
KS
44#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data)
45#define KDBUS_ITEM_SIZE(s) ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE)
46
6629161f
LP
47static int parse_unique_name(const char *s, uint64_t *id) {
48 int r;
49
50 assert(s);
51 assert(id);
52
53 if (!startswith(s, ":1."))
54 return 0;
55
56 r = safe_atou64(s + 3, id);
57 if (r < 0)
58 return r;
59
60 return 1;
61}
62
febfd508 63static void append_payload_vec(struct kdbus_item **d, const void *p, size_t sz) {
6629161f
LP
64 assert(d);
65 assert(p);
66 assert(sz > 0);
67
e86b80b8
LP
68 *d = ALIGN8_PTR(*d);
69
febfd508 70 (*d)->size = offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec);
6629161f 71 (*d)->type = KDBUS_MSG_PAYLOAD_VEC;
e0a974b4 72 (*d)->vec.address = (intptr_t) p;
6629161f
LP
73 (*d)->vec.size = sz;
74
febfd508 75 *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
6629161f
LP
76}
77
febfd508 78static void append_destination(struct kdbus_item **d, const char *s, size_t length) {
6629161f 79 assert(d);
b5baa8fe 80 assert(s);
6629161f 81
e86b80b8
LP
82 *d = ALIGN8_PTR(*d);
83
febfd508 84 (*d)->size = offsetof(struct kdbus_item, str) + length + 1;
6629161f 85 (*d)->type = KDBUS_MSG_DST_NAME;
51038c03 86 memcpy((*d)->str, s, length + 1);
6629161f 87
febfd508 88 *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
6629161f
LP
89}
90
febfd508 91static void* append_bloom(struct kdbus_item **d, size_t length) {
a56f19c4
LP
92 void *r;
93
b5baa8fe 94 assert(d);
b5baa8fe
LP
95
96 *d = ALIGN8_PTR(*d);
97
febfd508 98 (*d)->size = offsetof(struct kdbus_item, data) + length;
b5baa8fe 99 (*d)->type = KDBUS_MSG_BLOOM;
a56f19c4 100 r = (*d)->data;
b5baa8fe 101
febfd508 102 *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
a56f19c4
LP
103
104 return r;
105}
106
febfd508 107static void append_fds(struct kdbus_item **d, const int fds[], unsigned n_fds) {
9097fe29
LP
108 assert(d);
109 assert(fds);
110 assert(n_fds > 0);
111
112 *d = ALIGN8_PTR(*d);
febfd508 113 (*d)->size = offsetof(struct kdbus_item, fds) + sizeof(int) * n_fds;
9097fe29
LP
114 (*d)->type = KDBUS_MSG_UNIX_FDS;
115 memcpy((*d)->fds, fds, sizeof(int) * n_fds);
116
febfd508 117 *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
9097fe29
LP
118}
119
a56f19c4
LP
120static int bus_message_setup_bloom(sd_bus_message *m, void *bloom) {
121 unsigned i;
122 int r;
123
124 assert(m);
125 assert(bloom);
126
127 memset(bloom, 0, BLOOM_SIZE);
128
129 bloom_add_pair(bloom, "message-type", bus_message_type_to_string(m->header->type));
130
131 if (m->interface)
132 bloom_add_pair(bloom, "interface", m->interface);
133 if (m->member)
134 bloom_add_pair(bloom, "member", m->member);
135 if (m->path) {
136 bloom_add_pair(bloom, "path", m->path);
137 bloom_add_prefixes(bloom, "path-slash-prefix", m->path, '/');
138 }
139
140 r = sd_bus_message_rewind(m, true);
141 if (r < 0)
142 return r;
143
144 for (i = 0; i < 64; i++) {
145 char type;
146 const char *t;
147 char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
148 char *e;
149
150 r = sd_bus_message_peek_type(m, &type, NULL);
151 if (r < 0)
152 return r;
153
154 if (type != SD_BUS_TYPE_STRING &&
155 type != SD_BUS_TYPE_OBJECT_PATH &&
156 type != SD_BUS_TYPE_SIGNATURE)
157 break;
158
159 r = sd_bus_message_read_basic(m, type, &t);
160 if (r < 0)
161 return r;
162
163 e = stpcpy(buf, "arg");
164 if (i < 10)
165 *(e++) = '0' + i;
166 else {
167 *(e++) = '0' + (i / 10);
168 *(e++) = '0' + (i % 10);
169 }
170
171 *e = 0;
172 bloom_add_pair(bloom, buf, t);
173
174 strcpy(e, "-dot-prefix");
175 bloom_add_prefixes(bloom, buf, t, '.');
176 strcpy(e, "-slash-prefix");
177 bloom_add_prefixes(bloom, buf, t, '/');
178 }
179
180 return 0;
b5baa8fe
LP
181}
182
5b7d4c1c 183static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
febfd508 184 struct kdbus_item *d;
6629161f
LP
185 bool well_known;
186 uint64_t unique;
187 size_t sz, dl;
188 int r;
189
5b7d4c1c 190 assert(b);
6629161f
LP
191 assert(m);
192 assert(m->sealed);
e9a967f9
LP
193
194 if (m->kdbus)
195 return 0;
6629161f
LP
196
197 if (m->destination) {
198 r = parse_unique_name(m->destination, &unique);
199 if (r < 0)
200 return r;
201
202 well_known = r == 0;
203 } else
204 well_known = false;
205
b1454bf0 206 sz = offsetof(struct kdbus_msg, items);
6629161f 207
69aec65c 208 /* Add in fixed header, fields header and payload */
febfd508 209 sz += 3 * ALIGN8(offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec));
6629161f 210
69aec65c 211 /* Add space for bloom filter */
febfd508 212 sz += ALIGN8(offsetof(struct kdbus_item, data) + BLOOM_SIZE);
b5baa8fe 213
6629161f
LP
214 /* Add in well-known destination header */
215 if (well_known) {
216 dl = strlen(m->destination);
febfd508 217 sz += ALIGN8(offsetof(struct kdbus_item, str) + dl + 1);
6629161f
LP
218 }
219
9097fe29
LP
220 /* Add space for unix fds */
221 if (m->n_fds > 0)
febfd508 222 sz += ALIGN8(offsetof(struct kdbus_item, fds) + sizeof(int)*m->n_fds);
9097fe29 223
c556fe79 224 m->kdbus = memalign(8, sz);
6629161f
LP
225 if (!m->kdbus)
226 return -ENOMEM;
227
d9115e18
LP
228 memset(m->kdbus, 0, sz);
229
6629161f
LP
230 m->kdbus->flags =
231 ((m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
232 ((m->header->flags & SD_BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
233 m->kdbus->dst_id =
234 well_known ? 0 :
b5baa8fe 235 m->destination ? unique : KDBUS_DST_ID_BROADCAST;
6629161f
LP
236 m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS1;
237 m->kdbus->cookie = m->header->serial;
238
239 m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC;
240
b1454bf0 241 d = m->kdbus->items;
6629161f
LP
242
243 if (well_known)
244 append_destination(&d, m->destination, dl);
245
246 append_payload_vec(&d, m->header, sizeof(*m->header));
247
69aec65c
LP
248 if (m->fields)
249 append_payload_vec(&d, m->fields, ALIGN8(m->header->fields_size));
6629161f
LP
250
251 if (m->body)
252 append_payload_vec(&d, m->body, m->header->body_size);
253
5b7d4c1c
LP
254 if (m->kdbus->dst_id == KDBUS_DST_ID_BROADCAST) {
255 void *p;
256
a56f19c4
LP
257 p = append_bloom(&d, BLOOM_SIZE);
258 r = bus_message_setup_bloom(m, p);
259 if (r < 0) {
260 free(m->kdbus);
261 m->kdbus = NULL;
262 return -r;
263 }
5b7d4c1c 264 }
b5baa8fe 265
9097fe29
LP
266 if (m->n_fds > 0)
267 append_fds(&d, m->fds, m->n_fds);
268
e9a967f9 269 m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus;
6629161f
LP
270 assert(m->kdbus->size <= sz);
271
e9a967f9
LP
272 m->free_kdbus = true;
273
6629161f
LP
274 return 0;
275}
276
277int bus_kernel_take_fd(sd_bus *b) {
69aec65c
LP
278 struct kdbus_cmd_hello hello = {
279 .conn_flags =
799e7ea8
KS
280 KDBUS_HELLO_ACCEPT_FD|
281 KDBUS_HELLO_ATTACH_COMM|
282 KDBUS_HELLO_ATTACH_EXE|
283 KDBUS_HELLO_ATTACH_CMDLINE|
284 KDBUS_HELLO_ATTACH_CGROUP|
285 KDBUS_HELLO_ATTACH_CAPS|
286 KDBUS_HELLO_ATTACH_SECLABEL|
287 KDBUS_HELLO_ATTACH_AUDIT
69aec65c 288 };
6629161f
LP
289 int r;
290
291 assert(b);
292
f08838da
LP
293 if (b->is_server)
294 return -EINVAL;
295
6629161f
LP
296 r = ioctl(b->input_fd, KDBUS_CMD_HELLO, &hello);
297 if (r < 0)
298 return -errno;
299
5b7d4c1c
LP
300 /* The higher 32bit of both flags fields are considered
301 * 'incompatible flags'. Refuse them all for now. */
302 if (hello.bus_flags > 0xFFFFFFFFULL ||
303 hello.conn_flags > 0xFFFFFFFFULL)
304 return -ENOTSUP;
305
a56f19c4
LP
306 if (hello.bloom_size != BLOOM_SIZE)
307 return -ENOTSUP;
308
de297575
LP
309 if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello.id) < 0)
310 return -ENOMEM;
311
6629161f 312 b->is_kernel = true;
f08838da 313 b->bus_client = true;
9097fe29 314 b->can_fds = true;
6629161f
LP
315
316 r = bus_start_running(b);
317 if (r < 0)
318 return r;
319
320 return 1;
321}
322
323int bus_kernel_connect(sd_bus *b) {
324 assert(b);
325 assert(b->input_fd < 0);
326 assert(b->output_fd < 0);
327 assert(b->kernel);
328
f08838da
LP
329 if (b->is_server)
330 return -EINVAL;
331
6629161f 332 b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
c320885c 333 if (b->input_fd < 0)
6629161f
LP
334 return -errno;
335
336 b->output_fd = b->input_fd;
337
338 return bus_kernel_take_fd(b);
339}
340
341int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
342 int r;
343
344 assert(bus);
345 assert(m);
346 assert(bus->state == BUS_RUNNING);
347
5b7d4c1c 348 r = bus_message_setup_kmsg(bus, m);
6629161f
LP
349 if (r < 0)
350 return r;
351
352 r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
353 if (r < 0)
354 return errno == EAGAIN ? 0 : -errno;
355
51038c03 356 return 1;
6629161f
LP
357}
358
359static void close_kdbus_msg(struct kdbus_msg *k) {
febfd508 360 struct kdbus_item *d;
6629161f 361
6133cee2 362 KDBUS_ITEM_FOREACH(d, k) {
6629161f
LP
363
364 if (d->type != KDBUS_MSG_UNIX_FDS)
365 continue;
366
febfd508 367 close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int));
6629161f
LP
368 }
369}
370
371static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_message **ret) {
372 sd_bus_message *m = NULL;
febfd508 373 struct kdbus_item *d;
6629161f
LP
374 unsigned n_payload = 0, n_fds = 0;
375 _cleanup_free_ int *fds = NULL;
376 struct bus_header *h = NULL;
377 size_t total, n_bytes = 0, idx = 0;
69aec65c 378 const char *destination = NULL, *seclabel = NULL;
6629161f
LP
379 int r;
380
381 assert(bus);
382 assert(k);
383 assert(ret);
384
385 if (k->payload_type != KDBUS_PAYLOAD_DBUS1)
386 return 0;
387
6133cee2 388 KDBUS_ITEM_FOREACH(d, k) {
6629161f
LP
389 size_t l;
390
febfd508 391 l = d->size - offsetof(struct kdbus_item, data);
6629161f
LP
392
393 if (d->type == KDBUS_MSG_PAYLOAD) {
394
395 if (!h) {
396 if (l < sizeof(struct bus_header))
397 return -EBADMSG;
398
399 h = (struct bus_header*) d->data;
400 }
401
402 n_payload++;
403 n_bytes += l;
404
405 } else if (d->type == KDBUS_MSG_UNIX_FDS) {
406 int *f;
407 unsigned j;
408
409 j = l / sizeof(int);
410 f = realloc(fds, sizeof(int) * (n_fds + j));
411 if (!f)
412 return -ENOMEM;
413
414 fds = f;
9097fe29 415 memcpy(fds + n_fds, d->fds, sizeof(int) * j);
6629161f 416 n_fds += j;
f9be01f3 417
69aec65c 418 } else if (d->type == KDBUS_MSG_DST_NAME)
51038c03 419 destination = d->str;
69aec65c
LP
420 else if (d->type == KDBUS_MSG_SRC_SECLABEL)
421 seclabel = d->str;
6629161f
LP
422 }
423
424 if (!h)
425 return -EBADMSG;
426
427 r = bus_header_size(h, &total);
428 if (r < 0)
429 return r;
430
431 if (n_bytes != total)
432 return -EBADMSG;
433
69aec65c 434 r = bus_message_from_header(h, sizeof(struct bus_header), fds, n_fds, NULL, seclabel, 0, &m);
6629161f
LP
435 if (r < 0)
436 return r;
437
6133cee2 438 KDBUS_ITEM_FOREACH(d, k) {
6629161f
LP
439 size_t l;
440
febfd508 441 l = d->size - offsetof(struct kdbus_item, data);
6629161f 442
69aec65c 443 if (d->type == KDBUS_MSG_PAYLOAD) {
f9be01f3 444
69aec65c
LP
445 if (idx == sizeof(struct bus_header) &&
446 l == ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)))
447 m->fields = d->data;
448 else if (idx == sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) &&
449 l == BUS_MESSAGE_BODY_SIZE(m))
450 m->body = d->data;
451 else if (!(idx == 0 && l == sizeof(struct bus_header))) {
452 sd_bus_message_unref(m);
453 return -EBADMSG;
454 }
455
456 idx += l;
457 } else if (d->type == KDBUS_MSG_SRC_CREDS) {
458 m->pid_starttime = d->creds.starttime / NSEC_PER_USEC;
459 m->uid = d->creds.uid;
460 m->gid = d->creds.gid;
461 m->pid = d->creds.pid;
462 m->tid = d->creds.tid;
463 m->uid_valid = m->gid_valid = true;
464 } else if (d->type == KDBUS_MSG_TIMESTAMP) {
465 m->realtime = d->timestamp.realtime_ns / NSEC_PER_USEC;
466 m->monotonic = d->timestamp.monotonic_ns / NSEC_PER_USEC;
467 } else if (d->type == KDBUS_MSG_SRC_PID_COMM)
468 m->comm = d->str;
469 else if (d->type == KDBUS_MSG_SRC_TID_COMM)
470 m->tid_comm = d->str;
471 else if (d->type == KDBUS_MSG_SRC_EXE)
472 m->exe = d->str;
77930f11
LP
473 else if (d->type == KDBUS_MSG_SRC_CMDLINE) {
474 m->cmdline = d->str;
475 m->cmdline_length = l;
4a875b61
LP
476 } else if (d->type == KDBUS_MSG_SRC_CGROUP)
477 m->cgroup = d->str;
120f919e
LP
478 else if (d->type == KDBUS_MSG_SRC_AUDIT)
479 m->audit = &d->audit;
102ea8e4
LP
480 else if (d->type == KDBUS_MSG_SRC_CAPS) {
481 m->capability = d->data;
482 m->capability_size = l;
483 } else
77930f11 484 log_debug("Got unknown field from kernel %llu", d->type);
69aec65c 485 }
acb5a3cb 486
6629161f
LP
487 r = bus_message_parse_fields(m);
488 if (r < 0) {
489 sd_bus_message_unref(m);
490 return r;
491 }
492
51038c03
LP
493 if (k->src_id == KDBUS_SRC_ID_KERNEL)
494 m->sender = "org.freedesktop.DBus";
495 else {
496 snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id);
497 m->sender = m->sender_buffer;
498 }
499
500 if (!m->destination) {
501 if (destination)
502 m->destination = destination;
503 else if (k->dst_id != KDBUS_DST_ID_WELL_KNOWN_NAME &&
504 k->dst_id != KDBUS_DST_ID_BROADCAST) {
505 snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id);
506 m->destination = m->destination_buffer;
507 }
508 }
509
6629161f
LP
510 /* We take possession of the kmsg struct now */
511 m->kdbus = k;
512 m->free_kdbus = true;
513 m->free_fds = true;
514
515 fds = NULL;
516
517 *ret = m;
518 return 1;
519}
520
521int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) {
522 struct kdbus_msg *k;
f08838da 523 size_t sz = 1024;
6629161f
LP
524 int r;
525
526 assert(bus);
527 assert(m);
528
529 for (;;) {
530 void *q;
531
c556fe79 532 q = memalign(8, sz);
6629161f
LP
533 if (!q)
534 return -errno;
535
8e738242 536 free(bus->rbuffer);
6629161f
LP
537 k = bus->rbuffer = q;
538 k->size = sz;
539
7211f918
LP
540 /* Let's tell valgrind that there's really no need to
541 * initialize this fully. This should be removed again
542 * when valgrind learned the kdbus ioctls natively. */
beca33ee 543#ifdef HAVE_VALGRIND_MEMCHECK_H
7211f918 544 VALGRIND_MAKE_MEM_DEFINED(k, sz);
beca33ee 545#endif
7211f918 546
6629161f
LP
547 r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, bus->rbuffer);
548 if (r >= 0)
549 break;
550
551 if (errno == EAGAIN)
552 return 0;
553
e9a967f9 554 if (errno != ENOBUFS)
6629161f
LP
555 return -errno;
556
557 sz *= 2;
558 }
559
560 r = bus_kernel_make_message(bus, k, m);
561 if (r > 0)
562 bus->rbuffer = NULL;
563 else
564 close_kdbus_msg(k);
565
51038c03 566 return r < 0 ? r : 1;
6629161f
LP
567}
568
569int bus_kernel_create(const char *name, char **s) {
5b7d4c1c 570 struct kdbus_cmd_bus_make *make;
febfd508 571 struct kdbus_item *n, *cg;
6629161f
LP
572 size_t l;
573 int fd;
574 char *p;
575
576 assert(name);
577 assert(s);
578
579 fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
580 if (fd < 0)
581 return -errno;
582
583 l = strlen(name);
a2cef833 584 make = alloca0(offsetof(struct kdbus_cmd_bus_make, items) +
febfd508
KS
585 KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t) +
586 KDBUS_ITEM_HEADER_SIZE + DECIMAL_STR_MAX(uid_t) + 1 + l + 1);
a2cef833
KS
587
588 cg = make->items;
799e7ea8 589 cg->type = KDBUS_MAKE_CGROUP;
a2cef833 590 cg->data64[0] = 1;
febfd508 591 cg->size = KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t);
a2cef833
KS
592
593 n = KDBUS_ITEM_NEXT(cg);
799e7ea8 594 n->type = KDBUS_MAKE_NAME;
a2cef833 595 sprintf(n->str, "%lu-%s", (unsigned long) getuid(), name);
febfd508 596 n->size = KDBUS_ITEM_HEADER_SIZE + strlen(n->str) + 1;
a2cef833
KS
597
598 make->size = offsetof(struct kdbus_cmd_bus_make, items) + cg->size + n->size;
799e7ea8 599 make->flags = KDBUS_MAKE_ACCESS_WORLD | KDBUS_MAKE_POLICY_OPEN;
5b7d4c1c 600 make->bus_flags = 0;
a56f19c4 601 make->bloom_size = BLOOM_SIZE;
a56f19c4 602 assert_cc(BLOOM_SIZE % 8 == 0);
5b7d4c1c 603
a2cef833 604 p = strjoin("/dev/kdbus/", n->str, "/bus", NULL);
6629161f
LP
605 if (!p)
606 return -ENOMEM;
607
5b7d4c1c 608 if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) {
6629161f
LP
609 close_nointr_nofail(fd);
610 free(p);
611 return -errno;
612 }
613
614 if (s)
615 *s = p;
616
617 return fd;
618}