00001 
00012 
00013 
00014 
00015 
00016 #include <cstdio>
00017 #include <ctime>
00018 #include <avahi-common/alternative.h> 
00019 #include <avahi-common/timeval.h>
00020 #include <avahi-common/thread-watch.h>
00021 #include <boost/thread/thread.hpp>  
00022 #include <zeroconf_msgs/Protocols.h>
00023 #include "../../include/zeroconf_avahi/zeroconf.hpp"
00024 
00025 
00026 
00027 
00028 
00029 namespace zeroconf_avahi
00030 {
00031 
00032 
00033 
00034 
00035 
00036 Zeroconf::Zeroconf() :
00037     invalid_object(false), threaded_poll(NULL), client(NULL), interface(AVAHI_IF_UNSPEC), permitted_protocols(
00038         AVAHI_PROTO_INET) 
00039 {
00040   int error;
00041 
00042   
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   
00050   client = avahi_client_new(avahi_threaded_poll_get(threaded_poll), static_cast<AvahiClientFlags>(0),
00051                             Zeroconf::client_callback, this, &error);
00052 
00053   
00054   if (!client)
00055   {
00056     ROS_ERROR("Zeroconf: failed to create an avahi client.");
00057     invalid_object = true;
00058     return;
00059   }
00060 
00061   
00062 
00063 
00064 }
00065 
00066 Zeroconf::~Zeroconf()
00067 {
00068   
00069 
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(); 
00079     discovery_service_types.clear();
00080   }
00081   if (threaded_poll)
00082   {
00083     avahi_threaded_poll_stop(threaded_poll);
00084   }
00085   
00086 
00087 
00088   
00089   
00090   
00091 
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     
00109     avahi_threaded_poll_start(threaded_poll);
00110   }
00111 }
00112 
00113 bool Zeroconf::add_listener(std::string &service_type)
00114 {
00115   
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   
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   
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   
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       
00165       discovery_service_types.right.erase(browser_iter);
00166       
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   
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 
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 
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   
00257 
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), 
00267                                           service.name.c_str(), service.type.c_str(), service.domain.c_str(), NULL, 
00268                                           static_cast<uint16_t>(service.port), NULL); 
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   
00290   
00291   {
00292     boost::mutex::scoped_lock lock(service_mutex);
00293     committed_services.insert(service_bimap::value_type(group, service)); 
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       
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         
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 
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 
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           
00577           
00578           
00579           
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 
00604 
00605           zeroconf->discovered_services.erase(iter);
00606           
00607 
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 
00613 
00614           
00615           
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 
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 
00717 
00718           (*iter)->service.ipv4_addresses.clear();
00719           (*iter)->service.ipv6_addresses.clear();
00720           (*iter)->service.hostname = "";
00721           (*iter)->service.port = 0;
00722           
00723           
00724 
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       
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           
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; 
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 
00800 
00801           (*iter)->service = service;
00802           (*iter)->protocol = zeroconf->avahi_to_ros_protocol(protocol);
00803 
00804           
00805 
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 
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 
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       
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       
00914       zeroconf->add_service_non_threaded(service);
00915       break;
00916     }
00917 
00918     case AVAHI_ENTRY_GROUP_FAILURE:
00919       
00920       
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       
00931       break;
00932     }
00933     case AVAHI_ENTRY_GROUP_REGISTERING:
00934     {
00935       
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 
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   
00982 
00983   switch (state)
00984   {
00985     case AVAHI_CLIENT_S_RUNNING:
00986     {
00987       
00988 
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       
01004 
01005 
01006       break;
01007     }
01008     case AVAHI_CLIENT_S_REGISTERING:
01009     {
01010       ROS_DEBUG("Zeroconf: avahi client registering.");
01011 
01012       
01013 
01014 
01015 
01016 
01017       
01018       
01019       
01020       break;
01021     }
01022     case AVAHI_CLIENT_CONNECTING:
01023     {
01024       ROS_DEBUG("Zeroconf: avahi client registering.");
01025       break;
01026     }
01027   }
01028 }
01029 
01030 }