LCOV - code coverage report
Current view: top level - libsystemd/sd-bus - bus-track.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 123 251 49.0 %
Date: 2019-08-22 15:41:25 Functions: 17 30 56.7 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include "sd-bus.h"
       4             : 
       5             : #include "alloc-util.h"
       6             : #include "bus-internal.h"
       7             : #include "bus-track.h"
       8             : #include "bus-util.h"
       9             : 
      10             : struct track_item {
      11             :         unsigned n_ref;
      12             :         char *name;
      13             :         sd_bus_slot *slot;
      14             : };
      15             : 
      16             : struct sd_bus_track {
      17             :         unsigned n_ref;
      18             :         unsigned n_adding; /* are we in the process of adding a new name? */
      19             :         sd_bus *bus;
      20             :         sd_bus_track_handler_t handler;
      21             :         void *userdata;
      22             :         Hashmap *names;
      23             :         LIST_FIELDS(sd_bus_track, queue);
      24             :         Iterator iterator;
      25             :         bool in_list:1;    /* In bus->tracks? */
      26             :         bool in_queue:1;   /* In bus->track_queue? */
      27             :         bool modified:1;
      28             :         bool recursive:1;
      29             :         sd_bus_destroy_t destroy_callback;
      30             : 
      31             :         LIST_FIELDS(sd_bus_track, tracks);
      32             : };
      33             : 
      34             : #define MATCH_FOR_NAME(name)                            \
      35             :         strjoina("type='signal',"                       \
      36             :                  "sender='org.freedesktop.DBus',"       \
      37             :                  "path='/org/freedesktop/DBus',"        \
      38             :                  "interface='org.freedesktop.DBus',"    \
      39             :                  "member='NameOwnerChanged',"           \
      40             :                  "arg0='", name, "'")
      41             : 
      42           2 : static struct track_item* track_item_free(struct track_item *i) {
      43             : 
      44           2 :         if (!i)
      45           0 :                 return NULL;
      46             : 
      47           2 :         sd_bus_slot_unref(i->slot);
      48           2 :         free(i->name);
      49           2 :         return mfree(i);
      50             : }
      51             : 
      52           2 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free);
      53           1 : DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(track_item_hash_ops, char, string_hash_func, string_compare_func,
      54             :                                               struct track_item, track_item_free);
      55             : 
      56           4 : static void bus_track_add_to_queue(sd_bus_track *track) {
      57           4 :         assert(track);
      58             : 
      59             :         /* Adds the bus track object to the queue of objects we should dispatch next, subject to a number of
      60             :          * conditions. */
      61             : 
      62             :         /* Already in the queue? */
      63           4 :         if (track->in_queue)
      64           0 :                 return;
      65             : 
      66             :         /* if we are currently in the process of adding a new name, then let's not enqueue this just yet, let's wait
      67             :          * until the addition is complete. */
      68           4 :         if (track->n_adding > 0)
      69           0 :                 return;
      70             : 
      71             :         /* still referenced? */
      72           4 :         if (hashmap_size(track->names) > 0)
      73           0 :                 return;
      74             : 
      75             :         /* Nothing to call? */
      76           4 :         if (!track->handler)
      77           0 :                 return;
      78             : 
      79             :         /* Already closed? */
      80           4 :         if (!track->in_list)
      81           1 :                 return;
      82             : 
      83           3 :         LIST_PREPEND(queue, track->bus->track_queue, track);
      84           3 :         track->in_queue = true;
      85             : }
      86             : 
      87           8 : static void bus_track_remove_from_queue(sd_bus_track *track) {
      88           8 :         assert(track);
      89             : 
      90           8 :         if (!track->in_queue)
      91           5 :                 return;
      92             : 
      93           3 :         LIST_REMOVE(queue, track->bus->track_queue, track);
      94           3 :         track->in_queue = false;
      95             : }
      96             : 
      97           1 : static int bus_track_remove_name_fully(sd_bus_track *track, const char *name) {
      98             :         struct track_item *i;
      99             : 
     100           1 :         assert(track);
     101           1 :         assert(name);
     102             : 
     103           1 :         i = hashmap_remove(track->names, name);
     104           1 :         if (!i)
     105           0 :                 return 0;
     106             : 
     107           1 :         track_item_free(i);
     108             : 
     109           1 :         bus_track_add_to_queue(track);
     110             : 
     111           1 :         track->modified = true;
     112           1 :         return 1;
     113             : }
     114             : 
     115           2 : _public_ int sd_bus_track_new(
     116             :                 sd_bus *bus,
     117             :                 sd_bus_track **track,
     118             :                 sd_bus_track_handler_t handler,
     119             :                 void *userdata) {
     120             : 
     121             :         sd_bus_track *t;
     122             : 
     123           2 :         assert_return(bus, -EINVAL);
     124           2 :         assert_return(bus = bus_resolve(bus), -ENOPKG);
     125           2 :         assert_return(track, -EINVAL);
     126             : 
     127           2 :         if (!bus->bus_client)
     128           0 :                 return -EINVAL;
     129             : 
     130           2 :         t = new0(sd_bus_track, 1);
     131           2 :         if (!t)
     132           0 :                 return -ENOMEM;
     133             : 
     134           2 :         t->n_ref = 1;
     135           2 :         t->handler = handler;
     136           2 :         t->userdata = userdata;
     137           2 :         t->bus = sd_bus_ref(bus);
     138             : 
     139           2 :         LIST_PREPEND(tracks, bus->tracks, t);
     140           2 :         t->in_list = true;
     141             : 
     142           2 :         bus_track_add_to_queue(t);
     143             : 
     144           2 :         *track = t;
     145           2 :         return 0;
     146             : }
     147             : 
     148           2 : static sd_bus_track *track_free(sd_bus_track *track) {
     149           2 :         assert(track);
     150             : 
     151           2 :         if (track->in_list)
     152           1 :                 LIST_REMOVE(tracks, track->bus->tracks, track);
     153             : 
     154           2 :         bus_track_remove_from_queue(track);
     155           2 :         track->names = hashmap_free(track->names);
     156           2 :         track->bus = sd_bus_unref(track->bus);
     157             : 
     158           2 :         if (track->destroy_callback)
     159           0 :                 track->destroy_callback(track->userdata);
     160             : 
     161           2 :         return mfree(track);
     162             : }
     163             : 
     164        2359 : DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus_track, sd_bus_track, track_free);
     165             : 
     166           1 : static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
     167           1 :         sd_bus_track *track = userdata;
     168             :         const char *name, *old, *new;
     169             :         int r;
     170             : 
     171           1 :         assert(message);
     172           1 :         assert(track);
     173             : 
     174           1 :         r = sd_bus_message_read(message, "sss", &name, &old, &new);
     175           1 :         if (r < 0)
     176           0 :                 return 0;
     177             : 
     178           1 :         bus_track_remove_name_fully(track, name);
     179           1 :         return 0;
     180             : }
     181             : 
     182           2 : _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
     183           2 :         _cleanup_(track_item_freep) struct track_item *n = NULL;
     184             :         struct track_item *i;
     185             :         const char *match;
     186             :         int r;
     187             : 
     188           2 :         assert_return(track, -EINVAL);
     189           2 :         assert_return(service_name_is_valid(name), -EINVAL);
     190             : 
     191           2 :         i = hashmap_get(track->names, name);
     192           2 :         if (i) {
     193           0 :                 if (track->recursive) {
     194           0 :                         unsigned k = track->n_ref + 1;
     195             : 
     196           0 :                         if (k < track->n_ref) /* Check for overflow */
     197           0 :                                 return -EOVERFLOW;
     198             : 
     199           0 :                         track->n_ref = k;
     200             :                 }
     201             : 
     202           0 :                 bus_track_remove_from_queue(track);
     203           0 :                 return 0;
     204             :         }
     205             : 
     206           2 :         r = hashmap_ensure_allocated(&track->names, &track_item_hash_ops);
     207           2 :         if (r < 0)
     208           0 :                 return r;
     209             : 
     210           2 :         n = new0(struct track_item, 1);
     211           2 :         if (!n)
     212           0 :                 return -ENOMEM;
     213           2 :         n->name = strdup(name);
     214           2 :         if (!n->name)
     215           0 :                 return -ENOMEM;
     216             : 
     217             :         /* First, subscribe to this name */
     218          14 :         match = MATCH_FOR_NAME(name);
     219             : 
     220           2 :         bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
     221             : 
     222           2 :         r = sd_bus_add_match_async(track->bus, &n->slot, match, on_name_owner_changed, NULL, track);
     223           2 :         if (r < 0) {
     224           0 :                 bus_track_add_to_queue(track);
     225           0 :                 return r;
     226             :         }
     227             : 
     228           2 :         r = hashmap_put(track->names, n->name, n);
     229           2 :         if (r < 0) {
     230           0 :                 bus_track_add_to_queue(track);
     231           0 :                 return r;
     232             :         }
     233             : 
     234             :         /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
     235           2 :         track->n_adding++; /* again, make sure this isn't dispatch while we are working in it */
     236           2 :         r = sd_bus_get_name_creds(track->bus, name, 0, NULL);
     237           2 :         track->n_adding--;
     238           2 :         if (r < 0) {
     239           0 :                 hashmap_remove(track->names, name);
     240           0 :                 bus_track_add_to_queue(track);
     241           0 :                 return r;
     242             :         }
     243             : 
     244           2 :         n->n_ref = 1;
     245           2 :         n = NULL;
     246             : 
     247           2 :         bus_track_remove_from_queue(track);
     248           2 :         track->modified = true;
     249             : 
     250           2 :         return 1;
     251             : }
     252             : 
     253           0 : _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
     254             :         struct track_item *i;
     255             : 
     256           0 :         assert_return(name, -EINVAL);
     257             : 
     258           0 :         if (!track) /* Treat a NULL track object as an empty track object */
     259           0 :                 return 0;
     260             : 
     261           0 :         if (!track->recursive)
     262           0 :                 return bus_track_remove_name_fully(track, name);
     263             : 
     264           0 :         i = hashmap_get(track->names, name);
     265           0 :         if (!i)
     266           0 :                 return -EUNATCH;
     267           0 :         if (i->n_ref <= 0)
     268           0 :                 return -EUNATCH;
     269             : 
     270           0 :         i->n_ref--;
     271             : 
     272           0 :         if (i->n_ref <= 0)
     273           0 :                 return bus_track_remove_name_fully(track, name);
     274             : 
     275           0 :         return 1;
     276             : }
     277             : 
     278       18800 : _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
     279             : 
     280       18800 :         if (!track) /* Let's consider a NULL object equivalent to an empty object */
     281       18800 :                 return 0;
     282             : 
     283             :         /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
     284             :          * that this returns the number of names being watched, and multiple references to the same name are not
     285             :          * counted. */
     286             : 
     287           0 :         return hashmap_size(track->names);
     288             : }
     289             : 
     290           0 : _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
     291           0 :         assert_return(name, NULL);
     292             : 
     293           0 :         if (!track) /* Let's consider a NULL object equivalent to an empty object */
     294           0 :                 return NULL;
     295             : 
     296           0 :         return hashmap_get(track->names, (void*) name) ? name : NULL;
     297             : }
     298             : 
     299        1190 : _public_ const char* sd_bus_track_first(sd_bus_track *track) {
     300        1190 :         const char *n = NULL;
     301             : 
     302        1190 :         if (!track)
     303        1190 :                 return NULL;
     304             : 
     305           0 :         track->modified = false;
     306           0 :         track->iterator = ITERATOR_FIRST;
     307             : 
     308           0 :         (void) hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
     309           0 :         return n;
     310             : }
     311             : 
     312           0 : _public_ const char* sd_bus_track_next(sd_bus_track *track) {
     313           0 :         const char *n = NULL;
     314             : 
     315           0 :         if (!track)
     316           0 :                 return NULL;
     317             : 
     318           0 :         if (track->modified)
     319           0 :                 return NULL;
     320             : 
     321           0 :         (void) hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
     322           0 :         return n;
     323             : }
     324             : 
     325           0 : _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
     326             :         const char *sender;
     327             : 
     328           0 :         assert_return(track, -EINVAL);
     329           0 :         assert_return(m, -EINVAL);
     330             : 
     331           0 :         if (sd_bus_message_get_bus(m) != track->bus)
     332           0 :                 return -EINVAL;
     333             : 
     334           0 :         sender = sd_bus_message_get_sender(m);
     335           0 :         if (!sender)
     336           0 :                 return -EINVAL;
     337             : 
     338           0 :         return sd_bus_track_add_name(track, sender);
     339             : }
     340             : 
     341           0 : _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
     342             :         const char *sender;
     343             : 
     344           0 :         assert_return(m, -EINVAL);
     345             : 
     346           0 :         if (!track) /* Treat a NULL track object as an empty track object */
     347           0 :                 return 0;
     348             : 
     349           0 :         if (sd_bus_message_get_bus(m) != track->bus)
     350           0 :                 return -EINVAL;
     351             : 
     352           0 :         sender = sd_bus_message_get_sender(m);
     353           0 :         if (!sender)
     354           0 :                 return -EINVAL;
     355             : 
     356           0 :         return sd_bus_track_remove_name(track, sender);
     357             : }
     358             : 
     359           2 : _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
     360           2 :         assert_return(track, NULL);
     361             : 
     362           2 :         return track->bus;
     363             : }
     364             : 
     365           2 : void bus_track_dispatch(sd_bus_track *track) {
     366             :         int r;
     367             : 
     368           2 :         assert(track);
     369           2 :         assert(track->handler);
     370             : 
     371           2 :         bus_track_remove_from_queue(track);
     372             : 
     373           2 :         sd_bus_track_ref(track);
     374             : 
     375           2 :         r = track->handler(track, track->userdata);
     376           2 :         if (r < 0)
     377           0 :                 log_debug_errno(r, "Failed to process track handler: %m");
     378           2 :         else if (r == 0)
     379           1 :                 bus_track_add_to_queue(track);
     380             : 
     381           2 :         sd_bus_track_unref(track);
     382           2 : }
     383             : 
     384           1 : void bus_track_close(sd_bus_track *track) {
     385           1 :         assert(track);
     386             : 
     387             :         /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it
     388             :          * immediately, as we are closing now, but first flush out all names. */
     389             : 
     390           1 :         if (!track->in_list)
     391           0 :                 return; /* We already closed this one, don't close it again. */
     392             : 
     393             :         /* Remember that this one is closed now */
     394           1 :         LIST_REMOVE(tracks, track->bus->tracks, track);
     395           1 :         track->in_list = false;
     396             : 
     397             :         /* If there's no name in this one anyway, we don't have to dispatch */
     398           1 :         if (hashmap_isempty(track->names))
     399           0 :                 return;
     400             : 
     401             :         /* Let's flush out all names */
     402           1 :         hashmap_clear(track->names);
     403             : 
     404             :         /* Invoke handler */
     405           1 :         if (track->handler)
     406           1 :                 bus_track_dispatch(track);
     407             : }
     408             : 
     409           0 : _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
     410           0 :         assert_return(track, NULL);
     411             : 
     412           0 :         return track->userdata;
     413             : }
     414             : 
     415           0 : _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
     416             :         void *ret;
     417             : 
     418           0 :         assert_return(track, NULL);
     419             : 
     420           0 :         ret = track->userdata;
     421           0 :         track->userdata = userdata;
     422             : 
     423           0 :         return ret;
     424             : }
     425             : 
     426           0 : _public_ int sd_bus_track_set_destroy_callback(sd_bus_track *track, sd_bus_destroy_t callback) {
     427           0 :         assert_return(track, -EINVAL);
     428             : 
     429           0 :         track->destroy_callback = callback;
     430           0 :         return 0;
     431             : }
     432             : 
     433           0 : _public_ int sd_bus_track_get_destroy_callback(sd_bus_track *track, sd_bus_destroy_t *ret) {
     434           0 :         assert_return(track, -EINVAL);
     435             : 
     436           0 :         if (ret)
     437           0 :                 *ret = track->destroy_callback;
     438             : 
     439           0 :         return !!track->destroy_callback;
     440             : }
     441             : 
     442           0 : _public_ int sd_bus_track_set_recursive(sd_bus_track *track, int b) {
     443           0 :         assert_return(track, -EINVAL);
     444             : 
     445           0 :         if (track->recursive == !!b)
     446           0 :                 return 0;
     447             : 
     448           0 :         if (!hashmap_isempty(track->names))
     449           0 :                 return -EBUSY;
     450             : 
     451           0 :         track->recursive = b;
     452           0 :         return 0;
     453             : }
     454             : 
     455           0 : _public_ int sd_bus_track_get_recursive(sd_bus_track *track) {
     456           0 :         assert_return(track, -EINVAL);
     457             : 
     458           0 :         return track->recursive;
     459             : }
     460             : 
     461           0 : _public_ int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m) {
     462             :         const char *sender;
     463             : 
     464           0 :         assert_return(m, -EINVAL);
     465             : 
     466           0 :         if (!track) /* Let's consider a NULL object equivalent to an empty object */
     467           0 :                 return 0;
     468             : 
     469           0 :         if (sd_bus_message_get_bus(m) != track->bus)
     470           0 :                 return -EINVAL;
     471             : 
     472           0 :         sender = sd_bus_message_get_sender(m);
     473           0 :         if (!sender)
     474           0 :                 return -EINVAL;
     475             : 
     476           0 :         return sd_bus_track_count_name(track, sender);
     477             : }
     478             : 
     479           0 : _public_ int sd_bus_track_count_name(sd_bus_track *track, const char *name) {
     480             :         struct track_item *i;
     481             : 
     482           0 :         assert_return(service_name_is_valid(name), -EINVAL);
     483             : 
     484           0 :         if (!track) /* Let's consider a NULL object equivalent to an empty object */
     485           0 :                 return 0;
     486             : 
     487           0 :         i = hashmap_get(track->names, name);
     488           0 :         if (!i)
     489           0 :                 return 0;
     490             : 
     491           0 :         return i->n_ref;
     492             : }

Generated by: LCOV version 1.14