LCOV - code coverage report
Current view: top level - libsystemd/sd-bus - bus-track.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 123 251 49.0 %
Date: 2019-08-23 13:36:53 Functions: 17 30 56.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 84 252 33.3 %

           Branch data     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                 :          8 : static struct track_item* track_item_free(struct track_item *i) {
      43                 :            : 
      44         [ -  + ]:          8 :         if (!i)
      45                 :          0 :                 return NULL;
      46                 :            : 
      47                 :          8 :         sd_bus_slot_unref(i->slot);
      48                 :          8 :         free(i->name);
      49                 :          8 :         return mfree(i);
      50                 :            : }
      51                 :            : 
      52         [ -  + ]:          8 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free);
      53                 :          4 : 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                 :         16 : static void bus_track_add_to_queue(sd_bus_track *track) {
      57         [ -  + ]:         16 :         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         [ -  + ]:         16 :         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         [ -  + ]:         16 :         if (track->n_adding > 0)
      69                 :          0 :                 return;
      70                 :            : 
      71                 :            :         /* still referenced? */
      72         [ -  + ]:         16 :         if (hashmap_size(track->names) > 0)
      73                 :          0 :                 return;
      74                 :            : 
      75                 :            :         /* Nothing to call? */
      76         [ -  + ]:         16 :         if (!track->handler)
      77                 :          0 :                 return;
      78                 :            : 
      79                 :            :         /* Already closed? */
      80         [ +  + ]:         16 :         if (!track->in_list)
      81                 :          4 :                 return;
      82                 :            : 
      83   [ -  +  -  + ]:         12 :         LIST_PREPEND(queue, track->bus->track_queue, track);
      84                 :         12 :         track->in_queue = true;
      85                 :            : }
      86                 :            : 
      87                 :         32 : static void bus_track_remove_from_queue(sd_bus_track *track) {
      88         [ -  + ]:         32 :         assert(track);
      89                 :            : 
      90         [ +  + ]:         32 :         if (!track->in_queue)
      91                 :         20 :                 return;
      92                 :            : 
      93   [ -  +  -  +  :         12 :         LIST_REMOVE(queue, track->bus->track_queue, track);
             -  +  -  + ]
      94                 :         12 :         track->in_queue = false;
      95                 :            : }
      96                 :            : 
      97                 :          4 : static int bus_track_remove_name_fully(sd_bus_track *track, const char *name) {
      98                 :            :         struct track_item *i;
      99                 :            : 
     100         [ -  + ]:          4 :         assert(track);
     101         [ -  + ]:          4 :         assert(name);
     102                 :            : 
     103                 :          4 :         i = hashmap_remove(track->names, name);
     104         [ -  + ]:          4 :         if (!i)
     105                 :          0 :                 return 0;
     106                 :            : 
     107                 :          4 :         track_item_free(i);
     108                 :            : 
     109                 :          4 :         bus_track_add_to_queue(track);
     110                 :            : 
     111                 :          4 :         track->modified = true;
     112                 :          4 :         return 1;
     113                 :            : }
     114                 :            : 
     115                 :          8 : _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   [ -  +  -  + ]:          8 :         assert_return(bus, -EINVAL);
     124   [ -  +  -  + ]:          8 :         assert_return(bus = bus_resolve(bus), -ENOPKG);
     125   [ -  +  -  + ]:          8 :         assert_return(track, -EINVAL);
     126                 :            : 
     127         [ -  + ]:          8 :         if (!bus->bus_client)
     128                 :          0 :                 return -EINVAL;
     129                 :            : 
     130                 :          8 :         t = new0(sd_bus_track, 1);
     131         [ -  + ]:          8 :         if (!t)
     132                 :          0 :                 return -ENOMEM;
     133                 :            : 
     134                 :          8 :         t->n_ref = 1;
     135                 :          8 :         t->handler = handler;
     136                 :          8 :         t->userdata = userdata;
     137                 :          8 :         t->bus = sd_bus_ref(bus);
     138                 :            : 
     139   [ -  +  +  + ]:          8 :         LIST_PREPEND(tracks, bus->tracks, t);
     140                 :          8 :         t->in_list = true;
     141                 :            : 
     142                 :          8 :         bus_track_add_to_queue(t);
     143                 :            : 
     144                 :          8 :         *track = t;
     145                 :          8 :         return 0;
     146                 :            : }
     147                 :            : 
     148                 :          8 : static sd_bus_track *track_free(sd_bus_track *track) {
     149         [ -  + ]:          8 :         assert(track);
     150                 :            : 
     151         [ +  + ]:          8 :         if (track->in_list)
     152   [ -  +  -  +  :          4 :                 LIST_REMOVE(tracks, track->bus->tracks, track);
             -  +  -  + ]
     153                 :            : 
     154                 :          8 :         bus_track_remove_from_queue(track);
     155                 :          8 :         track->names = hashmap_free(track->names);
     156                 :          8 :         track->bus = sd_bus_unref(track->bus);
     157                 :            : 
     158         [ -  + ]:          8 :         if (track->destroy_callback)
     159                 :          0 :                 track->destroy_callback(track->userdata);
     160                 :            : 
     161                 :          8 :         return mfree(track);
     162                 :            : }
     163                 :            : 
     164   [ +  +  -  +  :       9524 : DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus_track, sd_bus_track, track_free);
                   +  + ]
     165                 :            : 
     166                 :          4 : static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
     167                 :          4 :         sd_bus_track *track = userdata;
     168                 :            :         const char *name, *old, *new;
     169                 :            :         int r;
     170                 :            : 
     171         [ -  + ]:          4 :         assert(message);
     172         [ -  + ]:          4 :         assert(track);
     173                 :            : 
     174                 :          4 :         r = sd_bus_message_read(message, "sss", &name, &old, &new);
     175         [ -  + ]:          4 :         if (r < 0)
     176                 :          0 :                 return 0;
     177                 :            : 
     178                 :          4 :         bus_track_remove_name_fully(track, name);
     179                 :          4 :         return 0;
     180                 :            : }
     181                 :            : 
     182                 :          8 : _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
     183                 :          8 :         _cleanup_(track_item_freep) struct track_item *n = NULL;
     184                 :            :         struct track_item *i;
     185                 :            :         const char *match;
     186                 :            :         int r;
     187                 :            : 
     188   [ -  +  -  + ]:          8 :         assert_return(track, -EINVAL);
     189   [ -  +  -  + ]:          8 :         assert_return(service_name_is_valid(name), -EINVAL);
     190                 :            : 
     191                 :          8 :         i = hashmap_get(track->names, name);
     192         [ -  + ]:          8 :         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                 :          8 :         r = hashmap_ensure_allocated(&track->names, &track_item_hash_ops);
     207         [ -  + ]:          8 :         if (r < 0)
     208                 :          0 :                 return r;
     209                 :            : 
     210                 :          8 :         n = new0(struct track_item, 1);
     211         [ -  + ]:          8 :         if (!n)
     212                 :          0 :                 return -ENOMEM;
     213                 :          8 :         n->name = strdup(name);
     214         [ -  + ]:          8 :         if (!n->name)
     215                 :          0 :                 return -ENOMEM;
     216                 :            : 
     217                 :            :         /* First, subscribe to this name */
     218   [ +  +  +  -  :         56 :         match = MATCH_FOR_NAME(name);
          -  +  -  +  +  
                +  +  - ]
     219                 :            : 
     220                 :          8 :         bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
     221                 :            : 
     222                 :          8 :         r = sd_bus_add_match_async(track->bus, &n->slot, match, on_name_owner_changed, NULL, track);
     223         [ -  + ]:          8 :         if (r < 0) {
     224                 :          0 :                 bus_track_add_to_queue(track);
     225                 :          0 :                 return r;
     226                 :            :         }
     227                 :            : 
     228                 :          8 :         r = hashmap_put(track->names, n->name, n);
     229         [ -  + ]:          8 :         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                 :          8 :         track->n_adding++; /* again, make sure this isn't dispatch while we are working in it */
     236                 :          8 :         r = sd_bus_get_name_creds(track->bus, name, 0, NULL);
     237                 :          8 :         track->n_adding--;
     238         [ -  + ]:          8 :         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                 :          8 :         n->n_ref = 1;
     245                 :          8 :         n = NULL;
     246                 :            : 
     247                 :          8 :         bus_track_remove_from_queue(track);
     248                 :          8 :         track->modified = true;
     249                 :            : 
     250                 :          8 :         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                 :      76471 : _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
     279                 :            : 
     280         [ +  - ]:      76471 :         if (!track) /* Let's consider a NULL object equivalent to an empty object */
     281                 :      76471 :                 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                 :       4808 : _public_ const char* sd_bus_track_first(sd_bus_track *track) {
     300                 :       4808 :         const char *n = NULL;
     301                 :            : 
     302         [ +  - ]:       4808 :         if (!track)
     303                 :       4808 :                 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                 :          8 : _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
     360   [ -  +  -  + ]:          8 :         assert_return(track, NULL);
     361                 :            : 
     362                 :          8 :         return track->bus;
     363                 :            : }
     364                 :            : 
     365                 :          8 : void bus_track_dispatch(sd_bus_track *track) {
     366                 :            :         int r;
     367                 :            : 
     368         [ -  + ]:          8 :         assert(track);
     369         [ -  + ]:          8 :         assert(track->handler);
     370                 :            : 
     371                 :          8 :         bus_track_remove_from_queue(track);
     372                 :            : 
     373                 :          8 :         sd_bus_track_ref(track);
     374                 :            : 
     375                 :          8 :         r = track->handler(track, track->userdata);
     376         [ -  + ]:          8 :         if (r < 0)
     377         [ #  # ]:          0 :                 log_debug_errno(r, "Failed to process track handler: %m");
     378         [ +  + ]:          8 :         else if (r == 0)
     379                 :          4 :                 bus_track_add_to_queue(track);
     380                 :            : 
     381                 :          8 :         sd_bus_track_unref(track);
     382                 :          8 : }
     383                 :            : 
     384                 :          4 : void bus_track_close(sd_bus_track *track) {
     385         [ -  + ]:          4 :         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         [ -  + ]:          4 :         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   [ -  +  +  -  :          4 :         LIST_REMOVE(tracks, track->bus->tracks, track);
             -  +  -  + ]
     395                 :          4 :         track->in_list = false;
     396                 :            : 
     397                 :            :         /* If there's no name in this one anyway, we don't have to dispatch */
     398         [ -  + ]:          4 :         if (hashmap_isempty(track->names))
     399                 :          0 :                 return;
     400                 :            : 
     401                 :            :         /* Let's flush out all names */
     402                 :          4 :         hashmap_clear(track->names);
     403                 :            : 
     404                 :            :         /* Invoke handler */
     405         [ +  - ]:          4 :         if (track->handler)
     406                 :          4 :                 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