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


zeroconf_avahi
Author(s): Daniel Stonier
autogenerated on Fri Aug 28 2015 13:44:14