bus-control.c revision cfeaa44a09756a93a881f786678973d9b1e382db
/*-*- 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 <stddef.h>
#include <errno.h>
#include "strv.h"
#include "sd-bus.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-control.h"
#include "bus-bloom.h"
#include "bus-util.h"
#include "capability.h"
int r;
if (!bus->bus_client)
return -EINVAL;
r = bus_ensure_running(bus);
if (r < 0)
return r;
return 0;
}
struct kdbus_cmd *n;
int r;
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(n, n->size);
#endif
if (r < 0)
return -errno;
if (n->return_flags & KDBUS_NAME_IN_QUEUE)
return 0;
return 1;
}
int r;
if (!(flags & SD_BUS_NAME_QUEUE))
r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"RequestName",
NULL,
&reply,
"su",
name,
param);
if (r < 0)
return r;
if (r < 0)
return r;
if (ret == BUS_NAME_ALREADY_OWNER)
return -EALREADY;
else if (ret == BUS_NAME_EXISTS)
return -EEXIST;
else if (ret == BUS_NAME_IN_QUEUE)
return 0;
else if (ret == BUS_NAME_PRIMARY_OWNER)
return 1;
return -EIO;
}
assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL);
if (!bus->bus_client)
return -EINVAL;
/* Don't allow requesting the special driver and local names */
return -EINVAL;
return -ENOTCONN;
else
}
struct kdbus_cmd *n;
int r;
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(n, n->size);
#endif
if (r < 0)
return -errno;
return 0;
}
int r;
r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"ReleaseName",
NULL,
&reply,
"s",
name);
if (r < 0)
return r;
if (r < 0)
return r;
if (ret == BUS_NAME_NON_EXISTENT)
return -ESRCH;
if (ret == BUS_NAME_NOT_OWNER)
return -EADDRINUSE;
if (ret == BUS_NAME_RELEASED)
return 0;
return -EINVAL;
}
if (!bus->bus_client)
return -EINVAL;
/* Don't allow releasing the special driver and local names */
return -EINVAL;
return -ENOTCONN;
else
}
struct kdbus_cmd_list cmd = {
};
uint64_t previous_id = 0;
int r;
/* Caller will free half-constructed list on failure... */
if (r < 0)
return -errno;
struct kdbus_item *item;
const char *entry_name = NULL;
char *n;
r = -ENOMEM;
goto fail;
}
r = strv_consume(x, n);
if (r < 0)
goto fail;
}
r = strv_extend(x, entry_name);
if (r < 0) {
r = -ENOMEM;
goto fail;
}
}
}
r = 0;
fail:
return r;
}
int r;
if (acquired) {
if (r < 0)
return r;
}
if (activatable) {
if (r < 0)
return r;
*activatable = y;
y = NULL;
}
if (acquired) {
*acquired = x;
x = NULL;
}
return 0;
}
int r;
if (acquired) {
r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"ListNames",
NULL,
&reply,
NULL);
if (r < 0)
return r;
r = sd_bus_message_read_strv(reply, &x);
if (r < 0)
return r;
}
if (activatable) {
r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"ListActivatableNames",
NULL,
&reply,
NULL);
if (r < 0)
return r;
r = sd_bus_message_read_strv(reply, &y);
if (r < 0)
return r;
*activatable = y;
y = NULL;
}
if (acquired) {
*acquired = x;
x = NULL;
}
return 0;
}
if (!bus->bus_client)
return -EINVAL;
return -ENOTCONN;
else
}
static int bus_populate_creds_from_items(
struct kdbus_info *info,
sd_bus_creds *c) {
struct kdbus_item *item;
uint64_t m;
int r;
assert(c);
case KDBUS_ITEM_PIDS:
c->mask |= SD_BUS_CREDS_PID;
}
c->mask |= SD_BUS_CREDS_TID;
}
if (mask & SD_BUS_CREDS_PPID) {
c->mask |= SD_BUS_CREDS_PPID;
/* The structure doesn't
* really distuingish the case
* where a process has no
* parent and where we don't
* know it because it could
* not be translated due to
* namespaces. However, we
* know that PID 1 has no
* parent process, hence let's
* patch that in, manually. */
c->ppid = 0;
c->mask |= SD_BUS_CREDS_PPID;
}
}
break;
case KDBUS_ITEM_CREDS:
c->mask |= SD_BUS_CREDS_UID;
}
c->mask |= SD_BUS_CREDS_EUID;
}
c->mask |= SD_BUS_CREDS_SUID;
}
c->mask |= SD_BUS_CREDS_FSUID;
}
c->mask |= SD_BUS_CREDS_GID;
}
c->mask |= SD_BUS_CREDS_EGID;
}
c->mask |= SD_BUS_CREDS_SGID;
}
c->mask |= SD_BUS_CREDS_FSGID;
}
break;
case KDBUS_ITEM_PID_COMM:
if (mask & SD_BUS_CREDS_COMM) {
if (r < 0)
return r;
c->mask |= SD_BUS_CREDS_COMM;
}
break;
case KDBUS_ITEM_TID_COMM:
if (mask & SD_BUS_CREDS_TID_COMM) {
if (r < 0)
return r;
c->mask |= SD_BUS_CREDS_TID_COMM;
}
break;
case KDBUS_ITEM_EXE:
if (mask & SD_BUS_CREDS_EXE) {
if (r < 0)
return r;
c->mask |= SD_BUS_CREDS_EXE;
}
break;
case KDBUS_ITEM_CMDLINE:
if (mask & SD_BUS_CREDS_CMDLINE) {
if (!c->cmdline)
return -ENOMEM;
c->mask |= SD_BUS_CREDS_CMDLINE;
}
break;
case KDBUS_ITEM_CGROUP:
m = (SD_BUS_CREDS_CGROUP | SD_BUS_CREDS_UNIT |
if (m) {
if (r < 0)
return r;
r = bus_get_root_path(bus);
if (r < 0)
return r;
if (r < 0)
return r;
c->mask |= m;
}
break;
case KDBUS_ITEM_CAPS:
if (m) {
item->size - offsetof(struct kdbus_item, caps.caps) < DIV_ROUND_UP(item->caps.last_cap, 32U) * 4 * 4)
return -EBADMSG;
if (!c->capability)
return -ENOMEM;
c->mask |= m;
}
break;
case KDBUS_ITEM_SECLABEL:
if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
if (r < 0)
return r;
c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
}
break;
case KDBUS_ITEM_AUDIT:
if (mask & SD_BUS_CREDS_AUDIT_SESSION_ID) {
}
if (mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
}
break;
case KDBUS_ITEM_OWNED_NAME:
if (r < 0)
return r;
}
break;
if (mask & SD_BUS_CREDS_DESCRIPTION) {
if (r < 0)
return r;
c->mask |= SD_BUS_CREDS_DESCRIPTION;
}
break;
case KDBUS_ITEM_AUXGROUPS:
if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) {
size_t i, n;
uid_t *g;
if (!g)
return -ENOMEM;
for (i = 0; i < n; i++)
free(c->supplementary_gids);
c->supplementary_gids = g;
c->n_supplementary_gids = n;
}
break;
}
}
return 0;
}
const char *name,
bool allow_activator,
sd_bus_creds **creds) {
struct kdbus_cmd_info *cmd;
struct kdbus_info *conn_info;
int r;
return -EOPNOTSUPP;
if (r < 0)
return r;
if (r > 0) {
} else {
}
/* If augmentation is on, and the bus didn't provide us
* can read the rest from /proc. */
if ((mask & SD_BUS_CREDS_AUGMENT) &&
(mask & (SD_BUS_CREDS_PPID|
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|
SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
mask |= SD_BUS_CREDS_PID;
if (r < 0)
return -errno;
/* Non-activated names are considered not available */
if (name[0] == ':')
r = -ENXIO;
else
r = -ESRCH;
goto fail;
}
c = bus_creds_new();
if (!c) {
r = -ENOMEM;
goto fail;
}
if (mask & SD_BUS_CREDS_UNIQUE_NAME) {
r = -ENOMEM;
goto fail;
}
c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
}
/* If KDBUS_ITEM_OWNED_NAME is requested then we'll get 0 of
them in case the service has no names. This does not mean
however that the list of owned names could not be
acquired. Hence, let's explicitly clarify that the data is
complete. */
if (r < 0)
goto fail;
r = bus_creds_add_more(c, mask, 0, 0);
if (r < 0)
goto fail;
if (creds) {
*creds = c;
c = NULL;
}
r = 0;
fail:
return r;
}
static int bus_get_name_creds_dbus1(
const char *name,
sd_bus_creds **creds) {
int r;
/* Only query the owner if the caller wants to know it or if
* the caller just wants to check whether a name exists */
r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"GetNameOwner",
NULL,
"s",
name);
if (r < 0)
return r;
if (r < 0)
return r;
}
if (mask != 0) {
c = bus_creds_new();
if (!c)
return -ENOMEM;
if (!c->unique_name)
return -ENOMEM;
c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
}
if ((mask & SD_BUS_CREDS_PID) ||
((mask & SD_BUS_CREDS_AUGMENT) &&
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|
SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
uint32_t u;
r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"GetConnectionUnixProcessID",
NULL,
&reply,
"s",
if (r < 0)
return r;
if (r < 0)
return r;
pid = u;
if (mask & SD_BUS_CREDS_PID) {
c->pid = u;
c->mask |= SD_BUS_CREDS_PID;
}
}
if (mask & SD_BUS_CREDS_EUID) {
uint32_t u;
r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"GetConnectionUnixUser",
NULL,
&reply,
"s",
if (r < 0)
return r;
if (r < 0)
return r;
c->euid = u;
c->mask |= SD_BUS_CREDS_EUID;
}
if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
const void *p = NULL;
r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"GetConnectionSELinuxSecurityContext",
&error,
&reply,
"s",
if (r < 0) {
return r;
} else {
if (r < 0)
return r;
if (!c->label)
return -ENOMEM;
c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
}
}
if (r < 0)
return r;
}
if (creds) {
*creds = c;
c = NULL;
}
return 0;
}
const char *name,
sd_bus_creds **creds) {
if (!bus->bus_client)
return -EINVAL;
return -EINVAL;
return -ENOTCONN;
else
}
struct kdbus_cmd_info cmd = {
.size = sizeof(struct kdbus_cmd_info),
};
struct kdbus_info *creator_info;
int r;
c = bus_creds_new();
if (!c)
return -ENOMEM;
/* If augmentation is on, and the bus doesn't didn't allow us
* can read the rest from /proc. */
if ((mask & SD_BUS_CREDS_AUGMENT) &&
(mask & (SD_BUS_CREDS_PPID|
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|
SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
mask |= SD_BUS_CREDS_PID;
if (r < 0)
return -errno;
if (r < 0)
return r;
if (r < 0)
return r;
*ret = c;
c = NULL;
return 0;
}
int r;
return -ENODATA;
c = bus_creds_new();
if (!c)
return -ENOMEM;
if (bus->ucred_valid) {
}
}
}
}
if (!c->label)
return -ENOMEM;
c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
}
if (r < 0)
return r;
*ret = c;
c = NULL;
return 0;
}
return -ENOTCONN;
else
}
const char *name,
const char *old_owner,
const char *new_owner) {
int is_name_id = -1, r;
struct kdbus_item *item;
/* If we encounter a match that could match against
* NameOwnerChanged messages, then we need to create
* KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE} and
* KDBUS_ITEM_ID_{ADD,REMOVE} matches for it, possibly
* multiple if the match is underspecified.
*
* The NameOwnerChanged signals take three parameters with
* unique or well-known names, but only some forms actually
* exist:
*
* WELLKNOWN, "", UNIQUE → KDBUS_ITEM_NAME_ADD
* WELLKNOWN, UNIQUE, "" → KDBUS_ITEM_NAME_REMOVE
* WELLKNOWN, UNIQUE, UNIQUE → KDBUS_ITEM_NAME_CHANGE
* UNIQUE, "", UNIQUE → KDBUS_ITEM_ID_ADD
* UNIQUE, UNIQUE, "" → KDBUS_ITEM_ID_REMOVE
*
* For the latter two the two unique names must be identical.
*
* */
if (name) {
if (is_name_id < 0)
return 0;
}
if (r < 0)
return 0;
if (r == 0)
return 0;
return 0;
} else
if (r < 0)
return r;
if (r == 0)
return 0;
return 0;
} else
if (is_name_id <= 0) {
struct kdbus_cmd_match *m;
/* If the name argument is missing or is a well-known
* name, then add KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}
* matches for it */
l);
l;
if (name)
/* If the old name is unset or empty, then
* this can match against added names */
if (r < 0)
return -errno;
}
/* If the new name is unset or empty, then
* this can match against removed names */
if (r < 0)
return -errno;
}
/* The CHANGE match we need in either case, because
* what is reported as a name change by the kernel
* might just be an owner change between starter and
* normal clients. For userspace such a change should
* subscribe to this unconditionally. */
if (r < 0)
return -errno;
}
if (is_name_id != 0) {
struct kdbus_cmd_match *m;
/* If the name argument is missing or is a unique
* name, then add KDBUS_ITEM_ID_{ADD,REMOVE} matches
* for it */
sizeof(struct kdbus_notify_id_change));
sizeof(struct kdbus_notify_id_change);
/* If the old name is unset or empty, then this can
* match against added ids */
if (r < 0)
return -errno;
}
/* If thew new name is unset or empty, then this can
* match against removed ids */
if (r < 0)
return -errno;
}
}
return 0;
}
struct bus_match_component *components,
unsigned n_components,
struct kdbus_cmd_match *m;
struct kdbus_item *item;
size_t sender_length = 0;
bool using_bloom = false;
unsigned i;
bool matches_name_change = true;
const char *name_change_arg[3] = {};
int r;
/* Monitor streams don't support matches, make this a NOP */
return 0;
for (i = 0; i < n_components; i++) {
struct bus_match_component *c = &components[i];
switch (c->type) {
case BUS_MATCH_SENDER:
matches_name_change = false;
if (r < 0)
return r;
else if (r > 0)
else {
}
break;
case BUS_MATCH_MESSAGE_TYPE:
if (c->value_u8 != SD_BUS_MESSAGE_SIGNAL)
matches_name_change = false;
bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "message-type", bus_message_type_to_string(c->value_u8));
using_bloom = true;
break;
case BUS_MATCH_INTERFACE:
matches_name_change = false;
using_bloom = true;
break;
case BUS_MATCH_MEMBER:
matches_name_change = false;
using_bloom = true;
break;
case BUS_MATCH_PATH:
matches_name_change = false;
using_bloom = true;
break;
case BUS_MATCH_PATH_NAMESPACE:
using_bloom = true;
}
break;
case BUS_MATCH_ARG...BUS_MATCH_ARG_LAST: {
using_bloom = true;
break;
}
case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: {
using_bloom = true;
break;
}
using_bloom = true;
break;
}
case BUS_MATCH_DESTINATION:
/* The bloom filter does not include
the destination, since it is only
available for broadcast messages
which do not carry a destination
since they are undirected. */
break;
case BUS_MATCH_ROOT:
case BUS_MATCH_VALUE:
case BUS_MATCH_LEAF:
case _BUS_MATCH_NODE_TYPE_MAX:
assert_not_reached("Invalid match type?");
}
}
if (using_bloom)
if (src_id != KDBUS_MATCH_ID_ANY) {
}
if (using_bloom) {
}
if (sender) {
}
if (r < 0)
return -errno;
if (matches_name_change) {
/* If this match could theoretically match
* NameOwnerChanged messages, we need to
* install a second non-bloom filter explitly
* for it */
if (r < 0)
return r;
}
return 0;
}
#define internal_match(bus, m) \
: (m))
static int bus_add_match_internal_dbus1(
const char *match) {
const char *e;
return sd_bus_call_method(
bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"AddMatch",
NULL,
NULL,
"s",
e);
}
const char *match,
struct bus_match_component *components,
unsigned n_components,
if (!bus->bus_client)
return -EINVAL;
else
}
struct kdbus_cmd_match m = {
};
int r;
/* Monitor streams don't support matches, make this a NOP */
return 0;
if (r < 0)
return -errno;
return 0;
}
static int bus_remove_match_internal_dbus1(
const char *match) {
const char *e;
return sd_bus_call_method(
bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"RemoveMatch",
NULL,
NULL,
"s",
e);
}
const char *match,
if (!bus->bus_client)
return -EINVAL;
else
}
const char *mid;
int r;
if (!bus->bus_client)
return -EINVAL;
return -ENOTCONN;
return sd_id128_get_machine(machine);
bus,
&m,
name,
"/",
"org.freedesktop.DBus.Peer",
"GetMachineId");
if (r < 0)
return r;
r = sd_bus_message_set_auto_start(m, false);
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
}