|
Packit |
34410b |
/*
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* Embedded Linux library
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* Copyright (C) 2016 Intel Corporation. All rights reserved.
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* This library is free software; you can redistribute it and/or
|
|
Packit |
34410b |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
34410b |
* License as published by the Free Software Foundation; either
|
|
Packit |
34410b |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
34410b |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
34410b |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
34410b |
* Lesser General Public License for more details.
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
34410b |
* License along with this library; if not, write to the Free Software
|
|
Packit |
34410b |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#ifdef HAVE_CONFIG_H
|
|
Packit |
34410b |
#include <config.h>
|
|
Packit |
34410b |
#endif
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#define _GNU_SOURCE
|
|
Packit |
34410b |
#include <stdlib.h>
|
|
Packit |
34410b |
#include <stdio.h>
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#include "util.h"
|
|
Packit |
34410b |
#include "queue.h"
|
|
Packit |
34410b |
#include "hashmap.h"
|
|
Packit |
34410b |
#include "string.h"
|
|
Packit |
34410b |
#include "dbus.h"
|
|
Packit |
34410b |
#include "dbus-private.h"
|
|
Packit |
34410b |
#include "gvariant-private.h"
|
|
Packit |
34410b |
#include "private.h"
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#define NODE_TYPE_CALLBACK L_DBUS_MATCH_NONE
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct filter_node {
|
|
Packit |
34410b |
enum l_dbus_match_type type;
|
|
Packit |
34410b |
union {
|
|
Packit |
34410b |
struct {
|
|
Packit |
34410b |
char *value;
|
|
Packit |
34410b |
struct filter_node *children;
|
|
Packit |
34410b |
bool remote_rule;
|
|
Packit |
34410b |
} match;
|
|
Packit |
34410b |
struct {
|
|
Packit |
34410b |
l_dbus_message_func_t func;
|
|
Packit |
34410b |
void *user_data;
|
|
Packit |
34410b |
} callback;
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
unsigned int id;
|
|
Packit |
34410b |
struct filter_node *next;
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct _dbus_filter {
|
|
Packit |
34410b |
struct l_dbus *dbus;
|
|
Packit |
34410b |
struct filter_node *root;
|
|
Packit |
34410b |
unsigned int signal_id;
|
|
Packit |
34410b |
unsigned int last_id;
|
|
Packit |
34410b |
const struct _dbus_filter_ops *driver;
|
|
Packit |
34410b |
struct _dbus_name_cache *name_cache;
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void filter_subtree_free(struct filter_node *node)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct filter_node *child, *next;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (node->type == NODE_TYPE_CALLBACK) {
|
|
Packit |
34410b |
l_free(node);
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
next = node->match.children;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
l_free(node->match.value);
|
|
Packit |
34410b |
l_free(node);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
while (next) {
|
|
Packit |
34410b |
child = next;
|
|
Packit |
34410b |
next = child->next;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
filter_subtree_free(child);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void dbus_filter_destroy(void *data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct _dbus_filter *filter = data;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (filter->root)
|
|
Packit |
34410b |
filter_subtree_free(filter->root);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
l_free(filter);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void filter_dispatch_match_recurse(struct _dbus_filter *filter,
|
|
Packit |
34410b |
struct filter_node *node,
|
|
Packit |
34410b |
struct l_dbus_message *message)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
const char *value = NULL;
|
|
Packit |
34410b |
const char *alt_value = NULL;
|
|
Packit |
34410b |
struct filter_node *child;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
switch ((int) node->type) {
|
|
Packit |
34410b |
case NODE_TYPE_CALLBACK:
|
|
Packit |
34410b |
node->callback.func(message, node->callback.user_data);
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
case L_DBUS_MATCH_SENDER:
|
|
Packit |
34410b |
value = l_dbus_message_get_sender(message);
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
case L_DBUS_MATCH_TYPE:
|
|
Packit |
34410b |
value = _dbus_message_get_type_as_string(message);
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
case L_DBUS_MATCH_PATH:
|
|
Packit |
34410b |
value = l_dbus_message_get_path(message);
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
case L_DBUS_MATCH_INTERFACE:
|
|
Packit |
34410b |
value = l_dbus_message_get_interface(message);
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
case L_DBUS_MATCH_MEMBER:
|
|
Packit |
34410b |
value = l_dbus_message_get_member(message);
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
case L_DBUS_MATCH_ARG0...(L_DBUS_MATCH_ARG0 + 63):
|
|
Packit |
34410b |
value = _dbus_message_get_nth_string_argument(message,
|
|
Packit |
34410b |
node->type - L_DBUS_MATCH_ARG0);
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!value)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (node->type == L_DBUS_MATCH_SENDER && filter->name_cache)
|
|
Packit |
34410b |
alt_value = _dbus_name_cache_lookup(filter->name_cache,
|
|
Packit |
34410b |
node->match.value);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (strcmp(value, node->match.value) &&
|
|
Packit |
34410b |
(!alt_value || strcmp(value, alt_value)))
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
for (child = node->match.children; child; child = child->next)
|
|
Packit |
34410b |
filter_dispatch_match_recurse(filter, child, message);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
void _dbus_filter_dispatch(struct l_dbus_message *message, void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct _dbus_filter *filter = user_data;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
filter_dispatch_match_recurse(filter, filter->root, message);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct _dbus_filter *_dbus_filter_new(struct l_dbus *dbus,
|
|
Packit |
34410b |
const struct _dbus_filter_ops *driver,
|
|
Packit |
34410b |
struct _dbus_name_cache *name_cache)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct _dbus_filter *filter;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
filter = l_new(struct _dbus_filter, 1);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
filter->dbus = dbus;
|
|
Packit |
34410b |
filter->driver = driver;
|
|
Packit |
34410b |
filter->name_cache = name_cache;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!filter->driver->skip_register)
|
|
Packit |
34410b |
filter->signal_id = l_dbus_register(dbus, _dbus_filter_dispatch,
|
|
Packit |
34410b |
filter,
|
|
Packit |
34410b |
dbus_filter_destroy);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return filter;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
void _dbus_filter_free(struct _dbus_filter *filter)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (!filter)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!filter->driver->skip_register)
|
|
Packit |
34410b |
l_dbus_unregister(filter->dbus, filter->signal_id);
|
|
Packit |
34410b |
else
|
|
Packit |
34410b |
dbus_filter_destroy(filter);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static int condition_compare(const void *a, const void *b)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
const struct _dbus_filter_condition *condition_a = a, *condition_b = b;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return condition_a->type - condition_b->type;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static bool remove_recurse(struct _dbus_filter *filter,
|
|
Packit |
34410b |
struct filter_node **node, unsigned int id)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct filter_node *tmp;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
for (; *node; node = &(*node)->next) {
|
|
Packit |
34410b |
if ((*node)->type == NODE_TYPE_CALLBACK && (*node)->id == id)
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if ((*node)->type != NODE_TYPE_CALLBACK &&
|
|
Packit |
34410b |
remove_recurse(filter, &(*node)->match.children,
|
|
Packit |
34410b |
id))
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!*node)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if ((*node)->type == NODE_TYPE_CALLBACK || !(*node)->match.children) {
|
|
Packit |
34410b |
tmp = *node;
|
|
Packit |
34410b |
*node = tmp->next;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (tmp->match.remote_rule)
|
|
Packit |
34410b |
filter->driver->remove_match(filter->dbus, tmp->id);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (tmp->type == L_DBUS_MATCH_SENDER && filter->name_cache &&
|
|
Packit |
34410b |
!_dbus_parse_unique_name(tmp->match.value,
|
|
Packit |
34410b |
NULL))
|
|
Packit |
34410b |
_dbus_name_cache_remove(filter->name_cache,
|
|
Packit |
34410b |
tmp->match.value);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
filter_subtree_free(tmp);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
unsigned int _dbus_filter_add_rule(struct _dbus_filter *filter,
|
|
Packit |
34410b |
const struct _dbus_filter_condition *rule,
|
|
Packit |
34410b |
int rule_len,
|
|
Packit |
34410b |
l_dbus_message_func_t signal_func,
|
|
Packit |
34410b |
void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct filter_node **node_ptr = &filter->root;
|
|
Packit |
34410b |
struct filter_node *node;
|
|
Packit |
34410b |
struct filter_node *parent = filter->root;
|
|
Packit |
34410b |
bool remote_rule = false;
|
|
Packit |
34410b |
struct _dbus_filter_condition sorted[rule_len];
|
|
Packit |
34410b |
struct _dbus_filter_condition *unused, *condition;
|
|
Packit |
34410b |
struct _dbus_filter_condition *end = sorted + rule_len;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
memcpy(sorted, rule, sizeof(sorted));
|
|
Packit |
34410b |
qsort(sorted, rule_len, sizeof(*condition), condition_compare);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* Find or create a path in the tree with a node for each
|
|
Packit |
34410b |
* condition in the rule, loop until all conditions have been
|
|
Packit |
34410b |
* used.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
unused = sorted;
|
|
Packit |
34410b |
while (unused < end) {
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* Find a child of the node that matches any unused
|
|
Packit |
34410b |
* condition. Note there could be multiple matches, we're
|
|
Packit |
34410b |
* happy with the first we can find.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
while (*node_ptr) {
|
|
Packit |
34410b |
node = *node_ptr;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
for (condition = unused; condition < end; condition++) {
|
|
Packit |
34410b |
if (condition->type > node->type) {
|
|
Packit |
34410b |
condition = end;
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (condition->type < node->type ||
|
|
Packit |
34410b |
condition->type ==
|
|
Packit |
34410b |
L_DBUS_MATCH_NONE)
|
|
Packit |
34410b |
continue;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!strcmp(node->match.value,
|
|
Packit |
34410b |
condition->value))
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (condition < end)
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
node_ptr = &node->next;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* Add a node */
|
|
Packit |
34410b |
if (!*node_ptr) {
|
|
Packit |
34410b |
condition = unused;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
node = l_new(struct filter_node, 1);
|
|
Packit |
34410b |
node->type = condition->type;
|
|
Packit |
34410b |
node->match.value = l_strdup(condition->value);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
*node_ptr = node;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (node->type == L_DBUS_MATCH_SENDER &&
|
|
Packit |
34410b |
filter->name_cache &&
|
|
Packit |
34410b |
!_dbus_parse_unique_name(
|
|
Packit |
34410b |
node->match.value,
|
|
Packit |
34410b |
NULL))
|
|
Packit |
34410b |
_dbus_name_cache_add(filter->name_cache,
|
|
Packit |
34410b |
node->match.value);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* Mark the condition used. We do this by setting
|
|
Packit |
34410b |
* condition->type to an invalid value unless it is the
|
|
Packit |
34410b |
* first condition left in which case we can push the
|
|
Packit |
34410b |
* rule start. Another option is to always push the rule
|
|
Packit |
34410b |
* start and memmove the still unused conditions by one
|
|
Packit |
34410b |
* if necessary.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
condition->type = L_DBUS_MATCH_NONE;
|
|
Packit |
34410b |
while (unused < end && unused[0].type == L_DBUS_MATCH_NONE)
|
|
Packit |
34410b |
unused++;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
node_ptr = &node->match.children;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
parent = node;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* Only have to call AddMatch if none of the parent nodes
|
|
Packit |
34410b |
* have yet created an AddMatch rule on the server.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
remote_rule |= node->match.remote_rule;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
node = l_new(struct filter_node, 1);
|
|
Packit |
34410b |
node->type = NODE_TYPE_CALLBACK;
|
|
Packit |
34410b |
node->callback.func = signal_func;
|
|
Packit |
34410b |
node->callback.user_data = user_data;
|
|
Packit |
34410b |
node->id = ++filter->last_id;
|
|
Packit |
34410b |
node->next = *node_ptr;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
*node_ptr = node;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!remote_rule) {
|
|
Packit |
34410b |
if (!filter->driver->add_match(filter->dbus, node->id,
|
|
Packit |
34410b |
rule, rule_len))
|
|
Packit |
34410b |
goto err;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
parent->id = node->id;
|
|
Packit |
34410b |
parent->match.remote_rule = true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return node->id;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
err:
|
|
Packit |
34410b |
/* Remove all the nodes we may have added */
|
|
Packit |
34410b |
node->id = (unsigned int) -1;
|
|
Packit |
34410b |
remove_recurse(filter, &filter->root, node->id);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bool _dbus_filter_remove_rule(struct _dbus_filter *filter, unsigned int id)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
return remove_recurse(filter, &filter->root, id);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
char *_dbus_filter_rule_to_str(const struct _dbus_filter_condition *rule,
|
|
Packit |
34410b |
int rule_len)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct l_string *str = l_string_new(63);
|
|
Packit |
34410b |
char *key, arg_buf[6];
|
|
Packit |
34410b |
const char *value, *endp;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
for (; rule_len; rule++, rule_len--) {
|
|
Packit |
34410b |
switch ((int) rule->type) {
|
|
Packit |
34410b |
case L_DBUS_MATCH_SENDER:
|
|
Packit |
34410b |
key = "sender";
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
case L_DBUS_MATCH_TYPE:
|
|
Packit |
34410b |
key = "type";
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
case L_DBUS_MATCH_PATH:
|
|
Packit |
34410b |
key = "path";
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
case L_DBUS_MATCH_INTERFACE:
|
|
Packit |
34410b |
key = "interface";
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
case L_DBUS_MATCH_MEMBER:
|
|
Packit |
34410b |
key = "member";
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
case L_DBUS_MATCH_ARG0...(L_DBUS_MATCH_ARG0 + 63):
|
|
Packit |
34410b |
key = arg_buf;
|
|
Packit |
34410b |
snprintf(arg_buf, sizeof(arg_buf), "arg%i",
|
|
Packit |
34410b |
rule->type - L_DBUS_MATCH_ARG0);
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
default:
|
|
Packit |
34410b |
l_string_free(str);
|
|
Packit |
34410b |
return NULL;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
l_string_append(str, key);
|
|
Packit |
34410b |
l_string_append(str, "='");
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* We only need to escape single-quotes in the values */
|
|
Packit |
34410b |
value = rule->value;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
while ((endp = strchr(value, '\''))) {
|
|
Packit |
34410b |
l_string_append_fixed(str, value, endp - value);
|
|
Packit |
34410b |
l_string_append(str, "'\\''");
|
|
Packit |
34410b |
|
|
Packit |
34410b |
value = endp + 1;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
l_string_append(str, value);
|
|
Packit |
34410b |
l_string_append_c(str, '\'');
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (rule_len > 1)
|
|
Packit |
34410b |
l_string_append_c(str, ',');
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return l_string_unwrap(str);
|
|
Packit |
34410b |
}
|