bus-match.c revision 4afd3348c7506dd1d36305b7bcb9feb8952b9d6b
/*-*- 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/>.
***/
#include "alloc-util.h"
#include "bus-internal.h"
#include "bus-match.h"
#include "bus-message.h"
#include "bus-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "hexdecoct.h"
#include "string-util.h"
#include "strv.h"
/* Example:
*
* A: type=signal,sender=foo,interface=bar
* B: type=signal,sender=quux,interface=fips
* C: type=signal,sender=quux,interface=waldo
* D: type=signal,member=test
* E: sender=miau
* F: type=signal
* G: type=signal
*
* results in this tree:
*
* BUS_MATCH_ROOT
* + BUS_MATCH_MESSAGE_TYPE
* | ` BUS_MATCH_VALUE: value == signal
* | + DBUS_MATCH_SENDER
* | | + BUS_MATCH_VALUE: value == foo
* | | | ` DBUS_MATCH_INTERFACE
* | | | ` BUS_MATCH_VALUE: value == bar
* | | | ` BUS_MATCH_LEAF: A
* | | ` BUS_MATCH_VALUE: value == quux
* | | ` DBUS_MATCH_INTERFACE
* | | | BUS_MATCH_VALUE: value == fips
* | | | ` BUS_MATCH_LEAF: B
* | | ` BUS_MATCH_VALUE: value == waldo
* | | ` BUS_MATCH_LEAF: C
* | + DBUS_MATCH_MEMBER
* | | ` BUS_MATCH_VALUE: value == test
* | | ` BUS_MATCH_LEAF: D
* | + BUS_MATCH_LEAF: F
* | ` BUS_MATCH_LEAF: G
* ` BUS_MATCH_SENDER
* ` BUS_MATCH_VALUE: value == miau
* ` BUS_MATCH_LEAF: E
*/
static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) {
return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_HAS_LAST;
}
static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) {
return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) ||
(t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST) ||
(t >= BUS_MATCH_ARG_HAS && t <= BUS_MATCH_ARG_HAS_LAST);
}
/* We are apparently linked into the parent's child
* list. Let's remove us from there. */
} else {
}
}
/* We might be in the parent's hash table, so clean
* this up */
}
}
}
return false;
return false;
return true;
return true;
}
static bool value_node_test(
struct bus_match_node *node,
const char *value_str,
char **value_strv,
sd_bus_message *m) {
/* Tests parameters against this value node, doing prefix
* magic and stuff. */
switch (parent_type) {
case BUS_MATCH_MESSAGE_TYPE:
case BUS_MATCH_SENDER:
return true;
char **i;
/* on kdbus we have the well known names list
* in the credentials, let's make use of that
* for an accurate match */
return true;
} else {
/* If we don't have kdbus, we don't know the
* well-known names of the senders. In that,
* let's just hope that dbus-daemon doesn't
* send us stuff we didn't want. */
return true;
}
return false;
case BUS_MATCH_DESTINATION:
case BUS_MATCH_INTERFACE:
case BUS_MATCH_MEMBER:
case BUS_MATCH_PATH:
case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
if (value_str)
return false;
case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: {
char **i;
return true;
return false;
}
if (value_str)
return false;
case BUS_MATCH_PATH_NAMESPACE:
case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
if (value_str)
return false;
default:
assert_not_reached("Invalid node type");
}
}
static bool value_node_same(
struct bus_match_node *node,
const char *value_str) {
/* Tests parameters against this value node, not doing prefix
* magic and stuff, i.e. this one actually compares the match
* itself. */
switch (parent_type) {
case BUS_MATCH_MESSAGE_TYPE:
case BUS_MATCH_SENDER:
case BUS_MATCH_DESTINATION:
case BUS_MATCH_INTERFACE:
case BUS_MATCH_MEMBER:
case BUS_MATCH_PATH:
case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
case BUS_MATCH_PATH_NAMESPACE:
case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
default:
assert_not_reached("Invalid node type");
}
}
int bus_match_run(
struct bus_match_node *node,
sd_bus_message *m) {
int r;
assert(m);
if (!node)
return 0;
return 0;
/* Not these special semantics: when traversing the tree we
* usually let bus_match_run() when called for a node
* recursively invoke bus_match_run(). There's are two
* exceptions here though, which are BUS_NODE_ROOT (which
* cannot have a sibling), and BUS_NODE_VALUE (whose siblings
* are invoked anyway by its parent. */
case BUS_MATCH_ROOT:
/* Run all children. Since we cannot have any siblings
* we won't call any. The children of the root node
* are compares or leaves, they will automatically
* call their siblings. */
case BUS_MATCH_VALUE:
/* Run all children. We don't execute any siblings, we
* assume our caller does that. The children of value
* nodes are compares or leaves, they will
* automatically call their siblings */
case BUS_MATCH_LEAF:
if (bus) {
return 0;
}
r = sd_bus_message_rewind(m, true);
if (r < 0)
return r;
/* Run the callback. And then invoke siblings. */
if (bus) {
}
if (bus) {
}
r = bus_maybe_reply_error(m, r, &error_buffer);
if (r != 0)
return r;
return 0;
}
case BUS_MATCH_MESSAGE_TYPE:
break;
case BUS_MATCH_SENDER:
/* FIXME: resolve test_str from a well-known to a unique name first */
break;
case BUS_MATCH_DESTINATION:
test_str = m->destination;
break;
case BUS_MATCH_INTERFACE:
break;
case BUS_MATCH_MEMBER:
break;
case BUS_MATCH_PATH:
case BUS_MATCH_PATH_NAMESPACE:
break;
case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
break;
case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
break;
break;
case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
break;
default:
assert_not_reached("Unknown match type.");
}
struct bus_match_node *found;
/* Lookup via hash table, nice! So let's jump directly. */
if (test_str)
else if (test_strv) {
char **i;
STRV_FOREACH(i, test_strv) {
if (found) {
if (r != 0)
return r;
}
}
else
if (found) {
if (r != 0)
return r;
}
} else {
struct bus_match_node *c;
/* No hash table, so let's iterate manually... */
continue;
r = bus_match_run(bus, c, m);
if (r != 0)
return r;
}
}
return 0;
/* And now, let's invoke our siblings */
}
static int bus_match_add_compare_value(
struct bus_match_node *where,
enum bus_match_node_type t,
const char *value_str,
struct bus_match_node **ret) {
int r;
;
if (c) {
/* Comparison node already exists? Then let's see if
* the value node exists too. */
if (t == BUS_MATCH_MESSAGE_TYPE)
else if (BUS_MATCH_CAN_HASH(t))
else {
;
}
if (n) {
*ret = n;
return 0;
}
} else {
/* Comparison node, doesn't exist yet? Then let's
* create it. */
if (!c) {
r = -ENOMEM;
goto fail;
}
c->type = t;
if (c->next)
if (t == BUS_MATCH_MESSAGE_TYPE) {
r = -ENOMEM;
goto fail;
}
} else if (BUS_MATCH_CAN_HASH(t)) {
r = -ENOMEM;
goto fail;
}
}
}
if (!n) {
r = -ENOMEM;
goto fail;
}
n->type = BUS_MATCH_VALUE;
if (value_str) {
r = -ENOMEM;
goto fail;
}
}
n->parent = c;
if (t == BUS_MATCH_MESSAGE_TYPE)
else
if (r < 0)
goto fail;
} else {
if (n->next)
c->child = n;
}
*ret = n;
return 1;
fail:
if (c)
if (n) {
free(n);
}
return r;
}
static int bus_match_find_compare_value(
struct bus_match_node *where,
enum bus_match_node_type t,
const char *value_str,
struct bus_match_node **ret) {
struct bus_match_node *c, *n;
;
if (!c)
return 0;
if (t == BUS_MATCH_MESSAGE_TYPE)
else if (BUS_MATCH_CAN_HASH(t))
else {
;
}
if (n) {
*ret = n;
return 1;
}
return 0;
}
static int bus_match_add_leaf(
struct bus_match_node *where,
struct match_callback *callback) {
struct bus_match_node *n;
if (!n)
return -ENOMEM;
n->type = BUS_MATCH_LEAF;
if (n->next)
callback->match_node = n;
return 1;
}
static int bus_match_find_leaf(
struct bus_match_node *where,
void *userdata,
struct bus_match_node **ret) {
struct bus_match_node *c;
sd_bus_slot *s;
if (c->type == BUS_MATCH_LEAF &&
*ret = c;
return 1;
}
}
return 0;
}
assert(k);
return BUS_MATCH_MESSAGE_TYPE;
return BUS_MATCH_SENDER;
return BUS_MATCH_DESTINATION;
return BUS_MATCH_INTERFACE;
return BUS_MATCH_MEMBER;
return BUS_MATCH_PATH;
return BUS_MATCH_PATH_NAMESPACE;
int j;
j = undecchar(k[3]);
if (j < 0)
return -EINVAL;
return BUS_MATCH_ARG + j;
}
int a, b;
enum bus_match_node_type t;
a = undecchar(k[3]);
b = undecchar(k[4]);
if (a <= 0 || b < 0)
return -EINVAL;
t = BUS_MATCH_ARG + a * 10 + b;
if (t > BUS_MATCH_ARG_LAST)
return -EINVAL;
return t;
}
int j;
j = undecchar(k[3]);
if (j < 0)
return -EINVAL;
return BUS_MATCH_ARG_PATH + j;
}
enum bus_match_node_type t;
int a, b;
a = undecchar(k[3]);
b = undecchar(k[4]);
if (a <= 0 || b < 0)
return -EINVAL;
t = BUS_MATCH_ARG_PATH + a * 10 + b;
if (t > BUS_MATCH_ARG_PATH_LAST)
return -EINVAL;
return t;
}
int j;
j = undecchar(k[3]);
if (j < 0)
return -EINVAL;
return BUS_MATCH_ARG_NAMESPACE + j;
}
enum bus_match_node_type t;
int a, b;
a = undecchar(k[3]);
b = undecchar(k[4]);
if (a <= 0 || b < 0)
return -EINVAL;
t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b;
if (t > BUS_MATCH_ARG_NAMESPACE_LAST)
return -EINVAL;
return t;
}
int j;
j = undecchar(k[3]);
if (j < 0)
return -EINVAL;
return BUS_MATCH_ARG_HAS + j;
}
enum bus_match_node_type t;
int a, b;
a = undecchar(k[3]);
b = undecchar(k[4]);
if (a <= 0 || b < 0)
return -EINVAL;
t = BUS_MATCH_ARG_HAS + a * 10 + b;
if (t > BUS_MATCH_ARG_HAS_LAST)
return -EINVAL;
return t;
}
return -EINVAL;
}
static int match_component_compare(const void *a, const void *b) {
const struct bus_match_component *x = a, *y = b;
return -1;
return 1;
return 0;
}
unsigned i;
for (i = 0; i < n_components; i++)
}
int bus_match_parse(
const char *match,
struct bus_match_component **_components,
unsigned *_n_components) {
const char *p = match;
unsigned n_components = 0, i;
int r;
while (*p != 0) {
const char *eq, *q;
enum bus_match_node_type t;
unsigned j = 0;
size_t value_allocated = 0;
uint8_t u;
/* Avahi's match rules appear to include whitespace, skip over it */
p += strspn(p, " ");
if (!eq)
return -EINVAL;
t = bus_match_node_type_from_string(p, eq - p);
if (t < 0)
return -EINVAL;
if (*q == 0) {
if (quoted) {
r = -EINVAL;
goto fail;
} else {
if (value)
value[j] = 0;
break;
}
}
if (!escaped) {
if (*q == '\\') {
escaped = true;
continue;
}
if (quoted) {
if (*q == '\'') {
if (value)
value[j] = 0;
break;
}
} else {
if (*q == ',') {
if (value)
value[j] = 0;
break;
}
}
}
r = -ENOMEM;
goto fail;
}
value[j++] = *q;
escaped = false;
}
if (!value) {
if (!value) {
r = -ENOMEM;
goto fail;
}
}
if (t == BUS_MATCH_MESSAGE_TYPE) {
r = bus_message_type_from_string(value, &u);
if (r < 0)
goto fail;
} else
u = 0;
r = -ENOMEM;
goto fail;
}
n_components++;
if (q[quoted] == 0)
break;
if (q[quoted] != ',') {
r = -EINVAL;
goto fail;
}
p = q + 1 + quoted;
}
/* Order the whole thing, so that we always generate the same tree */
/* Check for duplicates */
for (i = 0; i+1 < n_components; i++)
r = -EINVAL;
goto fail;
}
return 0;
fail:
return r;
}
unsigned i;
int r;
if (n_components <= 0)
return strdup("");
if (!f)
return NULL;
for (i = 0; i < n_components; i++) {
char buf[32];
if (i != 0)
fputc(',', f);
fputc('=', f);
fputc('\'', f);
else
fputc('\'', f);
}
r = fflush_and_check(f);
if (r < 0)
return NULL;
return buffer;
}
int bus_match_add(
struct bus_match_node *root,
struct bus_match_component *components,
unsigned n_components,
struct match_callback *callback) {
unsigned i;
struct bus_match_node *n;
int r;
n = root;
for (i = 0; i < n_components; i++) {
n, components[i].type,
if (r < 0)
return r;
}
return bus_match_add_leaf(n, callback);
}
int bus_match_remove(
struct bus_match_node *root,
struct match_callback *callback) {
if (!node)
return 0;
/* Free the leaf */
/* Prune the tree above */
while (pp) {
if (!bus_match_node_maybe_free(node))
break;
}
return 1;
}
int bus_match_find(
struct bus_match_node *root,
struct bus_match_component *components,
unsigned n_components,
void *userdata,
struct match_callback **ret) {
struct bus_match_node *n, **gc;
unsigned i;
int r;
n = root;
for (i = 0; i < n_components; i++) {
n, components[i].type,
&n);
if (r <= 0)
return r;
gc[i] = n;
}
if (r <= 0)
return r;
return 1;
}
struct bus_match_node *c;
if (!node)
return;
Iterator i;
bus_match_free(c);
}
bus_match_free(c);
}
switch (t) {
case BUS_MATCH_ROOT:
return "root";
case BUS_MATCH_VALUE:
return "value";
case BUS_MATCH_LEAF:
return "leaf";
case BUS_MATCH_MESSAGE_TYPE:
return "type";
case BUS_MATCH_SENDER:
return "sender";
case BUS_MATCH_DESTINATION:
return "destination";
case BUS_MATCH_INTERFACE:
return "interface";
case BUS_MATCH_MEMBER:
return "member";
case BUS_MATCH_PATH:
return "path";
case BUS_MATCH_PATH_NAMESPACE:
return "path_namespace";
case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
return buf;
case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
return buf;
return buf;
case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
return buf;
default:
return NULL;
}
}
struct bus_match_node *c;
char buf[32];
if (!node)
return;
else
puts(" root");
printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata);
else
putchar('\n');
Iterator i;
}
}
enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) {
bool found_driver = false;
unsigned i;
if (n_components <= 0)
return BUS_MATCH_GENERIC;
/* Checks whether the specified match can only match the
* pseudo-service for local messages, which we detect by
* sender, interface or path. If a match is not restricted to
* local messages, then we check if it only matches on the
* driver. */
for (i = 0; i < n_components; i++) {
const struct bus_match_component *c = components + i;
if (c->type == BUS_MATCH_SENDER) {
return BUS_MATCH_LOCAL;
found_driver = true;
}
return BUS_MATCH_LOCAL;
return BUS_MATCH_LOCAL;
}
}