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