LCOV - code coverage report
Current view: top level - libsystemd/sd-bus - bus-match.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 451 572 78.8 %
Date: 2019-08-22 15:41:25 Functions: 19 20 95.0 %

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

Generated by: LCOV version 1.14