2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2013-03-31 16:16:37 +02:00
|
|
|
|
2017-12-11 19:50:30 +01:00
|
|
|
#include <stdio_ext.h>
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2013-03-31 16:16:37 +02:00
|
|
|
#include "bus-internal.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "bus-match.h"
|
2013-03-31 16:16:37 +02:00
|
|
|
#include "bus-message.h"
|
2015-08-27 16:32:22 +02:00
|
|
|
#include "bus-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2015-10-26 18:05:03 +01:00
|
|
|
#include "fileio.h"
|
2015-10-26 16:41:43 +01:00
|
|
|
#include "hexdecoct.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
2013-12-21 02:56:34 +01:00
|
|
|
#include "strv.h"
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
/* 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) {
|
2015-08-25 19:28:30 +02:00
|
|
|
return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_HAS_LAST;
|
2013-03-31 16:16:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) {
|
|
|
|
return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) ||
|
2015-08-25 19:28:30 +02:00
|
|
|
(t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST) ||
|
|
|
|
(t >= BUS_MATCH_ARG_HAS && t <= BUS_MATCH_ARG_HAS_LAST);
|
2013-03-31 16:16:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void bus_match_node_free(struct bus_match_node *node) {
|
|
|
|
assert(node);
|
|
|
|
assert(node->parent);
|
|
|
|
assert(!node->child);
|
|
|
|
assert(node->type != BUS_MATCH_ROOT);
|
|
|
|
assert(node->type < _BUS_MATCH_NODE_TYPE_MAX);
|
|
|
|
|
|
|
|
if (node->parent->child) {
|
|
|
|
/* We are apparently linked into the parent's child
|
|
|
|
* list. Let's remove us from there. */
|
|
|
|
if (node->prev) {
|
|
|
|
assert(node->prev->next == node);
|
|
|
|
node->prev->next = node->next;
|
|
|
|
} else {
|
|
|
|
assert(node->parent->child == node);
|
|
|
|
node->parent->child = node->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node->next)
|
|
|
|
node->next->prev = node->prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node->type == BUS_MATCH_VALUE) {
|
|
|
|
/* We might be in the parent's hash table, so clean
|
|
|
|
* this up */
|
|
|
|
|
|
|
|
if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
|
|
|
|
hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8));
|
|
|
|
else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str)
|
|
|
|
hashmap_remove(node->parent->compare.children, node->value.str);
|
|
|
|
|
|
|
|
free(node->value.str);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BUS_MATCH_IS_COMPARE(node->type)) {
|
|
|
|
assert(hashmap_isempty(node->compare.children));
|
|
|
|
hashmap_free(node->compare.children);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool bus_match_node_maybe_free(struct bus_match_node *node) {
|
|
|
|
assert(node);
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
if (node->type == BUS_MATCH_ROOT)
|
|
|
|
return false;
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
if (node->child)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
bus_match_node_free(node);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool value_node_test(
|
|
|
|
struct bus_match_node *node,
|
|
|
|
enum bus_match_node_type parent_type,
|
|
|
|
uint8_t value_u8,
|
2013-12-21 02:56:34 +01:00
|
|
|
const char *value_str,
|
2014-11-28 19:16:37 +01:00
|
|
|
char **value_strv,
|
2013-12-21 02:56:34 +01:00
|
|
|
sd_bus_message *m) {
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
assert(node);
|
|
|
|
assert(node->type == BUS_MATCH_VALUE);
|
|
|
|
|
|
|
|
/* Tests parameters against this value node, doing prefix
|
|
|
|
* magic and stuff. */
|
|
|
|
|
|
|
|
switch (parent_type) {
|
|
|
|
|
|
|
|
case BUS_MATCH_MESSAGE_TYPE:
|
|
|
|
return node->value.u8 == value_u8;
|
|
|
|
|
|
|
|
case BUS_MATCH_SENDER:
|
2013-10-30 02:05:38 +01:00
|
|
|
if (streq_ptr(node->value.str, value_str))
|
|
|
|
return true;
|
|
|
|
|
2013-12-21 02:56:34 +01:00
|
|
|
if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
|
|
|
|
char **i;
|
2013-10-30 02:05:38 +01:00
|
|
|
|
2013-12-21 02:56:34 +01:00
|
|
|
/* on kdbus we have the well known names list
|
|
|
|
* in the credentials, let's make use of that
|
|
|
|
* for an accurate match */
|
|
|
|
|
|
|
|
STRV_FOREACH(i, m->creds.well_known_names)
|
|
|
|
if (streq_ptr(node->value.str, *i))
|
|
|
|
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. */
|
|
|
|
|
|
|
|
if (node->value.str[0] != ':' && value_str && value_str[0] == ':')
|
|
|
|
return true;
|
|
|
|
}
|
2013-10-30 02:05:38 +01:00
|
|
|
|
|
|
|
return false;
|
|
|
|
|
2013-12-21 02:56:34 +01:00
|
|
|
case BUS_MATCH_DESTINATION:
|
2013-03-31 16:16:37 +02:00
|
|
|
case BUS_MATCH_INTERFACE:
|
|
|
|
case BUS_MATCH_MEMBER:
|
|
|
|
case BUS_MATCH_PATH:
|
2015-08-25 19:28:30 +02:00
|
|
|
case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
|
2013-03-31 16:16:37 +02:00
|
|
|
|
2014-11-28 19:16:37 +01:00
|
|
|
if (value_str)
|
|
|
|
return streq_ptr(node->value.str, value_str);
|
|
|
|
|
2015-08-25 19:28:30 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: {
|
|
|
|
char **i;
|
|
|
|
|
2014-11-28 19:16:37 +01:00
|
|
|
STRV_FOREACH(i, value_strv)
|
|
|
|
if (streq_ptr(node->value.str, *i))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-25 19:28:30 +02:00
|
|
|
case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
|
2014-11-28 19:16:37 +01:00
|
|
|
if (value_str)
|
|
|
|
return namespace_simple_pattern(node->value.str, value_str);
|
|
|
|
|
|
|
|
return false;
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
case BUS_MATCH_PATH_NAMESPACE:
|
|
|
|
return path_simple_pattern(node->value.str, value_str);
|
|
|
|
|
2015-08-25 19:28:30 +02:00
|
|
|
case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
|
2014-11-28 19:16:37 +01:00
|
|
|
if (value_str)
|
|
|
|
return path_complex_pattern(node->value.str, value_str);
|
|
|
|
|
|
|
|
return false;
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
assert_not_reached("Invalid node type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool value_node_same(
|
|
|
|
struct bus_match_node *node,
|
|
|
|
enum bus_match_node_type parent_type,
|
|
|
|
uint8_t value_u8,
|
|
|
|
const char *value_str) {
|
|
|
|
|
|
|
|
/* Tests parameters against this value node, not doing prefix
|
|
|
|
* magic and stuff, i.e. this one actually compares the match
|
2014-12-10 20:00:05 +01:00
|
|
|
* itself. */
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
assert(node);
|
|
|
|
assert(node->type == BUS_MATCH_VALUE);
|
|
|
|
|
|
|
|
switch (parent_type) {
|
|
|
|
|
|
|
|
case BUS_MATCH_MESSAGE_TYPE:
|
|
|
|
return node->value.u8 == value_u8;
|
|
|
|
|
|
|
|
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:
|
2015-08-25 19:28:30 +02:00
|
|
|
case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
|
2013-03-31 16:16:37 +02:00
|
|
|
case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
|
|
|
|
case BUS_MATCH_PATH_NAMESPACE:
|
|
|
|
case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
|
|
|
|
return streq(node->value.str, value_str);
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert_not_reached("Invalid node type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int bus_match_run(
|
|
|
|
sd_bus *bus,
|
|
|
|
struct bus_match_node *node,
|
|
|
|
sd_bus_message *m) {
|
|
|
|
|
2014-11-28 19:16:37 +01:00
|
|
|
_cleanup_strv_free_ char **test_strv = NULL;
|
2013-03-31 16:16:37 +02:00
|
|
|
const char *test_str = NULL;
|
|
|
|
uint8_t test_u8 = 0;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
return 0;
|
|
|
|
|
2013-04-05 03:55:58 +02:00
|
|
|
if (bus && bus->match_callbacks_modified)
|
|
|
|
return 0;
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
/* 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. */
|
|
|
|
|
|
|
|
switch (node->type) {
|
|
|
|
|
|
|
|
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. */
|
2013-05-16 21:14:56 +02:00
|
|
|
return bus_match_run(bus, node->child, m);
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
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 */
|
|
|
|
|
|
|
|
assert(node->child);
|
2013-05-16 21:14:56 +02:00
|
|
|
return bus_match_run(bus, node->child, m);
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
case BUS_MATCH_LEAF:
|
|
|
|
|
2013-04-05 03:55:58 +02:00
|
|
|
if (bus) {
|
2014-05-15 01:15:30 +02:00
|
|
|
if (node->leaf.callback->last_iteration == bus->iteration_counter)
|
2013-04-05 03:55:58 +02:00
|
|
|
return 0;
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
node->leaf.callback->last_iteration = bus->iteration_counter;
|
2013-04-05 03:55:58 +02:00
|
|
|
}
|
|
|
|
|
2013-04-14 17:46:41 +02:00
|
|
|
r = sd_bus_message_rewind(m, true);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
/* Run the callback. And then invoke siblings. */
|
2014-05-15 17:54:32 +02:00
|
|
|
if (node->leaf.callback->callback) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
|
2014-05-15 17:08:24 +02:00
|
|
|
sd_bus_slot *slot;
|
2013-11-21 19:34:37 +01:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
slot = container_of(node->leaf.callback, sd_bus_slot, match_callback);
|
2014-08-18 17:41:56 +02:00
|
|
|
if (bus) {
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_ref(slot);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_handler = node->leaf.callback->callback;
|
|
|
|
bus->current_userdata = slot->userdata;
|
|
|
|
}
|
2015-04-29 18:35:10 +02:00
|
|
|
r = node->leaf.callback->callback(m, slot->userdata, &error_buffer);
|
2014-08-18 17:41:56 +02:00
|
|
|
if (bus) {
|
|
|
|
bus->current_userdata = NULL;
|
|
|
|
bus->current_handler = NULL;
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_unref(slot);
|
2014-08-18 17:41:56 +02:00
|
|
|
}
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2013-11-21 19:34:37 +01:00
|
|
|
r = bus_maybe_reply_error(m, r, &error_buffer);
|
2013-05-19 18:39:08 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2014-03-14 21:12:36 +01:00
|
|
|
|
|
|
|
if (bus && bus->match_callbacks_modified)
|
|
|
|
return 0;
|
2013-05-19 18:39:08 +02:00
|
|
|
}
|
2013-03-31 16:16:37 +02:00
|
|
|
|
2013-05-16 21:14:56 +02:00
|
|
|
return bus_match_run(bus, node->next, m);
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
case BUS_MATCH_MESSAGE_TYPE:
|
|
|
|
test_u8 = m->header->type;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BUS_MATCH_SENDER:
|
|
|
|
test_str = m->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:
|
|
|
|
test_str = m->interface;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BUS_MATCH_MEMBER:
|
|
|
|
test_str = m->member;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BUS_MATCH_PATH:
|
|
|
|
case BUS_MATCH_PATH_NAMESPACE:
|
|
|
|
test_str = m->path;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
|
2015-08-25 19:28:30 +02:00
|
|
|
(void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str);
|
2013-03-31 16:16:37 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
|
2015-08-25 19:28:30 +02:00
|
|
|
(void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str);
|
2013-03-31 16:16:37 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
|
2015-08-25 19:28:30 +02:00
|
|
|
(void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
|
|
|
|
(void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv);
|
2013-03-31 16:16:37 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert_not_reached("Unknown match type.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BUS_MATCH_CAN_HASH(node->type)) {
|
|
|
|
struct bus_match_node *found;
|
|
|
|
|
|
|
|
/* Lookup via hash table, nice! So let's jump directly. */
|
|
|
|
|
|
|
|
if (test_str)
|
|
|
|
found = hashmap_get(node->compare.children, test_str);
|
2014-11-28 19:16:37 +01:00
|
|
|
else if (test_strv) {
|
|
|
|
char **i;
|
|
|
|
|
|
|
|
STRV_FOREACH(i, test_strv) {
|
|
|
|
found = hashmap_get(node->compare.children, *i);
|
|
|
|
if (found) {
|
|
|
|
r = bus_match_run(bus, found, m);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
found = NULL;
|
|
|
|
} else if (node->type == BUS_MATCH_MESSAGE_TYPE)
|
2013-03-31 16:16:37 +02:00
|
|
|
found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
|
|
|
|
else
|
|
|
|
found = NULL;
|
|
|
|
|
|
|
|
if (found) {
|
2013-05-16 21:14:56 +02:00
|
|
|
r = bus_match_run(bus, found, m);
|
2013-03-31 16:16:37 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
struct bus_match_node *c;
|
|
|
|
|
|
|
|
/* No hash table, so let's iterate manually... */
|
|
|
|
|
|
|
|
for (c = node->child; c; c = c->next) {
|
2014-11-28 19:16:37 +01:00
|
|
|
if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m))
|
2013-03-31 16:16:37 +02:00
|
|
|
continue;
|
|
|
|
|
2013-05-16 21:14:56 +02:00
|
|
|
r = bus_match_run(bus, c, m);
|
2013-03-31 16:16:37 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2016-06-29 22:22:12 +02:00
|
|
|
|
|
|
|
if (bus && bus->match_callbacks_modified)
|
|
|
|
return 0;
|
2013-03-31 16:16:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-05 03:55:58 +02:00
|
|
|
if (bus && bus->match_callbacks_modified)
|
|
|
|
return 0;
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
/* And now, let's invoke our siblings */
|
2013-05-16 21:14:56 +02:00
|
|
|
return bus_match_run(bus, node->next, m);
|
2013-03-31 16:16:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int bus_match_add_compare_value(
|
|
|
|
struct bus_match_node *where,
|
|
|
|
enum bus_match_node_type t,
|
|
|
|
uint8_t value_u8,
|
|
|
|
const char *value_str,
|
|
|
|
struct bus_match_node **ret) {
|
|
|
|
|
|
|
|
struct bus_match_node *c = NULL, *n = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(where);
|
2017-09-28 10:17:04 +02:00
|
|
|
assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
|
2013-03-31 16:16:37 +02:00
|
|
|
assert(BUS_MATCH_IS_COMPARE(t));
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
for (c = where->child; c && c->type != t; c = c->next)
|
|
|
|
;
|
|
|
|
|
|
|
|
if (c) {
|
|
|
|
/* Comparison node already exists? Then let's see if
|
|
|
|
* the value node exists too. */
|
|
|
|
|
|
|
|
if (t == BUS_MATCH_MESSAGE_TYPE)
|
|
|
|
n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
|
|
|
|
else if (BUS_MATCH_CAN_HASH(t))
|
|
|
|
n = hashmap_get(c->compare.children, value_str);
|
|
|
|
else {
|
|
|
|
for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n) {
|
|
|
|
*ret = n;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Comparison node, doesn't exist yet? Then let's
|
|
|
|
* create it. */
|
|
|
|
|
|
|
|
c = new0(struct bus_match_node, 1);
|
|
|
|
if (!c) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
c->type = t;
|
|
|
|
c->parent = where;
|
|
|
|
c->next = where->child;
|
|
|
|
if (c->next)
|
|
|
|
c->next->prev = c;
|
|
|
|
where->child = c;
|
|
|
|
|
|
|
|
if (t == BUS_MATCH_MESSAGE_TYPE) {
|
2014-08-13 01:00:18 +02:00
|
|
|
c->compare.children = hashmap_new(NULL);
|
2013-03-31 16:16:37 +02:00
|
|
|
if (!c->compare.children) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else if (BUS_MATCH_CAN_HASH(t)) {
|
2014-08-13 01:00:18 +02:00
|
|
|
c->compare.children = hashmap_new(&string_hash_ops);
|
2013-03-31 16:16:37 +02:00
|
|
|
if (!c->compare.children) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
n = new0(struct bus_match_node, 1);
|
|
|
|
if (!n) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
n->type = BUS_MATCH_VALUE;
|
|
|
|
n->value.u8 = value_u8;
|
|
|
|
if (value_str) {
|
|
|
|
n->value.str = strdup(value_str);
|
|
|
|
if (!n->value.str) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
n->parent = c;
|
|
|
|
if (c->compare.children) {
|
|
|
|
|
|
|
|
if (t == BUS_MATCH_MESSAGE_TYPE)
|
|
|
|
r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n);
|
|
|
|
else
|
|
|
|
r = hashmap_put(c->compare.children, n->value.str, n);
|
|
|
|
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
} else {
|
|
|
|
n->next = c->child;
|
|
|
|
if (n->next)
|
|
|
|
n->next->prev = n;
|
|
|
|
c->child = n;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret = n;
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (c)
|
|
|
|
bus_match_node_maybe_free(c);
|
|
|
|
|
|
|
|
if (n) {
|
|
|
|
free(n->value.str);
|
|
|
|
free(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bus_match_find_compare_value(
|
|
|
|
struct bus_match_node *where,
|
|
|
|
enum bus_match_node_type t,
|
|
|
|
uint8_t value_u8,
|
|
|
|
const char *value_str,
|
|
|
|
struct bus_match_node **ret) {
|
|
|
|
|
|
|
|
struct bus_match_node *c, *n;
|
|
|
|
|
|
|
|
assert(where);
|
2017-09-28 10:17:04 +02:00
|
|
|
assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
|
2013-03-31 16:16:37 +02:00
|
|
|
assert(BUS_MATCH_IS_COMPARE(t));
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
for (c = where->child; c && c->type != t; c = c->next)
|
|
|
|
;
|
|
|
|
|
|
|
|
if (!c)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (t == BUS_MATCH_MESSAGE_TYPE)
|
|
|
|
n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
|
|
|
|
else if (BUS_MATCH_CAN_HASH(t))
|
|
|
|
n = hashmap_get(c->compare.children, value_str);
|
|
|
|
else {
|
2014-10-29 17:58:43 +01:00
|
|
|
for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
|
2013-03-31 16:16:37 +02:00
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n) {
|
|
|
|
*ret = n;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bus_match_add_leaf(
|
|
|
|
struct bus_match_node *where,
|
2014-05-15 01:15:30 +02:00
|
|
|
struct match_callback *callback) {
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
struct bus_match_node *n;
|
|
|
|
|
|
|
|
assert(where);
|
2017-09-28 10:17:04 +02:00
|
|
|
assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
|
2014-05-15 01:15:30 +02:00
|
|
|
assert(callback);
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
n = new0(struct bus_match_node, 1);
|
|
|
|
if (!n)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
n->type = BUS_MATCH_LEAF;
|
|
|
|
n->parent = where;
|
|
|
|
n->next = where->child;
|
|
|
|
if (n->next)
|
|
|
|
n->next->prev = n;
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
n->leaf.callback = callback;
|
2014-05-15 01:15:30 +02:00
|
|
|
callback->match_node = n;
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
where->child = n;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bus_match_find_leaf(
|
|
|
|
struct bus_match_node *where,
|
2013-03-31 16:26:14 +02:00
|
|
|
sd_bus_message_handler_t callback,
|
2013-03-31 16:16:37 +02:00
|
|
|
void *userdata,
|
|
|
|
struct bus_match_node **ret) {
|
|
|
|
|
|
|
|
struct bus_match_node *c;
|
|
|
|
|
|
|
|
assert(where);
|
2017-09-28 10:17:04 +02:00
|
|
|
assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
|
2013-03-31 16:16:37 +02:00
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
for (c = where->child; c; c = c->next) {
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot *s;
|
|
|
|
|
|
|
|
s = container_of(c->leaf.callback, sd_bus_slot, match_callback);
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
if (c->type == BUS_MATCH_LEAF &&
|
2014-05-15 01:15:30 +02:00
|
|
|
c->leaf.callback->callback == callback &&
|
|
|
|
s->userdata == userdata) {
|
2013-03-31 16:16:37 +02:00
|
|
|
*ret = c;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) {
|
|
|
|
assert(k);
|
|
|
|
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 4 && startswith(k, "type"))
|
2013-03-31 16:16:37 +02:00
|
|
|
return BUS_MATCH_MESSAGE_TYPE;
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 6 && startswith(k, "sender"))
|
2013-03-31 16:16:37 +02:00
|
|
|
return BUS_MATCH_SENDER;
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 11 && startswith(k, "destination"))
|
2013-03-31 16:16:37 +02:00
|
|
|
return BUS_MATCH_DESTINATION;
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 9 && startswith(k, "interface"))
|
2013-03-31 16:16:37 +02:00
|
|
|
return BUS_MATCH_INTERFACE;
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 6 && startswith(k, "member"))
|
2013-03-31 16:16:37 +02:00
|
|
|
return BUS_MATCH_MEMBER;
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 4 && startswith(k, "path"))
|
2013-03-31 16:16:37 +02:00
|
|
|
return BUS_MATCH_PATH;
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 14 && startswith(k, "path_namespace"))
|
2013-03-31 16:16:37 +02:00
|
|
|
return BUS_MATCH_PATH_NAMESPACE;
|
|
|
|
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 4 && startswith(k, "arg")) {
|
2013-03-31 16:16:37 +02:00
|
|
|
int j;
|
|
|
|
|
|
|
|
j = undecchar(k[3]);
|
|
|
|
if (j < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return BUS_MATCH_ARG + j;
|
|
|
|
}
|
|
|
|
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 5 && startswith(k, "arg")) {
|
2013-03-31 16:16:37 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 8 && startswith(k, "arg") && startswith(k + 4, "path")) {
|
2013-03-31 16:16:37 +02:00
|
|
|
int j;
|
|
|
|
|
|
|
|
j = undecchar(k[3]);
|
|
|
|
if (j < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return BUS_MATCH_ARG_PATH + j;
|
|
|
|
}
|
|
|
|
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 9 && startswith(k, "arg") && startswith(k + 5, "path")) {
|
2013-03-31 16:16:37 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 13 && startswith(k, "arg") && startswith(k + 4, "namespace")) {
|
2013-03-31 16:16:37 +02:00
|
|
|
int j;
|
|
|
|
|
|
|
|
j = undecchar(k[3]);
|
|
|
|
if (j < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return BUS_MATCH_ARG_NAMESPACE + j;
|
|
|
|
}
|
|
|
|
|
2013-08-22 03:20:55 +02:00
|
|
|
if (n == 14 && startswith(k, "arg") && startswith(k + 5, "namespace")) {
|
2013-03-31 16:16:37 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-08-25 19:28:30 +02:00
|
|
|
if (n == 7 && startswith(k, "arg") && startswith(k + 4, "has")) {
|
|
|
|
int j;
|
|
|
|
|
|
|
|
j = undecchar(k[3]);
|
|
|
|
if (j < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return BUS_MATCH_ARG_HAS + j;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n == 8 && startswith(k, "arg") && startswith(k + 5, "has")) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int match_component_compare(const void *a, const void *b) {
|
2013-05-19 18:39:08 +02:00
|
|
|
const struct bus_match_component *x = a, *y = b;
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
if (x->type < y->type)
|
|
|
|
return -1;
|
|
|
|
if (x->type > y->type)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-19 18:39:08 +02:00
|
|
|
void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) {
|
2013-03-31 16:16:37 +02:00
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
for (i = 0; i < n_components; i++)
|
|
|
|
free(components[i].value_str);
|
|
|
|
|
|
|
|
free(components);
|
|
|
|
}
|
|
|
|
|
2013-05-19 18:39:08 +02:00
|
|
|
int bus_match_parse(
|
2013-03-31 16:16:37 +02:00
|
|
|
const char *match,
|
2013-05-19 18:39:08 +02:00
|
|
|
struct bus_match_component **_components,
|
2013-03-31 16:16:37 +02:00
|
|
|
unsigned *_n_components) {
|
|
|
|
|
|
|
|
const char *p = match;
|
2013-05-19 18:39:08 +02:00
|
|
|
struct bus_match_component *components = NULL;
|
2013-03-31 16:16:37 +02:00
|
|
|
size_t components_allocated = 0;
|
|
|
|
unsigned n_components = 0, i;
|
|
|
|
_cleanup_free_ char *value = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(match);
|
|
|
|
assert(_components);
|
|
|
|
assert(_n_components);
|
|
|
|
|
|
|
|
while (*p != 0) {
|
|
|
|
const char *eq, *q;
|
|
|
|
enum bus_match_node_type t;
|
|
|
|
unsigned j = 0;
|
|
|
|
size_t value_allocated = 0;
|
2014-01-08 11:09:25 +01:00
|
|
|
bool escaped = false, quoted;
|
2013-03-31 16:16:37 +02:00
|
|
|
uint8_t u;
|
|
|
|
|
2014-11-27 03:20:51 +01:00
|
|
|
/* Avahi's match rules appear to include whitespace, skip over it */
|
|
|
|
p += strspn(p, " ");
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
eq = strchr(p, '=');
|
|
|
|
if (!eq)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
t = bus_match_node_type_from_string(p, eq - p);
|
|
|
|
if (t < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-01-08 11:09:25 +01:00
|
|
|
quoted = eq[1] == '\'';
|
|
|
|
|
|
|
|
for (q = eq + 1 + quoted;; q++) {
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
if (*q == 0) {
|
2014-01-08 11:09:25 +01:00
|
|
|
|
|
|
|
if (quoted) {
|
|
|
|
r = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
} else {
|
|
|
|
if (value)
|
|
|
|
value[j] = 0;
|
|
|
|
break;
|
|
|
|
}
|
2013-03-31 16:16:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!escaped) {
|
|
|
|
if (*q == '\\') {
|
|
|
|
escaped = true;
|
|
|
|
continue;
|
|
|
|
}
|
2014-01-08 11:09:25 +01:00
|
|
|
|
|
|
|
if (quoted) {
|
|
|
|
if (*q == '\'') {
|
|
|
|
if (value)
|
|
|
|
value[j] = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (*q == ',') {
|
|
|
|
if (value)
|
|
|
|
value[j] = 0;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2013-03-31 16:16:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-12 00:58:45 +02:00
|
|
|
if (!GREEDY_REALLOC(value, value_allocated, j + 2)) {
|
2013-03-31 16:16:37 +02:00
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
value[j++] = *q;
|
|
|
|
escaped = false;
|
|
|
|
}
|
|
|
|
|
2013-12-12 21:25:31 +01:00
|
|
|
if (!value) {
|
|
|
|
value = strdup("");
|
|
|
|
if (!value) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
if (t == BUS_MATCH_MESSAGE_TYPE) {
|
|
|
|
r = bus_message_type_from_string(value, &u);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
2015-07-31 19:56:38 +02:00
|
|
|
value = mfree(value);
|
2013-03-31 16:16:37 +02:00
|
|
|
} else
|
|
|
|
u = 0;
|
|
|
|
|
2013-04-12 00:58:45 +02:00
|
|
|
if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) {
|
2013-03-31 16:16:37 +02:00
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
components[n_components].type = t;
|
2018-04-05 07:26:26 +02:00
|
|
|
components[n_components].value_str = TAKE_PTR(value);
|
2013-03-31 16:16:37 +02:00
|
|
|
components[n_components].value_u8 = u;
|
|
|
|
n_components++;
|
|
|
|
|
2014-02-16 03:01:17 +01:00
|
|
|
if (q[quoted] == 0)
|
2013-03-31 16:16:37 +02:00
|
|
|
break;
|
|
|
|
|
2014-01-08 11:09:25 +01:00
|
|
|
if (q[quoted] != ',') {
|
2013-03-31 16:16:37 +02:00
|
|
|
r = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2014-01-08 11:09:25 +01:00
|
|
|
p = q + 1 + quoted;
|
2013-03-31 16:16:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Order the whole thing, so that we always generate the same tree */
|
2013-10-12 01:33:13 +02:00
|
|
|
qsort_safe(components, n_components, sizeof(struct bus_match_component), match_component_compare);
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
/* Check for duplicates */
|
|
|
|
for (i = 0; i+1 < n_components; i++)
|
|
|
|
if (components[i].type == components[i+1].type) {
|
|
|
|
r = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
*_components = components;
|
|
|
|
*_n_components = n_components;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
2013-05-19 18:39:08 +02:00
|
|
|
bus_match_parse_free(components, n_components);
|
2013-03-31 16:16:37 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-12-19 03:02:45 +01:00
|
|
|
char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
|
2015-08-30 23:19:34 +02:00
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
2013-12-19 03:02:45 +01:00
|
|
|
char *buffer = NULL;
|
|
|
|
size_t size = 0;
|
|
|
|
unsigned i;
|
2015-07-29 20:31:07 +02:00
|
|
|
int r;
|
2013-12-19 03:02:45 +01:00
|
|
|
|
|
|
|
if (n_components <= 0)
|
|
|
|
return strdup("");
|
|
|
|
|
|
|
|
assert(components);
|
|
|
|
|
|
|
|
f = open_memstream(&buffer, &size);
|
|
|
|
if (!f)
|
|
|
|
return NULL;
|
|
|
|
|
2017-12-11 19:50:30 +01:00
|
|
|
__fsetlocking(f, FSETLOCKING_BYCALLER);
|
|
|
|
|
2013-12-19 03:02:45 +01:00
|
|
|
for (i = 0; i < n_components; i++) {
|
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
if (i != 0)
|
2017-12-11 19:50:30 +01:00
|
|
|
fputc(',', f);
|
2013-12-19 03:02:45 +01:00
|
|
|
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f);
|
|
|
|
fputc('=', f);
|
|
|
|
fputc('\'', f);
|
2013-12-19 03:02:45 +01:00
|
|
|
|
|
|
|
if (components[i].type == BUS_MATCH_MESSAGE_TYPE)
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs(bus_message_type_to_string(components[i].value_u8), f);
|
2013-12-19 03:02:45 +01:00
|
|
|
else
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs(components[i].value_str, f);
|
2013-12-19 03:02:45 +01:00
|
|
|
|
2017-12-11 19:50:30 +01:00
|
|
|
fputc('\'', f);
|
2013-12-19 03:02:45 +01:00
|
|
|
}
|
|
|
|
|
2015-07-29 20:31:07 +02:00
|
|
|
r = fflush_and_check(f);
|
|
|
|
if (r < 0)
|
2013-12-19 03:02:45 +01:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
int bus_match_add(
|
|
|
|
struct bus_match_node *root,
|
2013-05-19 18:39:08 +02:00
|
|
|
struct bus_match_component *components,
|
|
|
|
unsigned n_components,
|
2014-05-15 01:15:30 +02:00
|
|
|
struct match_callback *callback) {
|
2013-03-31 16:16:37 +02:00
|
|
|
|
2013-05-19 18:39:08 +02:00
|
|
|
unsigned i;
|
2013-03-31 16:16:37 +02:00
|
|
|
struct bus_match_node *n;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(root);
|
2014-05-15 01:15:30 +02:00
|
|
|
assert(callback);
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
n = root;
|
|
|
|
for (i = 0; i < n_components; i++) {
|
|
|
|
r = bus_match_add_compare_value(
|
|
|
|
n, components[i].type,
|
|
|
|
components[i].value_u8, components[i].value_str, &n);
|
|
|
|
if (r < 0)
|
2013-05-19 18:39:08 +02:00
|
|
|
return r;
|
2013-03-31 16:16:37 +02:00
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
return bus_match_add_leaf(n, callback);
|
|
|
|
}
|
2013-03-31 16:16:37 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
int bus_match_remove(
|
|
|
|
struct bus_match_node *root,
|
|
|
|
struct match_callback *callback) {
|
2013-03-31 16:16:37 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
struct bus_match_node *node, *pp;
|
|
|
|
|
|
|
|
assert(root);
|
|
|
|
assert(callback);
|
|
|
|
|
|
|
|
node = callback->match_node;
|
|
|
|
if (!node)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
assert(node->type == BUS_MATCH_LEAF);
|
|
|
|
|
|
|
|
callback->match_node = NULL;
|
|
|
|
|
|
|
|
/* Free the leaf */
|
|
|
|
pp = node->parent;
|
|
|
|
bus_match_node_free(node);
|
|
|
|
|
|
|
|
/* Prune the tree above */
|
|
|
|
while (pp) {
|
|
|
|
node = pp;
|
|
|
|
pp = node->parent;
|
|
|
|
|
|
|
|
if (!bus_match_node_maybe_free(node))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
2013-03-31 16:16:37 +02:00
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
int bus_match_find(
|
2013-03-31 16:16:37 +02:00
|
|
|
struct bus_match_node *root,
|
2013-05-19 18:39:08 +02:00
|
|
|
struct bus_match_component *components,
|
|
|
|
unsigned n_components,
|
2013-03-31 16:26:14 +02:00
|
|
|
sd_bus_message_handler_t callback,
|
2013-05-19 18:39:08 +02:00
|
|
|
void *userdata,
|
2014-05-15 01:15:30 +02:00
|
|
|
struct match_callback **ret) {
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
struct bus_match_node *n, **gc;
|
2014-05-15 01:15:30 +02:00
|
|
|
unsigned i;
|
2013-03-31 16:16:37 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(root);
|
2014-05-15 01:15:30 +02:00
|
|
|
assert(ret);
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
gc = newa(struct bus_match_node*, n_components);
|
|
|
|
|
|
|
|
n = root;
|
|
|
|
for (i = 0; i < n_components; i++) {
|
|
|
|
r = bus_match_find_compare_value(
|
|
|
|
n, components[i].type,
|
|
|
|
components[i].value_u8, components[i].value_str,
|
|
|
|
&n);
|
|
|
|
if (r <= 0)
|
2013-05-19 18:39:08 +02:00
|
|
|
return r;
|
2013-03-31 16:16:37 +02:00
|
|
|
|
|
|
|
gc[i] = n;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = bus_match_find_leaf(n, callback, userdata, &n);
|
|
|
|
if (r <= 0)
|
2013-05-19 18:39:08 +02:00
|
|
|
return r;
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
*ret = n->leaf.callback;
|
|
|
|
return 1;
|
2013-03-31 16:16:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void bus_match_free(struct bus_match_node *node) {
|
|
|
|
struct bus_match_node *c;
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (BUS_MATCH_CAN_HASH(node->type)) {
|
|
|
|
Iterator i;
|
|
|
|
|
|
|
|
HASHMAP_FOREACH(c, node->compare.children, i)
|
|
|
|
bus_match_free(c);
|
|
|
|
|
|
|
|
assert(hashmap_isempty(node->compare.children));
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((c = node->child))
|
|
|
|
bus_match_free(c);
|
|
|
|
|
|
|
|
if (node->type != BUS_MATCH_ROOT)
|
|
|
|
bus_match_node_free(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
|
|
|
|
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:
|
|
|
|
snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
|
|
|
|
return buf;
|
|
|
|
|
|
|
|
case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
|
|
|
|
snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
|
|
|
|
return buf;
|
|
|
|
|
|
|
|
case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
|
|
|
|
snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
|
|
|
|
return buf;
|
|
|
|
|
2015-08-25 19:28:30 +02:00
|
|
|
case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
|
|
|
|
snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS);
|
|
|
|
return buf;
|
|
|
|
|
2013-03-31 16:16:37 +02:00
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void bus_match_dump(struct bus_match_node *node, unsigned level) {
|
|
|
|
struct bus_match_node *c;
|
|
|
|
_cleanup_free_ char *pfx = NULL;
|
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pfx = strrep(" ", level);
|
|
|
|
printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
|
|
|
|
|
|
|
|
if (node->type == BUS_MATCH_VALUE) {
|
|
|
|
if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
|
|
|
|
printf(" <%u>\n", node->value.u8);
|
|
|
|
else
|
|
|
|
printf(" <%s>\n", node->value.str);
|
|
|
|
} else if (node->type == BUS_MATCH_ROOT)
|
|
|
|
puts(" root");
|
|
|
|
else if (node->type == BUS_MATCH_LEAF)
|
2014-05-15 01:15:30 +02:00
|
|
|
printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata);
|
2013-03-31 16:16:37 +02:00
|
|
|
else
|
|
|
|
putchar('\n');
|
|
|
|
|
|
|
|
if (BUS_MATCH_CAN_HASH(node->type)) {
|
|
|
|
Iterator i;
|
|
|
|
|
|
|
|
HASHMAP_FOREACH(c, node->compare.children, i)
|
|
|
|
bus_match_dump(c, level + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (c = node->child; c; c = c->next)
|
|
|
|
bus_match_dump(c, level + 1);
|
|
|
|
}
|
2015-06-17 11:42:39 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
assert(components);
|
|
|
|
|
|
|
|
/* 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) {
|
|
|
|
if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
|
|
|
|
return BUS_MATCH_LOCAL;
|
|
|
|
|
|
|
|
if (streq_ptr(c->value_str, "org.freedesktop.DBus"))
|
|
|
|
found_driver = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
|
|
|
|
return BUS_MATCH_LOCAL;
|
|
|
|
|
|
|
|
if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local"))
|
|
|
|
return BUS_MATCH_LOCAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC;
|
|
|
|
|
|
|
|
}
|