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