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 : }
|