From: Lennart Poettering Date: Thu, 11 Apr 2013 21:09:29 +0000 (+0200) Subject: bus: basic implementation of kdbus client side X-Git-Tag: v202~148 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=6629161f827c82889cf45cfcdce62dcb543eda23;hp=4cda0f2116a69b0c54afe518c930022fb1a38022 bus: basic implementation of kdbus client side --- diff --git a/Makefile.am b/Makefile.am index ec81f53ab..ae58c1696 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1700,6 +1700,8 @@ libsystemd_bus_la_SOURCES = \ src/libsystemd-bus/bus-internal.h \ src/libsystemd-bus/bus-socket.c \ src/libsystemd-bus/bus-socket.h \ + src/libsystemd-bus/bus-kernel.c \ + src/libsystemd-bus/bus-kernel.h \ src/libsystemd-bus/bus-message.c \ src/libsystemd-bus/bus-message.h \ src/libsystemd-bus/bus-signature.c \ @@ -4105,6 +4107,10 @@ hwdb-update: http://standards.ieee.org/develop/regauth/iab/iab.txt && \ ./ids-update.pl ) +kdbus-update: + ( cd $(top_srcdir)/src/libsystemd-bus/ && \ + wget -N https://d-bus.googlecode.com/git/kdbus.h ) + upload: all distcheck cp -v systemd-$(VERSION).tar.xz /home/lennart/git.fedora/systemd/ scp systemd-$(VERSION).tar.xz fdo:/srv/www.freedesktop.org/www/software/systemd/ diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h index 6ff3163cf..7cca9998b 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 000000000..c10fcc54d --- /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 . +***/ + +#include + +#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 000000000..ac746afe0 --- /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 . +***/ + +#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 fb4718085..eeb1d823e 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 126ced008..3d1bb6233 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 5e285c9e5..bce81aeff 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 000000000..ba59fd0d3 --- /dev/null +++ b/src/libsystemd-bus/kdbus.h @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2013 Kay Sievers + * Copyright (C) 2013 Greg Kroah-Hartman + * 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 +#include +#include +#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 89172e636..29487cbd3 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, ';'); @@ -696,6 +742,13 @@ static int bus_start_address(sd_bus *b) { if (r >= 0) 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; } @@ -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) {