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


zeroconf_avahi
Author(s): Daniel Stonier
autogenerated on Mon Oct 6 2014 09:12:37