summaryrefslogtreecommitdiff
path: root/src/libsystemd-bus
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsystemd-bus')
-rw-r--r--src/libsystemd-bus/bus-internal.h3
-rw-r--r--src/libsystemd-bus/bus-kernel.c388
-rw-r--r--src/libsystemd-bus/bus-kernel.h32
-rw-r--r--src/libsystemd-bus/bus-message.c90
-rw-r--r--src/libsystemd-bus/bus-message.h26
-rw-r--r--src/libsystemd-bus/bus-socket.c2
-rw-r--r--src/libsystemd-bus/kdbus.h360
-rw-r--r--src/libsystemd-bus/sd-bus.c93
8 files changed, 953 insertions, 41 deletions
diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h
index 6ff3163cfd..7cca9998bb 100644
--- a/src/libsystemd-bus/bus-internal.h
+++ b/src/libsystemd-bus/bus-internal.h
@@ -81,6 +81,7 @@ struct sd_bus {
int input_fd, output_fd;
int message_version;
+ bool is_kernel:1;
bool negotiate_fds:1;
bool can_fds:1;
bool bus_client:1;
@@ -122,6 +123,8 @@ struct sd_bus {
} sockaddr;
socklen_t sockaddr_size;
+ char *kernel;
+
sd_id128_t server_id;
char *address;
diff --git a/src/libsystemd-bus/bus-kernel.c b/src/libsystemd-bus/bus-kernel.c
new file mode 100644
index 0000000000..c10fcc54df
--- /dev/null
+++ b/src/libsystemd-bus/bus-kernel.c
@@ -0,0 +1,388 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+
+#include "util.h"
+
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-kernel.h"
+
+#define KDBUS_MSG_FOREACH_DATA(d, k) \
+ for ((d) = (k)->data; \
+ (uint8_t*) (d) < (uint8_t*) (k) + (k)->size; \
+ (d) = (struct kdbus_msg_data*) ((uint8_t*) (d) + ALIGN8((d)->size)))
+
+
+
+static int parse_unique_name(const char *s, uint64_t *id) {
+ int r;
+
+ assert(s);
+ assert(id);
+
+ if (!startswith(s, ":1."))
+ return 0;
+
+ r = safe_atou64(s + 3, id);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static void append_payload_vec(struct kdbus_msg_data **d, const void *p, size_t sz) {
+ assert(d);
+ assert(p);
+ assert(sz > 0);
+
+ (*d)->size = offsetof(struct kdbus_msg_data, vec) + sizeof(struct kdbus_vec);
+ (*d)->type = KDBUS_MSG_PAYLOAD_VEC;
+ (*d)->vec.address = (uint64_t) p;
+ (*d)->vec.size = sz;
+
+ *d = (struct kdbus_msg_data*) ((uint8_t*) *d + ALIGN8((*d)->size));
+}
+
+static void append_destination(struct kdbus_msg_data **d, const char *s, size_t length) {
+ assert(d);
+ assert(d);
+
+ (*d)->size = offsetof(struct kdbus_msg_data, data) + length + 1;
+ (*d)->type = KDBUS_MSG_DST_NAME;
+ memcpy((*d)->data, s, length + 1);
+
+ *d = (struct kdbus_msg_data*) ((uint8_t*) *d + ALIGN8((*d)->size));
+}
+
+static int bus_message_setup_kmsg(sd_bus_message *m) {
+ struct kdbus_msg_data *d;
+ bool well_known;
+ uint64_t unique;
+ size_t sz, dl;
+ int r;
+
+ assert(m);
+ assert(m->sealed);
+ assert(!m->kdbus);
+
+ if (m->destination) {
+ r = parse_unique_name(m->destination, &unique);
+ if (r < 0)
+ return r;
+
+ well_known = r == 0;
+ } else
+ well_known = false;
+
+ sz = offsetof(struct kdbus_msg, data);
+
+ /* Add in fixed header, fields header, fields header padding and payload */
+ sz += 4 * ALIGN8(offsetof(struct kdbus_msg_data, vec) + sizeof(struct kdbus_vec));
+
+ /* Add in well-known destination header */
+ if (well_known) {
+ dl = strlen(m->destination);
+ sz += ALIGN8(offsetof(struct kdbus_msg, data) + dl + 1);
+ }
+
+ m->kdbus = malloc0(sz);
+ if (!m->kdbus)
+ return -ENOMEM;
+
+ m->kdbus->flags =
+ ((m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
+ ((m->header->flags & SD_BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
+ m->kdbus->dst_id =
+ well_known ? 0 :
+ m->destination ? unique : (uint64_t) -1;
+ m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS1;
+ m->kdbus->cookie = m->header->serial;
+
+ m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC;
+
+ d = m->kdbus->data;
+
+ if (well_known)
+ append_destination(&d, m->destination, dl);
+
+ append_payload_vec(&d, m->header, sizeof(*m->header));
+
+ if (m->fields) {
+ append_payload_vec(&d, m->fields, m->header->fields_size);
+
+ if (m->header->fields_size % 8 != 0) {
+ static const uint8_t padding[7] = {};
+
+ append_payload_vec(&d, padding, 8 - (m->header->fields_size % 8));
+ }
+ }
+
+ if (m->body)
+ append_payload_vec(&d, m->body, m->header->body_size);
+
+ m->kdbus->size = (uint8_t*) m - (uint8_t*) m->kdbus;
+ assert(m->kdbus->size <= sz);
+
+ return 0;
+}
+
+int bus_kernel_take_fd(sd_bus *b) {
+ struct kdbus_cmd_hello hello = {};
+ int r;
+
+ assert(b);
+
+ r = ioctl(b->input_fd, KDBUS_CMD_HELLO, &hello);
+ if (r < 0)
+ return -errno;
+
+ b->is_kernel = true;
+
+ r = bus_start_running(b);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_kernel_connect(sd_bus *b) {
+ assert(b);
+ assert(b->input_fd < 0);
+ assert(b->output_fd < 0);
+ assert(b->kernel);
+
+ b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (b->input_fd)
+ return -errno;
+
+ b->output_fd = b->input_fd;
+
+ return bus_kernel_take_fd(b);
+}
+
+int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(bus->state == BUS_RUNNING);
+
+ r = bus_message_setup_kmsg(m);
+ if (r < 0)
+ return r;
+
+ r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
+ if (r < 0)
+ return errno == EAGAIN ? 0 : -errno;
+
+ return 0;
+}
+
+static void close_kdbus_msg(struct kdbus_msg *k) {
+ struct kdbus_msg_data *d;
+
+ KDBUS_MSG_FOREACH_DATA(d, k) {
+
+ if (d->type != KDBUS_MSG_UNIX_FDS)
+ continue;
+
+ close_many(d->fds, (d->size - offsetof(struct kdbus_msg_data, fds)) / sizeof(int));
+ }
+}
+
+static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_message **ret) {
+ sd_bus_message *m = NULL;
+ struct kdbus_msg_data *d;
+ unsigned n_payload = 0, n_fds = 0;
+ _cleanup_free_ int *fds = NULL;
+ struct bus_header *h = NULL;
+ size_t total, n_bytes = 0, idx = 0;
+ int r;
+
+ assert(bus);
+ assert(k);
+ assert(ret);
+
+ if (k->payload_type != KDBUS_PAYLOAD_DBUS1)
+ return 0;
+
+ KDBUS_MSG_FOREACH_DATA(d, k) {
+ size_t l;
+
+ l = d->size - offsetof(struct kdbus_msg_data, data);
+
+ if (d->type == KDBUS_MSG_PAYLOAD) {
+
+ if (!h) {
+ if (l < sizeof(struct bus_header))
+ return -EBADMSG;
+
+ h = (struct bus_header*) d->data;
+ }
+
+ n_payload++;
+ n_bytes += l;
+
+ } else if (d->type == KDBUS_MSG_UNIX_FDS) {
+ int *f;
+ unsigned j;
+
+ j = l / sizeof(int);
+ f = realloc(fds, sizeof(int) * (n_fds + j));
+ if (!f)
+ return -ENOMEM;
+
+ fds = f;
+ memcpy(fds + n_fds, d->fds, j);
+ n_fds += j;
+ }
+ }
+
+ if (!h)
+ return -EBADMSG;
+
+ r = bus_header_size(h, &total);
+ if (r < 0)
+ return r;
+
+ if (n_bytes != total)
+ return -EBADMSG;
+
+ r = bus_message_from_header(h, sizeof(struct bus_header), fds, n_fds, NULL, NULL, 0, &m);
+ if (r < 0)
+ return r;
+
+ KDBUS_MSG_FOREACH_DATA(d, k) {
+ size_t l;
+
+ if (d->type != KDBUS_MSG_PAYLOAD)
+ continue;
+
+ l = d->size - offsetof(struct kdbus_msg_data, data);
+
+ if (idx == sizeof(struct bus_header) &&
+ l == BUS_MESSAGE_FIELDS_SIZE(m))
+ m->fields = d->data;
+ else if (idx == sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) &&
+ l == BUS_MESSAGE_BODY_SIZE(m))
+ m->body = d->data;
+ else {
+ sd_bus_message_unref(m);
+ return -EBADMSG;
+ }
+
+ idx += l;
+ }
+
+ r = bus_message_parse_fields(m);
+ if (r < 0) {
+ sd_bus_message_unref(m);
+ return r;
+ }
+
+ /* We take possession of the kmsg struct now */
+ m->kdbus = k;
+ m->free_kdbus = true;
+ m->free_fds = true;
+
+ fds = NULL;
+
+ *ret = m;
+ return 1;
+}
+
+int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) {
+ struct kdbus_msg *k;
+ size_t sz = 128;
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ for (;;) {
+ void *q;
+
+ q = realloc(bus->rbuffer, sz);
+ if (!q)
+ return -errno;
+
+ k = bus->rbuffer = q;
+ k->size = sz;
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, bus->rbuffer);
+ if (r >= 0)
+ break;
+
+ if (errno == EAGAIN)
+ return 0;
+
+ if (errno != -EMSGSIZE)
+ return -errno;
+
+ sz *= 2;
+ }
+
+ r = bus_kernel_make_message(bus, k, m);
+ if (r > 0)
+ bus->rbuffer = NULL;
+ else
+ close_kdbus_msg(k);
+
+ return r;
+}
+
+int bus_kernel_create(const char *name, char **s) {
+ struct kdbus_cmd_fname *fname;
+ size_t l;
+ int fd;
+ char *p;
+
+ assert(name);
+ assert(s);
+
+ fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ l = strlen(name);
+ fname = alloca(offsetof(struct kdbus_cmd_fname, name) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1);
+ sprintf(fname->name, "%lu-%s", (unsigned long) getuid(), name);
+ fname->size = offsetof(struct kdbus_cmd_fname, name) + strlen(fname->name) + 1;
+ fname->kernel_flags = KDBUS_CMD_FNAME_ACCESS_WORLD;
+ fname->user_flags = 0;
+
+ p = strjoin("/dev/kdbus/", fname->name, "/bus", NULL);
+ if (!p)
+ return -ENOMEM;
+
+ if (ioctl(fd, KDBUS_CMD_BUS_MAKE, &fname) < 0) {
+ close_nointr_nofail(fd);
+ free(p);
+ return -errno;
+ }
+
+ if (s)
+ *s = p;
+
+ return fd;
+}
diff --git a/src/libsystemd-bus/bus-kernel.h b/src/libsystemd-bus/bus-kernel.h
new file mode 100644
index 0000000000..ac746afe03
--- /dev/null
+++ b/src/libsystemd-bus/bus-kernel.h
@@ -0,0 +1,32 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+int bus_kernel_connect(sd_bus *b);
+int bus_kernel_take_fd(sd_bus *b);
+
+int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m);
+int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m);
+
+int bus_kernel_create(const char *name, char **s);
diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c
index fb4718085b..eeb1d823e8 100644
--- a/src/libsystemd-bus/bus-message.c
+++ b/src/libsystemd-bus/bus-message.c
@@ -25,6 +25,7 @@
#include "util.h"
#include "utf8.h"
#include "strv.h"
+#include "time-util.h"
#include "sd-bus.h"
#include "bus-message.h"
@@ -32,7 +33,6 @@
#include "bus-type.h"
#include "bus-signature.h"
-static int message_parse_fields(sd_bus_message *m);
static int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored);
static void reset_containers(sd_bus_message *m) {
@@ -62,6 +62,9 @@ static void message_free(sd_bus_message *m) {
if (m->free_body)
free(m->body);
+ if (m->free_kdbus)
+ free(m->kdbus);
+
if (m->free_fds) {
close_many(m->fds, m->n_fds);
free(m->fds);
@@ -225,19 +228,19 @@ static int message_append_field_uint32(sd_bus_message *m, uint8_t h, uint32_t x)
return 0;
}
-int bus_message_from_malloc(
+int bus_message_from_header(
void *buffer,
size_t length,
int *fds,
unsigned n_fds,
const struct ucred *ucred,
const char *label,
+ size_t extra,
sd_bus_message **ret) {
sd_bus_message *m;
struct bus_header *h;
- size_t total, fs, bs, label_sz, a;
- int r;
+ size_t a, label_sz;
assert(buffer || length <= 0);
assert(fds || n_fds <= 0);
@@ -256,24 +259,16 @@ int bus_message_from_malloc(
if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID)
return -EBADMSG;
- if (h->endian == SD_BUS_NATIVE_ENDIAN) {
- fs = h->fields_size;
- bs = h->body_size;
- } else if (h->endian == SD_BUS_REVERSE_ENDIAN) {
- fs = bswap_32(h->fields_size);
- bs = bswap_32(h->body_size);
- } else
+ if (h->endian != SD_BUS_LITTLE_ENDIAN &&
+ h->endian != SD_BUS_BIG_ENDIAN)
return -EBADMSG;
- total = sizeof(struct bus_header) + ALIGN8(fs) + bs;
- if (length != total)
- return -EBADMSG;
+ a = ALIGN(sizeof(sd_bus_message)) + ALIGN(extra);
if (label) {
label_sz = strlen(label);
- a = ALIGN(sizeof(sd_bus_message)) + label_sz + 1;
- } else
- a = sizeof(sd_bus_message);
+ a += label_sz + 1;
+ }
m = malloc0(a);
if (!m)
@@ -282,8 +277,6 @@ int bus_message_from_malloc(
m->n_ref = 1;
m->sealed = true;
m->header = h;
- m->fields = (uint8_t*) buffer + sizeof(struct bus_header);
- m->body = (uint8_t*) buffer + sizeof(struct bus_header) + ALIGN8(fs);
m->fds = fds;
m->n_fds = n_fds;
@@ -295,15 +288,43 @@ int bus_message_from_malloc(
}
if (label) {
- m->label = (char*) m + ALIGN(sizeof(sd_bus_message));
+ m->label = (char*) m + ALIGN(sizeof(sd_bus_message)) + ALIGN(extra);
memcpy(m->label, label, label_sz + 1);
}
+ *ret = m;
+ return 0;
+}
+
+int bus_message_from_malloc(
+ void *buffer,
+ size_t length,
+ int *fds,
+ unsigned n_fds,
+ const struct ucred *ucred,
+ const char *label,
+ sd_bus_message **ret) {
+
+ sd_bus_message *m;
+ int r;
+
+ r = bus_message_from_header(buffer, length, fds, n_fds, ucred, label, 0, &m);
+ if (r < 0)
+ return r;
+
+ if (length != BUS_MESSAGE_SIZE(m)) {
+ r = -EBADMSG;
+ goto fail;
+ }
+
+ m->fields = (uint8_t*) buffer + sizeof(struct bus_header);
+ m->body = (uint8_t*) buffer + sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m));
+
m->n_iovec = 1;
m->iovec[0].iov_base = buffer;
m->iovec[0].iov_len = length;
- r = message_parse_fields(m);
+ r = bus_message_parse_fields(m);
if (r < 0)
goto fail;
@@ -2600,7 +2621,7 @@ static int message_skip_fields(
}
}
-static int message_parse_fields(sd_bus_message *m) {
+int bus_message_parse_fields(sd_bus_message *m) {
size_t ri;
int r;
uint32_t unix_fds = 0;
@@ -3041,7 +3062,7 @@ int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) {
assert(buffer);
assert(sz);
- total = bus_message_size(m);
+ total = BUS_MESSAGE_SIZE(m);
p = malloc(total);
if (!p)
@@ -3133,12 +3154,21 @@ const char* bus_message_get_arg(sd_bus_message *m, unsigned i) {
return t;
}
-size_t bus_message_size(sd_bus_message *m) {
- assert(m);
- assert(m->sealed);
+int bus_header_size(struct bus_header *h, size_t *sum) {
+ size_t fs, bs;
+
+ assert(h);
+ assert(sum);
+
+ if (h->endian == SD_BUS_NATIVE_ENDIAN) {
+ fs = h->fields_size;
+ bs = h->body_size;
+ } else if (h->endian == SD_BUS_REVERSE_ENDIAN) {
+ fs = bswap_32(h->fields_size);
+ bs = bswap_32(h->body_size);
+ } else
+ return -EBADMSG;
- return
- sizeof(*m->header) +
- ALIGN8(m->header->fields_size) +
- m->header->body_size;
+ *sum = sizeof(struct bus_header) + ALIGN8(fs) + bs;
+ return 0;
}
diff --git a/src/libsystemd-bus/bus-message.h b/src/libsystemd-bus/bus-message.h
index 126ced0083..3d1bb62334 100644
--- a/src/libsystemd-bus/bus-message.h
+++ b/src/libsystemd-bus/bus-message.h
@@ -27,6 +27,7 @@
#include "macro.h"
#include "sd-bus.h"
+#include "kdbus.h"
struct bus_container {
char enclosing;
@@ -74,11 +75,13 @@ struct sd_bus_message {
bool free_header:1;
bool free_fields:1;
bool free_body:1;
+ bool free_kdbus:1;
bool free_fds:1;
struct bus_header *header;
void *fields;
void *body;
+ struct kdbus_msg *kdbus;
char *label;
@@ -94,6 +97,8 @@ struct sd_bus_message {
unsigned n_iovec;
char *peeked_signature;
+
+ usec_t timeout;
};
#define BUS_MESSAGE_NEED_BSWAP(m) ((m)->header->endian != SD_BUS_NATIVE_ENDIAN)
@@ -122,6 +127,13 @@ static inline uint32_t BUS_MESSAGE_FIELDS_SIZE(sd_bus_message *m) {
return BUS_MESSAGE_BSWAP32(m, m->header->fields_size);
}
+static inline uint32_t BUS_MESSAGE_SIZE(sd_bus_message *m) {
+ return
+ sizeof(struct bus_header) +
+ ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) +
+ BUS_MESSAGE_BODY_SIZE(m);
+}
+
static inline void bus_message_unrefp(sd_bus_message **m) {
sd_bus_message_unref(*m);
}
@@ -133,6 +145,16 @@ int bus_message_dump(sd_bus_message *m);
int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz);
int bus_message_read_strv_extend(sd_bus_message *m, char ***l);
+int bus_message_from_header(
+ void *header,
+ size_t length,
+ int *fds,
+ unsigned n_fds,
+ const struct ucred *ucred,
+ const char *label,
+ size_t extra,
+ sd_bus_message **ret);
+
int bus_message_from_malloc(
void *buffer,
size_t length,
@@ -146,4 +168,6 @@ const char* bus_message_get_arg(sd_bus_message *m, unsigned i);
int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap);
-size_t bus_message_size(sd_bus_message *m);
+int bus_message_parse_fields(sd_bus_message *m);
+
+int bus_header_size(struct bus_header *h, size_t *sum);
diff --git a/src/libsystemd-bus/bus-socket.c b/src/libsystemd-bus/bus-socket.c
index 5e285c9e52..bce81aeffc 100644
--- a/src/libsystemd-bus/bus-socket.c
+++ b/src/libsystemd-bus/bus-socket.c
@@ -762,7 +762,7 @@ int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
assert(idx);
assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
- if (*idx >= bus_message_size(m))
+ if (*idx >= BUS_MESSAGE_SIZE(m))
return 0;
bus_message_setup_iovec(m);
diff --git a/src/libsystemd-bus/kdbus.h b/src/libsystemd-bus/kdbus.h
new file mode 100644
index 0000000000..ba59fd0d37
--- /dev/null
+++ b/src/libsystemd-bus/kdbus.h
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2013 Kay Sievers
+ * Copyright (C) 2013 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+ * Copyright (C) 2013 Linux Foundation
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef _KDBUS_H_
+#define _KDBUS_H_
+
+#ifndef __KERNEL__
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <linux/types.h>
+#endif
+
+#define KDBUS_IOC_MAGIC 0x95
+
+/* Message sent from kernel to userspace, when the owner or starter of
+ * a well-known name changes */
+struct kdbus_manager_msg_name_change {
+ __u64 old_id;
+ __u64 new_id;
+ __u64 flags; /* 0 or (possibly?) KDBUS_CMD_NAME_IN_QUEUE */
+ char name[0];
+};
+
+struct kdbus_manager_msg_id_change {
+ __u64 id;
+ __u64 flags; /* The kernel flags field from KDBUS_CMD_HELLO */
+};
+
+struct kdbus_creds {
+ __u64 uid;
+ __u64 gid;
+ __u64 pid;
+ __u64 tid;
+
+ /* The starttime of the process PID. This is useful to detect
+ PID overruns from the client side. i.e. if you use the PID to
+ look something up in /proc/$PID/ you can afterwards check the
+ starttime field of it to ensure you didn't run into a PID
+ ovretun. */
+ __u64 starttime;
+};
+
+#define KDBUS_SRC_ID_KERNEL (0)
+#define KDBUS_DST_ID_WELL_KNOWN_NAME (0)
+#define KDBUS_MATCH_SRC_ID_ANY (~0ULL)
+#define KDBUS_DST_ID_BROADCAST (~0ULL)
+
+/* Message Data Types */
+enum {
+ /* Filled in by userspace */
+ KDBUS_MSG_NULL, /* empty record */
+ KDBUS_MSG_PAYLOAD, /* .data */
+ KDBUS_MSG_PAYLOAD_VEC, /* .data_vec, converted into _PAYLOAD at delivery */
+ KDBUS_MSG_MMAP, /* .data_vec */
+ KDBUS_MSG_MMAP_DONATE, /* .data_vec, unmap the memory from the sender */
+ KDBUS_MSG_UNIX_FDS, /* .data_fds of file descriptors */
+ KDBUS_MSG_BLOOM, /* for broadcasts, carries bloom filter blob */
+ KDBUS_MSG_DST_NAME, /* destination's well-known name */
+
+ /* Filled in by kernelspace */
+ KDBUS_MSG_SRC_NAMES = 0x200,/* NUL separated string list with well-known names of source */
+ KDBUS_MSG_TIMESTAMP, /* .ts_ns of CLOCK_MONOTONIC */
+ KDBUS_MSG_SRC_CREDS, /* .creds */
+ KDBUS_MSG_SRC_COMM, /* optional */
+ KDBUS_MSG_SRC_EXE, /* optional */
+ KDBUS_MSG_SRC_CMDLINE, /* optional */
+ KDBUS_MSG_SRC_CGROUP, /* optional, specified which one */
+ KDBUS_MSG_SRC_CAPS, /* caps data blob */
+ KDBUS_MSG_SRC_SECLABEL, /* NUL terminated string */
+ KDBUS_MSG_SRC_AUDIT, /* array of two uint64_t of audit loginuid + sessiond */
+
+ /* Special messages from kernel, consisting of one and only one of these data blocks */
+ KDBUS_MSG_NAME_ADD = 0x400,/* .name_change */
+ KDBUS_MSG_NAME_REMOVE, /* .name_change */
+ KDBUS_MSG_NAME_CHANGE, /* .name_change */
+ KDBUS_MSG_ID_ADD, /* .id_change */
+ KDBUS_MSG_ID_REMOVE, /* .id_change */
+ KDBUS_MSG_ID_CHANGE, /* .id_change */
+ KDBUS_MSG_REPLY_TIMEOUT, /* empty, but .reply_cookie in .kdbus_msg is filled in */
+ KDBUS_MSG_REPLY_DEAD, /* dito */
+};
+
+struct kdbus_vec {
+ __u64 address;
+ __u64 size;
+};
+
+/**
+ * struct kdbus_msg_data - chain of data blocks
+ *
+ * size: overall data record size
+ * type: kdbus_msg_data_type of data
+ */
+struct kdbus_msg_data {
+ __u64 size;
+ __u64 type;
+ union {
+ /* inline data */
+ __u8 data[0];
+ __u32 data_u32[0];
+ __u64 data_u64[0];
+
+ /* data vector */
+ struct kdbus_vec vec;
+
+ /* specific fields */
+ int fds[0]; /* int array of file descriptors */
+ __u64 ts_ns; /* timestamp in nanoseconds */
+ struct kdbus_creds creds;
+ struct kdbus_manager_msg_name_change name_change;
+ struct kdbus_manager_msg_id_change id_change;
+ };
+};
+
+enum {
+ KDBUS_MSG_FLAGS_EXPECT_REPLY = 1,
+ KDBUS_MSG_FLAGS_NO_AUTO_START = 2, /* possibly? */
+};
+
+enum {
+ KDBUS_PAYLOAD_NONE = 0,
+ KDBUS_PAYLOAD_DBUS1 = 0x4442757356657231ULL, /* 'DBusVer1' */
+ KDBUS_PAYLOAD_GVARIANT = 0x4756617269616e74ULL, /* 'GVariant' */
+};
+
+/**
+ * struct kdbus_msg
+ *
+ * set by userspace:
+ * dst_id: destination id
+ * flags: KDBUS_MSG_FLAGS_*
+ * data_size: overall message size
+ * data: data records
+ *
+ * set by kernel:
+ * src_id: who sent the message
+ */
+struct kdbus_msg {
+ __u64 size;
+ __u64 flags;
+ __u64 dst_id; /* connection, 0 == name in data, ~0 broadcast */
+ __u64 src_id; /* connection, 0 == kernel */
+ __u64 payload_type; /* 'DBusVer1', 'GVariant', ... */
+ __u64 cookie; /* userspace-supplied cookie */
+ union {
+ __u64 cookie_reply; /* cookie we reply to */
+ __u64 timeout_ns; /* timespan to wait for reply */
+ };
+ struct kdbus_msg_data data[0];
+};
+
+enum {
+ KDBUS_POLICY_NAME,
+ KDBUS_POLICY_ACCESS,
+};
+
+enum {
+ KDBUS_POLICY_USER,
+ KDBUS_POLICY_GROUP,
+ KDBUS_POLICY_WORLD,
+};
+
+enum {
+ KDBUS_POLICY_RECV = 4,
+ KDBUS_POLICY_SEND = 2,
+ KDBUS_POLICY_OWN = 1,
+};
+
+struct kdbus_policy {
+ __u64 size;
+ __u64 type; /* NAME or ACCESS */
+ union {
+ char name[0];
+ struct {
+ __u32 type; /* USER, GROUP, WORLD */
+ __u32 bits; /* RECV, SEND, OWN */
+ __u64 id; /* uid, gid, 0 */
+ } access;
+ };
+};
+
+struct kdbus_cmd_policy {
+ __u64 size;
+ __u8 buffer[0]; /* a series of KDBUS_POLICY_NAME plus one or more KDBUS_POLICY_ACCESS each. */
+};
+
+enum {
+ KDBUS_CMD_HELLO_STARTER = 1,
+ KDBUS_CMD_HELLO_ACCEPT_FD = 2,
+ KDBUS_CMD_HELLO_ACCEPT_MMAP = 4,
+};
+
+enum {
+ KDBUS_CMD_FNAME_ACCESS_GROUP = 1,
+ KDBUS_CMD_FNAME_ACCESS_WORLD = 2,
+};
+
+struct kdbus_cmd_hello {
+ /* userspace → kernel, kernel → userspace */
+ __u64 kernel_flags; /* userspace specifies its
+ * capabilities and more, kernel
+ * returns its capabilites and
+ * more. Kernel might refuse client's
+ * capabilities by returning an error
+ * from KDBUS_CMD_HELLO */
+
+ /* userspace → kernel */
+ __u64 pid; /* To allow translator services which
+ * connect to the bus on behalf of
+ * somebody else, allow specifiying
+ * the PID of the client to connect on
+ * behalf on. Normal clients should
+ * pass this as 0 (i.e. to do things
+ * under their own PID). Priviliged
+ * clients can pass != 0, to operate
+ * on behalf of somebody else. */
+
+ /* kernel → userspace */
+ __u64 bus_flags; /* this is .flags copied verbatim from
+ * from original KDBUS_CMD_BUS_MAKE
+ * ioctl. It's intended to be useful
+ * to do negotiation of features of
+ * the payload that is transfreted. */
+ __u64 id; /* peer id */
+};
+
+struct kdbus_cmd_fname {
+ __u64 size;
+ __u64 kernel_flags; /* userspace → kernel, kernel → userspace
+ * When creating a bus/ns/ep feature
+ * kernel negotiation done the same
+ * way as for KDBUS_CMD_BUS_MAKE. */
+ __u64 user_flags; /* userspace → kernel
+ * When a bus is created this value is
+ * copied verbatim into the bus
+ * structure and returned from
+ * KDBUS_CMD_HELLO, later */
+ char name[0];
+};
+
+enum {
+ /* userspace → kernel */
+ KDBUS_CMD_NAME_REPLACE_EXISTING = 1,
+ KDBUS_CMD_NAME_QUEUE = 2,
+ KDBUS_CMD_NAME_ALLOW_REPLACEMENT = 4,
+ KDBUS_CMD_NAME_STEAL_MESSAGES = 8,
+
+ /* kernel → userspace */
+ KDBUS_CMD_NAME_IN_QUEUE = 0x200,
+};
+
+struct kdbus_cmd_name {
+ __u64 size;
+ __u64 flags;
+ __u64 id; /* We allow registration/deregestration of names of other peers */
+ char name[0];
+};
+
+struct kdbus_cmd_names {
+ __u64 size;
+ struct kdbus_cmd_name names[0];
+};
+
+enum {
+ KDBUS_CMD_NAME_INFO_ITEM_NAME,
+ KDBUS_CMD_NAME_INFO_ITEM_SECLABEL,
+ KDBUS_CMD_NAME_INFO_ITEM_AUDIT,
+};
+
+struct kdbus_cmd_name_info_item {
+ __u64 size;
+ __u64 type;
+ __u8 data[0];
+};
+
+struct kdbus_cmd_name_info {
+ __u64 size; /* overall size of info */
+ __u64 flags;
+ __u64 id; /* either ID, or 0 and _ITEM_NAME follows */
+ struct kdbus_creds creds;
+ struct kdbus_cmd_name_info_item item[0]; /* list of item records */
+};
+
+enum {
+ KDBUS_CMD_MATCH_BLOOM, /* Matches a mask blob against KDBUS_MSG_BLOOM */
+ KDBUS_CMD_MATCH_SRC_NAME, /* Matches a name string against KDBUS_MSG_SRC_NAMES */
+ KDBUS_CMD_MATCH_NAME_ADD, /* Matches a name string against KDBUS_MSG_NAME_ADD */
+ KDBUS_CMD_MATCH_NAME_REMOVE, /* Matches a name string against KDBUS_MSG_NAME_REMOVE */
+ KDBUS_CMD_MATCH_NAME_CHANGE, /* Matches a name string against KDBUS_MSG_NAME_CHANGE */
+ KDBUS_CMD_MATCH_ID_ADD, /* Matches an ID against KDBUS_MSG_ID_ADD */
+ KDBUS_CMD_MATCH_ID_REMOVE, /* Matches an ID against KDBUS_MSG_ID_REMOVE */
+ KDBUS_CMD_MATCH_ID_CHANGE, /* Matches an ID against KDBUS_MSG_ID_CHANGE */
+};
+
+struct kdbus_cmd_match_item {
+ __u64 size;
+ __u64 type;
+ __u8 data[0];
+};
+
+struct kdbus_cmd_match {
+ __u64 size;
+ __u64 id; /* We allow registration/deregestration of matches for other peers */
+ __u64 cookie; /* userspace supplied cookie; when removing; kernel deletes everything with same cookie */
+ __u64 src_id; /* ~0: any. other: exact unique match */
+ struct kdbus_cmd_match_item items[0];
+};
+
+struct kdbus_cmd_monitor {
+ __u64 id; /* We allow setting the monitor flag of other peers */
+ int enabled; /* A boolean to enable/disable monitoring */
+};
+
+/* FD states:
+ * control nodes: unset
+ * bus owner (via KDBUS_CMD_BUS_MAKE)
+ * ns owner (via KDBUS_CMD_NS_MAKE)
+ *
+ * ep nodes: unset
+ * connected (via KDBUS_CMD_HELLO)
+ * starter (via KDBUS_CMD_HELLO with KDBUS_CMD_HELLO_STARTER)
+ * ep owner (via KDBUS_CMD_EP_MAKE)
+ */
+enum kdbus_cmd {
+ /* kdbus control node commands: require unset state */
+ KDBUS_CMD_BUS_MAKE = _IOWR(KDBUS_IOC_MAGIC, 0x00, struct kdbus_cmd_fname),
+ KDBUS_CMD_NS_MAKE = _IOWR(KDBUS_IOC_MAGIC, 0x10, struct kdbus_cmd_fname),
+
+ /* kdbus control node commands: require bus owner state */
+ KDBUS_CMD_BUS_POLICY_SET = _IOWR(KDBUS_IOC_MAGIC, 0x20, struct kdbus_cmd_policy),
+
+ /* kdbus ep node commands: require unset state */
+ KDBUS_CMD_EP_MAKE = _IOWR(KDBUS_IOC_MAGIC, 0x30, struct kdbus_cmd_fname),
+ KDBUS_CMD_HELLO = _IOWR(KDBUS_IOC_MAGIC, 0x31, struct kdbus_cmd_hello),
+
+ /* kdbus ep node commands: require connected state */
+ KDBUS_CMD_MSG_SEND = _IOWR(KDBUS_IOC_MAGIC, 0x40, struct kdbus_msg),
+ KDBUS_CMD_MSG_RECV = _IOWR(KDBUS_IOC_MAGIC, 0x41, struct kdbus_msg),
+
+ KDBUS_CMD_NAME_ACQUIRE = _IOWR(KDBUS_IOC_MAGIC, 0x50, struct kdbus_cmd_name),
+ KDBUS_CMD_NAME_RELEASE = _IOWR(KDBUS_IOC_MAGIC, 0x51, struct kdbus_cmd_name),
+ KDBUS_CMD_NAME_LIST = _IOWR(KDBUS_IOC_MAGIC, 0x52, struct kdbus_cmd_names),
+ KDBUS_CMD_NAME_QUERY = _IOWR(KDBUS_IOC_MAGIC, 0x53, struct kdbus_cmd_name_info),
+
+ KDBUS_CMD_MATCH_ADD = _IOWR(KDBUS_IOC_MAGIC, 0x60, struct kdbus_cmd_match),
+ KDBUS_CMD_MATCH_REMOVE = _IOWR(KDBUS_IOC_MAGIC, 0x61, struct kdbus_cmd_match),
+ KDBUS_CMD_MONITOR = _IOWR(KDBUS_IOC_MAGIC, 0x62, struct kdbus_cmd_monitor),
+
+ /* kdbus ep node commands: require ep owner state */
+ KDBUS_CMD_EP_POLICY_SET = _IOWR(KDBUS_IOC_MAGIC, 0x70, struct kdbus_cmd_policy),
+};
+#endif
diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c
index 89172e6369..29487cbd31 100644
--- a/src/libsystemd-bus/sd-bus.c
+++ b/src/libsystemd-bus/sd-bus.c
@@ -37,6 +37,7 @@
#include "bus-message.h"
#include "bus-type.h"
#include "bus-socket.h"
+#include "bus-kernel.h"
#include "bus-control.h"
static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
@@ -259,7 +260,7 @@ static int bus_send_hello(sd_bus *bus) {
assert(bus);
- if (!bus->bus_client)
+ if (!bus->bus_client || bus->is_kernel)
return 0;
r = sd_bus_message_new_method_call(
@@ -596,6 +597,41 @@ fail:
return r;
}
+static int parse_kernel_address(sd_bus *b, const char **p, char **guid) {
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "path", &path);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ skip_address_key(p);
+ }
+
+ if (!path)
+ return -EINVAL;
+
+ free(b->kernel);
+ b->kernel = path;
+ path = NULL;
+
+ return 0;
+}
+
static void bus_reset_parsed_address(sd_bus *b) {
assert(b);
@@ -606,6 +642,8 @@ static void bus_reset_parsed_address(sd_bus *b) {
b->exec_path = NULL;
b->exec_argv = NULL;
b->server_id = SD_ID128_NULL;
+ free(b->kernel);
+ b->kernel = NULL;
}
static int bus_parse_next_address(sd_bus *b) {
@@ -657,6 +695,14 @@ static int bus_parse_next_address(sd_bus *b) {
break;
+ } else if (startswith(a, "kernel:")) {
+
+ a += 7;
+ r = parse_kernel_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+
+ break;
}
a = strchr(a, ';');
@@ -697,6 +743,13 @@ static int bus_start_address(sd_bus *b) {
return r;
b->last_connect_error = -r;
+ } else if (b->kernel) {
+
+ r = bus_kernel_connect(b);
+ if (r >= 0)
+ return r;
+
+ b->last_connect_error = -r;
}
r = bus_parse_next_address(b);
@@ -715,6 +768,7 @@ int bus_next_address(sd_bus *b) {
}
static int bus_start_fd(sd_bus *b) {
+ struct stat st;
int r;
assert(b);
@@ -739,7 +793,13 @@ static int bus_start_fd(sd_bus *b) {
return r;
}
- return bus_socket_take_fd(b);
+ if (fstat(b->input_fd, &st) < 0)
+ return -errno;
+
+ if (S_ISCHR(b->input_fd))
+ return bus_kernel_take_fd(b);
+ else
+ return bus_socket_take_fd(b);
}
int sd_bus_start(sd_bus *bus) {
@@ -757,7 +817,7 @@ int sd_bus_start(sd_bus *bus) {
if (bus->input_fd >= 0)
r = bus_start_fd(bus);
- else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path)
+ else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel)
r = bus_start_address(bus);
else
return -EINVAL;
@@ -958,14 +1018,18 @@ static int dispatch_wqueue(sd_bus *bus) {
while (bus->wqueue_size > 0) {
- r = bus_socket_write_message(bus, bus->wqueue[0], &bus->windex);
+ if (bus->is_kernel)
+ r = bus_kernel_write_message(bus, bus->wqueue[0]);
+ else
+ r = bus_socket_write_message(bus, bus->wqueue[0], &bus->windex);
+
if (r < 0) {
sd_bus_close(bus);
return r;
} else if (r == 0)
/* Didn't do anything this time */
return ret;
- else if (bus->windex >= bus_message_size(bus->wqueue[0])) {
+ else if (bus->is_kernel || bus->windex >= BUS_MESSAGE_SIZE(bus->wqueue[0])) {
/* Fully written. Let's drop the entry from
* the queue.
*
@@ -1010,7 +1074,11 @@ static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) {
/* Try to read a new message */
do {
- r = bus_socket_read_message(bus, &z);
+ if (bus->is_kernel)
+ r = bus_kernel_read_message(bus, &z);
+ else
+ r = bus_socket_read_message(bus, &z);
+
if (r < 0) {
sd_bus_close(bus);
return r;
@@ -1062,11 +1130,15 @@ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) {
if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) {
size_t idx = 0;
- r = bus_socket_write_message(bus, m, &idx);
+ if (bus->is_kernel)
+ r = bus_kernel_write_message(bus, m);
+ else
+ r = bus_socket_write_message(bus, m, &idx);
+
if (r < 0) {
sd_bus_close(bus);
return r;
- } else if (idx < bus_message_size(m)) {
+ } else if (!bus->is_kernel && idx < BUS_MESSAGE_SIZE(m)) {
/* Wasn't fully written. So let's remember how
* much was written. Note that the first entry
* of the wqueue array is always allocated so
@@ -1304,7 +1376,10 @@ int sd_bus_send_with_reply_and_block(
room = true;
}
- r = bus_socket_read_message(bus, &incoming);
+ if (bus->is_kernel)
+ r = bus_kernel_read_message(bus, &incoming);
+ else
+ r = bus_socket_read_message(bus, &incoming);
if (r < 0)
return r;
if (incoming) {