bus-kernel.c revision e821075a23fdfa3ca7738fc30bb2d4c430fe10c0
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
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/>.
***/
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#include <fcntl.h>
#include <malloc.h>
#include "util.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-kernel.h"
#include "bus-bloom.h"
#include "bus-util.h"
int r;
assert(s);
if (!startswith(s, ":1."))
return 0;
if (r < 0)
return r;
return 1;
}
assert(d);
*d = ALIGN8_PTR(*d);
/* Note that p can be NULL, which encodes a region full of
* zeroes, which is useful to optimize certain padding
* conditions */
(*d)->type = KDBUS_ITEM_PAYLOAD_VEC;
}
assert(d);
*d = ALIGN8_PTR(*d);
(*d)->type = KDBUS_ITEM_PAYLOAD_MEMFD;
}
assert(d);
assert(s);
*d = ALIGN8_PTR(*d);
(*d)->type = KDBUS_ITEM_DST_NAME;
}
void *r;
assert(d);
*d = ALIGN8_PTR(*d);
(*d)->type = KDBUS_ITEM_BLOOM;
r = (*d)->data;
return r;
}
assert(d);
*d = ALIGN8_PTR(*d);
(*d)->type = KDBUS_ITEM_FDS;
}
unsigned i;
int r;
assert(m);
if (m->interface)
if (m->member)
if (m->path) {
}
r = sd_bus_message_rewind(m, true);
if (r < 0)
return r;
for (i = 0; i < 64; i++) {
char type;
const char *t;
char *e;
if (r < 0)
return r;
if (type != SD_BUS_TYPE_STRING &&
type != SD_BUS_TYPE_OBJECT_PATH &&
break;
r = sd_bus_message_read_basic(m, type, &t);
if (r < 0)
return r;
if (i < 10)
*(e++) = '0' + i;
else {
*(e++) = '0' + (i / 10);
*(e++) = '0' + (i % 10);
}
*e = 0;
strcpy(e, "-dot-prefix");
strcpy(e, "-slash-prefix");
}
return 0;
}
struct bus_body_part *part;
struct kdbus_item *d;
bool well_known;
unsigned i;
int r;
assert(b);
assert(m);
if (m->kdbus)
return 0;
if (m->destination) {
if (r < 0)
return r;
well_known = r == 0;
} else
well_known = false;
/* Add in fixed header, fields header and payload */
/* Add space for bloom filter */
/* Add in well-known destination header */
if (well_known) {
}
/* Add space for unix fds */
if (m->n_fds > 0)
if (!m->kdbus) {
r = -ENOMEM;
goto fail;
}
m->free_kdbus = true;
well_known ? 0 :
if (well_known)
MESSAGE_FOREACH_PART(part, i, m) {
/* If this is padding then simply send a
* vector with a NULL data pointer which the
* kernel will just pass through. This is the
* most efficient way to encode zeroes */
continue;
}
/* Try to send a memfd, if the part is
* sealed and this is not a broadcast. Since we can only */
continue;
}
/* Otherwise let's send a vector to the actual data,
* for that we need to map it first. */
r = bus_body_part_map(part);
if (r < 0)
goto fail;
}
void *p;
p = append_bloom(&d, BLOOM_SIZE);
r = bus_message_setup_bloom(m, p);
if (r < 0)
goto fail;
}
if (m->n_fds > 0)
return 0;
fail:
m->poisoned = true;
return r;
}
int bus_kernel_take_fd(sd_bus *b) {
struct kdbus_cmd_hello hello;
int r;
assert(b);
if (b->is_server)
return -EINVAL;
b->use_memfd = 1;
if (r < 0)
return -errno;
if (!b->kdbus_buffer) {
if (b->kdbus_buffer == MAP_FAILED) {
b->kdbus_buffer = NULL;
return -errno;
}
}
/* The higher 32bit of both flags fields are considered
* 'incompatible flags'. Refuse them all for now. */
return -ENOTSUP;
return -ENOTSUP;
return -ENOMEM;
b->is_kernel = true;
b->bus_client = true;
/* the kernel told us the UUID of the underlying bus */
return bus_start_running(b);
}
int bus_kernel_connect(sd_bus *b) {
assert(b);
if (b->is_server)
return -EINVAL;
if (b->input_fd < 0)
return -errno;
return bus_kernel_take_fd(b);
}
int r;
assert(m);
/* If we can't deliver, we want room for the error message */
r = bus_rqueue_make_room(bus);
if (r < 0)
return r;
r = bus_message_setup_kmsg(bus, m);
if (r < 0)
return r;
if (r < 0) {
return 0;
/* ENXIO: unique name not known
* ESRCH: well-known name not known */
sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Destination %s not known", m->destination);
else
return 0;
} else if (errno == EADDRNOTAVAIL) {
/* EADDRNOTAVAIL: activation is possible, but turned off in request flags */
sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Activation of %s not requested", m->destination);
else
return 0;
} else
return -errno;
bus,
&error,
&reply);
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
return 1;
}
struct kdbus_item *d;
assert(k);
KDBUS_PART_FOREACH(d, k, items) {
if (d->type == KDBUS_ITEM_FDS)
else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD)
}
}
static int push_name_owner_changed(sd_bus *bus, const char *name, const char *old_owner, const char *new_owner) {
int r;
bus,
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"NameOwnerChanged",
&m);
if (r < 0)
return r;
if (r < 0)
return r;
m->sender = "org.freedesktop.DBus";
r = bus_seal_synthetic_message(bus, m);
if (r < 0)
return r;
m = NULL;
return 1;
}
assert(k);
assert(d);
if (d->name_change.flags != 0)
return 0;
if (d->type == KDBUS_ITEM_NAME_ADD)
old_owner[0] = 0;
else
if (d->type == KDBUS_ITEM_NAME_REMOVE)
new_owner[0] = 0;
else
}
char owner[UNIQUE_NAME_MAX];
assert(k);
assert(d);
return push_name_owner_changed(
}
int r;
assert(k);
assert(d);
bus,
k->cookie_reply,
d->type == KDBUS_ITEM_REPLY_TIMEOUT ?
&m);
if (r < 0)
return r;
m->sender = "org.freedesktop.DBus";
r = bus_seal_synthetic_message(bus, m);
if (r < 0)
return r;
m = NULL;
return 1;
}
};
assert(k);
KDBUS_PART_FOREACH(d, k, items) {
if (d->type >= _KDBUS_ITEM_KERNEL_BASE && d->type < _KDBUS_ITEM_KERNEL_BASE + ELEMENTSOF(translate)) {
if (found)
return -EBADMSG;
found = d;
} else
}
if (!found) {
log_debug("Didn't find a kernel message to translate.");
return 0;
}
}
sd_bus_message *m = NULL;
struct kdbus_item *d;
unsigned n_fds = 0;
struct bus_header *h = NULL;
int r;
assert(k);
KDBUS_PART_FOREACH(d, k, items) {
size_t l;
switch (d->type) {
case KDBUS_ITEM_PAYLOAD_OFF:
if (!h) {
return -EBADMSG;
}
break;
case KDBUS_ITEM_PAYLOAD_MEMFD:
if (!h)
return -EBADMSG;
break;
case KDBUS_ITEM_FDS: {
int *f;
unsigned j;
j = l / sizeof(int);
if (!f)
return -ENOMEM;
fds = f;
n_fds += j;
break;
}
case KDBUS_ITEM_SECLABEL:
break;
}
}
if (!h)
return -EBADMSG;
r = bus_header_message_size(h, &total);
if (r < 0)
return r;
return -EBADMSG;
if (r < 0)
return r;
KDBUS_PART_FOREACH(d, k, items) {
size_t l;
switch (d->type) {
case KDBUS_ITEM_PAYLOAD_OFF: {
struct bus_body_part *part;
/* Contains body material */
part = message_append_part(m);
if (!part) {
r = -ENOMEM;
goto fail;
}
/* A -1 offset is NUL padding. */
if (idx >= begin_body) {
} else {
}
}
break;
}
case KDBUS_ITEM_PAYLOAD_MEMFD: {
struct bus_body_part *part;
if (idx < BUS_MESSAGE_BODY_BEGIN(m)) {
r = -EBADMSG;
goto fail;
}
part = message_append_part(m);
if (!part) {
r = -ENOMEM;
goto fail;
}
break;
}
case KDBUS_ITEM_CREDS:
m->creds.mask |= (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID) & bus->creds_mask;
break;
case KDBUS_ITEM_TIMESTAMP:
break;
case KDBUS_ITEM_PID_COMM:
break;
case KDBUS_ITEM_TID_COMM:
break;
case KDBUS_ITEM_EXE:
break;
case KDBUS_ITEM_CMDLINE:
m->creds.cmdline_size = l;
break;
case KDBUS_ITEM_CGROUP:
m->creds.mask |= (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID) & bus->creds_mask;
break;
case KDBUS_ITEM_AUDIT:
break;
case KDBUS_ITEM_CAPS:
m->creds.capability_size = l;
m->creds.mask |= (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS) & bus->creds_mask;
break;
case KDBUS_ITEM_DST_NAME:
destination = d->str;
break;
case KDBUS_ITEM_NAMES:
m->creds.well_known_names_size = l;
break;
case KDBUS_ITEM_FDS:
case KDBUS_ITEM_SECLABEL:
break;
default:
}
}
r = bus_message_parse_fields(m);
if (r < 0)
goto fail;
if (k->src_id == KDBUS_SRC_ID_KERNEL)
m->sender = "org.freedesktop.DBus";
else {
}
if (!m->destination) {
if (destination)
m->destination = destination;
else if (k->dst_id != KDBUS_DST_ID_WELL_KNOWN_NAME &&
k->dst_id != KDBUS_DST_ID_BROADCAST) {
snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id);
m->destination = m->destination_buffer;
}
}
/* We take possession of the kmsg struct now */
m->kdbus = k;
m->release_kdbus = true;
m->free_fds = true;
return 1;
fail:
if (m) {
struct bus_body_part *part;
unsigned i;
/* Make sure the memfds are not freed twice */
MESSAGE_FOREACH_PART(part, i, m)
}
return r;
}
struct kdbus_msg *k;
int r;
r = bus_rqueue_make_room(bus);
if (r < 0)
return r;
if (r < 0) {
return 0;
return -errno;
}
if (k->payload_type == KDBUS_PAYLOAD_DBUS1)
r = bus_kernel_make_message(bus, k);
else if (k->payload_type == KDBUS_PAYLOAD_KERNEL)
r = bus_kernel_translate_message(bus, k);
else
r = 0;
if (r <= 0)
close_kdbus_msg(bus, k);
return r < 0 ? r : 1;
}
struct memfd_cache *c;
int fd;
return -ENOTSUP;
if (bus->n_memfd_cache <= 0) {
int r;
if (r < 0)
return -errno;
*size = 0;
return fd;
}
return fd;
}
if (size > 0)
}
struct memfd_cache *c;
return;
}
return;
}
/* If overly long, let's return a bit to the OS */
} else
}
void bus_kernel_flush_memfd(sd_bus *b) {
unsigned i;
assert(b);
for (i = 0; i < b->n_memfd_cache; i++)
}
uint64_t f = 0;
if (!(flags & SD_BUS_NAME_DO_NOT_QUEUE))
f |= KDBUS_NAME_QUEUE;
*kdbus_flags = f;
return 0;
}
uint64_t m = 0;
if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID))
m |= KDBUS_ATTACH_CREDS;
m |= KDBUS_ATTACH_COMM;
if (mask & SD_BUS_CREDS_EXE)
m |= KDBUS_ATTACH_EXE;
if (mask & SD_BUS_CREDS_CMDLINE)
m |= KDBUS_ATTACH_CMDLINE;
if (mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID))
m |= KDBUS_ATTACH_CGROUP;
if (mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS))
m |= KDBUS_ATTACH_CAPS;
if (mask & SD_BUS_CREDS_SELINUX_CONTEXT)
m |= KDBUS_ATTACH_SECLABEL;
m |= KDBUS_ATTACH_AUDIT;
m |= KDBUS_ATTACH_NAMES;
*kdbus_mask = m;
return 0;
}
int bus_kernel_create_bus(const char *name, char **s) {
struct kdbus_cmd_bus_make *make;
struct kdbus_item *n;
int fd;
assert(s);
if (fd < 0)
return -errno;
n->type = KDBUS_MAKE_NAME;
return -errno;
}
/* The higher 32bit of the flags field are considered
* 'incompatible flags'. Refuse them all for now. */
return -ENOTSUP;
}
if (s) {
char *p;
if (!p) {
return -ENOMEM;
}
*s = p;
}
return fd;
}
struct kdbus_cmd_hello *hello;
struct kdbus_item *n;
char *p;
int fd;
if (fd < 0)
return -errno;
n->type = KDBUS_ITEM_STARTER_NAME;
return -errno;
}
/* The higher 32bit of both flags fields are considered
* 'incompatible flags'. Refuse them all for now. */
return -ENOTSUP;
}
return -ENOTSUP;
}
return fd;
}
int bus_kernel_create_namespace(const char *name, char **s) {
struct kdbus_cmd_ns_make *make;
struct kdbus_item *n;
int fd;
assert(s);
if (fd < 0)
return -errno;
n->type = KDBUS_MAKE_NAME;
return -errno;
}
/* The higher 32bit of the flags field are considered
* 'incompatible flags'. Refuse them all for now. */
return -ENOTSUP;
}
if (s) {
char *p;
if (!p) {
return -ENOMEM;
}
*s = p;
}
return fd;
}