zeroconf.cpp
Go to the documentation of this file.
00001 
00012 /*****************************************************************************
00013  ** Includes
00014  *****************************************************************************/
00015 
00016 #include <cstdio>
00017 #include <ctime>
00018 #include <avahi-common/alternative.h> // avahi_alternative_service_name
00019 #include <avahi-common/timeval.h>
00020 #include <avahi-common/thread-watch.h>
00021 #include <boost/thread/thread.hpp>  // sleep
00022 #include <zeroconf_msgs/Protocols.h>
00023 #include "../../include/zeroconf_avahi/zeroconf.hpp"
00024 
00025 /*****************************************************************************
00026  ** Namespaces
00027  *****************************************************************************/
00028 
00029 namespace zeroconf_avahi
00030 {
00031 
00032 /*****************************************************************************
00033  ** Implementation
00034  *****************************************************************************/
00035 
00036 Zeroconf::Zeroconf() :
00037     invalid_object(false), threaded_poll(NULL), client(NULL), interface(AVAHI_IF_UNSPEC), permitted_protocols(
00038         AVAHI_PROTO_INET) // AVAHI_PROTO_UNSPEC, AVAHI_PROTO_INET, AVAHI_PROTO_INET6
00039 {
00040   int error;
00041 
00042   /* Allocate main loop object */
00043   if (!(threaded_poll = avahi_threaded_poll_new()))
00044   {
00045     ROS_ERROR("Zeroconf: failed to create an avahi threaded  poll.");
00046     invalid_object = true;
00047     return;
00048   }
00049   /* Allocate a new client */
00050   client = avahi_client_new(avahi_threaded_poll_get(threaded_poll), static_cast<AvahiClientFlags>(0),
00051                             Zeroconf::client_callback, this, &error);
00052 
00053   /* Check whether creating the client object succeeded */
00054   if (!client)
00055   {
00056     ROS_ERROR("Zeroconf: failed to create an avahi client.");
00057     invalid_object = true;
00058     return;
00059   }
00060 
00061   /* Don't start spinning until client_callback detects AVAHI_CLIENT_S_RUNNING. This may mean the user has to wait a
00062    * little bit before adding a service.
00063    */
00064 }
00065 
00066 Zeroconf::~Zeroconf()
00067 {
00068   /*********************
00069    ** Listeners
00070    **********************/
00071   {
00072     boost::mutex::scoped_lock lock(service_mutex);
00073     for (discovery_bimap::left_const_iterator iter = discovery_service_types.left.begin();
00074         iter != discovery_service_types.left.end(); ++iter)
00075     {
00076       avahi_service_browser_free(iter->first);
00077     }
00078     discovered_services.clear(); // avahi_service_resolver_free will get called on each here
00079     discovery_service_types.clear();
00080   }
00081   if (threaded_poll)
00082   {
00083     avahi_threaded_poll_stop(threaded_poll);
00084   }
00085   /*********************
00086    ** Publishers
00087    **********************/
00088   // free'ing entry groups shouldn't be necessary (don't know exactly, but haven't seen it in any of the official
00089   // code, so its probably done when calling avahi_client_free().
00090   /*********************
00091    ** General
00092    **********************/
00093   if (client)
00094   {
00095     avahi_client_free(client);
00096   }
00097   if (threaded_poll)
00098   {
00099     avahi_threaded_poll_free(threaded_poll);
00100   }
00101 }
00102 
00103 void Zeroconf::spin()
00104 {
00105   if (!invalid_object)
00106   {
00107     ROS_DEBUG("Zeroconf: starting the threaded poll.");
00108     // does this have a return value I should check?
00109     avahi_threaded_poll_start(threaded_poll);
00110   }
00111 }
00112 
00113 bool Zeroconf::add_listener(std::string &service_type)
00114 {
00115   /* Check if we're already listening for it. */
00116   {
00117     boost::mutex::scoped_lock lock(service_mutex);
00118     discovery_bimap::right_iterator browser_iter = discovery_service_types.right.find(service_type);
00119     if (browser_iter != discovery_service_types.right.end())
00120     {
00121       ROS_WARN_STREAM("Zeroconf : already listening for services of type '" << service_type << "'");
00122       return false;
00123     }
00124   }
00125 
00126   /* Create the service browser */
00127   AvahiServiceBrowser *service_browser = NULL;
00128   avahi_threaded_poll_lock(threaded_poll);
00129   if (!(service_browser = avahi_service_browser_new(client, interface, permitted_protocols, service_type.c_str(), NULL,
00130                                                     static_cast<AvahiLookupFlags>(0), Zeroconf::discovery_callback,
00131                                                     this)))
00132   {
00133     ROS_ERROR_STREAM(
00134         "Zeroconf: failed to create an avahi service browser: " << avahi_strerror(avahi_client_errno(client)));
00135     return false;
00136   }
00137   avahi_threaded_poll_unlock(threaded_poll);
00138   /* Update the internal data */
00139   {
00140     boost::mutex::scoped_lock lock(service_mutex);
00141     discovery_service_types.insert(discovery_bimap::value_type(service_browser, service_type));
00142   }
00143   ROS_INFO_STREAM("Zeroconf: added a listener [" << service_type << "]");
00144   return true;
00145 }
00146 
00147 bool Zeroconf::remove_listener(const std::string &service_type)
00148 {
00149   AvahiServiceBrowser *service_browser = NULL;
00150 
00151   /* Check if we're already listening for it. */
00152   {
00153     boost::mutex::scoped_lock lock(service_mutex);
00154     discovery_bimap::right_iterator browser_iter = discovery_service_types.right.find(service_type);
00155     if (browser_iter == discovery_service_types.right.end())
00156     {
00157       ROS_WARN_STREAM("Zeroconf : not currently listening for '" << service_type << "', aborting listener removal.");
00158       return false;
00159     }
00160     else
00161     {
00162       ROS_INFO_STREAM("Zeroconf: removing a listener [" << service_type << "]");
00163       service_browser = browser_iter->second;
00164       // delete internal browser pointers and storage
00165       discovery_service_types.right.erase(browser_iter);
00166       // delete internally resolved list
00167       discovered_service_set::iterator iter = discovered_services.begin();
00168       while (iter != discovered_services.end())
00169       {
00170         if ((*iter)->service.type == service_type)
00171         {
00172           ROS_INFO_STREAM("Zeroconf: erasing element " << *iter);
00173           discovered_services.erase(iter++);
00174         }
00175         else
00176         {
00177           ROS_INFO_STREAM("Zeroconf: not erasing element " << *iter);
00178           ++iter;
00179         }
00180       }
00181     }
00182   }
00183   /* Remove the avahi browser */
00184   if (service_browser)
00185   {
00186     avahi_threaded_poll_lock(threaded_poll);
00187     avahi_service_browser_free(service_browser);
00188     avahi_threaded_poll_unlock(threaded_poll);
00189   }
00190   return true;
00191 }
00205 bool Zeroconf::add_service(PublishedService &service)
00206 {
00207 
00208   avahi_threaded_poll_lock(threaded_poll);
00209   bool result = add_service_non_threaded(service);
00210   avahi_threaded_poll_unlock(threaded_poll);
00211   return result;
00212 }
00213 
00222 bool Zeroconf::add_service_non_threaded(PublishedService &service)
00223 {
00224   /*
00225    * We may still be initialising (from constructor)...check that we're up and running."
00226    */
00227   if (avahi_client_get_state(client) != AVAHI_CLIENT_S_RUNNING)
00228   {
00229     ROS_ERROR("Zeroconf: avahi_client_state is not running (probably still registering with avahi daemon)");
00230     return false;
00231   }
00232 
00233   /*
00234    * Check we're not already publishing this exact service
00235    */
00236   {
00237     boost::mutex::scoped_lock lock(service_mutex);
00238     service_bimap::right_const_iterator iter;
00239     iter = committed_services.right.find(service);
00240     if (iter != committed_services.right.end())
00241     {
00242       ROS_WARN_STREAM(
00243           "Zeroconf: this node is currently already committing this service [" << service.name << "][" << service.type << "][" << service.port << "]");
00244       return true;
00245     }
00246     iter = established_services.right.find(service);
00247     if (iter != established_services.right.end())
00248     {
00249       ROS_WARN_STREAM(
00250           "Zeroconf: this node has already established this service [" << service.name << "][" << service.type << "][" << service.port << "]");
00251       return true;
00252     }
00253   }
00254 
00255   ROS_DEBUG_STREAM("Zeroconf: adding a new service [" << service.name << "][" << service.type << "]");
00256   /* If this is the first time we're called, let's create a new
00257    * entry group if necessary */
00258   AvahiEntryGroup* group = NULL;
00259   if (!(group = avahi_entry_group_new(client, entry_group_callback, this)))
00260   {
00261     ROS_ERROR_STREAM("Zeroconf: avahi_entry_group_new() failed: " << avahi_strerror(avahi_client_errno(client)));
00262     fail();
00263     return false;
00264   }
00265 
00266   int ret = avahi_entry_group_add_service(group, interface, permitted_protocols, static_cast<AvahiPublishFlags>(0), // AVAHI_PUBLISH_USE_MULTICAST - we don't seem to need this, but others said they have needed it.
00267                                           service.name.c_str(), service.type.c_str(), service.domain.c_str(), NULL, // automatically sets a hostname for us
00268                                           static_cast<uint16_t>(service.port), NULL); // txt description
00269   if (ret < 0)
00270   {
00271     avahi_entry_group_free(group);
00272     if (ret == AVAHI_ERR_COLLISION)
00273     {
00274       std::string old_name = service.name;
00275       PublishedService new_service = service;
00276       service.name = avahi_alternative_service_name(service.name.c_str());
00277       ROS_WARN_STREAM(
00278           "Zeroconf: local service name collision, renaming [" << service.name << "][" << new_service.name << "]");
00279       return add_service_non_threaded(service);
00280     }
00281     else
00282     {
00283       ROS_ERROR_STREAM(
00284           "Zeroconf: failed to add service [" << service.type.c_str() << "][" << avahi_strerror(ret) << "]");
00285       fail();
00286       return false;
00287     }
00288   }
00289   /* Commit the entry group */
00290   // add to the map first, just so that the callback doesn't fire before we can find it in the map
00291   {
00292     boost::mutex::scoped_lock lock(service_mutex);
00293     committed_services.insert(service_bimap::value_type(group, service)); // should check the return value...
00294   }
00295   if ((ret = avahi_entry_group_commit(group)) < 0)
00296   {
00297     ROS_ERROR_STREAM("Zeroconf: failed to commit entry group [" << avahi_strerror(ret) << "]");
00298     avahi_entry_group_free(group);
00299     {
00300       boost::mutex::scoped_lock lock(service_mutex);
00301       committed_services.left.erase(group);
00302     }
00303     fail();
00304     return false;
00305   }
00306   ROS_DEBUG("Zeroconf: service committed, waiting for callback...");
00307   return true;
00308 }
00309 bool Zeroconf::remove_service(const PublishedService &service)
00310 {
00311 
00312   AvahiEntryGroup *group = NULL;
00313   bool erased = false;
00314   {
00315     boost::mutex::scoped_lock lock(service_mutex);
00316     service_bimap::right_const_iterator iter = established_services.right.find(service);
00317     if (iter != established_services.right.end())
00318     {
00319       group = iter->second;
00320       established_services.right.erase(service);
00321       erased = true;
00322       ROS_INFO_STREAM("Zeroconf: removing service [" << service.name << "][" << service.type << "]");
00323     }
00324     else
00325     {
00326       ROS_WARN_STREAM(
00327           "Zeroconf: couldn't remove not currently advertised service [" << service.name << "][" << service.type << "]");
00328     }
00329   }
00330   if (group)
00331   {
00332     avahi_threaded_poll_lock(threaded_poll);
00333     avahi_entry_group_reset(group);
00334     avahi_entry_group_free(group);
00335     avahi_threaded_poll_unlock(threaded_poll);
00336   }
00337   return erased;
00338 }
00347 void Zeroconf::list_discovered_services(const std::string &service_type,
00348                                         std::vector<zeroconf_msgs::DiscoveredService> &list)
00349 {
00350   list.clear();
00351   boost::mutex::scoped_lock lock(service_mutex);
00352   if (service_type == "")
00353   {
00354     for (discovered_service_set::iterator iter = discovered_services.begin(); iter != discovered_services.end(); ++iter)
00355     {
00356       // ignore services that aren't currently resolved
00357       if (((*iter)->service.ipv4_addresses.size() != 0) || ((*iter)->service.ipv6_addresses.size() != 0))
00358       {
00359         list.push_back((*iter)->service);
00360       }
00361     }
00362   }
00363   else
00364   {
00365     for (discovered_service_set::iterator iter = discovered_services.begin(); iter != discovered_services.end(); ++iter)
00366     {
00367       if ((*iter)->service.type == service_type)
00368       {
00369         // ignore services that aren't currently resolved
00370         if (((*iter)->service.ipv4_addresses.size() != 0) || ((*iter)->service.ipv6_addresses.size() != 0))
00371         {
00372           list.push_back((*iter)->service);
00373         }
00374       }
00375     }
00376   }
00377 }
00378 void Zeroconf::list_published_services(const std::string &service_type,
00379                                        std::vector<zeroconf_msgs::PublishedService> &list)
00380 {
00381   list.clear();
00382   boost::mutex::scoped_lock lock(service_mutex);
00383   if (service_type == "")
00384   {
00385     for (service_bimap::left_const_iterator iter = established_services.left.begin();
00386         iter != established_services.left.end(); ++iter)
00387     {
00388       list.push_back(iter->second);
00389     }
00390   }
00391   else
00392   {
00393     for (service_bimap::left_const_iterator iter = established_services.left.begin();
00394         iter != established_services.left.end(); ++iter)
00395     {
00396       if (iter->second.type == service_type)
00397       {
00398         list.push_back(iter->second);
00399       }
00400     }
00401   }
00402 }
00403 
00404 /*****************************************************************************
00405  ** Utilities
00406  *****************************************************************************/
00407 
00408 int Zeroconf::ros_to_avahi_protocol(const int &protocol)
00409 {
00410   switch (protocol)
00411   {
00412     case (zeroconf_msgs::Protocols::UNSPECIFIED):
00413     {
00414       return AVAHI_PROTO_UNSPEC;
00415     }
00416     case (zeroconf_msgs::Protocols::IPV4):
00417     {
00418       return AVAHI_PROTO_INET;
00419     }
00420     case (zeroconf_msgs::Protocols::IPV6):
00421     {
00422       return AVAHI_PROTO_INET6;
00423     }
00424     default:
00425       return AVAHI_PROTO_UNSPEC;
00426   }
00427 }
00428 
00429 std::string Zeroconf::ros_to_txt_protocol(const int &protocol)
00430 {
00431   switch (protocol)
00432   {
00433     case (zeroconf_msgs::Protocols::UNSPECIFIED):
00434     {
00435       return "unspecified";
00436     }
00437     case (zeroconf_msgs::Protocols::IPV4):
00438     {
00439       return "ipv4";
00440     }
00441     case (zeroconf_msgs::Protocols::IPV6):
00442     {
00443       return "ipv6";
00444     }
00445     default:
00446       return "unspecified";
00447   }
00448 }
00449 
00450 int Zeroconf::avahi_to_ros_protocol(const int &protocol)
00451 {
00452   switch (protocol)
00453   {
00454     case (AVAHI_PROTO_UNSPEC):
00455     {
00456       return zeroconf_msgs::Protocols::UNSPECIFIED;
00457     }
00458     case (AVAHI_PROTO_INET):
00459     {
00460       return zeroconf_msgs::Protocols::IPV4;
00461     }
00462     case (AVAHI_PROTO_INET6):
00463     {
00464       return zeroconf_msgs::Protocols::IPV6;
00465     }
00466     default:
00467       return zeroconf_msgs::Protocols::UNSPECIFIED;
00468   }
00469 }
00470 
00471 std::string Zeroconf::avahi_to_txt_protocol(const int &protocol)
00472 {
00473   switch (protocol)
00474   {
00475     case (AVAHI_PROTO_UNSPEC):
00476     {
00477       return "unspecified";
00478     }
00479     case (AVAHI_PROTO_INET):
00480     {
00481       return "ipv4";
00482     }
00483     case (AVAHI_PROTO_INET6):
00484     {
00485       return "ipv6";
00486     }
00487     default:
00488       return "unspecified";
00489   }
00490 }
00491 
00500 Zeroconf::discovered_service_set::iterator Zeroconf::find_discovered_service(zeroconf_msgs::DiscoveredService &service)
00501 {
00502   discovered_service_set::iterator iter = discovered_services.begin();
00503   while (iter != discovered_services.end())
00504   {
00505     if (((*iter)->service.name == service.name) && ((*iter)->service.type == service.type)
00506         && ((*iter)->service.domain == service.domain))
00507     {
00508       return iter;
00509     }
00510     else
00511     {
00512       ++iter;
00513     }
00514   }
00515   return discovered_services.end();
00516 }
00517 
00518 /*****************************************************************************
00519  ** Discovery Callbacks
00520  *****************************************************************************/
00535 void Zeroconf::discovery_callback(AvahiServiceBrowser *browser, AvahiIfIndex interface, AvahiProtocol protocol,
00536                                   AvahiBrowserEvent event, const char *name, const char *type, const char *domain,
00537                                   AvahiLookupResultFlags flags, void* userdata)
00538 {
00539 
00540   Zeroconf *zeroconf = reinterpret_cast<Zeroconf*>(userdata);
00541   assert(browser);
00542 
00543   switch (event)
00544   {
00545     case AVAHI_BROWSER_FAILURE:
00546       ROS_ERROR_STREAM(
00547           "Zeroconf: browser failure [" << avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(browser))));
00548       avahi_threaded_poll_quit(zeroconf->threaded_poll);
00549       return;
00550 
00551     case AVAHI_BROWSER_NEW:
00552     {
00553       zeroconf_msgs::DiscoveredService service;
00554       service.name = name;
00555       service.type = type;
00556       service.domain = domain;
00557 
00558       AvahiServiceResolver* resolver = avahi_service_resolver_new(zeroconf->client, interface, protocol, name, type,
00559                                                                   domain, zeroconf->permitted_protocols,
00560                                                                   static_cast<AvahiLookupFlags>(0),
00561                                                                   Zeroconf::resolve_callback, zeroconf);
00562       if (!resolver)
00563       {
00564         ROS_ERROR_STREAM(
00565             "Zeroconf: avahi resolver failure (avahi daemon problem) [" << name << "][" << avahi_strerror(avahi_client_errno(zeroconf->client)) << "][" << interface << "][" << zeroconf->avahi_to_txt_protocol(protocol) << "]");
00566         break;
00567       }
00568       {
00569         boost::mutex::scoped_lock lock(zeroconf->service_mutex);
00570         boost::shared_ptr<DiscoveredAvahiService> new_service(
00571             new DiscoveredAvahiService(service, resolver, interface, zeroconf->avahi_to_ros_protocol(protocol)));
00572         if ((zeroconf->discovered_services.insert(new_service)).second)
00573         {
00574           ROS_INFO_STREAM(
00575               "Zeroconf: discovered new service [" << name << "][" << type << "][" << domain << "][" << interface << "][" << zeroconf->avahi_to_txt_protocol(protocol) << "]");
00576           // we signal in the resolver, not here...though this might be a bad design
00577           // decision if the connection is up and down alot.
00578           // if ( zeroconf->new_connection_signal ) {
00579           //     zeroconf->new_connection_signal(service);
00580           // }
00581         }
00582         else
00583         {
00584           ROS_ERROR_STREAM(
00585               "Tried to insert a new service on top of an old stored one - probably a bug in zeroconf_avahi!");
00586         }
00587       }
00588       break;
00589     }
00590 
00591     case AVAHI_BROWSER_REMOVE:
00592     {
00593       zeroconf_msgs::DiscoveredService service;
00594       service.name = name;
00595       service.type = type;
00596       service.domain = domain;
00597       {
00598         boost::mutex::scoped_lock lock(zeroconf->service_mutex);
00599         discovered_service_set::iterator iter = zeroconf->find_discovered_service(service);
00600         if (iter != zeroconf->discovered_services.end())
00601         {
00602           /*********************
00603            ** Update
00604            **********************/
00605           zeroconf->discovered_services.erase(iter);
00606           /*********************
00607            ** Logging
00608            **********************/
00609           ROS_INFO_STREAM(
00610               "Zeroconf: service was removed [" << service.name << "][" << service.type << "][" << service.domain << "][" << interface << "][" << zeroconf->ros_to_txt_protocol(zeroconf->avahi_to_ros_protocol(protocol)) << "]");
00611           /*********************
00612            ** Signal
00613            **********************/
00614           // we signal here...though this might get muddled if the connection is up/down alot
00615           // I haven't road tested this much yet at all.
00616           if (zeroconf->lost_connection_signal)
00617           {
00618             zeroconf->lost_connection_signal(service);
00619           }
00620         }
00621         else
00622         {
00623           ROS_ERROR_STREAM(
00624               "Zeroconf: attempted to remove a non-discovered service (probably a bug in zeroconf_avahi!)");
00625         }
00626       }
00627       break;
00628     }
00629     case AVAHI_BROWSER_ALL_FOR_NOW:
00630     case AVAHI_BROWSER_CACHE_EXHAUSTED:
00631     {
00632       if (event == AVAHI_BROWSER_CACHE_EXHAUSTED)
00633       {
00634         ROS_DEBUG("Zeroconf: browser event occured [cache exhausted]");
00635       }
00636       else
00637       {
00638         ROS_DEBUG("Zeroconf: browser event occured [all for now]");
00639       }
00640       break;
00641     }
00642   }
00643 }
00675 void Zeroconf::resolve_callback(AvahiServiceResolver *resolver, AvahiIfIndex interface, AvahiProtocol protocol,
00676                                 AvahiResolverEvent event, const char *name, const char *type, const char *domain,
00677                                 const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt,
00678                                 AvahiLookupResultFlags flags, void* userdata)
00679 {
00680 
00681   Zeroconf *zeroconf = reinterpret_cast<Zeroconf*>(userdata);
00682   assert(resolver);
00683 
00684   switch (event)
00685   {
00686     case AVAHI_RESOLVER_FAILURE:
00687     {
00688       zeroconf_msgs::DiscoveredService service;
00689       service.name = name;
00690       service.type = type;
00691       service.domain = domain;
00692       {
00693         boost::mutex::scoped_lock lock(zeroconf->service_mutex);
00694         discovered_service_set::iterator iter = zeroconf->find_discovered_service(service);
00695         if (iter != zeroconf->discovered_services.end())
00696         {
00697           /*********************
00698            ** Logging
00699            **********************/
00700           if ((*iter)->service.ipv4_addresses.size() != 0)
00701           {
00702             ROS_WARN_STREAM(
00703                 "Zeroconf: timed out resolving service [" << name << "][" << type << "][" << domain << "][" << interface << "][" << zeroconf->avahi_to_txt_protocol(protocol) << "][" << (*iter)->service.ipv4_addresses[0] << ":" << (*iter)->service.port << "]");
00704           }
00705           else if ((*iter)->service.ipv6_addresses.size() != 0)
00706           {
00707             ROS_WARN_STREAM(
00708                 "Zeroconf: timed out resolving service [" << name << "][" << type << "][" << domain << "][" << interface << "][" << zeroconf->avahi_to_txt_protocol(protocol) << "][" << (*iter)->service.ipv6_addresses[0] << ":" << (*iter)->service.port << "]");
00709           }
00710           else
00711           {
00712             ROS_WARN_STREAM(
00713                 "Zeroconf: timed out resolving service [" << name << "][" << type << "][" << domain << "][" << interface << "][" << zeroconf->avahi_to_txt_protocol(protocol) << "]");
00714           }
00715           /*********************
00716            ** Update
00717            **********************/
00718           (*iter)->service.ipv4_addresses.clear();
00719           (*iter)->service.ipv6_addresses.clear();
00720           (*iter)->service.hostname = "";
00721           (*iter)->service.port = 0;
00722           // could reset all the other stuff too, but the above is important.
00723           /*********************
00724            ** Signals
00725            **********************/
00726           if (zeroconf->lost_connection_signal)
00727           {
00728             zeroconf->lost_connection_signal(service);
00729           }
00730         }
00731         else
00732         {
00733           ROS_ERROR_STREAM(
00734               "Zeroconf: timed out resolving a service that was not saved, probably a zeroconf_avahi bug!");
00735         }
00736       }
00737       break;
00738     }
00739     case AVAHI_RESOLVER_FOUND:
00740     {
00741       char a[AVAHI_ADDRESS_STR_MAX], *t;
00742 
00743       // workaround for avahi bug 1) above
00744       boost::this_thread::sleep(boost::posix_time::milliseconds(500));
00745 
00746       t = avahi_string_list_to_string(txt);
00747       avahi_address_snprint(a, sizeof(a), address);
00748 
00749       zeroconf_msgs::DiscoveredService service;
00750       service.name = name;
00751       service.type = type;
00752       service.domain = domain;
00753       bool error = false;
00754       switch (zeroconf->avahi_to_ros_protocol(protocol))
00755       {
00756         case (zeroconf_msgs::Protocols::IPV4):
00757         {
00758           service.ipv4_addresses.push_back(a);
00759           // workaround for avahi bug 2) above
00760           size_t found = std::string(a).find(":");
00761           if (found != std::string::npos)
00762           {
00763             ROS_WARN_STREAM(
00764                 "Zeroconf: avahi is behaving badly (bug) - set an ipv6 address for an ipv4 service, recovering...");
00765             avahi_free(t);
00766             error = true;
00767             break;
00768           }
00769           break;
00770         }
00771         case (zeroconf_msgs::Protocols::IPV6):
00772         {
00773           service.ipv6_addresses.push_back(a);
00774           break;
00775         }
00776         default:
00777           break; // should never get here
00778       }
00779       if (error)
00780       {
00781         break;
00782       }
00783 
00784       service.hostname = host_name;
00785       service.port = port;
00786       service.description = t;
00787       service.cookie = avahi_string_list_get_service_cookie(txt);
00788       service.is_local = ((flags & AVAHI_LOOKUP_RESULT_LOCAL) == 0 ? false : true);
00789       service.our_own = ((flags & AVAHI_LOOKUP_RESULT_OUR_OWN) == 0 ? false : true);
00790       service.wide_area = ((flags & AVAHI_LOOKUP_RESULT_WIDE_AREA) == 0 ? false : true);
00791       service.multicast = ((flags & AVAHI_LOOKUP_RESULT_MULTICAST) == 0 ? false : true);
00792       service.cached = ((flags & AVAHI_LOOKUP_RESULT_CACHED) == 0 ? false : true);
00793       {
00794         boost::mutex::scoped_lock lock(zeroconf->service_mutex);
00795         discovered_service_set::iterator iter = zeroconf->find_discovered_service(service);
00796         if (iter != zeroconf->discovered_services.end())
00797         {
00798           /*********************
00799            ** Update service info
00800            **********************/
00801           (*iter)->service = service;
00802           (*iter)->protocol = zeroconf->avahi_to_ros_protocol(protocol);
00803 
00804           /*********************
00805            ** Logging
00806            **********************/
00807           ROS_INFO_STREAM(
00808               "Zeroconf: resolved service [" << name << "][" << type << "][" << domain << "][" << interface << "][" << zeroconf->ros_to_txt_protocol((*iter)->protocol) << "][" << a << ":" << service.port << "]");
00809           ROS_DEBUG_STREAM("Zeroconf: \tname: " << service.name);
00810           ROS_DEBUG_STREAM("Zeroconf: \ttype: " << service.type);
00811           ROS_DEBUG_STREAM("Zeroconf: \tdomain: " << service.domain);
00812           ROS_DEBUG_STREAM("Zeroconf: \tinterface: " << interface);
00813           ROS_DEBUG_STREAM("Zeroconf: \tprotocol: " << zeroconf->ros_to_txt_protocol((*iter)->protocol));
00814           ROS_DEBUG_STREAM("Zeroconf: \thostname: " << service.hostname);
00815           ROS_DEBUG_STREAM("Zeroconf: \taddress: " << a);
00816           ROS_DEBUG_STREAM("Zeroconf: \tport: " << service.port);
00817           ROS_DEBUG_STREAM("Zeroconf: \tdescription: " << service.description);
00818           ROS_DEBUG_STREAM("Zeroconf: \tcookie: " << service.cookie);
00819           ROS_DEBUG_STREAM("Zeroconf: \tis_local: " << (service.is_local ? 1 : 0 ));
00820           ROS_DEBUG_STREAM("Zeroconf: \tour_own: " << (service.our_own ? 1 : 0 ));
00821           ROS_DEBUG_STREAM("Zeroconf: \twide_area: " << (service.wide_area ? 1 : 0 ));
00822           ROS_DEBUG_STREAM("Zeroconf: \tmulticast: " << (service.multicast ? 1 : 0 ));
00823           /*********************
00824            ** Signals
00825            **********************/
00826           if (zeroconf->new_connection_signal)
00827           {
00828             zeroconf->new_connection_signal(service);
00829           }
00830         }
00831         else
00832         {
00833           ROS_ERROR_STREAM(
00834               "Zeroconf: timed out resolving a service that was not saved, probably a zeroconf_avahi bug!");
00835         }
00836       }
00837 
00838       avahi_free(t);
00839       break;
00840     }
00841   }
00842 }
00843 
00844 /*****************************************************************************
00845  ** Publisher Callback
00846  *****************************************************************************/
00860 void Zeroconf::entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata)
00861 {
00862 
00863   Zeroconf *zeroconf = static_cast<Zeroconf*>(userdata);
00864   switch (state)
00865   {
00866     case AVAHI_ENTRY_GROUP_ESTABLISHED:
00867     {
00868       PublishedService service;
00869       {
00870         boost::mutex::scoped_lock lock(zeroconf->service_mutex);
00871         service_bimap::left_const_iterator left = zeroconf->committed_services.left.find(g);
00872         if (left != zeroconf->committed_services.left.end())
00873         {
00874           service = left->second;
00875         }
00876         else
00877         {
00878           ROS_ERROR(
00879               "Zeroconf : should never reach here, please report a bug in zeroconf_avahi's entry_group_callback.");
00880           return;
00881         }
00882         zeroconf->established_services.insert(service_bimap::value_type(g, service));
00883         zeroconf->committed_services.left.erase(g);
00884       }
00885       ROS_INFO_STREAM(
00886           "Zeroconf: service successfully established [" << service.name << "][" << service.type << "][" << service.port << "]");
00887       break;
00888     }
00889     case AVAHI_ENTRY_GROUP_COLLISION:
00890     {
00891       /* A service name collision with a 'remote' service happened. Let's pick a new name */
00892       PublishedService service;
00893       {
00894         boost::mutex::scoped_lock lock(zeroconf->service_mutex);
00895         service_bimap::left_const_iterator left = zeroconf->committed_services.left.find(g);
00896         if (left != zeroconf->committed_services.left.end())
00897         {
00898           service = left->second;
00899         }
00900         else
00901         {
00902           ROS_ERROR(
00903               "Zeroconf : should never reach here, please report a bug in zeroconf_avahi's entry_group_callback.");
00904           return;
00905         }
00906         zeroconf->committed_services.left.erase(g);
00907       }
00908       std::string alternative_name = avahi_alternative_service_name(service.name.c_str());
00909       ROS_INFO_STREAM(
00910           "Zeroconf: service name collision, renaming service [" << service.name << "]" << "][" << alternative_name << "]");
00911       service.name = alternative_name;
00912       avahi_entry_group_free(g);
00913       /* And recreate the services - already in the poll thread, so don' tneed to lock. */
00914       zeroconf->add_service_non_threaded(service);
00915       break;
00916     }
00917 
00918     case AVAHI_ENTRY_GROUP_FAILURE:
00919       /* Some kind of failure happened while we were registering our services */
00920       // drop our committed_service here.
00921       ROS_DEBUG_STREAM(
00922           "Zeroconf: group state changed, system failure when trying to register service [" << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))) << "]");
00923       avahi_entry_group_free(g);
00924       zeroconf->fail();
00925       break;
00926 
00927     case AVAHI_ENTRY_GROUP_UNCOMMITED:
00928     {
00929       ROS_DEBUG_STREAM("Zeroconf: group state changed, service uncommitted");
00930       // This is just mid-process, no need to handle committed_services
00931       break;
00932     }
00933     case AVAHI_ENTRY_GROUP_REGISTERING:
00934     {
00935       // This is just mid-process, no need to handle committed_services
00936       PublishedService service;
00937       {
00938         boost::mutex::scoped_lock lock(zeroconf->service_mutex);
00939         service_bimap::left_const_iterator left = zeroconf->committed_services.left.find(g);
00940         if (left != zeroconf->committed_services.left.end())
00941         {
00942           service = left->second;
00943         }
00944         else
00945         {
00946           ROS_ERROR(
00947               "Zeroconf : should never reach here, please report a bug in zeroconf_avahi's entry_group_callback.");
00948           return;
00949         }
00950       }
00951       ROS_DEBUG_STREAM(
00952           "Zeroconf: group state changed, service registering [" << service.name << "][" << service.type << "]");
00953       break;
00954     }
00955     default:
00956     {
00957       ROS_DEBUG_STREAM("Zeroconf: group state changed, ended in an unknown state [" << state << "]");
00958       break;
00959     }
00960   }
00961 }
00962 
00963 /*****************************************************************************
00964  ** Daemon Callbacks
00965  *****************************************************************************/
00975 void Zeroconf::client_callback(AvahiClient *c, AvahiClientState state, void *userdata)
00976 {
00977 
00978   Zeroconf *zeroconf = static_cast<Zeroconf*>(userdata);
00979   assert(c);
00980 
00981   /* Called whenever the client or server state changes */
00982 
00983   switch (state)
00984   {
00985     case AVAHI_CLIENT_S_RUNNING:
00986     {
00987       /* The server has startup successfully and registered its host
00988        * name on the network, so it's time to fire up */
00989       ROS_DEBUG("Zeroconf: avahi client up and running.");
00990       zeroconf->spin();
00991       break;
00992     }
00993     case AVAHI_CLIENT_FAILURE:
00994     {
00995       ROS_ERROR_STREAM("Zeroconf: avahi client failure [" << avahi_strerror(avahi_client_errno(c)) << "]");
00996       zeroconf->fail();
00997 
00998       break;
00999     }
01000     case AVAHI_CLIENT_S_COLLISION:
01001     {
01002       ROS_DEBUG("Zeroconf: avahi client collision.");
01003       /* Let's drop our registered services. When the server is back
01004        * in AVAHI_SERVER_RUNNING state we will register them
01005        * again with the new host name. */
01006       break;
01007     }
01008     case AVAHI_CLIENT_S_REGISTERING:
01009     {
01010       ROS_DEBUG("Zeroconf: avahi client registering.");
01011 
01012       /* The server records are now being established. This
01013        * might be caused by a host name change. We need to wait
01014        * for our own records to register until the host name is
01015        * properly established. */
01016 
01017       // official example resets the entry group here
01018       // since we have multiple groups, handling should be a bit more
01019       // complicated - ToDo
01020       break;
01021     }
01022     case AVAHI_CLIENT_CONNECTING:
01023     {
01024       ROS_DEBUG("Zeroconf: avahi client registering.");
01025       break;
01026     }
01027   }
01028 }
01029 
01030 } // namespace zeroconf_avahi


zeroconf_avahi
Author(s): Daniel Stonier
autogenerated on Thu Jan 2 2014 12:14:44