dbus-filter.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /*
  2. *
  3. * Embedded Linux library
  4. *
  5. * Copyright (C) 2016 Intel Corporation. All rights reserved.
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. */
  22. #ifdef HAVE_CONFIG_H
  23. #include <config.h>
  24. #endif
  25. #define _GNU_SOURCE
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28. #include "util.h"
  29. #include "queue.h"
  30. #include "hashmap.h"
  31. #include "string.h"
  32. #include "dbus.h"
  33. #include "dbus-private.h"
  34. #include "gvariant-private.h"
  35. #include "private.h"
  36. #include "useful.h"
  37. #define NODE_TYPE_CALLBACK L_DBUS_MATCH_NONE
  38. struct filter_node {
  39. enum l_dbus_match_type type;
  40. union {
  41. struct {
  42. char *value;
  43. struct filter_node *children;
  44. bool remote_rule;
  45. } match;
  46. struct {
  47. l_dbus_message_func_t func;
  48. void *user_data;
  49. } callback;
  50. };
  51. unsigned int id;
  52. struct filter_node *next;
  53. };
  54. struct _dbus_filter {
  55. struct l_dbus *dbus;
  56. struct filter_node *root;
  57. unsigned int signal_id;
  58. unsigned int last_id;
  59. const struct _dbus_filter_ops *driver;
  60. struct _dbus_name_cache *name_cache;
  61. };
  62. static void filter_subtree_free(struct filter_node *node)
  63. {
  64. struct filter_node *child, *next;
  65. if (node->type == NODE_TYPE_CALLBACK) {
  66. l_free(node);
  67. return;
  68. }
  69. next = node->match.children;
  70. l_free(node->match.value);
  71. l_free(node);
  72. while (next) {
  73. child = next;
  74. next = child->next;
  75. filter_subtree_free(child);
  76. }
  77. }
  78. static void dbus_filter_destroy(void *data)
  79. {
  80. struct _dbus_filter *filter = data;
  81. if (filter->root)
  82. filter_subtree_free(filter->root);
  83. l_free(filter);
  84. }
  85. static void filter_dispatch_match_recurse(struct _dbus_filter *filter,
  86. struct filter_node *node,
  87. struct l_dbus_message *message)
  88. {
  89. const char *value = NULL;
  90. const char *alt_value = NULL;
  91. struct filter_node *child;
  92. switch ((int) node->type) {
  93. case NODE_TYPE_CALLBACK:
  94. node->callback.func(message, node->callback.user_data);
  95. return;
  96. case L_DBUS_MATCH_SENDER:
  97. value = l_dbus_message_get_sender(message);
  98. break;
  99. case L_DBUS_MATCH_TYPE:
  100. value = _dbus_message_get_type_as_string(message);
  101. break;
  102. case L_DBUS_MATCH_PATH:
  103. value = l_dbus_message_get_path(message);
  104. break;
  105. case L_DBUS_MATCH_INTERFACE:
  106. value = l_dbus_message_get_interface(message);
  107. break;
  108. case L_DBUS_MATCH_MEMBER:
  109. value = l_dbus_message_get_member(message);
  110. break;
  111. case L_DBUS_MATCH_ARG0...(L_DBUS_MATCH_ARG0 + 63):
  112. value = _dbus_message_get_nth_string_argument(message,
  113. node->type - L_DBUS_MATCH_ARG0);
  114. break;
  115. }
  116. if (!value)
  117. return;
  118. if (node->type == L_DBUS_MATCH_SENDER && filter->name_cache)
  119. alt_value = _dbus_name_cache_lookup(filter->name_cache,
  120. node->match.value);
  121. if (strcmp(value, node->match.value) &&
  122. (!alt_value || strcmp(value, alt_value)))
  123. return;
  124. for (child = node->match.children; child; child = child->next)
  125. filter_dispatch_match_recurse(filter, child, message);
  126. }
  127. void _dbus_filter_dispatch(struct l_dbus_message *message, void *user_data)
  128. {
  129. struct _dbus_filter *filter = user_data;
  130. filter_dispatch_match_recurse(filter, filter->root, message);
  131. }
  132. struct _dbus_filter *_dbus_filter_new(struct l_dbus *dbus,
  133. const struct _dbus_filter_ops *driver,
  134. struct _dbus_name_cache *name_cache)
  135. {
  136. struct _dbus_filter *filter;
  137. filter = l_new(struct _dbus_filter, 1);
  138. filter->dbus = dbus;
  139. filter->driver = driver;
  140. filter->name_cache = name_cache;
  141. if (!filter->driver->skip_register)
  142. filter->signal_id = l_dbus_register(dbus, _dbus_filter_dispatch,
  143. filter,
  144. dbus_filter_destroy);
  145. return filter;
  146. }
  147. void _dbus_filter_free(struct _dbus_filter *filter)
  148. {
  149. if (!filter)
  150. return;
  151. if (!filter->driver->skip_register)
  152. l_dbus_unregister(filter->dbus, filter->signal_id);
  153. else
  154. dbus_filter_destroy(filter);
  155. }
  156. static int condition_compare(const void *a, const void *b)
  157. {
  158. const struct _dbus_filter_condition *condition_a = a, *condition_b = b;
  159. return condition_a->type - condition_b->type;
  160. }
  161. static bool remove_recurse(struct _dbus_filter *filter,
  162. struct filter_node **node, unsigned int id)
  163. {
  164. struct filter_node *tmp;
  165. for (; *node; node = &(*node)->next) {
  166. if ((*node)->type == NODE_TYPE_CALLBACK && (*node)->id == id)
  167. break;
  168. if ((*node)->type != NODE_TYPE_CALLBACK &&
  169. remove_recurse(filter, &(*node)->match.children,
  170. id))
  171. break;
  172. }
  173. if (!*node)
  174. return false;
  175. if ((*node)->type == NODE_TYPE_CALLBACK || !(*node)->match.children) {
  176. tmp = *node;
  177. *node = tmp->next;
  178. if (tmp->match.remote_rule)
  179. filter->driver->remove_match(filter->dbus, tmp->id);
  180. if (tmp->type == L_DBUS_MATCH_SENDER && filter->name_cache &&
  181. !_dbus_parse_unique_name(tmp->match.value,
  182. NULL))
  183. _dbus_name_cache_remove(filter->name_cache,
  184. tmp->match.value);
  185. filter_subtree_free(tmp);
  186. }
  187. return true;
  188. }
  189. unsigned int _dbus_filter_add_rule(struct _dbus_filter *filter,
  190. const struct _dbus_filter_condition *rule,
  191. int rule_len,
  192. l_dbus_message_func_t signal_func,
  193. void *user_data)
  194. {
  195. struct filter_node **node_ptr = &filter->root;
  196. struct filter_node *node;
  197. struct filter_node *parent = filter->root;
  198. bool remote_rule = false;
  199. struct _dbus_filter_condition sorted[rule_len];
  200. struct _dbus_filter_condition *unused;
  201. struct _dbus_filter_condition *condition;
  202. struct _dbus_filter_condition *end = sorted + rule_len;
  203. memcpy(sorted, rule, sizeof(sorted));
  204. qsort(sorted, rule_len, sizeof(*condition), condition_compare);
  205. /*
  206. * Find or create a path in the tree with a node for each
  207. * condition in the rule, loop until all conditions have been
  208. * used.
  209. */
  210. unused = sorted;
  211. while (unused < end) {
  212. /*
  213. * Find a child of the node that matches any unused
  214. * condition. Note there could be multiple matches, we're
  215. * happy with the first we can find.
  216. */
  217. while (*node_ptr) {
  218. node = *node_ptr;
  219. for (condition = unused; condition < end; condition++) {
  220. if (condition->type > node->type) {
  221. condition = end;
  222. break;
  223. }
  224. if (condition->type < node->type ||
  225. condition->type ==
  226. L_DBUS_MATCH_NONE)
  227. continue;
  228. if (!strcmp(node->match.value,
  229. condition->value))
  230. break;
  231. }
  232. if (condition < end)
  233. break;
  234. node_ptr = &node->next;
  235. }
  236. /* Add a node */
  237. if (!*node_ptr) {
  238. condition = unused;
  239. node = l_new(struct filter_node, 1);
  240. node->type = condition->type;
  241. node->match.value = l_strdup(condition->value);
  242. *node_ptr = node;
  243. if (node->type == L_DBUS_MATCH_SENDER &&
  244. filter->name_cache &&
  245. !_dbus_parse_unique_name(
  246. node->match.value,
  247. NULL))
  248. _dbus_name_cache_add(filter->name_cache,
  249. node->match.value);
  250. }
  251. _Pragma("GCC diagnostic push")
  252. _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
  253. /*
  254. * Mark the condition used. We do this by setting
  255. * condition->type to an invalid value unless it is the
  256. * first condition left in which case we can push the
  257. * rule start. Another option is to always push the rule
  258. * start and memmove the still unused conditions by one
  259. * if necessary.
  260. */
  261. condition->type = L_DBUS_MATCH_NONE;
  262. while (unused < end && unused[0].type == L_DBUS_MATCH_NONE)
  263. unused++;
  264. node_ptr = &node->match.children;
  265. parent = node;
  266. /*
  267. * Only have to call AddMatch if none of the parent nodes
  268. * have yet created an AddMatch rule on the server.
  269. */
  270. remote_rule |= node->match.remote_rule;
  271. _Pragma("GCC diagnostic pop")
  272. }
  273. node = l_new(struct filter_node, 1);
  274. node->type = NODE_TYPE_CALLBACK;
  275. node->callback.func = signal_func;
  276. node->callback.user_data = user_data;
  277. node->id = ++filter->last_id;
  278. node->next = *node_ptr;
  279. *node_ptr = node;
  280. if (!remote_rule) {
  281. if (!filter->driver->add_match(filter->dbus, node->id,
  282. rule, rule_len))
  283. goto err;
  284. _Pragma("GCC diagnostic push")
  285. _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
  286. parent->id = node->id;
  287. parent->match.remote_rule = true;
  288. _Pragma("GCC diagnostic pop")
  289. }
  290. return node->id;
  291. err:
  292. /* Remove all the nodes we may have added */
  293. node->id = (unsigned int) -1;
  294. remove_recurse(filter, &filter->root, node->id);
  295. return 0;
  296. }
  297. bool _dbus_filter_remove_rule(struct _dbus_filter *filter, unsigned int id)
  298. {
  299. return remove_recurse(filter, &filter->root, id);
  300. }
  301. char *_dbus_filter_rule_to_str(const struct _dbus_filter_condition *rule,
  302. int rule_len)
  303. {
  304. struct l_string *str = l_string_new(63);
  305. char *key, arg_buf[6];
  306. const char *value, *endp;
  307. for (; rule_len; rule++, rule_len--) {
  308. switch ((int) rule->type) {
  309. case L_DBUS_MATCH_SENDER:
  310. key = "sender";
  311. break;
  312. case L_DBUS_MATCH_TYPE:
  313. key = "type";
  314. break;
  315. case L_DBUS_MATCH_PATH:
  316. key = "path";
  317. break;
  318. case L_DBUS_MATCH_INTERFACE:
  319. key = "interface";
  320. break;
  321. case L_DBUS_MATCH_MEMBER:
  322. key = "member";
  323. break;
  324. case L_DBUS_MATCH_ARG0...(L_DBUS_MATCH_ARG0 + 63):
  325. key = arg_buf;
  326. snprintf(arg_buf, sizeof(arg_buf), "arg%i",
  327. rule->type - L_DBUS_MATCH_ARG0);
  328. break;
  329. default:
  330. l_string_free(str);
  331. return NULL;
  332. }
  333. l_string_append(str, key);
  334. l_string_append(str, "='");
  335. /* We only need to escape single-quotes in the values */
  336. value = rule->value;
  337. while ((endp = strchr(value, '\''))) {
  338. l_string_append_fixed(str, value, endp - value);
  339. l_string_append(str, "'\\''");
  340. value = endp + 1;
  341. }
  342. l_string_append(str, value);
  343. l_string_append_c(str, '\'');
  344. if (rule_len > 1)
  345. l_string_append_c(str, ',');
  346. }
  347. return l_string_unwrap(str);
  348. }