Home | History | Annotate | Download | only in avahi-common
      1 /***
      2   This file is part of avahi.
      3 
      4   avahi is free software; you can redistribute it and/or modify it
      5   under the terms of the GNU Lesser General Public License as
      6   published by the Free Software Foundation; either version 2.1 of the
      7   License, or (at your option) any later version.
      8 
      9   avahi is distributed in the hope that it will be useful, but WITHOUT
     10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
     12   Public License for more details.
     13 
     14   You should have received a copy of the GNU Lesser General Public
     15   License along with avahi; if not, write to the Free Software
     16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
     17   USA.
     18 ***/
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #include <config.h>
     22 #endif
     23 
     24 #include <assert.h>
     25 #include <stdio.h>
     26 
     27 #include "avahi-malloc.h"
     28 #include "timeval.h"
     29 #include "dbus-watch-glue.h"
     30 
     31 static AvahiWatchEvent translate_dbus_to_avahi(unsigned int f) {
     32     AvahiWatchEvent e = 0;
     33 
     34     if (f & DBUS_WATCH_READABLE)
     35         e |= AVAHI_WATCH_IN;
     36     if (f & DBUS_WATCH_WRITABLE)
     37         e |= AVAHI_WATCH_OUT;
     38     if (f & DBUS_WATCH_ERROR)
     39         e |= AVAHI_WATCH_ERR;
     40     if (f & DBUS_WATCH_HANGUP)
     41         e |= AVAHI_WATCH_HUP;
     42 
     43     return e;
     44 }
     45 
     46 static unsigned int translate_avahi_to_dbus(AvahiWatchEvent e) {
     47     unsigned int f = 0;
     48 
     49     if (e & AVAHI_WATCH_IN)
     50         f |= DBUS_WATCH_READABLE;
     51     if (e & AVAHI_WATCH_OUT)
     52         f |= DBUS_WATCH_WRITABLE;
     53     if (e & AVAHI_WATCH_ERR)
     54         f |= DBUS_WATCH_ERROR;
     55     if (e & AVAHI_WATCH_HUP)
     56         f |= DBUS_WATCH_HANGUP;
     57 
     58     return f;
     59 }
     60 
     61 typedef struct {
     62     DBusConnection *connection;
     63     const AvahiPoll *poll_api;
     64     AvahiTimeout *dispatch_timeout;
     65     int ref;
     66 } ConnectionData;
     67 
     68 static ConnectionData *connection_data_ref(ConnectionData *d) {
     69     assert(d);
     70     assert(d->ref >= 1);
     71 
     72     d->ref++;
     73     return d;
     74 }
     75 
     76 static void connection_data_unref(ConnectionData *d) {
     77     assert(d);
     78     assert(d->ref >= 1);
     79 
     80     if (--d->ref <= 0) {
     81         d->poll_api->timeout_free(d->dispatch_timeout);
     82         avahi_free(d);
     83     }
     84 }
     85 
     86 static void request_dispatch(ConnectionData *d, int enable) {
     87     static const struct timeval tv = { 0, 0 };
     88     assert(d);
     89 
     90     if (enable) {
     91         assert(dbus_connection_get_dispatch_status(d->connection) == DBUS_DISPATCH_DATA_REMAINS);
     92         d->poll_api->timeout_update(d->dispatch_timeout, &tv);
     93     } else
     94         d->poll_api->timeout_update(d->dispatch_timeout, NULL);
     95 }
     96 
     97 static void dispatch_timeout_callback(AvahiTimeout *t, void *userdata) {
     98     ConnectionData *d = userdata;
     99     assert(t);
    100     assert(d);
    101 
    102     connection_data_ref(d);
    103     dbus_connection_ref(d->connection);
    104 
    105     if (dbus_connection_dispatch(d->connection) == DBUS_DISPATCH_DATA_REMAINS)
    106         /* If there's still data, request that this handler is called again */
    107         request_dispatch(d, 1);
    108     else
    109         request_dispatch(d, 0);
    110 
    111     dbus_connection_unref(d->connection);
    112     connection_data_unref(d);
    113 }
    114 
    115 static void watch_callback(AvahiWatch *avahi_watch, AVAHI_GCC_UNUSED int fd, AvahiWatchEvent events, void *userdata) {
    116     DBusWatch *dbus_watch = userdata;
    117 
    118     assert(avahi_watch);
    119     assert(dbus_watch);
    120 
    121     dbus_watch_handle(dbus_watch, translate_avahi_to_dbus(events));
    122     /* Ignore the return value */
    123 }
    124 
    125 static dbus_bool_t update_watch(const AvahiPoll *poll_api, DBusWatch *dbus_watch) {
    126     AvahiWatch *avahi_watch;
    127     dbus_bool_t b;
    128 
    129     assert(dbus_watch);
    130 
    131     avahi_watch = dbus_watch_get_data(dbus_watch);
    132 
    133     b = dbus_watch_get_enabled(dbus_watch);
    134 
    135     if (b && !avahi_watch) {
    136 
    137         if (!(avahi_watch = poll_api->watch_new(
    138                   poll_api,
    139 #if (DBUS_VERSION_MAJOR == 1 && DBUS_VERSION_MINOR == 1 && DBUS_VERSION_MICRO >= 1) || (DBUS_VERSION_MAJOR == 1 && DBUS_VERSION_MINOR > 1) || (DBUS_VERSION_MAJOR > 1)
    140                   dbus_watch_get_unix_fd(dbus_watch),
    141 #else
    142                   dbus_watch_get_fd(dbus_watch),
    143 #endif
    144                   translate_dbus_to_avahi(dbus_watch_get_flags(dbus_watch)),
    145                   watch_callback,
    146                   dbus_watch)))
    147             return FALSE;
    148 
    149         dbus_watch_set_data(dbus_watch, avahi_watch, NULL);
    150 
    151     } else if (!b && avahi_watch) {
    152 
    153         poll_api->watch_free(avahi_watch);
    154         dbus_watch_set_data(dbus_watch, NULL, NULL);
    155 
    156     } else if (avahi_watch) {
    157 
    158         /* Update flags */
    159         poll_api->watch_update(avahi_watch, dbus_watch_get_flags(dbus_watch));
    160     }
    161 
    162     return TRUE;
    163 }
    164 
    165 static dbus_bool_t add_watch(DBusWatch *dbus_watch, void *userdata) {
    166     ConnectionData *d = userdata;
    167 
    168     assert(dbus_watch);
    169     assert(d);
    170 
    171     return update_watch(d->poll_api, dbus_watch);
    172 }
    173 
    174 static void remove_watch(DBusWatch *dbus_watch, void *userdata) {
    175     ConnectionData *d = userdata;
    176     AvahiWatch *avahi_watch;
    177 
    178     assert(dbus_watch);
    179     assert(d);
    180 
    181     if ((avahi_watch = dbus_watch_get_data(dbus_watch))) {
    182         d->poll_api->watch_free(avahi_watch);
    183         dbus_watch_set_data(dbus_watch, NULL, NULL);
    184     }
    185 }
    186 
    187 static void watch_toggled(DBusWatch *dbus_watch, void *userdata) {
    188     ConnectionData *d = userdata;
    189 
    190     assert(dbus_watch);
    191     assert(d);
    192 
    193     update_watch(d->poll_api, dbus_watch);
    194 }
    195 
    196 typedef struct TimeoutData {
    197     const AvahiPoll *poll_api;
    198     AvahiTimeout *avahi_timeout;
    199     DBusTimeout *dbus_timeout;
    200     int ref;
    201 } TimeoutData;
    202 
    203 static TimeoutData* timeout_data_ref(TimeoutData *t) {
    204     assert(t);
    205     assert(t->ref >= 1);
    206 
    207     t->ref++;
    208     return t;
    209 }
    210 
    211 static void timeout_data_unref(TimeoutData *t) {
    212     assert(t);
    213     assert(t->ref >= 1);
    214 
    215     if (--t->ref <= 0) {
    216         if (t->avahi_timeout)
    217             t->poll_api->timeout_free(t->avahi_timeout);
    218 
    219         avahi_free(t);
    220     }
    221 }
    222 
    223 static void update_timeout(TimeoutData *timeout) {
    224     assert(timeout);
    225     assert(timeout->ref >= 1);
    226 
    227     if (dbus_timeout_get_enabled(timeout->dbus_timeout)) {
    228         struct timeval tv;
    229         avahi_elapse_time(&tv, dbus_timeout_get_interval(timeout->dbus_timeout), 0);
    230         timeout->poll_api->timeout_update(timeout->
    231                                       avahi_timeout, &tv);
    232     } else
    233         timeout->poll_api->timeout_update(timeout->avahi_timeout, NULL);
    234 
    235 }
    236 
    237 static void timeout_callback(AvahiTimeout *avahi_timeout, void *userdata) {
    238     TimeoutData *timeout = userdata;
    239 
    240     assert(avahi_timeout);
    241     assert(timeout);
    242 
    243     timeout_data_ref(timeout);
    244 
    245     dbus_timeout_handle(timeout->dbus_timeout);
    246     /* Ignore the return value */
    247 
    248     if (timeout->avahi_timeout)
    249         update_timeout(timeout);
    250 
    251     timeout_data_unref(timeout);
    252 }
    253 
    254 static dbus_bool_t add_timeout(DBusTimeout *dbus_timeout, void *userdata) {
    255     TimeoutData *timeout;
    256     ConnectionData *d = userdata;
    257     struct timeval tv;
    258     dbus_bool_t b;
    259 
    260     assert(dbus_timeout);
    261     assert(d);
    262 
    263     if (!(timeout = avahi_new(TimeoutData, 1)))
    264         return FALSE;
    265 
    266     timeout->dbus_timeout = dbus_timeout;
    267     timeout->poll_api = d->poll_api;
    268     timeout->ref = 1;
    269 
    270     if ((b = dbus_timeout_get_enabled(dbus_timeout)))
    271         avahi_elapse_time(&tv, dbus_timeout_get_interval(dbus_timeout), 0);
    272 
    273     if (!(timeout->avahi_timeout = d->poll_api->timeout_new(
    274               d->poll_api,
    275               b ? &tv : NULL,
    276               timeout_callback,
    277               timeout))) {
    278         avahi_free(timeout);
    279         return FALSE;
    280     }
    281 
    282     dbus_timeout_set_data(dbus_timeout, timeout, (DBusFreeFunction) timeout_data_unref);
    283     return TRUE;
    284 }
    285 
    286 static void remove_timeout(DBusTimeout *dbus_timeout, void *userdata) {
    287     ConnectionData *d = userdata;
    288     TimeoutData *timeout;
    289 
    290     assert(dbus_timeout);
    291     assert(d);
    292 
    293     timeout = dbus_timeout_get_data(dbus_timeout);
    294     assert(timeout);
    295 
    296     d->poll_api->timeout_free(timeout->avahi_timeout);
    297     timeout->avahi_timeout = NULL;
    298 }
    299 
    300 static void timeout_toggled(DBusTimeout *dbus_timeout, AVAHI_GCC_UNUSED void *userdata) {
    301     TimeoutData *timeout;
    302 
    303     assert(dbus_timeout);
    304     timeout = dbus_timeout_get_data(dbus_timeout);
    305     assert(timeout);
    306 
    307     update_timeout(timeout);
    308 }
    309 
    310 static void dispatch_status(AVAHI_GCC_UNUSED DBusConnection *connection, DBusDispatchStatus new_status, void *userdata) {
    311     ConnectionData *d = userdata;
    312 
    313     if (new_status == DBUS_DISPATCH_DATA_REMAINS)
    314         request_dispatch(d, 1);
    315  }
    316 
    317 int avahi_dbus_connection_glue(DBusConnection *c, const AvahiPoll *poll_api) {
    318     ConnectionData *d = NULL;
    319 
    320     assert(c);
    321     assert(poll_api);
    322 
    323     if (!(d = avahi_new(ConnectionData, 1)))
    324         goto fail;;
    325 
    326     d->poll_api = poll_api;
    327     d->connection = c;
    328     d->ref = 1;
    329 
    330     if (!(d->dispatch_timeout = poll_api->timeout_new(poll_api, NULL, dispatch_timeout_callback, d)))
    331         goto fail;
    332 
    333     if (!(dbus_connection_set_watch_functions(c, add_watch, remove_watch, watch_toggled, connection_data_ref(d), (DBusFreeFunction)connection_data_unref)))
    334         goto fail;
    335 
    336     if (!(dbus_connection_set_timeout_functions(c, add_timeout, remove_timeout, timeout_toggled, connection_data_ref(d), (DBusFreeFunction)connection_data_unref)))
    337         goto fail;
    338 
    339     dbus_connection_set_dispatch_status_function(c, dispatch_status, connection_data_ref(d), (DBusFreeFunction)connection_data_unref);
    340 
    341     if (dbus_connection_get_dispatch_status(c) == DBUS_DISPATCH_DATA_REMAINS)
    342         request_dispatch(d, 1);
    343 
    344     connection_data_unref(d);
    345 
    346     return 0;
    347 
    348 fail:
    349 
    350     if (d) {
    351         d->poll_api->timeout_free(d->dispatch_timeout);
    352 
    353         avahi_free(d);
    354     }
    355 
    356     return -1;
    357 }
    358