LCOV - code coverage report
Current view: top level - servus - servus.cpp (source / functions) Hit Total Coverage
Test: Servus Lines: 121 156 77.6 %
Date: 2018-10-03 03:09:57 Functions: 38 46 82.6 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2012-2017, Stefan Eilemann <eile@eyescale.ch>
       3             :  *
       4             :  * This file is part of Servus <https://github.com/HBPVIS/Servus>
       5             :  *
       6             :  * This library is free software; you can redistribute it and/or modify it under
       7             :  * the terms of the GNU Lesser General Public License version 3.0 as published
       8             :  * by the Free Software Foundation.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful, but WITHOUT
      11             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      12             :  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      13             :  * details.
      14             :  *
      15             :  * You should have received a copy of the GNU Lesser General Public License
      16             :  * along with this library; if not, write to the Free Software Foundation, Inc.,
      17             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             :  */
      19             : 
      20             : #include "servus.h"
      21             : 
      22             : #include "listener.h"
      23             : 
      24             : #include <cstring>
      25             : #include <map>
      26             : #include <unordered_set>
      27             : 
      28             : // for NI_MAXHOST
      29             : #ifdef _WIN32
      30             : #include <ws2tcpip.h>
      31             : #else
      32             : #include <netdb.h>
      33             : #include <unistd.h>
      34             : #endif
      35             : 
      36             : namespace servus
      37             : {
      38             : #define ANNOUNCE_TIMEOUT 1000 /*ms*/
      39             : 
      40             : namespace
      41             : {
      42           5 : static const std::string _empty;
      43             : typedef std::map<std::string, std::string> ValueMap;
      44             : typedef std::map<std::string, ValueMap> InstanceMap;
      45             : typedef ValueMap::const_iterator ValueMapCIter;
      46             : typedef InstanceMap::const_iterator InstanceMapCIter;
      47             : typedef std::unordered_set<Listener*> Listeners;
      48             : }
      49             : 
      50             : class Servus::Impl
      51             : {
      52             : public:
      53           8 :     explicit Impl(const std::string& name)
      54           8 :         : _name(name)
      55             :     {
      56           8 :     }
      57           8 :     virtual ~Impl() {}
      58             :     virtual std::string getClassName() const = 0;
      59             : 
      60           4 :     const std::string& getName() const { return _name; }
      61           4 :     void set(const std::string& key, const std::string& value)
      62             :     {
      63           4 :         _data[key] = value;
      64           4 :         _updateRecord();
      65           4 :     }
      66             : 
      67           3 :     Strings getKeys() const
      68             :     {
      69           3 :         Strings keys;
      70           9 :         for (ValueMapCIter i = _data.begin(); i != _data.end(); ++i)
      71           6 :             keys.push_back(i->first);
      72           3 :         return keys;
      73             :     }
      74             : 
      75           4 :     const std::string& get(const std::string& key) const
      76             :     {
      77           4 :         ValueMapCIter i = _data.find(key);
      78           4 :         if (i != _data.end())
      79           2 :             return i->second;
      80           2 :         return _empty;
      81             :     }
      82             : 
      83             :     virtual servus::Servus::Result announce(const unsigned short port,
      84             :                                             const std::string& instance) = 0;
      85             :     virtual void withdraw() = 0;
      86             :     virtual bool isAnnounced() const = 0;
      87             : 
      88             :     virtual servus::Servus::Result beginBrowsing(
      89             :         const servus::Servus::Interface interface_) = 0;
      90             :     virtual servus::Servus::Result browse(const int32_t timeout) = 0;
      91             : 
      92             :     virtual void endBrowsing() = 0;
      93             :     virtual bool isBrowsing() const = 0;
      94             : 
      95           3 :     Strings discover(const ::servus::Servus::Interface addr,
      96             :                      const unsigned browseTime)
      97             :     {
      98           6 :         const auto& res = beginBrowsing(addr);
      99           3 :         if (res == Servus::Result::SUCCESS || res == Servus::Result::PENDING)
     100             :         {
     101           3 :             browse(browseTime);
     102           3 :             if (res == Servus::Result::SUCCESS)
     103           3 :                 endBrowsing();
     104             :         }
     105           6 :         return getInstances();
     106             :     }
     107             : 
     108           9 :     Strings getInstances() const
     109             :     {
     110           9 :         Strings instances;
     111          16 :         for (auto i : _instanceMap)
     112           7 :             instances.push_back(i.first);
     113             : 
     114           9 :         return instances;
     115             :     }
     116             : 
     117           1 :     Strings getKeys(const std::string& instance) const
     118             :     {
     119           1 :         Strings keys;
     120           1 :         InstanceMapCIter i = _instanceMap.find(instance);
     121           1 :         if (i == _instanceMap.end())
     122           0 :             return keys;
     123             : 
     124           3 :         for (auto j : i->second)
     125           2 :             keys.push_back(j.first);
     126           1 :         return keys;
     127             :     }
     128             : 
     129           2 :     bool containsKey(const std::string& instance, const std::string& key) const
     130             :     {
     131           2 :         InstanceMapCIter i = _instanceMap.find(instance);
     132           2 :         if (i == _instanceMap.end())
     133           0 :             return false;
     134             : 
     135           2 :         const ValueMap& values = i->second;
     136           2 :         ValueMapCIter j = values.find(key);
     137           2 :         if (j == values.end())
     138           0 :             return false;
     139           2 :         return true;
     140             :     }
     141             : 
     142          10 :     const std::string& get(const std::string& instance,
     143             :                            const std::string& key) const
     144             :     {
     145          10 :         InstanceMapCIter i = _instanceMap.find(instance);
     146          10 :         if (i == _instanceMap.end())
     147           1 :             return _empty;
     148             : 
     149           9 :         const ValueMap& values = i->second;
     150           9 :         ValueMapCIter j = values.find(key);
     151           9 :         if (j == values.end())
     152           1 :             return _empty;
     153           8 :         return j->second;
     154             :     }
     155             : 
     156           4 :     void addListener(Listener* listener)
     157             :     {
     158           4 :         if (listener)
     159           4 :             _listeners.insert(listener);
     160           4 :     }
     161             : 
     162           4 :     void removeListener(Listener* listener)
     163             :     {
     164           4 :         if (listener)
     165           4 :             _listeners.erase(listener);
     166           4 :     }
     167             : 
     168           0 :     void getData(servus::Servus::Data& data) const { data = _instanceMap; }
     169             : protected:
     170             :     const std::string _name;
     171             :     InstanceMap _instanceMap; //!< last discovered data
     172             :     ValueMap _data;           //!< self data to announce
     173             :     Listeners _listeners;
     174             : 
     175             :     virtual void _updateRecord() = 0;
     176             : };
     177             : }
     178             : 
     179             : // Impls need detail interface definition above
     180             : #ifdef SERVUS_USE_DNSSD
     181             : #include "dnssd/servus.h"
     182             : #elif defined(SERVUS_USE_AVAHI_CLIENT)
     183             : #include "avahi/servus.h"
     184             : #endif
     185             : #include "none/servus.h"
     186             : #include "test/servus.h"
     187             : 
     188             : namespace servus
     189             : {
     190             : namespace
     191             : {
     192           8 : std::unique_ptr<Servus::Impl> _chooseImplementation(const std::string& name)
     193             : {
     194           8 :     if (name == TEST_DRIVER)
     195           3 :         return std::unique_ptr<Servus::Impl>(new test::Servus);
     196             :     try
     197             :     {
     198             : #ifdef SERVUS_USE_DNSSD
     199             :         return std::unique_ptr<Servus::Impl>(new dnssd::Servus(name));
     200             : #elif defined(SERVUS_USE_AVAHI_CLIENT)
     201           5 :         return std::unique_ptr<Servus::Impl>(new avahi::Servus(name));
     202             : #endif
     203             :         return std::unique_ptr<Servus::Impl>(new none::Servus(name));
     204             :     }
     205           0 :     catch (const std::runtime_error& error)
     206             :     {
     207           0 :         std::cerr << "Error starting Servus client: " << error.what()
     208           0 :                   << std::endl;
     209           0 :         return std::unique_ptr<Servus::Impl>(new servus::none::Servus(name));
     210             :     }
     211             : }
     212             : }
     213             : 
     214           8 : Servus::Servus(const std::string& name)
     215           8 :     : _impl(_chooseImplementation(name))
     216             : {
     217           8 : }
     218             : 
     219           8 : Servus::~Servus()
     220             : {
     221           8 : }
     222             : 
     223           2 : bool Servus::isAvailable()
     224             : {
     225             : #if defined(SERVUS_USE_DNSSD) || defined(SERVUS_USE_AVAHI_CLIENT)
     226           2 :     return true;
     227             : #endif
     228             :     return false;
     229             : }
     230             : 
     231           4 : const std::string& Servus::getName() const
     232             : {
     233           4 :     return _impl->getName();
     234             : }
     235             : 
     236           4 : std::string Servus::Result::getString() const
     237             : {
     238           4 :     const int32_t code = getCode();
     239           4 :     switch (code)
     240             :     {
     241             : #ifdef SERVUS_USE_DNSSD
     242             :     case kDNSServiceErr_Unknown:
     243             :         return "unknown error";
     244             :     case kDNSServiceErr_NoSuchName:
     245             :         return "name not found";
     246             :     case kDNSServiceErr_NoMemory:
     247             :         return "out of memory";
     248             :     case kDNSServiceErr_BadParam:
     249             :         return "bad parameter";
     250             :     case kDNSServiceErr_BadReference:
     251             :         return "bad reference";
     252             :     case kDNSServiceErr_BadState:
     253             :         return "bad state";
     254             :     case kDNSServiceErr_BadFlags:
     255             :         return "bad flags";
     256             :     case kDNSServiceErr_Unsupported:
     257             :         return "unsupported";
     258             :     case kDNSServiceErr_NotInitialized:
     259             :         return "not initialized";
     260             :     case kDNSServiceErr_AlreadyRegistered:
     261             :         return "already registered";
     262             :     case kDNSServiceErr_NameConflict:
     263             :         return "name conflict";
     264             :     case kDNSServiceErr_Invalid:
     265             :         return "invalid value";
     266             :     case kDNSServiceErr_Firewall:
     267             :         return "firewall";
     268             :     case kDNSServiceErr_Incompatible:
     269             :         return "client library incompatible with daemon";
     270             :     case kDNSServiceErr_BadInterfaceIndex:
     271             :         return "bad interface index";
     272             :     case kDNSServiceErr_Refused:
     273             :         return "refused";
     274             :     case kDNSServiceErr_NoSuchRecord:
     275             :         return "no such record";
     276             :     case kDNSServiceErr_NoAuth:
     277             :         return "no authentication";
     278             :     case kDNSServiceErr_NoSuchKey:
     279             :         return "no such key";
     280             :     case kDNSServiceErr_NATTraversal:
     281             :         return "NAT traversal";
     282             :     case kDNSServiceErr_DoubleNAT:
     283             :         return "double NAT";
     284             :     case kDNSServiceErr_BadTime:
     285             :         return "bad time";
     286             : #endif
     287             : 
     288             :     case PENDING:
     289           0 :         return "operation pending";
     290             :     case NOT_SUPPORTED:
     291           0 :         return "Servus compiled without ZeroConf support";
     292             :     case POLL_ERROR:
     293           0 :         return "Error polling for events";
     294             :     default:
     295           4 :         if (code > 0)
     296           0 :             return ::strerror(code);
     297           4 :         return servus::Result::getString();
     298             :     }
     299             : }
     300             : 
     301           4 : void Servus::set(const std::string& key, const std::string& value)
     302             : {
     303           4 :     _impl->set(key, value);
     304           4 : }
     305             : 
     306           3 : Strings Servus::getKeys() const
     307             : {
     308           3 :     return _impl->getKeys();
     309             : }
     310             : 
     311           4 : const std::string& Servus::get(const std::string& key) const
     312             : {
     313           4 :     return _impl->get(key);
     314             : }
     315             : 
     316           6 : Servus::Result Servus::announce(const unsigned short port,
     317             :                                 const std::string& instance)
     318             : {
     319           6 :     return _impl->announce(port, instance);
     320             : }
     321             : 
     322           3 : void Servus::withdraw()
     323             : {
     324           3 :     _impl->withdraw();
     325           3 : }
     326             : 
     327           0 : bool Servus::isAnnounced() const
     328             : {
     329           0 :     return _impl->isAnnounced();
     330             : }
     331             : 
     332           3 : Strings Servus::discover(const Interface addr, const unsigned browseTime)
     333             : {
     334           3 :     return _impl->discover(addr, browseTime);
     335             : }
     336             : 
     337           4 : Servus::Result Servus::beginBrowsing(const servus::Servus::Interface addr)
     338             : {
     339           4 :     return _impl->beginBrowsing(addr);
     340             : }
     341             : 
     342          37 : Servus::Result Servus::browse(int32_t timeout)
     343             : {
     344          37 :     return _impl->browse(timeout);
     345             : }
     346             : 
     347           3 : void Servus::endBrowsing()
     348             : {
     349           3 :     _impl->endBrowsing();
     350           3 : }
     351             : 
     352           5 : bool Servus::isBrowsing() const
     353             : {
     354           5 :     return _impl->isBrowsing();
     355             : }
     356             : 
     357           6 : Strings Servus::getInstances() const
     358             : {
     359           6 :     return _impl->getInstances();
     360             : }
     361             : 
     362           1 : Strings Servus::getKeys(const std::string& instance) const
     363             : {
     364           1 :     return _impl->getKeys(instance);
     365             : }
     366             : 
     367           1 : const std::string& Servus::getHost(const std::string& instance) const
     368             : {
     369           1 :     return get(instance, "servus_host");
     370             : }
     371             : 
     372           2 : bool Servus::containsKey(const std::string& instance,
     373             :                          const std::string& key) const
     374             : {
     375           2 :     return _impl->containsKey(instance, key);
     376             : }
     377             : 
     378          10 : const std::string& Servus::get(const std::string& instance,
     379             :                                const std::string& key) const
     380             : {
     381          10 :     return _impl->get(instance, key);
     382             : }
     383             : 
     384           4 : void Servus::addListener(Listener* listener)
     385             : {
     386           4 :     _impl->addListener(listener);
     387           4 : }
     388             : 
     389           4 : void Servus::removeListener(Listener* listener)
     390             : {
     391           4 :     _impl->removeListener(listener);
     392           4 : }
     393             : 
     394           0 : void Servus::getData(Data& data)
     395             : {
     396           0 :     _impl->getData(data);
     397           0 : }
     398             : 
     399           0 : std::string getHostname()
     400             : {
     401           0 :     char hostname[NI_MAXHOST + 1] = {0};
     402           0 :     gethostname(hostname, NI_MAXHOST);
     403           0 :     hostname[NI_MAXHOST] = '\0';
     404           0 :     return std::string(hostname);
     405             : }
     406             : 
     407           0 : std::ostream& operator<<(std::ostream& os, const Servus& servus)
     408             : {
     409           0 :     os << "Servus instance" << (servus.isAnnounced() ? " " : " not ")
     410           0 :        << "announced" << (servus.isBrowsing() ? " " : " not ")
     411           0 :        << "browsing, implementation" << servus._impl->getClassName();
     412             : 
     413           0 :     const Strings& keys = servus.getKeys();
     414           0 :     for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i)
     415           0 :         os << std::endl << "    " << *i << " = " << servus.get(*i);
     416             : 
     417           0 :     return os;
     418             : }
     419             : 
     420           0 : std::ostream& operator<<(std::ostream& os, const Servus::Interface& addr)
     421             : {
     422           0 :     switch (addr)
     423             :     {
     424             :     case Servus::IF_ALL:
     425           0 :         return os << " all ";
     426             :     case Servus::IF_LOCAL:
     427           0 :         return os << " local ";
     428             :     }
     429           0 :     return os;
     430             : }
     431          15 : }

Generated by: LCOV version 1.11