Fawkes API  Fawkes Development Version
avahi_thread.cpp
00001 
00002 /***************************************************************************
00003  *  avahi_thread.cpp - Avahi thread
00004  *
00005  *  Created: Wed Nov 08 11:19:25 2006
00006  *  Copyright  2006  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <netcomm/dns-sd/avahi_thread.h>
00025 #include <netcomm/dns-sd/avahi_resolver_handler.h>
00026 
00027 #include <core/threading/mutex.h>
00028 #include <core/threading/wait_condition.h>
00029 #include <core/exceptions/software.h>
00030 
00031 #include <avahi-client/lookup.h>
00032 #include <avahi-client/publish.h>
00033 #include <avahi-common/alternative.h>
00034 #include <avahi-common/simple-watch.h>
00035 #include <avahi-common/malloc.h>
00036 #include <avahi-common/error.h>
00037 #include <avahi-common/timeval.h>
00038 
00039 #include <sys/socket.h>
00040 #include <sys/types.h>
00041 #include <netinet/in.h>
00042 #include <cstdlib>
00043 #include <cstddef>
00044 #include <cstring>
00045 
00046 namespace fawkes {
00047 
00048 /** @class AvahiThread netcomm/dns-sd/avahi_thread.h
00049  * Avahi main thread.
00050  * This thread handles all tasks related to avahi. This is the single
00051  * interaction point with the Avahi adapter.
00052  *
00053  * @ingroup NetComm
00054  * @author Tim Niemueller
00055  */
00056 
00057 /** Constructor. */
00058 AvahiThread::AvahiThread()
00059   : Thread("AvahiThread")
00060 {
00061   simple_poll = NULL;
00062   client = NULL;
00063 
00064   need_recover = false;
00065   do_reset_groups = false;
00066 
00067   init_wc = new WaitCondition();
00068 
00069   set_prepfin_conc_loop(true);
00070 }
00071 
00072 
00073 /** Destructor. */
00074 AvahiThread::~AvahiThread()
00075 {
00076   delete init_wc;
00077 
00078   erase_groups();
00079   erase_browsers();
00080 
00081   if ( client )
00082     avahi_client_free( client );
00083 
00084   if ( simple_poll )
00085     avahi_simple_poll_free( simple_poll );
00086 
00087 }
00088 
00089 
00090 /** Avahi thread loop.
00091  * The avahi thread calls the simple poll iterate to poll with an infinite
00092  * timeout. This way the loop blocks until an event occurs.
00093  */
00094 void
00095 AvahiThread::loop()
00096 {
00097   if ( need_recover ) {
00098     if ( client ) {
00099       avahi_client_free( client );
00100       client = NULL;
00101     }
00102 
00103     if ( simple_poll ) {
00104       avahi_simple_poll_free( simple_poll );
00105       simple_poll = NULL;
00106     }
00107   }
00108 
00109   if ( ! simple_poll ) {
00110     // Init
00111     int error;
00112 
00113     if ( (simple_poll = avahi_simple_poll_new()) ) {
00114 
00115       client = avahi_client_new( avahi_simple_poll_get(simple_poll), AVAHI_CLIENT_NO_FAIL,
00116                                  AvahiThread::client_callback, this, &error );
00117 
00118       if ( ! client ) {
00119         avahi_simple_poll_free( simple_poll );
00120       }
00121     }
00122   }
00123 
00124   if ( client ) {
00125     if ( do_reset_groups ) {
00126       reset_groups();
00127       recreate_services();
00128     }
00129     if ( need_recover ) {
00130       erase_groups();
00131       erase_browsers();
00132       recreate_services();
00133       recreate_browsers();
00134     }
00135     if ( client_state == AVAHI_CLIENT_S_RUNNING ) {
00136       remove_pending_services();
00137       remove_pending_browsers();
00138       create_pending_services();
00139       create_pending_browsers();
00140       start_hostname_resolvers();
00141       start_address_resolvers();
00142     }
00143 
00144     need_recover = false;
00145 
00146     avahi_simple_poll_iterate( simple_poll, -1);
00147   }
00148 }
00149 
00150 
00151 /** Recover froma broken Avahi connection.
00152  * This will erase all service browsers and announced service groups
00153  * and will try to reconnect in the next loop.
00154  */
00155 void
00156 AvahiThread::recover()
00157 {
00158   need_recover = true;
00159   wake_poller();
00160 }
00161 
00162 void
00163 AvahiThread::wake_poller()
00164 {
00165   if ( simple_poll ) {
00166     avahi_simple_poll_wakeup( simple_poll );
00167   }
00168 }
00169 
00170 
00171 /** Called whenever the client or server state changes.
00172  * @param c Avahi client
00173  * @param state new state
00174  * @param instance Instance of AvahiThread that triggered the event.
00175  */
00176 void
00177 AvahiThread::client_callback(AvahiClient *c, AvahiClientState state, void *instance)
00178 {
00179   AvahiThread *at = static_cast<AvahiThread *>(instance);
00180   at->client_state = state;
00181 
00182   switch (state) {
00183   case AVAHI_CLIENT_S_RUNNING:        
00184     /* The server has startup successfully and registered its host
00185      * name on the network, so it's time to create our services */
00186     //printf("(Client): RUNNING\n");
00187     //at->create_browsers();
00188     //at->set_available( true );
00189     at->init_done();
00190     break;
00191 
00192   case AVAHI_CLIENT_S_COLLISION:
00193     //printf("(Client): COLLISION\n");
00194     /* Let's drop our registered services. When the server is back
00195      * in AVAHI_SERVER_RUNNING state we will register them
00196      * again with the new host name. */
00197     at->do_reset_groups = true;
00198     break;
00199             
00200   case AVAHI_CLIENT_FAILURE:          
00201     // Doh!
00202     //printf("(Client): FAILURE\n");
00203     at->recover();
00204     break;
00205 
00206   case AVAHI_CLIENT_CONNECTING:
00207     //printf("(Client): CONNECTING\n");
00208     break;
00209 
00210   case AVAHI_CLIENT_S_REGISTERING:
00211     // Ignored
00212     //printf("(Client): REGISTERING\n");
00213     break;
00214   }
00215 }
00216 
00217 /* **********************************************************************************
00218  * Avahi Service Publisher methods
00219  * **********************************************************************************/
00220 
00221 
00222 /** Publish service.
00223  * @param service service to publish.
00224  */
00225 void
00226 AvahiThread::publish_service(NetworkService *service)
00227 {
00228   if ( __services.find(service) == __services.end() ) {
00229     __pending_services.push_locked(service);
00230   } else {
00231     throw Exception("Service already registered");
00232   }
00233 
00234   wake_poller();
00235 }
00236 
00237 
00238 void
00239 AvahiThread::unpublish_service(NetworkService *service)
00240 {
00241   if ( __services.find(service) != __services.end() ) {
00242     __pending_remove_services.push_locked(service);
00243   } else {
00244     throw Exception("Service not registered");
00245   }
00246 
00247   wake_poller();
00248 }
00249 
00250 
00251 /** Create services. */
00252 AvahiEntryGroup *
00253 AvahiThread::create_service(const NetworkService &service, AvahiEntryGroup *exgroup)
00254 {
00255   // the following errors are non-fatal, they can happen since Avahi is started
00256   // asynchronously, just ignore them by bailing out
00257   if ( ! client )  return NULL;
00258 
00259   AvahiEntryGroup *group;
00260   if ( exgroup ) {
00261     group = exgroup;
00262   } else {
00263     if ( ! (group = avahi_entry_group_new(client,
00264                                           AvahiThread::entry_group_callback,
00265                                           this))) {
00266       throw NullPointerException("Cannot create service group");
00267     }
00268   }
00269 
00270   // only IPv4 for now
00271   AvahiStringList *al = NULL;
00272   const std::list<std::string> &l = service.txt();
00273   for (std::list<std::string>::const_iterator j = l.begin(); j != l.end(); ++j) {
00274     al = avahi_string_list_add(al, j->c_str());
00275   }
00276   if ( avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET,
00277                                             AVAHI_PUBLISH_USE_MULTICAST,
00278                                             service.name(), service.type(),
00279                                             service.domain(), service.host(),
00280                                             service.port(), al) < 0) {
00281     avahi_string_list_free(al);
00282     throw Exception("Adding Avahi services failed");
00283   }
00284   avahi_string_list_free(al);
00285 
00286   /* Tell the server to register the service */
00287   if (avahi_entry_group_commit(group) < 0) {
00288     throw Exception("Registering Avahi services failed");
00289   }
00290 
00291   return group;
00292 }
00293 
00294 void
00295 AvahiThread::recreate_services()
00296 {
00297   for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
00298     (*__sit).second = create_service((*__sit).first, (*__sit).second);
00299   }
00300 }
00301 
00302 
00303 void
00304 AvahiThread::create_pending_services()
00305 {
00306   __pending_services.lock();
00307   while ( ! __pending_services.empty()) {
00308     NetworkService &s = __pending_services.front();
00309     __services[s] = create_service(s, NULL);
00310     __pending_services.pop();
00311   }
00312   __pending_services.unlock();
00313 }
00314 
00315 
00316 void
00317 AvahiThread::remove_pending_services()
00318 {
00319   __pending_remove_services.lock();
00320   while ( ! __pending_remove_services.empty()) {
00321     NetworkService &s = __pending_remove_services.front();
00322     if ( __services.find(s) != __services.end() ) {
00323       group_erase(__services[s]);
00324       __services.erase_locked(s);
00325     }
00326     __pending_remove_services.pop();
00327   }
00328   __pending_remove_services.unlock();
00329 }
00330 
00331 
00332 /** Drop our registered services.
00333  * When the server is back in AVAHI_SERVER_RUNNING state we will register them
00334  * again with the new host name (triggered by AvahiThread).
00335  */
00336 void
00337 AvahiThread::group_reset(AvahiEntryGroup *g)
00338 {
00339   if ( g ) {
00340     avahi_entry_group_reset(g);
00341   }
00342 }
00343 
00344 
00345 /** Erase service group. */
00346 void
00347 AvahiThread::group_erase(AvahiEntryGroup *g)
00348 {
00349   if ( g ) {
00350     avahi_entry_group_reset( g );
00351     avahi_entry_group_free( g );
00352   }
00353 }
00354 
00355 
00356 void
00357 AvahiThread::erase_groups()
00358 {
00359   for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
00360     group_erase((*__sit).second);
00361     (*__sit).second = NULL;
00362   }
00363 }
00364 
00365 
00366 void
00367 AvahiThread::reset_groups()
00368 {
00369   for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
00370     group_reset((*__sit).second);
00371   }
00372 }
00373 
00374 
00375 /** Called if there was a name collision. */
00376 void
00377 AvahiThread::name_collision(AvahiEntryGroup *g)
00378 {
00379   for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
00380     if ( (*__sit).second == g ) {
00381       NetworkService alternate_service((*__sit).first);
00382 
00383       /* A service name collision happened. Let's pick a new name */
00384       char *n = avahi_alternative_service_name((*__sit).first.name());
00385       alternate_service.set_name(n);
00386       avahi_free(n);
00387 
00388       __pending_remove_services.push_locked((*__sit).first);
00389       __pending_services.push_locked(alternate_service);
00390     }
00391   }
00392 }
00393 
00394 
00395 /** Callback for Avahi.
00396  * @param g entry group
00397  * @param state new state
00398  * @param instance instance of AvahiThread that triggered the event.
00399  */
00400 void
00401 AvahiThread::entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
00402                                   void *instance)
00403 {
00404   AvahiThread *at = static_cast<AvahiThread *>(instance);
00405 
00406   switch (state) {
00407   case AVAHI_ENTRY_GROUP_ESTABLISHED :
00408     /* The entry group has been established successfully */
00409     //fprintf(stderr, "Service '%s' successfully established.\n", name);
00410     break;
00411 
00412   case AVAHI_ENTRY_GROUP_COLLISION : {
00413     at->name_collision(g);
00414     break;
00415   }
00416   
00417   case AVAHI_ENTRY_GROUP_FAILURE :
00418     /* Some kind of failure happened while we were registering our services */
00419     at->recover();
00420     break;
00421 
00422   case AVAHI_ENTRY_GROUP_UNCOMMITED:
00423   case AVAHI_ENTRY_GROUP_REGISTERING:
00424     break;
00425   }
00426 }
00427 
00428 
00429 /* **********************************************************************************
00430  * Avahi Browser Publisher methods
00431  * **********************************************************************************/
00432 
00433 
00434 /** Add a result handler.
00435  * A handler is added for the given service type. A search is initiated
00436  * for the given service and the given handler is called for added or
00437  * removed services or if an error occurs.
00438  * @param service_type string of the service type
00439  * @param h The ServiceBrowseHandler
00440  */
00441 void
00442 AvahiThread::watch_service(const char *service_type, ServiceBrowseHandler *h)
00443 {
00444   __handlers[service_type].push_back(h);
00445   __pending_browsers.push_locked(service_type);
00446 
00447   wake_poller();
00448 }
00449 
00450 
00451 /** Remove a handler.
00452  * The handler is removed and no further events will be emitted to the
00453  * handler.
00454  * @param service_type service type to de-register the handler for
00455  * @param h the handler
00456  */
00457 void
00458 AvahiThread::unwatch_service(const char *service_type, ServiceBrowseHandler *h)
00459 {
00460   if ( __handlers.find(service_type) != __handlers.end() ) {
00461     __handlers[service_type].remove(h);
00462     if ( __handlers[service_type].size() == 0 ) {
00463       if ( __browsers.find(service_type) != __browsers.end() ) {
00464         __pending_browser_removes.push_locked(service_type);
00465         //avahi_service_browser_free(__browsers[service_type]);
00466         //__browsers.erase(service_type);
00467       }
00468       __handlers.erase(service_type);
00469     }
00470   }
00471 
00472   wake_poller();
00473 }
00474 
00475 
00476 /** Create browser for a given service.
00477  * @param service_type service type
00478  */
00479 void
00480 AvahiThread::create_browser(const char *service_type)
00481 {
00482   if ( __browsers.find(service_type) == __browsers.end() ) {
00483     if ( client ) {
00484       AvahiServiceBrowser *b = avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
00485                                                          AVAHI_PROTO_UNSPEC,
00486                                                          service_type, NULL, (AvahiLookupFlags)0,
00487                                                          AvahiThread::browse_callback, this);
00488 
00489       if ( ! b ) {
00490         __handlers[service_type].pop_back();
00491         throw NullPointerException("Could not instantiate AvahiServiceBrowser");
00492       }
00493       __browsers[service_type] = b;
00494     }
00495   }
00496 }
00497 
00498 
00499 /** Create browsers.
00500  * Creates browser for all services.
00501  */
00502 void
00503 AvahiThread::recreate_browsers()
00504 {
00505   LockMap< std::string, std::list<ServiceBrowseHandler *> >::iterator i;
00506   for (i = __handlers.begin(); i != __handlers.end(); ++i) {
00507     create_browser( (*i).first.c_str() );
00508   }
00509 }
00510 
00511 
00512 void
00513 AvahiThread::create_pending_browsers()
00514 {
00515   __pending_browsers.lock();
00516   while ( ! __pending_browsers.empty() ) {
00517     //printf("Creating browser for %s\n", __pending_browsers.front().c_str());
00518     create_browser(__pending_browsers.front().c_str());
00519     __pending_browsers.pop();
00520   }
00521   __pending_browsers.unlock();
00522 }
00523 
00524 
00525 void
00526 AvahiThread::remove_pending_browsers()
00527 {
00528   __pending_browser_removes.lock();
00529   while ( ! __pending_browser_removes.empty()) {
00530     std::string &s = __pending_browser_removes.front();
00531     avahi_service_browser_free(__browsers[s]);
00532     __browsers.erase_locked(s);
00533     __pending_browser_removes.pop();
00534   }
00535   __pending_browser_removes.unlock();
00536 }
00537 
00538 
00539 /** Erase all browsers. */
00540 void
00541 AvahiThread::erase_browsers()
00542 {
00543   std::map< std::string, AvahiServiceBrowser * >::iterator i;
00544   for (i = __browsers.begin(); i != __browsers.end(); ++i) {
00545     avahi_service_browser_free((*i).second);
00546   }
00547   __browsers.clear();
00548 }
00549 
00550 
00551 /** Call handler for a removed service.
00552  * @param name name
00553  * @param type type
00554  * @param domain domain
00555  */
00556 void
00557 AvahiThread::call_handler_service_removed( const char *name,
00558                                             const char *type,
00559                                             const char *domain)
00560 {
00561   if ( __handlers.find(type) != __handlers.end() ) {
00562     std::list<ServiceBrowseHandler *>::iterator i;
00563     for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
00564       (*i)->service_removed(name, type, domain);
00565     }
00566   }
00567 }
00568 
00569 
00570 /** Call handler for an added service.
00571  * @param name name
00572  * @param type type
00573  * @param domain domain
00574  * @param host_name host name
00575  * @param address address of host
00576  * @param port port of service
00577  * @þaram txt list of TXT records
00578  * @param flags flags
00579  */
00580 void
00581 AvahiThread::call_handler_service_added( const char *name,
00582                                           const char *type,
00583                                           const char *domain,
00584                                           const char *host_name,
00585                                           const AvahiAddress *address,
00586                                           uint16_t port,
00587                                           std::list<std::string> &txt,
00588                                           AvahiLookupResultFlags flags)
00589 {
00590   struct sockaddr_in *s = NULL;
00591   socklen_t slen;
00592   if ( address->proto == AVAHI_PROTO_INET ) {
00593     slen = sizeof(struct sockaddr_in);
00594     s = (struct sockaddr_in *)malloc(slen);
00595     s->sin_addr.s_addr = address->data.ipv4.address;
00596   } else {
00597     // ignore
00598     return;
00599   }
00600   if ( __handlers.find(type) != __handlers.end() ) {
00601     std::list<ServiceBrowseHandler *>::iterator i;
00602     for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
00603       (*i)->service_added(name, type, domain, host_name,
00604                           (struct sockaddr *)s, slen, port, txt, (int)flags);
00605     }
00606   }
00607   free(s);
00608 }
00609 
00610 
00611 /** Call handler for failure.
00612  * @param name name
00613  * @param type type
00614  * @param domain domain
00615  */
00616 void
00617 AvahiThread::call_handler_failed( const char *name,
00618                                    const char *type,
00619                                    const char *domain)
00620 {
00621   if ( __handlers.find(type) != __handlers.end() ) {
00622     std::list<ServiceBrowseHandler *>::iterator i;
00623     for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
00624       (*i)->browse_failed(name, type, domain);
00625     }
00626   }
00627 }
00628 
00629 
00630 /** Call handler "all for now".
00631  * @param type type
00632  */
00633 void
00634 AvahiThread::call_handler_all_for_now(const char *type)
00635 {
00636   if ( __handlers.find(type) != __handlers.end() ) {
00637     std::list<ServiceBrowseHandler *>::iterator i;
00638     for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
00639       (*i)->all_for_now();
00640     }
00641   }
00642 }
00643 
00644 
00645 /** Call handler "cache exhausted".
00646  * @param type type
00647  */
00648 void
00649 AvahiThread::call_handler_cache_exhausted(const char *type)
00650 {
00651   if ( __handlers.find(type) != __handlers.end() ) {
00652     std::list<ServiceBrowseHandler *>::iterator i;
00653     for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
00654       (*i)->cache_exhausted();
00655     }
00656   }
00657 }
00658 
00659 
00660 /** Callback for Avahi.
00661  * Callback called by Avahi.
00662  * @param b service browser
00663  * @param interface interface index
00664  * @param protocol protocol
00665  * @param event event
00666  * @param name name
00667  * @param type type
00668  * @param domain domain
00669  * @param flags flags
00670  * @param instance pointer to the AvahiThread instance that initiated
00671  * the search
00672  */
00673 void
00674 AvahiThread::browse_callback( AvahiServiceBrowser *b,
00675                               AvahiIfIndex interface,
00676                               AvahiProtocol protocol,
00677                               AvahiBrowserEvent event,
00678                               const char *name,
00679                               const char *type,
00680                               const char *domain,
00681                               AvahiLookupResultFlags flags,
00682                               void *instance)
00683 {
00684   AvahiThread *at = static_cast<AvahiThread *>(instance);
00685 
00686   switch (event) {
00687   case AVAHI_BROWSER_FAILURE:
00688     //printf("(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
00689     return;
00690 
00691   case AVAHI_BROWSER_NEW:
00692     //printf("(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
00693     // We ignore the returned resolver object. In the callback
00694     // function we free it. If the server is terminated before
00695     // the callback function is called the server will free
00696     // the resolver for us.
00697     if (!(avahi_service_resolver_new(at->client, interface, protocol,
00698                                      name, type, domain, protocol, (AvahiLookupFlags)0,
00699                                      AvahiThread::resolve_callback, instance))) {
00700       throw NullPointerException("Could not instantiate resolver");
00701     }
00702     break;
00703 
00704   case AVAHI_BROWSER_REMOVE:
00705     // handler
00706     //printf("(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
00707     at->call_handler_service_removed(name, type, domain);
00708     break;
00709 
00710   case AVAHI_BROWSER_ALL_FOR_NOW:
00711     // handler
00712     //printf("(Browser) ALL_FOR_NOW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
00713     at->call_handler_all_for_now(type);
00714     break;
00715 
00716   case AVAHI_BROWSER_CACHE_EXHAUSTED:
00717     // handler
00718     //printf("(Browser) CACHE_EXHAUSTED: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
00719     at->call_handler_cache_exhausted(type);
00720     break;
00721 
00722   }
00723 }
00724 
00725 
00726 /** Callback for Avahi.
00727  * Callback called by Avahi.
00728  * @param r service resolver
00729  * @param interface interface index
00730  * @param protocol protocol
00731  * @param event event
00732  * @param name name
00733  * @param type type
00734  * @param domain domain
00735  * @param host_name host name
00736  * @param address address
00737  * @param port port
00738  * @param txt TXT records
00739  * @param flags flags
00740  * @param instance pointer to the AvahiThread instance that initiated
00741  * the search
00742  */
00743 void
00744 AvahiThread::resolve_callback( AvahiServiceResolver *r,
00745                                AVAHI_GCC_UNUSED AvahiIfIndex interface,
00746                                AVAHI_GCC_UNUSED AvahiProtocol protocol,
00747                                AvahiResolverEvent event,
00748                                const char *name,
00749                                const char *type,
00750                                const char *domain,
00751                                const char *host_name,
00752                                const AvahiAddress *address,
00753                                 uint16_t port,
00754                                AvahiStringList *txt,
00755                                AvahiLookupResultFlags flags,
00756                                void *instance)
00757 {
00758   AvahiThread *at = static_cast<AvahiThread *>(instance);
00759 
00760   switch (event) {
00761   case AVAHI_RESOLVER_FAILURE:
00762     // handler failure
00763     at->call_handler_failed(name, type, domain);
00764     break;
00765 
00766   case AVAHI_RESOLVER_FOUND:
00767     // handler add
00768     {
00769       std::list< std::string > txts;
00770       AvahiStringList *l = txt;
00771 
00772       txts.clear();
00773       while ( l ) {
00774         txts.push_back((char *)avahi_string_list_get_text(l));
00775         l = avahi_string_list_get_next( l );
00776       }
00777 
00778       at->call_handler_service_added(name, type, domain, host_name, address, port, txts, flags);
00779     }
00780     break;
00781   }
00782 
00783   avahi_service_resolver_free(r);
00784 }
00785 
00786 
00787 /* **********************************************************************************
00788  * Avahi resolver methods
00789  * **********************************************************************************/
00790 
00791 
00792 /** Order name resolution.
00793  * This initiates resolution of a name. The method immediately returns and will not
00794  * wait for the result.
00795  * @param name name to resolve.
00796  * @param handler handler to call for the result
00797  */
00798 void
00799 AvahiThread::resolve_name(const char *name, AvahiResolverHandler *handler)
00800 {
00801   AvahiResolverCallbackData *data = new AvahiResolverCallbackData(this, handler);
00802 
00803   if ( __pending_hostname_resolves.find(name) == __pending_hostname_resolves.end()) {
00804     __pending_hostname_resolves[name] = data;
00805   }
00806 
00807   wake_poller();
00808 }
00809 
00810 
00811 void
00812 AvahiThread::start_hostname_resolver(const char *name, AvahiResolverCallbackData *data)
00813 {
00814   AvahiHostNameResolver *resolver;
00815   if ( (resolver = avahi_host_name_resolver_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
00816                                                 name, AVAHI_PROTO_INET,
00817                                                 AVAHI_LOOKUP_USE_MULTICAST,
00818                                                 AvahiThread::host_name_resolver_callback,
00819                                                 data) ) == NULL ) {
00820     throw Exception("Cannot create Avahi name resolver");
00821   } else {
00822     __running_hostname_resolvers.push_back(resolver);
00823   }
00824 
00825 }
00826 
00827 
00828 void
00829 AvahiThread::start_hostname_resolvers()
00830 {
00831   for (__phrit = __pending_hostname_resolves.begin(); __phrit != __pending_hostname_resolves.end(); ++__phrit) {
00832     start_hostname_resolver((*__phrit).first.c_str(), (*__phrit).second);
00833   }
00834   __pending_hostname_resolves.clear();
00835 }
00836 
00837 
00838 void
00839 AvahiThread::start_address_resolvers()
00840 {
00841   for (__parit = __pending_address_resolves.begin(); __parit != __pending_address_resolves.end(); ++__parit) {
00842     start_address_resolver((*__parit).first, (*__parit).second);
00843   }
00844   __pending_address_resolves.clear();
00845 }
00846 
00847 
00848 /** Order address resolution.
00849  * This initiates resolution of an address. The method immediately returns and will not
00850  * wait for the result.
00851  * @param addr address to resolve, currently only struct sockaddr_in is supported (IPv4)
00852  * @param addrlen length of addr in bytes
00853  * @param handler handler to call for the result
00854  */
00855 void
00856 AvahiThread::resolve_address(struct sockaddr *addr, socklen_t addrlen,
00857                              AvahiResolverHandler *handler)
00858 {
00859   if ( addrlen != sizeof(struct sockaddr_in) ) {
00860     throw Exception("Only IPv4 is currently supported");
00861   }
00862 
00863   struct sockaddr_in *in_addr = (struct sockaddr_in *)calloc(1, sizeof(struct sockaddr_in));
00864   memcpy(in_addr, addr, sizeof(struct sockaddr_in));
00865   AvahiResolverCallbackData *data = new AvahiResolverCallbackData(this, handler);
00866 
00867   __pending_address_resolves[in_addr] = data;
00868   wake_poller();
00869 }
00870 
00871 
00872 void
00873 AvahiThread::start_address_resolver(struct sockaddr_in *in_addr, AvahiResolverCallbackData *data)
00874 {
00875   AvahiAddress a;
00876   a.proto = AVAHI_PROTO_INET;
00877   a.data.ipv4.address = in_addr->sin_addr.s_addr;
00878 
00879   AvahiAddressResolver *resolver;
00880   if ( (resolver = avahi_address_resolver_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
00881                                               &a, AVAHI_LOOKUP_USE_MULTICAST,
00882                                               AvahiThread::address_resolver_callback,
00883                                               data) ) == NULL ) {
00884     Exception e("Cannot create Avahi address resolver");
00885     e.append("Avahi error: %s", avahi_strerror(avahi_client_errno(client)));
00886     throw e;
00887   } else {
00888     __running_address_resolvers.push_back_locked(resolver);
00889   }
00890 }
00891 
00892 
00893 /** Remove hostname resolver.
00894  * Used internally by callback.
00895  * @param r resolver
00896  */
00897 void
00898 AvahiThread::remove_hostname_resolver(AvahiHostNameResolver *r)
00899 {
00900   __running_hostname_resolvers.remove_locked(r);
00901 }
00902 
00903 
00904 /** Remove address resolver.
00905  * Used internally by callback.
00906  * @param r resolver
00907  */
00908 void
00909 AvahiThread::remove_address_resolver(AvahiAddressResolver *r)
00910 {
00911   __running_address_resolvers.remove_locked(r);
00912 }
00913 
00914 
00915 /** Internal callback.
00916  * Callback for avahi.
00917  */
00918 void
00919 AvahiThread::host_name_resolver_callback(AvahiHostNameResolver *r,
00920                                          AvahiIfIndex interface,
00921                                          AvahiProtocol protocol,
00922                                          AvahiResolverEvent event,
00923                                          const char *name,
00924                                          const AvahiAddress *a,
00925                                          AvahiLookupResultFlags flags,
00926                                          void *userdata)
00927 {
00928   AvahiResolverCallbackData *cd = static_cast<AvahiResolverCallbackData *>(userdata);
00929 
00930   cd->first->remove_hostname_resolver(r);
00931   avahi_host_name_resolver_free(r);
00932 
00933   switch (event) {
00934   case AVAHI_RESOLVER_FOUND:
00935     {
00936       struct sockaddr_in *res = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
00937       res->sin_family = (unsigned short)avahi_proto_to_af(protocol);
00938       res->sin_addr.s_addr = a->data.ipv4.address;
00939       
00940       cd->second->resolved_name(strdup(name), (struct sockaddr *)res, sizeof(struct sockaddr_in));
00941     }
00942     break;
00943     
00944   case AVAHI_RESOLVER_FAILURE:
00945   default:
00946     cd->second->name_resolution_failed(strdup(name));
00947     break;
00948   }
00949 
00950   delete cd;
00951 }
00952 
00953 
00954 /** Internal callback.
00955  * Callback for avahi.
00956  */
00957 void
00958 AvahiThread::address_resolver_callback(AvahiAddressResolver *r,
00959                                          AvahiIfIndex interface,
00960                                          AvahiProtocol protocol,
00961                                          AvahiResolverEvent event,
00962                                          const AvahiAddress *a,
00963                                          const char *name,
00964                                          AvahiLookupResultFlags flags,
00965                                          void *userdata)
00966 {
00967   AvahiResolverCallbackData *cd = static_cast<AvahiResolverCallbackData *>(userdata);
00968 
00969   cd->first->remove_address_resolver(r);
00970   avahi_address_resolver_free(r);
00971 
00972   struct sockaddr_in *res = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
00973   res->sin_family = (unsigned short)avahi_proto_to_af(protocol);
00974   res->sin_addr.s_addr = a->data.ipv4.address;
00975 
00976    switch (event) {
00977   case AVAHI_RESOLVER_FOUND:
00978     cd->second->resolved_address((struct sockaddr_in *)res, sizeof(struct sockaddr_in),
00979                                  strdup(name));
00980     break;
00981   case AVAHI_RESOLVER_FAILURE:
00982   default:
00983     cd->second->address_resolution_failed((struct sockaddr_in *)res,
00984                                           sizeof(struct sockaddr_in));
00985     break;
00986   }
00987 
00988   delete cd;
00989 }
00990 
00991 
00992 /** Unlocks init lock.
00993  * Only to be called by client_callback().
00994  */
00995 void
00996 AvahiThread::init_done()
00997 {
00998   wake_poller();
00999   init_wc->wake_all();
01000 }
01001 
01002 
01003 /** Waits for the AvahiThread to be initialized.
01004  * You can use this if you want to wait until the thread has been
01005  * fully initialized and may be used. Since the happens in this thread
01006  * it is in general not immediately ready after start().
01007  * This will block the calling thread until the AvahiThread has
01008  * been initialized. This is done by waiting for a release of an
01009  * initialization mutex.
01010  */
01011 void
01012 AvahiThread::wait_initialized()
01013 {
01014   init_wc->wait();
01015 }
01016 
01017 } // end namespace fawkes