LCOV - code coverage report
Current view: top level - libsystemd/sd-bus - bus-match.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 405 566 71.6 %
Date: 2015-07-29 18:47:03 Functions: 19 23 82.6 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2013 Lennart Poettering
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include "bus-internal.h"
      23             : #include "bus-message.h"
      24             : #include "bus-match.h"
      25             : #include "bus-util.h"
      26             : #include "strv.h"
      27             : 
      28             : /* Example:
      29             :  *
      30             :  *  A: type=signal,sender=foo,interface=bar
      31             :  *  B: type=signal,sender=quux,interface=fips
      32             :  *  C: type=signal,sender=quux,interface=waldo
      33             :  *  D: type=signal,member=test
      34             :  *  E: sender=miau
      35             :  *  F: type=signal
      36             :  *  G: type=signal
      37             :  *
      38             :  *  results in this tree:
      39             :  *
      40             :  *  BUS_MATCH_ROOT
      41             :  *  + BUS_MATCH_MESSAGE_TYPE
      42             :  *  | ` BUS_MATCH_VALUE: value == signal
      43             :  *  |   + DBUS_MATCH_SENDER
      44             :  *  |   | + BUS_MATCH_VALUE: value == foo
      45             :  *  |   | | ` DBUS_MATCH_INTERFACE
      46             :  *  |   | |   ` BUS_MATCH_VALUE: value == bar
      47             :  *  |   | |     ` BUS_MATCH_LEAF: A
      48             :  *  |   | ` BUS_MATCH_VALUE: value == quux
      49             :  *  |   |   ` DBUS_MATCH_INTERFACE
      50             :  *  |   |     | BUS_MATCH_VALUE: value == fips
      51             :  *  |   |     | ` BUS_MATCH_LEAF: B
      52             :  *  |   |     ` BUS_MATCH_VALUE: value == waldo
      53             :  *  |   |       ` BUS_MATCH_LEAF: C
      54             :  *  |   + DBUS_MATCH_MEMBER
      55             :  *  |   | ` BUS_MATCH_VALUE: value == test
      56             :  *  |   |   ` BUS_MATCH_LEAF: D
      57             :  *  |   + BUS_MATCH_LEAF: F
      58             :  *  |   ` BUS_MATCH_LEAF: G
      59             :  *  ` BUS_MATCH_SENDER
      60             :  *    ` BUS_MATCH_VALUE: value == miau
      61             :  *      ` BUS_MATCH_LEAF: E
      62             :  */
      63             : 
      64         352 : static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) {
      65         352 :         return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_NAMESPACE_LAST;
      66             : }
      67             : 
      68         471 : static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) {
      69         656 :         return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) ||
      70          75 :                 (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST);
      71             : }
      72             : 
      73         181 : static void bus_match_node_free(struct bus_match_node *node) {
      74         181 :         assert(node);
      75         181 :         assert(node->parent);
      76         181 :         assert(!node->child);
      77         181 :         assert(node->type != BUS_MATCH_ROOT);
      78         181 :         assert(node->type < _BUS_MATCH_NODE_TYPE_MAX);
      79             : 
      80         181 :         if (node->parent->child) {
      81             :                 /* We are apparently linked into the parent's child
      82             :                  * list. Let's remove us from there. */
      83         130 :                 if (node->prev) {
      84           1 :                         assert(node->prev->next == node);
      85           1 :                         node->prev->next = node->next;
      86             :                 } else {
      87         129 :                         assert(node->parent->child == node);
      88         129 :                         node->parent->child = node->next;
      89             :                 }
      90             : 
      91         130 :                 if (node->next)
      92          10 :                         node->next->prev = node->prev;
      93             :         }
      94             : 
      95         181 :         if (node->type == BUS_MATCH_VALUE) {
      96             :                 /* We might be in the parent's hash table, so clean
      97             :                  * this up */
      98             : 
      99          68 :                 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
     100           4 :                         hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8));
     101          64 :                 else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str)
     102          47 :                         hashmap_remove(node->parent->compare.children, node->value.str);
     103             : 
     104          68 :                 free(node->value.str);
     105             :         }
     106             : 
     107         181 :         if (BUS_MATCH_IS_COMPARE(node->type)) {
     108          60 :                 assert(hashmap_isempty(node->compare.children));
     109          60 :                 hashmap_free(node->compare.children);
     110             :         }
     111             : 
     112         181 :         free(node);
     113         181 : }
     114             : 
     115         129 : static bool bus_match_node_maybe_free(struct bus_match_node *node) {
     116         129 :         assert(node);
     117             : 
     118         129 :         if (node->type == BUS_MATCH_ROOT)
     119          34 :                 return false;
     120             : 
     121          95 :         if (node->child)
     122           3 :                 return false;
     123             : 
     124          92 :         if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children))
     125           1 :                 return true;
     126             : 
     127          91 :         bus_match_node_free(node);
     128          91 :         return true;
     129             : }
     130             : 
     131          19 : static bool value_node_test(
     132             :                 struct bus_match_node *node,
     133             :                 enum bus_match_node_type parent_type,
     134             :                 uint8_t value_u8,
     135             :                 const char *value_str,
     136             :                 char **value_strv,
     137             :                 sd_bus_message *m) {
     138             : 
     139          19 :         assert(node);
     140          19 :         assert(node->type == BUS_MATCH_VALUE);
     141             : 
     142             :         /* Tests parameters against this value node, doing prefix
     143             :          * magic and stuff. */
     144             : 
     145          19 :         switch (parent_type) {
     146             : 
     147             :         case BUS_MATCH_MESSAGE_TYPE:
     148           0 :                 return node->value.u8 == value_u8;
     149             : 
     150             :         case BUS_MATCH_SENDER:
     151           2 :                 if (streq_ptr(node->value.str, value_str))
     152           0 :                         return true;
     153             : 
     154           2 :                 if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
     155             :                         char **i;
     156             : 
     157             :                         /* on kdbus we have the well known names list
     158             :                          * in the credentials, let's make use of that
     159             :                          * for an accurate match */
     160             : 
     161           0 :                         STRV_FOREACH(i, m->creds.well_known_names)
     162           0 :                                 if (streq_ptr(node->value.str, *i))
     163           0 :                                         return true;
     164             : 
     165             :                 } else {
     166             : 
     167             :                         /* If we don't have kdbus, we don't know the
     168             :                          * well-known names of the senders. In that,
     169             :                          * let's just hope that dbus-daemon doesn't
     170             :                          * send us stuff we didn't want. */
     171             : 
     172           2 :                         if (node->value.str[0] != ':' && value_str && value_str[0] == ':')
     173           0 :                                 return true;
     174             :                 }
     175             : 
     176           2 :                 return false;
     177             : 
     178             :         case BUS_MATCH_DESTINATION:
     179             :         case BUS_MATCH_INTERFACE:
     180             :         case BUS_MATCH_MEMBER:
     181             :         case BUS_MATCH_PATH:
     182             :         case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: {
     183             :                 char **i;
     184             : 
     185           0 :                 if (value_str)
     186           0 :                         return streq_ptr(node->value.str, value_str);
     187             : 
     188           0 :                 STRV_FOREACH(i, value_strv)
     189           0 :                         if (streq_ptr(node->value.str, *i))
     190           0 :                                 return true;
     191             : 
     192           0 :                 return false;
     193             :         }
     194             : 
     195             :         case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: {
     196             :                 char **i;
     197             : 
     198           2 :                 if (value_str)
     199           2 :                         return namespace_simple_pattern(node->value.str, value_str);
     200             : 
     201           0 :                 STRV_FOREACH(i, value_strv)
     202           0 :                         if (namespace_simple_pattern(node->value.str, *i))
     203           0 :                                 return true;
     204           0 :                 return false;
     205             :         }
     206             : 
     207             :         case BUS_MATCH_PATH_NAMESPACE:
     208          10 :                 return path_simple_pattern(node->value.str, value_str);
     209             : 
     210             :         case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: {
     211             :                 char **i;
     212             : 
     213           5 :                 if (value_str)
     214           5 :                         return path_complex_pattern(node->value.str, value_str);
     215             : 
     216           0 :                 STRV_FOREACH(i, value_strv)
     217           0 :                         if (path_complex_pattern(node->value.str, *i))
     218           0 :                                 return true;
     219             : 
     220           0 :                 return false;
     221             :         }
     222             : 
     223             :         default:
     224           0 :                 assert_not_reached("Invalid node type");
     225             :         }
     226             : }
     227             : 
     228           4 : static bool value_node_same(
     229             :                 struct bus_match_node *node,
     230             :                 enum bus_match_node_type parent_type,
     231             :                 uint8_t value_u8,
     232             :                 const char *value_str) {
     233             : 
     234             :         /* Tests parameters against this value node, not doing prefix
     235             :          * magic and stuff, i.e. this one actually compares the match
     236             :          * itself. */
     237             : 
     238           4 :         assert(node);
     239           4 :         assert(node->type == BUS_MATCH_VALUE);
     240             : 
     241           4 :         switch (parent_type) {
     242             : 
     243             :         case BUS_MATCH_MESSAGE_TYPE:
     244           0 :                 return node->value.u8 == value_u8;
     245             : 
     246             :         case BUS_MATCH_SENDER:
     247             :         case BUS_MATCH_DESTINATION:
     248             :         case BUS_MATCH_INTERFACE:
     249             :         case BUS_MATCH_MEMBER:
     250             :         case BUS_MATCH_PATH:
     251             :         case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
     252             :         case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
     253             :         case BUS_MATCH_PATH_NAMESPACE:
     254             :         case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
     255           4 :                 return streq(node->value.str, value_str);
     256             : 
     257             :         default:
     258           0 :                 assert_not_reached("Invalid node type");
     259             :         }
     260             : }
     261             : 
     262       31329 : int bus_match_run(
     263             :                 sd_bus *bus,
     264             :                 struct bus_match_node *node,
     265             :                 sd_bus_message *m) {
     266             : 
     267       62658 :         _cleanup_strv_free_ char **test_strv = NULL;
     268       31329 :         const char *test_str = NULL;
     269       31329 :         uint8_t test_u8 = 0;
     270             :         int r;
     271             : 
     272       31329 :         assert(m);
     273             : 
     274       31329 :         if (!node)
     275       15610 :                 return 0;
     276             : 
     277       15719 :         if (bus && bus->match_callbacks_modified)
     278           0 :                 return 0;
     279             : 
     280             :         /* Not these special semantics: when traversing the tree we
     281             :          * usually let bus_match_run() when called for a node
     282             :          * recursively invoke bus_match_run(). There's are two
     283             :          * exceptions here though, which are BUS_NODE_ROOT (which
     284             :          * cannot have a sibling), and BUS_NODE_VALUE (whose siblings
     285             :          * are invoked anyway by its parent. */
     286             : 
     287       15719 :         switch (node->type) {
     288             : 
     289             :         case BUS_MATCH_ROOT:
     290             : 
     291             :                 /* Run all children. Since we cannot have any siblings
     292             :                  * we won't call any. The children of the root node
     293             :                  * are compares or leaves, they will automatically
     294             :                  * call their siblings. */
     295       15557 :                 return bus_match_run(bus, node->child, m);
     296             : 
     297             :         case BUS_MATCH_VALUE:
     298             : 
     299             :                 /* Run all children. We don't execute any siblings, we
     300             :                  * assume our caller does that. The children of value
     301             :                  * nodes are compares or leaves, they will
     302             :                  * automatically call their siblings */
     303             : 
     304          56 :                 assert(node->child);
     305          56 :                 return bus_match_run(bus, node->child, m);
     306             : 
     307             :         case BUS_MATCH_LEAF:
     308             : 
     309          43 :                 if (bus) {
     310          23 :                         if (node->leaf.callback->last_iteration == bus->iteration_counter)
     311           0 :                                 return 0;
     312             : 
     313          23 :                         node->leaf.callback->last_iteration = bus->iteration_counter;
     314             :                 }
     315             : 
     316          43 :                 r = sd_bus_message_rewind(m, true);
     317          43 :                 if (r < 0)
     318           0 :                         return r;
     319             : 
     320             :                 /* Run the callback. And then invoke siblings. */
     321          43 :                 if (node->leaf.callback->callback) {
     322          84 :                         _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
     323             :                         sd_bus_slot *slot;
     324             : 
     325          42 :                         slot = container_of(node->leaf.callback, sd_bus_slot, match_callback);
     326          42 :                         if (bus) {
     327          22 :                                 bus->current_slot = sd_bus_slot_ref(slot);
     328          22 :                                 bus->current_handler = node->leaf.callback->callback;
     329          22 :                                 bus->current_userdata = slot->userdata;
     330             :                         }
     331          42 :                         r = node->leaf.callback->callback(m, slot->userdata, &error_buffer);
     332          42 :                         if (bus) {
     333          22 :                                 bus->current_userdata = NULL;
     334          22 :                                 bus->current_handler = NULL;
     335          22 :                                 bus->current_slot = sd_bus_slot_unref(slot);
     336             :                         }
     337             : 
     338          42 :                         r = bus_maybe_reply_error(m, r, &error_buffer);
     339          42 :                         if (r != 0)
     340           1 :                                 return r;
     341             : 
     342          41 :                         if (bus && bus->match_callbacks_modified)
     343           0 :                                 return 0;
     344             :                 }
     345             : 
     346          42 :                 return bus_match_run(bus, node->next, m);
     347             : 
     348             :         case BUS_MATCH_MESSAGE_TYPE:
     349          13 :                 test_u8 = m->header->type;
     350          13 :                 break;
     351             : 
     352             :         case BUS_MATCH_SENDER:
     353           2 :                 test_str = m->sender;
     354             :                 /* FIXME: resolve test_str from a well-known to a unique name first */
     355           2 :                 break;
     356             : 
     357             :         case BUS_MATCH_DESTINATION:
     358           0 :                 test_str = m->destination;
     359           0 :                 break;
     360             : 
     361             :         case BUS_MATCH_INTERFACE:
     362           9 :                 test_str = m->interface;
     363           9 :                 break;
     364             : 
     365             :         case BUS_MATCH_MEMBER:
     366          10 :                 test_str = m->member;
     367          10 :                 break;
     368             : 
     369             :         case BUS_MATCH_PATH:
     370             :         case BUS_MATCH_PATH_NAMESPACE:
     371          15 :                 test_str = m->path;
     372          15 :                 break;
     373             : 
     374             :         case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
     375           7 :                 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str, &test_strv);
     376           7 :                 break;
     377             : 
     378             :         case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
     379           5 :                 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str, &test_strv);
     380           5 :                 break;
     381             : 
     382             :         case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
     383           2 :                 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str, &test_strv);
     384           2 :                 break;
     385             : 
     386             :         default:
     387           0 :                 assert_not_reached("Unknown match type.");
     388             :         }
     389             : 
     390          63 :         if (BUS_MATCH_CAN_HASH(node->type)) {
     391             :                 struct bus_match_node *found;
     392             : 
     393             :                 /* Lookup via hash table, nice! So let's jump directly. */
     394             : 
     395          46 :                 if (test_str)
     396          30 :                         found = hashmap_get(node->compare.children, test_str);
     397          16 :                 else if (test_strv) {
     398             :                         char **i;
     399             : 
     400          10 :                         STRV_FOREACH(i, test_strv) {
     401           7 :                                 found = hashmap_get(node->compare.children, *i);
     402           7 :                                 if (found) {
     403           7 :                                         r = bus_match_run(bus, found, m);
     404           7 :                                         if (r != 0)
     405           0 :                                                 return r;
     406             :                                 }
     407             :                         }
     408             : 
     409           3 :                         found = NULL;
     410          13 :                 } else if (node->type == BUS_MATCH_MESSAGE_TYPE)
     411          13 :                         found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
     412             :                 else
     413           0 :                         found = NULL;
     414             : 
     415          46 :                 if (found) {
     416          35 :                         r = bus_match_run(bus, found, m);
     417          35 :                         if (r != 0)
     418           2 :                                 return r;
     419             :                 }
     420             :         } else {
     421             :                 struct bus_match_node *c;
     422             : 
     423             :                 /* No hash table, so let's iterate manually... */
     424             : 
     425          36 :                 for (c = node->child; c; c = c->next) {
     426          19 :                         if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m))
     427           5 :                                 continue;
     428             : 
     429          14 :                         r = bus_match_run(bus, c, m);
     430          14 :                         if (r != 0)
     431           0 :                                 return r;
     432             :                 }
     433             :         }
     434             : 
     435          61 :         if (bus && bus->match_callbacks_modified)
     436           0 :                 return 0;
     437             : 
     438             :         /* And now, let's invoke our siblings */
     439          61 :         return bus_match_run(bus, node->next, m);
     440             : }
     441             : 
     442          79 : static int bus_match_add_compare_value(
     443             :                 struct bus_match_node *where,
     444             :                 enum bus_match_node_type t,
     445             :                 uint8_t value_u8,
     446             :                 const char *value_str,
     447             :                 struct bus_match_node **ret) {
     448             : 
     449          79 :         struct bus_match_node *c = NULL, *n = NULL;
     450             :         int r;
     451             : 
     452          79 :         assert(where);
     453          79 :         assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
     454          79 :         assert(BUS_MATCH_IS_COMPARE(t));
     455          79 :         assert(ret);
     456             : 
     457          79 :         for (c = where->child; c && c->type != t; c = c->next)
     458             :                 ;
     459             : 
     460          79 :         if (c) {
     461             :                 /* Comparison node already exists? Then let's see if
     462             :                  * the value node exists too. */
     463             : 
     464          19 :                 if (t == BUS_MATCH_MESSAGE_TYPE)
     465           4 :                         n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
     466          15 :                 else if (BUS_MATCH_CAN_HASH(t))
     467          11 :                         n = hashmap_get(c->compare.children, value_str);
     468             :                 else {
     469           4 :                         for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
     470             :                                 ;
     471             :                 }
     472             : 
     473          19 :                 if (n) {
     474          11 :                         *ret = n;
     475          11 :                         return 0;
     476             :                 }
     477             :         } else {
     478             :                 /* Comparison node, doesn't exist yet? Then let's
     479             :                  * create it. */
     480             : 
     481          60 :                 c = new0(struct bus_match_node, 1);
     482          60 :                 if (!c) {
     483           0 :                         r = -ENOMEM;
     484           0 :                         goto fail;
     485             :                 }
     486             : 
     487          60 :                 c->type = t;
     488          60 :                 c->parent = where;
     489          60 :                 c->next = where->child;
     490          60 :                 if (c->next)
     491           9 :                         c->next->prev = c;
     492          60 :                 where->child = c;
     493             : 
     494          60 :                 if (t == BUS_MATCH_MESSAGE_TYPE) {
     495           3 :                         c->compare.children = hashmap_new(NULL);
     496           3 :                         if (!c->compare.children) {
     497           0 :                                 r = -ENOMEM;
     498           0 :                                 goto fail;
     499             :                         }
     500          57 :                 } else if (BUS_MATCH_CAN_HASH(t)) {
     501          41 :                         c->compare.children = hashmap_new(&string_hash_ops);
     502          41 :                         if (!c->compare.children) {
     503           0 :                                 r = -ENOMEM;
     504           0 :                                 goto fail;
     505             :                         }
     506             :                 }
     507             :         }
     508             : 
     509          68 :         n = new0(struct bus_match_node, 1);
     510          68 :         if (!n) {
     511           0 :                 r = -ENOMEM;
     512           0 :                 goto fail;
     513             :         }
     514             : 
     515          68 :         n->type = BUS_MATCH_VALUE;
     516          68 :         n->value.u8 = value_u8;
     517          68 :         if (value_str) {
     518          64 :                 n->value.str = strdup(value_str);
     519          64 :                 if (!n->value.str) {
     520           0 :                         r = -ENOMEM;
     521           0 :                         goto fail;
     522             :                 }
     523             :         }
     524             : 
     525          68 :         n->parent = c;
     526          68 :         if (c->compare.children) {
     527             : 
     528          51 :                 if (t == BUS_MATCH_MESSAGE_TYPE)
     529           4 :                         r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n);
     530             :                 else
     531          47 :                         r = hashmap_put(c->compare.children, n->value.str, n);
     532             : 
     533          51 :                 if (r < 0)
     534           0 :                         goto fail;
     535             :         } else {
     536          17 :                 n->next = c->child;
     537          17 :                 if (n->next)
     538           1 :                         n->next->prev = n;
     539          17 :                 c->child = n;
     540             :         }
     541             : 
     542          68 :         *ret = n;
     543          68 :         return 1;
     544             : 
     545             : fail:
     546           0 :         if (c)
     547           0 :                 bus_match_node_maybe_free(c);
     548             : 
     549           0 :         if (n) {
     550           0 :                 free(n->value.str);
     551           0 :                 free(n);
     552             :         }
     553             : 
     554           0 :         return r;
     555             : }
     556             : 
     557           0 : static int bus_match_find_compare_value(
     558             :                 struct bus_match_node *where,
     559             :                 enum bus_match_node_type t,
     560             :                 uint8_t value_u8,
     561             :                 const char *value_str,
     562             :                 struct bus_match_node **ret) {
     563             : 
     564             :         struct bus_match_node *c, *n;
     565             : 
     566           0 :         assert(where);
     567           0 :         assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
     568           0 :         assert(BUS_MATCH_IS_COMPARE(t));
     569           0 :         assert(ret);
     570             : 
     571           0 :         for (c = where->child; c && c->type != t; c = c->next)
     572             :                 ;
     573             : 
     574           0 :         if (!c)
     575           0 :                 return 0;
     576             : 
     577           0 :         if (t == BUS_MATCH_MESSAGE_TYPE)
     578           0 :                 n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
     579           0 :         else if (BUS_MATCH_CAN_HASH(t))
     580           0 :                 n = hashmap_get(c->compare.children, value_str);
     581             :         else {
     582           0 :                 for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
     583             :                         ;
     584             :         }
     585             : 
     586           0 :         if (n) {
     587           0 :                 *ret = n;
     588           0 :                 return 1;
     589             :         }
     590             : 
     591           0 :         return 0;
     592             : }
     593             : 
     594          53 : static int bus_match_add_leaf(
     595             :                 struct bus_match_node *where,
     596             :                 struct match_callback *callback) {
     597             : 
     598             :         struct bus_match_node *n;
     599             : 
     600          53 :         assert(where);
     601          53 :         assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
     602          53 :         assert(callback);
     603             : 
     604          53 :         n = new0(struct bus_match_node, 1);
     605          53 :         if (!n)
     606           0 :                 return -ENOMEM;
     607             : 
     608          53 :         n->type = BUS_MATCH_LEAF;
     609          53 :         n->parent = where;
     610          53 :         n->next = where->child;
     611          53 :         if (n->next)
     612           1 :                 n->next->prev = n;
     613             : 
     614          53 :         n->leaf.callback = callback;
     615          53 :         callback->match_node = n;
     616             : 
     617          53 :         where->child = n;
     618             : 
     619          53 :         return 1;
     620             : }
     621             : 
     622           0 : static int bus_match_find_leaf(
     623             :                 struct bus_match_node *where,
     624             :                 sd_bus_message_handler_t callback,
     625             :                 void *userdata,
     626             :                 struct bus_match_node **ret) {
     627             : 
     628             :         struct bus_match_node *c;
     629             : 
     630           0 :         assert(where);
     631           0 :         assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
     632           0 :         assert(ret);
     633             : 
     634           0 :         for (c = where->child; c; c = c->next) {
     635             :                 sd_bus_slot *s;
     636             : 
     637           0 :                 s = container_of(c->leaf.callback, sd_bus_slot, match_callback);
     638             : 
     639           0 :                 if (c->type == BUS_MATCH_LEAF &&
     640           0 :                     c->leaf.callback->callback == callback &&
     641           0 :                     s->userdata == userdata) {
     642           0 :                         *ret = c;
     643           0 :                         return 1;
     644             :                 }
     645             :         }
     646             : 
     647           0 :         return 0;
     648             : }
     649             : 
     650         285 : enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) {
     651         285 :         assert(k);
     652             : 
     653         285 :         if (n == 4 && startswith(k, "type"))
     654           8 :                 return BUS_MATCH_MESSAGE_TYPE;
     655         277 :         if (n == 6 && startswith(k, "sender"))
     656           6 :                 return BUS_MATCH_SENDER;
     657         271 :         if (n == 11 && startswith(k, "destination"))
     658           1 :                 return BUS_MATCH_DESTINATION;
     659         270 :         if (n == 9 && startswith(k, "interface"))
     660          15 :                 return BUS_MATCH_INTERFACE;
     661         255 :         if (n == 6 && startswith(k, "member"))
     662          14 :                 return BUS_MATCH_MEMBER;
     663         241 :         if (n == 4 && startswith(k, "path"))
     664          16 :                 return BUS_MATCH_PATH;
     665         225 :         if (n == 14 && startswith(k, "path_namespace"))
     666          11 :                 return BUS_MATCH_PATH_NAMESPACE;
     667             : 
     668         214 :         if (n == 4 && startswith(k, "arg")) {
     669             :                 int j;
     670             : 
     671          26 :                 j = undecchar(k[3]);
     672          26 :                 if (j < 0)
     673           0 :                         return -EINVAL;
     674             : 
     675          26 :                 return BUS_MATCH_ARG + j;
     676             :         }
     677             : 
     678         188 :         if (n == 5 && startswith(k, "arg")) {
     679             :                 int a, b;
     680             :                 enum bus_match_node_type t;
     681             : 
     682          54 :                 a = undecchar(k[3]);
     683          54 :                 b = undecchar(k[4]);
     684          54 :                 if (a <= 0 || b < 0)
     685           0 :                         return -EINVAL;
     686             : 
     687          54 :                 t = BUS_MATCH_ARG + a * 10 + b;
     688          54 :                 if (t > BUS_MATCH_ARG_LAST)
     689           0 :                         return -EINVAL;
     690             : 
     691          54 :                 return t;
     692             :         }
     693             : 
     694         134 :         if (n == 8 && startswith(k, "arg") && startswith(k + 4, "path")) {
     695             :                 int j;
     696             : 
     697          15 :                 j = undecchar(k[3]);
     698          15 :                 if (j < 0)
     699           0 :                         return -EINVAL;
     700             : 
     701          15 :                 return BUS_MATCH_ARG_PATH + j;
     702             :         }
     703             : 
     704         119 :         if (n == 9 && startswith(k, "arg") && startswith(k + 5, "path")) {
     705             :                 enum bus_match_node_type t;
     706             :                 int a, b;
     707             : 
     708          54 :                 a = undecchar(k[3]);
     709          54 :                 b = undecchar(k[4]);
     710          54 :                 if (a <= 0 || b < 0)
     711           0 :                         return -EINVAL;
     712             : 
     713          54 :                 t = BUS_MATCH_ARG_PATH + a * 10 + b;
     714          54 :                 if (t > BUS_MATCH_ARG_PATH_LAST)
     715           0 :                         return -EINVAL;
     716             : 
     717          54 :                 return t;
     718             :         }
     719             : 
     720          65 :         if (n == 13 && startswith(k, "arg") && startswith(k + 4, "namespace")) {
     721             :                 int j;
     722             : 
     723          11 :                 j = undecchar(k[3]);
     724          11 :                 if (j < 0)
     725           0 :                         return -EINVAL;
     726             : 
     727          11 :                 return BUS_MATCH_ARG_NAMESPACE + j;
     728             :         }
     729             : 
     730          54 :         if (n == 14 && startswith(k, "arg") && startswith(k + 5, "namespace")) {
     731             :                 enum bus_match_node_type t;
     732             :                 int a, b;
     733             : 
     734          54 :                 a = undecchar(k[3]);
     735          54 :                 b = undecchar(k[4]);
     736          54 :                 if (a <= 0 || b < 0)
     737           0 :                         return -EINVAL;
     738             : 
     739          54 :                 t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b;
     740          54 :                 if (t > BUS_MATCH_ARG_NAMESPACE_LAST)
     741           0 :                         return -EINVAL;
     742             : 
     743          54 :                 return t;
     744             :         }
     745             : 
     746           0 :         return -EINVAL;
     747             : }
     748             : 
     749          44 : static int match_component_compare(const void *a, const void *b) {
     750          44 :         const struct bus_match_component *x = a, *y = b;
     751             : 
     752          44 :         if (x->type < y->type)
     753          26 :                 return -1;
     754          18 :         if (x->type > y->type)
     755          18 :                 return 1;
     756             : 
     757           0 :         return 0;
     758             : }
     759             : 
     760          59 : void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) {
     761             :         unsigned i;
     762             : 
     763         146 :         for (i = 0; i < n_components; i++)
     764          87 :                 free(components[i].value_str);
     765             : 
     766          59 :         free(components);
     767          59 : }
     768             : 
     769          59 : int bus_match_parse(
     770             :                 const char *match,
     771             :                 struct bus_match_component **_components,
     772             :                 unsigned *_n_components) {
     773             : 
     774          59 :         const char *p = match;
     775          59 :         struct bus_match_component *components = NULL;
     776          59 :         size_t components_allocated = 0;
     777          59 :         unsigned n_components = 0, i;
     778         118 :         _cleanup_free_ char *value = NULL;
     779             :         int r;
     780             : 
     781          59 :         assert(match);
     782          59 :         assert(_components);
     783          59 :         assert(_n_components);
     784             : 
     785         153 :         while (*p != 0) {
     786             :                 const char *eq, *q;
     787             :                 enum bus_match_node_type t;
     788          87 :                 unsigned j = 0;
     789          87 :                 size_t value_allocated = 0;
     790          87 :                 bool escaped = false, quoted;
     791             :                 uint8_t u;
     792             : 
     793             :                 /* Avahi's match rules appear to include whitespace, skip over it */
     794          87 :                 p += strspn(p, " ");
     795             : 
     796          87 :                 eq = strchr(p, '=');
     797          87 :                 if (!eq)
     798           0 :                         return -EINVAL;
     799             : 
     800          87 :                 t = bus_match_node_type_from_string(p, eq - p);
     801          87 :                 if (t < 0)
     802           0 :                         return -EINVAL;
     803             : 
     804          87 :                 quoted = eq[1] == '\'';
     805             : 
     806         770 :                 for (q = eq + 1 + quoted;; q++) {
     807             : 
     808         770 :                         if (*q == 0) {
     809             : 
     810           0 :                                 if (quoted) {
     811           0 :                                         r = -EINVAL;
     812           0 :                                         goto fail;
     813             :                                 } else {
     814           0 :                                         if (value)
     815           0 :                                                 value[j] = 0;
     816           0 :                                         break;
     817             :                                 }
     818             :                         }
     819             : 
     820         770 :                         if (!escaped) {
     821         768 :                                 if (*q == '\\') {
     822           2 :                                         escaped = true;
     823           2 :                                         continue;
     824             :                                 }
     825             : 
     826         766 :                                 if (quoted) {
     827         760 :                                         if (*q == '\'') {
     828          86 :                                                 if (value)
     829          86 :                                                         value[j] = 0;
     830          86 :                                                 break;
     831             :                                         }
     832             :                                 } else {
     833           6 :                                         if (*q == ',') {
     834           1 :                                                 if (value)
     835           1 :                                                         value[j] = 0;
     836             : 
     837           1 :                                                 break;
     838             :                                         }
     839             :                                 }
     840             :                         }
     841             : 
     842         681 :                         if (!GREEDY_REALLOC(value, value_allocated, j + 2)) {
     843           0 :                                 r = -ENOMEM;
     844           0 :                                 goto fail;
     845             :                         }
     846             : 
     847         681 :                         value[j++] = *q;
     848         681 :                         escaped = false;
     849         683 :                 }
     850             : 
     851          87 :                 if (!value) {
     852           0 :                         value = strdup("");
     853           0 :                         if (!value) {
     854           0 :                                 r = -ENOMEM;
     855           0 :                                 goto fail;
     856             :                         }
     857             :                 }
     858             : 
     859          87 :                 if (t == BUS_MATCH_MESSAGE_TYPE) {
     860           7 :                         r = bus_message_type_from_string(value, &u);
     861           7 :                         if (r < 0)
     862           0 :                                 goto fail;
     863             : 
     864           7 :                         free(value);
     865           7 :                         value = NULL;
     866             :                 } else
     867          80 :                         u = 0;
     868             : 
     869          87 :                 if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) {
     870           0 :                         r = -ENOMEM;
     871           0 :                         goto fail;
     872             :                 }
     873             : 
     874          87 :                 components[n_components].type = t;
     875          87 :                 components[n_components].value_str = value;
     876          87 :                 components[n_components].value_u8 = u;
     877          87 :                 n_components++;
     878             : 
     879          87 :                 value = NULL;
     880             : 
     881          87 :                 if (q[quoted] == 0)
     882          52 :                         break;
     883             : 
     884          35 :                 if (q[quoted] != ',') {
     885           0 :                         r = -EINVAL;
     886           0 :                         goto fail;
     887             :                 }
     888             : 
     889          35 :                 p = q + 1 + quoted;
     890             :         }
     891             : 
     892             :         /* Order the whole thing, so that we always generate the same tree */
     893          59 :         qsort_safe(components, n_components, sizeof(struct bus_match_component), match_component_compare);
     894             : 
     895             :         /* Check for duplicates */
     896          90 :         for (i = 0; i+1 < n_components; i++)
     897          31 :                 if (components[i].type == components[i+1].type) {
     898           0 :                         r = -EINVAL;
     899           0 :                         goto fail;
     900             :                 }
     901             : 
     902          59 :         *_components = components;
     903          59 :         *_n_components = n_components;
     904             : 
     905          59 :         return 0;
     906             : 
     907             : fail:
     908           0 :         bus_match_parse_free(components, n_components);
     909           0 :         return r;
     910             : }
     911             : 
     912           0 : char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
     913           0 :         _cleanup_free_ FILE *f = NULL;
     914           0 :         char *buffer = NULL;
     915           0 :         size_t size = 0;
     916             :         unsigned i;
     917             : 
     918           0 :         if (n_components <= 0)
     919           0 :                 return strdup("");
     920             : 
     921           0 :         assert(components);
     922             : 
     923           0 :         f = open_memstream(&buffer, &size);
     924           0 :         if (!f)
     925           0 :                 return NULL;
     926             : 
     927           0 :         for (i = 0; i < n_components; i++) {
     928             :                 char buf[32];
     929             : 
     930           0 :                 if (i != 0)
     931           0 :                         fputc(',', f);
     932             : 
     933           0 :                 fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f);
     934           0 :                 fputc('=', f);
     935           0 :                 fputc('\'', f);
     936             : 
     937           0 :                 if (components[i].type == BUS_MATCH_MESSAGE_TYPE)
     938           0 :                         fputs(bus_message_type_to_string(components[i].value_u8), f);
     939             :                 else
     940           0 :                         fputs(components[i].value_str, f);
     941             : 
     942           0 :                 fputc('\'', f);
     943             :         }
     944             : 
     945           0 :         fflush(f);
     946           0 :         if (ferror(f))
     947           0 :                 return NULL;
     948             : 
     949           0 :         return buffer;
     950             : }
     951             : 
     952          53 : int bus_match_add(
     953             :                 struct bus_match_node *root,
     954             :                 struct bus_match_component *components,
     955             :                 unsigned n_components,
     956             :                 struct match_callback *callback) {
     957             : 
     958             :         unsigned i;
     959             :         struct bus_match_node *n;
     960             :         int r;
     961             : 
     962          53 :         assert(root);
     963          53 :         assert(callback);
     964             : 
     965          53 :         n = root;
     966         132 :         for (i = 0; i < n_components; i++) {
     967         158 :                 r = bus_match_add_compare_value(
     968          79 :                                 n, components[i].type,
     969          79 :                                 components[i].value_u8, components[i].value_str, &n);
     970          79 :                 if (r < 0)
     971           0 :                         return r;
     972             :         }
     973             : 
     974          53 :         return bus_match_add_leaf(n, callback);
     975             : }
     976             : 
     977          37 : int bus_match_remove(
     978             :                 struct bus_match_node *root,
     979             :                 struct match_callback *callback) {
     980             : 
     981             :         struct bus_match_node *node, *pp;
     982             : 
     983          37 :         assert(root);
     984          37 :         assert(callback);
     985             : 
     986          37 :         node = callback->match_node;
     987          37 :         if (!node)
     988           0 :                 return 0;
     989             : 
     990          37 :         assert(node->type == BUS_MATCH_LEAF);
     991             : 
     992          37 :         callback->match_node = NULL;
     993             : 
     994             :         /* Free the leaf */
     995          37 :         pp = node->parent;
     996          37 :         bus_match_node_free(node);
     997             : 
     998             :         /* Prune the tree above */
     999          37 :         while (pp) {
    1000         129 :                 node = pp;
    1001         129 :                 pp = node->parent;
    1002             : 
    1003         129 :                 if (!bus_match_node_maybe_free(node))
    1004          37 :                         break;
    1005             :         }
    1006             : 
    1007          37 :         return 1;
    1008             : }
    1009             : 
    1010           0 : int bus_match_find(
    1011             :                 struct bus_match_node *root,
    1012             :                 struct bus_match_component *components,
    1013             :                 unsigned n_components,
    1014             :                 sd_bus_message_handler_t callback,
    1015             :                 void *userdata,
    1016             :                 struct match_callback **ret) {
    1017             : 
    1018             :         struct bus_match_node *n, **gc;
    1019             :         unsigned i;
    1020             :         int r;
    1021             : 
    1022           0 :         assert(root);
    1023           0 :         assert(ret);
    1024             : 
    1025           0 :         gc = newa(struct bus_match_node*, n_components);
    1026             : 
    1027           0 :         n = root;
    1028           0 :         for (i = 0; i < n_components; i++) {
    1029           0 :                 r = bus_match_find_compare_value(
    1030           0 :                                 n, components[i].type,
    1031           0 :                                 components[i].value_u8, components[i].value_str,
    1032             :                                 &n);
    1033           0 :                 if (r <= 0)
    1034           0 :                         return r;
    1035             : 
    1036           0 :                 gc[i] = n;
    1037             :         }
    1038             : 
    1039           0 :         r = bus_match_find_leaf(n, callback, userdata, &n);
    1040           0 :         if (r <= 0)
    1041           0 :                 return r;
    1042             : 
    1043           0 :         *ret = n->leaf.callback;
    1044           0 :         return 1;
    1045             : }
    1046             : 
    1047         148 : void bus_match_free(struct bus_match_node *node) {
    1048             :         struct bus_match_node *c;
    1049             : 
    1050         148 :         if (!node)
    1051           0 :                 return;
    1052             : 
    1053         148 :         if (BUS_MATCH_CAN_HASH(node->type)) {
    1054             :                 Iterator i;
    1055             : 
    1056          42 :                 HASHMAP_FOREACH(c, node->compare.children, i)
    1057          18 :                         bus_match_free(c);
    1058             : 
    1059          12 :                 assert(hashmap_isempty(node->compare.children));
    1060             :         }
    1061             : 
    1062         331 :         while ((c = node->child))
    1063          35 :                 bus_match_free(c);
    1064             : 
    1065         148 :         if (node->type != BUS_MATCH_ROOT)
    1066          53 :                 bus_match_node_free(node);
    1067             : }
    1068             : 
    1069         326 : const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
    1070         326 :         switch (t) {
    1071             : 
    1072             :         case BUS_MATCH_ROOT:
    1073           4 :                 return "root";
    1074             : 
    1075             :         case BUS_MATCH_VALUE:
    1076          51 :                 return "value";
    1077             : 
    1078             :         case BUS_MATCH_LEAF:
    1079          37 :                 return "leaf";
    1080             : 
    1081             :         case BUS_MATCH_MESSAGE_TYPE:
    1082           4 :                 return "type";
    1083             : 
    1084             :         case BUS_MATCH_SENDER:
    1085           3 :                 return "sender";
    1086             : 
    1087             :         case BUS_MATCH_DESTINATION:
    1088           1 :                 return "destination";
    1089             : 
    1090             :         case BUS_MATCH_INTERFACE:
    1091           8 :                 return "interface";
    1092             : 
    1093             :         case BUS_MATCH_MEMBER:
    1094           5 :                 return "member";
    1095             : 
    1096             :         case BUS_MATCH_PATH:
    1097           5 :                 return "path";
    1098             : 
    1099             :         case BUS_MATCH_PATH_NAMESPACE:
    1100           3 :                 return "path_namespace";
    1101             : 
    1102             :         case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
    1103          74 :                 snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
    1104          74 :                 return buf;
    1105             : 
    1106             :         case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
    1107          65 :                 snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
    1108          65 :                 return buf;
    1109             : 
    1110             :         case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
    1111          66 :                 snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
    1112          66 :                 return buf;
    1113             : 
    1114             :         default:
    1115           0 :                 return NULL;
    1116             :         }
    1117             : }
    1118             : 
    1119         124 : void bus_match_dump(struct bus_match_node *node, unsigned level) {
    1120             :         struct bus_match_node *c;
    1121         248 :         _cleanup_free_ char *pfx = NULL;
    1122             :         char buf[32];
    1123             : 
    1124         124 :         if (!node)
    1125           0 :                 return;
    1126             : 
    1127         124 :         pfx = strrep("  ", level);
    1128         124 :         printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
    1129             : 
    1130         124 :         if (node->type == BUS_MATCH_VALUE) {
    1131          50 :                 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
    1132           5 :                         printf(" <%u>\n", node->value.u8);
    1133             :                 else
    1134          45 :                         printf(" <%s>\n", node->value.str);
    1135          74 :         } else if (node->type == BUS_MATCH_ROOT)
    1136           3 :                 puts(" root");
    1137          71 :         else if (node->type == BUS_MATCH_LEAF)
    1138          36 :                 printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata);
    1139             :         else
    1140          35 :                 putchar('\n');
    1141             : 
    1142         124 :         if (BUS_MATCH_CAN_HASH(node->type)) {
    1143             :                 Iterator i;
    1144             : 
    1145          97 :                 HASHMAP_FOREACH(c, node->compare.children, i)
    1146          41 :                         bus_match_dump(c, level + 1);
    1147             :         }
    1148             : 
    1149         204 :         for (c = node->child; c; c = c->next)
    1150          80 :                 bus_match_dump(c, level + 1);
    1151             : }
    1152             : 
    1153          41 : enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) {
    1154          41 :         bool found_driver = false;
    1155             :         unsigned i;
    1156             : 
    1157          41 :         if (n_components <= 0)
    1158           2 :                 return BUS_MATCH_GENERIC;
    1159             : 
    1160          39 :         assert(components);
    1161             : 
    1162             :         /* Checks whether the specified match can only match the
    1163             :          * pseudo-service for local messages, which we detect by
    1164             :          * sender, interface or path. If a match is not restricted to
    1165             :          * local messages, then we check if it only matches on the
    1166             :          * driver. */
    1167             : 
    1168          90 :         for (i = 0; i < n_components; i++) {
    1169          54 :                 const struct bus_match_component *c = components + i;
    1170             : 
    1171          54 :                 if (c->type == BUS_MATCH_SENDER) {
    1172           2 :                         if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
    1173           1 :                                 return BUS_MATCH_LOCAL;
    1174             : 
    1175           1 :                         if (streq_ptr(c->value_str, "org.freedesktop.DBus"))
    1176           1 :                                 found_driver = true;
    1177             :                 }
    1178             : 
    1179          53 :                 if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
    1180           1 :                         return BUS_MATCH_LOCAL;
    1181             : 
    1182          52 :                 if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local"))
    1183           1 :                         return BUS_MATCH_LOCAL;
    1184             :         }
    1185             : 
    1186          36 :         return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC;
    1187             : 
    1188             : }

Generated by: LCOV version 1.11