Line data Source code
1 :
2 : /* Copyright (c) 2012-2014, Stefan Eilemann <eile@eyescale.ch>
3 : *
4 : * This file is part of Lunchbox <https://github.com/Eyescale/Lunchbox>
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 2.1 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 : #include "debug.h"
22 :
23 : namespace lunchbox
24 : {
25 : #define ANNOUNCE_TIMEOUT 1000 /*ms*/
26 :
27 : namespace detail
28 : {
29 30 : static const std::string empty_;
30 : typedef std::map< std::string, std::string > ValueMap;
31 : typedef std::map< std::string, ValueMap > InstanceMap;
32 : typedef ValueMap::const_iterator ValueMapCIter;
33 : typedef InstanceMap::const_iterator InstanceMapCIter;
34 :
35 : class Servus
36 : {
37 : public:
38 3 : Servus() {}
39 3 : virtual ~Servus() {}
40 :
41 2 : void set( const std::string& key, const std::string& value )
42 : {
43 2 : _data[ key ] = value;
44 2 : _updateRecord();
45 2 : }
46 :
47 3 : Strings getKeys() const
48 : {
49 3 : Strings keys;
50 9 : for( ValueMapCIter i = _data.begin(); i != _data.end(); ++i )
51 6 : keys.push_back( i->first );
52 3 : return keys;
53 : }
54 :
55 0 : const std::string& get( const std::string& key ) const
56 : {
57 0 : ValueMapCIter i = _data.find( key );
58 0 : if( i != _data.end( ))
59 0 : return i->second;
60 0 : return empty_;
61 : }
62 :
63 : virtual lunchbox::Servus::Result announce( const unsigned short port,
64 : const std::string& instance ) =0;
65 : virtual void withdraw() = 0;
66 : virtual bool isAnnounced() const = 0;
67 :
68 : virtual lunchbox::Servus::Result beginBrowsing(
69 : const lunchbox::Servus::Interface interface_ ) = 0;
70 : virtual lunchbox::Servus::Result browse( const int32_t timeout ) = 0;
71 :
72 : virtual void endBrowsing() = 0;
73 : virtual bool isBrowsing() const = 0;
74 : virtual Strings discover( const lunchbox::Servus::Interface interface_,
75 : const unsigned browseTime ) = 0;
76 :
77 6 : Strings getInstances() const
78 : {
79 6 : Strings instances;
80 39 : for( InstanceMapCIter i = _instanceMap.begin();
81 26 : i != _instanceMap.end(); ++i )
82 : {
83 7 : instances.push_back( i->first );
84 : }
85 6 : return instances;
86 : }
87 :
88 0 : Strings getKeys( const std::string& instance ) const
89 : {
90 0 : Strings keys;
91 0 : InstanceMapCIter i = _instanceMap.find( instance );
92 0 : if( i == _instanceMap.end( ))
93 0 : return keys;
94 :
95 0 : const ValueMap& values = i->second;
96 0 : for( ValueMapCIter j = values.begin(); j != values.end(); ++j )
97 0 : keys.push_back( j->first );
98 0 : return keys;
99 : }
100 :
101 0 : bool containsKey( const std::string& instance, const std::string& key )
102 : const
103 : {
104 0 : InstanceMapCIter i = _instanceMap.find( instance );
105 0 : if( i == _instanceMap.end( ))
106 0 : return false;
107 :
108 0 : const ValueMap& values = i->second;
109 0 : ValueMapCIter j = values.find( key );
110 0 : if( j == values.end( ))
111 0 : return false;
112 0 : return true;
113 : }
114 :
115 4 : const std::string& get( const std::string& instance,
116 : const std::string& key ) const
117 : {
118 4 : InstanceMapCIter i = _instanceMap.find( instance );
119 4 : if( i == _instanceMap.end( ))
120 0 : return detail::empty_;
121 :
122 4 : const ValueMap& values = i->second;
123 4 : ValueMapCIter j = values.find( key );
124 4 : if( j == values.end( ))
125 0 : return detail::empty_;
126 4 : return j->second;
127 : }
128 :
129 0 : void getData( lunchbox::Servus::Data& data ) const
130 : {
131 0 : data = _instanceMap;
132 0 : }
133 :
134 : protected:
135 : InstanceMap _instanceMap; //!< last discovered data
136 : ValueMap _data; //!< self data to announce
137 :
138 : virtual void _updateRecord() = 0;
139 : };
140 :
141 : }
142 : }
143 :
144 : // Impls need detail interface definition above
145 : #ifdef LUNCHBOX_USE_DNSSD
146 : # include "dnssd/servus.h"
147 : #elif defined(LUNCHBOX_USE_AVAHI_CLIENT)
148 : # include "avahi/servus.h"
149 : #endif
150 : #include "none/servus.h"
151 :
152 : // http://stackoverflow.com/questions/14430906/multi-threaded-avahi-resolving-causes-segfault
153 : #include "lock.h"
154 : #include "scopedMutex.h"
155 : #ifdef __APPLE__
156 : static lunchbox::Lock* lock_( 0 );
157 : #else
158 30 : static lunchbox::Lock lock_;
159 : #endif
160 :
161 : namespace lunchbox
162 : {
163 1 : bool Servus::isAvailable()
164 : {
165 : #if defined(LUNCHBOX_USE_DNSSD) || defined(LUNCHBOX_USE_AVAHI_CLIENT)
166 1 : return true;
167 : #endif
168 : return false;
169 : }
170 :
171 3 : Servus::Servus( const std::string& name LB_UNUSED )
172 : #ifdef LUNCHBOX_USE_DNSSD
173 : : _impl( new dnssd::Servus( name ))
174 : #elif defined(LUNCHBOX_USE_AVAHI_CLIENT)
175 3 : : _impl( new avahi::Servus( name ))
176 : #else
177 : : _impl( new none::Servus( ))
178 : #endif
179 3 : {}
180 :
181 3 : Servus::~Servus()
182 : {
183 3 : delete _impl;
184 3 : }
185 :
186 0 : std::string Servus::Result::getString() const
187 : {
188 0 : const int32_t code = getCode();
189 0 : switch( code )
190 : {
191 : #ifdef LUNCHBOX_USE_DNSSD
192 : case kDNSServiceErr_Unknown: return "unknown error";
193 : case kDNSServiceErr_NoSuchName: return "name not found";
194 : case kDNSServiceErr_NoMemory: return "out of memory";
195 : case kDNSServiceErr_BadParam: return "bad parameter";
196 : case kDNSServiceErr_BadReference: return "bad reference";
197 : case kDNSServiceErr_BadState: return "bad state";
198 : case kDNSServiceErr_BadFlags: return "bad flags";
199 : case kDNSServiceErr_Unsupported: return "unsupported";
200 : case kDNSServiceErr_NotInitialized: return "not initialized";
201 : case kDNSServiceErr_AlreadyRegistered: return "already registered";
202 : case kDNSServiceErr_NameConflict: return "name conflict";
203 : case kDNSServiceErr_Invalid: return "invalid value";
204 : case kDNSServiceErr_Firewall: return "firewall";
205 : case kDNSServiceErr_Incompatible:
206 : return "client library incompatible with daemon";
207 : case kDNSServiceErr_BadInterfaceIndex: return "bad interface index";
208 : case kDNSServiceErr_Refused: return "refused";
209 : case kDNSServiceErr_NoSuchRecord: return "no such record";
210 : case kDNSServiceErr_NoAuth: return "no authentication";
211 : case kDNSServiceErr_NoSuchKey: return "no such key";
212 : case kDNSServiceErr_NATTraversal: return "NAT traversal";
213 : case kDNSServiceErr_DoubleNAT: return "double NAT";
214 : case kDNSServiceErr_BadTime: return "bad time";
215 : #endif
216 :
217 0 : case PENDING: return "operation pending";
218 0 : case NOT_SUPPORTED: return "Lunchbox compiled without ZeroConf support";
219 0 : case POLL_ERROR: return "Error polling for events";
220 : default:
221 0 : if( code > 0 )
222 0 : return ::strerror( code );
223 0 : return lunchbox::Result::getString();
224 : }
225 : }
226 :
227 2 : void Servus::set( const std::string& key, const std::string& value )
228 : {
229 2 : _impl->set( key, value );
230 2 : }
231 :
232 3 : Strings Servus::getKeys() const
233 : {
234 3 : return _impl->getKeys();
235 : }
236 :
237 0 : const std::string& Servus::get( const std::string& key ) const
238 : {
239 0 : return _impl->get( key );
240 : }
241 :
242 3 : Servus::Result Servus::announce( const unsigned short port,
243 : const std::string& instance )
244 : {
245 3 : ScopedWrite mutex( lock_ );
246 3 : return _impl->announce( port, instance );
247 : }
248 :
249 1 : void Servus::withdraw()
250 : {
251 1 : ScopedWrite mutex( lock_ );
252 1 : _impl->withdraw();
253 1 : }
254 :
255 0 : bool Servus::isAnnounced() const
256 : {
257 0 : return _impl->isAnnounced();
258 : }
259 :
260 2 : Strings Servus::discover( const Interface addr, const unsigned browseTime )
261 : {
262 2 : ScopedWrite mutex( lock_ );
263 2 : return _impl->discover( addr, browseTime );
264 : }
265 :
266 2 : Servus::Result Servus::beginBrowsing( const lunchbox::Servus::Interface addr )
267 : {
268 2 : ScopedWrite mutex( lock_ );
269 2 : return _impl->beginBrowsing( addr );
270 : }
271 :
272 3 : Servus::Result Servus::browse( int32_t timeout )
273 : {
274 3 : ScopedWrite mutex( lock_ );
275 3 : return _impl->browse( timeout );
276 : }
277 :
278 1 : void Servus::endBrowsing()
279 : {
280 1 : ScopedWrite mutex( lock_ );
281 1 : _impl->endBrowsing();
282 1 : }
283 :
284 5 : bool Servus::isBrowsing() const
285 : {
286 5 : return _impl->isBrowsing();
287 : }
288 :
289 4 : Strings Servus::getInstances() const
290 : {
291 4 : return _impl->getInstances();
292 : }
293 :
294 0 : Strings Servus::getKeys( const std::string& instance ) const
295 : {
296 0 : return _impl->getKeys( instance );
297 : }
298 :
299 0 : bool Servus::containsKey( const std::string& instance,
300 : const std::string& key ) const
301 : {
302 0 : return _impl->containsKey( instance, key );
303 : }
304 :
305 4 : const std::string& Servus::get( const std::string& instance,
306 : const std::string& key ) const
307 : {
308 4 : return _impl->get( instance, key );
309 : }
310 :
311 0 : void Servus::getData( Data& data )
312 : {
313 0 : _impl->getData( data );
314 0 : }
315 :
316 0 : std::ostream& operator << ( std::ostream& os, const Servus& servus )
317 : {
318 0 : os << disableFlush << disableHeader << "Servus instance"
319 0 : << (servus.isAnnounced() ? " " : " not ") << "announced"
320 0 : << (servus.isBrowsing() ? " " : " not ") << "browsing, implementation"
321 0 : << className( servus._impl ) << indent;
322 :
323 0 : const Strings& keys = servus.getKeys();
324 0 : for( StringsCIter i = keys.begin(); i != keys.end(); ++i )
325 0 : os << std::endl << *i << " = " << servus.get( *i );
326 :
327 0 : return os << exdent << enableHeader << enableFlush;
328 : }
329 :
330 0 : std::ostream& operator << ( std::ostream& os , const Servus::Interface& addr )
331 : {
332 0 : switch( addr )
333 : {
334 0 : case Servus::IF_ALL: return os << " all ";
335 0 : case Servus::IF_LOCAL: return os << " local ";
336 : }
337 0 : return os;
338 : }
339 :
340 90 : }
|