24 #include <netcomm/dns-sd/avahi_thread.h> 25 #include <netcomm/dns-sd/avahi_resolver_handler.h> 27 #include <core/threading/mutex.h> 28 #include <core/threading/wait_condition.h> 29 #include <core/exceptions/software.h> 30 #include <utils/misc/string_conversions.h> 32 #include <avahi-client/lookup.h> 33 #include <avahi-client/publish.h> 34 #include <avahi-common/alternative.h> 35 #include <avahi-common/simple-watch.h> 36 #include <avahi-common/malloc.h> 37 #include <avahi-common/error.h> 38 #include <avahi-common/timeval.h> 40 #include <sys/socket.h> 41 #include <sys/types.h> 42 #include <netinet/in.h> 66 do_reset_groups =
false;
79 remove_pending_services();
80 remove_pending_browsers();
86 avahi_client_free( client );
89 avahi_simple_poll_free( simple_poll );
101 if ( need_recover ) {
103 avahi_client_free( client );
108 avahi_simple_poll_free( simple_poll );
113 if ( ! simple_poll ) {
117 if ( (simple_poll = avahi_simple_poll_new()) ) {
119 client = avahi_client_new( avahi_simple_poll_get(simple_poll), AVAHI_CLIENT_NO_FAIL,
120 AvahiThread::client_callback,
this, &error );
123 avahi_simple_poll_free( simple_poll );
129 if ( do_reset_groups ) {
133 if ( need_recover ) {
139 if ( client_state == AVAHI_CLIENT_S_RUNNING ) {
140 remove_pending_services();
141 remove_pending_browsers();
142 create_pending_services();
143 create_pending_browsers();
144 start_hostname_resolvers();
145 start_address_resolvers();
148 need_recover =
false;
150 avahi_simple_poll_iterate( simple_poll, -1);
160 AvahiThread::recover()
167 AvahiThread::wake_poller()
170 avahi_simple_poll_wakeup( simple_poll );
181 AvahiThread::client_callback(AvahiClient *c, AvahiClientState state,
void *instance)
184 at->client_state = state;
187 case AVAHI_CLIENT_S_RUNNING:
196 case AVAHI_CLIENT_S_COLLISION:
201 at->do_reset_groups =
true;
204 case AVAHI_CLIENT_FAILURE:
210 case AVAHI_CLIENT_CONNECTING:
214 case AVAHI_CLIENT_S_REGISTERING:
232 if ( __services.find(service) == __services.end() ) {
233 __pending_services.push_locked(service);
235 throw Exception(
"Service already registered");
245 if ( __services.find(service) != __services.end() ) {
246 __pending_remove_services.push_locked(service);
248 throw Exception(
"Service not registered");
257 AvahiThread::create_service(
const NetworkService &service, AvahiEntryGroup *exgroup)
261 if ( ! client )
return NULL;
263 AvahiEntryGroup *group;
267 if ( ! (group = avahi_entry_group_new(client,
268 AvahiThread::entry_group_callback,
274 AvahiStringList *al = NULL;
275 const std::list<std::string> &l = service.
txt();
276 for (std::list<std::string>::const_iterator j = l.begin(); j != l.end(); ++j) {
277 al = avahi_string_list_add(al, j->c_str());
281 int rv = AVAHI_ERR_COLLISION;
282 for (
int i = 1; (i <= 100) && (rv == AVAHI_ERR_COLLISION); ++i) {
289 rv = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC,
291 AVAHI_PUBLISH_USE_MULTICAST,
292 name.c_str(), service.
type(),
297 if ((i > 1) && (rv >= 0)) {
302 avahi_string_list_free(al);
305 throw Exception(
"Adding Avahi/mDNS-SD service failed: %s", avahi_strerror(rv));
317 if (avahi_entry_group_commit(group) < 0) {
318 throw Exception(
"Registering Avahi services failed");
325 AvahiThread::recreate_services()
327 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
328 (*__sit).second = create_service(__sit->first, __sit->second);
334 AvahiThread::create_pending_services()
336 __pending_services.lock();
337 while ( ! __pending_services.empty()) {
339 __services[s] = create_service(s, NULL);
340 __pending_services.pop();
342 __pending_services.unlock();
347 AvahiThread::remove_pending_services()
351 __pending_remove_services.lock();
352 while ( ! __pending_remove_services.empty()) {
354 if ( __services.find(s) != __services.end() ) {
355 group_erase(__services[s]);
356 __services.erase_locked(s);
358 __pending_remove_services.pop();
360 __pending_remove_services.unlock();
370 AvahiThread::group_reset(AvahiEntryGroup *g)
373 avahi_entry_group_reset(g);
380 AvahiThread::group_erase(AvahiEntryGroup *g)
383 avahi_entry_group_reset( g );
384 avahi_entry_group_free( g );
390 AvahiThread::erase_groups()
392 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
393 if (__sit->second) group_erase(__sit->second);
394 __sit->second = NULL;
400 AvahiThread::reset_groups()
402 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
403 group_reset((*__sit).second);
410 AvahiThread::name_collision(AvahiEntryGroup *g)
412 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
413 if ( (*__sit).second == g ) {
417 char *n = avahi_alternative_service_name((*__sit).first.name());
421 __pending_remove_services.push_locked((*__sit).first);
422 __pending_services.push_locked(alternate_service);
434 AvahiThread::entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
440 case AVAHI_ENTRY_GROUP_ESTABLISHED :
445 case AVAHI_ENTRY_GROUP_COLLISION : {
446 at->name_collision(g);
450 case AVAHI_ENTRY_GROUP_FAILURE :
455 case AVAHI_ENTRY_GROUP_UNCOMMITED:
456 case AVAHI_ENTRY_GROUP_REGISTERING:
477 __handlers[service_type].push_back(h);
493 if ( __handlers.find(service_type) != __handlers.end() ) {
494 __handlers[service_type].remove(h);
495 if ( __handlers[service_type].size() == 0 ) {
496 if ( __browsers.find(service_type) != __browsers.end() ) {
497 __pending_browser_removes.
push_locked(service_type);
501 __handlers.erase(service_type);
513 AvahiThread::create_browser(
const char *service_type)
515 if ( __browsers.find(service_type) == __browsers.end() ) {
517 AvahiServiceBrowser *b = avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
519 service_type, NULL, (AvahiLookupFlags)0,
520 AvahiThread::browse_callback,
this);
523 __handlers[service_type].pop_back();
526 __browsers[service_type] = b;
536 AvahiThread::recreate_browsers()
539 for (i = __handlers.begin(); i != __handlers.end(); ++i) {
540 create_browser( (*i).first.c_str() );
546 AvahiThread::create_pending_browsers()
548 __pending_browsers.
lock();
549 while ( ! __pending_browsers.empty() ) {
551 create_browser(__pending_browsers.front().c_str());
552 __pending_browsers.pop();
554 __pending_browsers.
unlock();
559 AvahiThread::remove_pending_browsers()
563 __pending_browser_removes.
lock();
564 while ( ! __pending_browser_removes.empty()) {
565 std::string &s = __pending_browser_removes.front();
566 avahi_service_browser_free(__browsers[s]);
568 __pending_browser_removes.pop();
570 __pending_browser_removes.
unlock();
577 AvahiThread::erase_browsers()
579 std::map< std::string, AvahiServiceBrowser * >::iterator i;
580 for (i = __browsers.begin(); i != __browsers.end(); ++i) {
581 avahi_service_browser_free((*i).second);
593 AvahiThread::call_handler_service_removed(
const char *
name,
597 if ( __handlers.find(type) != __handlers.end() ) {
598 std::list<ServiceBrowseHandler *>::iterator i;
599 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
600 (*i)->service_removed(name, type, domain);
617 AvahiThread::call_handler_service_added(
const char *name,
620 const char *host_name,
621 const AvahiAddress *address,
623 std::list<std::string> &txt,
624 AvahiLookupResultFlags flags)
626 struct sockaddr_in *s = NULL;
628 if ( address->proto == AVAHI_PROTO_INET ) {
629 slen =
sizeof(
struct sockaddr_in);
630 s = (
struct sockaddr_in *)malloc(slen);
631 s->sin_addr.s_addr = address->data.ipv4.address;
636 if ( __handlers.find(type) != __handlers.end() ) {
637 std::list<ServiceBrowseHandler *>::iterator i;
638 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
639 (*i)->service_added(name, type, domain, host_name,
640 (
struct sockaddr *)s, slen, port, txt, (
int)flags);
653 AvahiThread::call_handler_failed(
const char *name,
657 if ( __handlers.find(type) != __handlers.end() ) {
658 std::list<ServiceBrowseHandler *>::iterator i;
659 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
660 (*i)->browse_failed(name, type, domain);
670 AvahiThread::call_handler_all_for_now(
const char *type)
672 if ( __handlers.find(type) != __handlers.end() ) {
673 std::list<ServiceBrowseHandler *>::iterator i;
674 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
685 AvahiThread::call_handler_cache_exhausted(
const char *type)
687 if ( __handlers.find(type) != __handlers.end() ) {
688 std::list<ServiceBrowseHandler *>::iterator i;
689 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
690 (*i)->cache_exhausted();
710 AvahiThread::browse_callback( AvahiServiceBrowser *b,
711 AvahiIfIndex interface,
712 AvahiProtocol protocol,
713 AvahiBrowserEvent event,
717 AvahiLookupResultFlags flags,
723 case AVAHI_BROWSER_FAILURE:
727 case AVAHI_BROWSER_NEW:
733 if (!(avahi_service_resolver_new(at->client, interface, protocol,
734 name, type, domain, protocol, (AvahiLookupFlags)0,
735 AvahiThread::resolve_callback, instance))) {
740 case AVAHI_BROWSER_REMOVE:
743 at->call_handler_service_removed(name, type, domain);
746 case AVAHI_BROWSER_ALL_FOR_NOW:
749 at->call_handler_all_for_now(type);
752 case AVAHI_BROWSER_CACHE_EXHAUSTED:
755 at->call_handler_cache_exhausted(type);
780 AvahiThread::resolve_callback( AvahiServiceResolver *r,
781 AVAHI_GCC_UNUSED AvahiIfIndex interface,
782 AVAHI_GCC_UNUSED AvahiProtocol protocol,
783 AvahiResolverEvent event,
787 const char *host_name,
788 const AvahiAddress *address,
790 AvahiStringList *txt,
791 AvahiLookupResultFlags flags,
797 case AVAHI_RESOLVER_FAILURE:
799 at->call_handler_failed(name, type, domain);
802 case AVAHI_RESOLVER_FOUND:
805 std::list< std::string > txts;
806 AvahiStringList *l = txt;
810 txts.push_back((
char *)avahi_string_list_get_text(l));
811 l = avahi_string_list_get_next( l );
814 at->call_handler_service_added(name, type, domain, host_name, address, port, txts, flags);
819 avahi_service_resolver_free(r);
837 AvahiResolverCallbackData *data =
new AvahiResolverCallbackData(
this, handler);
839 if ( __pending_hostname_resolves.find(name) == __pending_hostname_resolves.end()) {
840 __pending_hostname_resolves[
name] = data;
848 AvahiThread::start_hostname_resolver(
const char *name, AvahiResolverCallbackData *data)
850 AvahiHostNameResolver *resolver;
851 if ( (resolver = avahi_host_name_resolver_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
852 name, AVAHI_PROTO_INET,
853 AVAHI_LOOKUP_USE_MULTICAST,
854 AvahiThread::host_name_resolver_callback,
856 throw Exception(
"Cannot create Avahi name resolver");
858 __running_hostname_resolvers.push_back(resolver);
865 AvahiThread::start_hostname_resolvers()
867 for (__phrit = __pending_hostname_resolves.begin(); __phrit != __pending_hostname_resolves.end(); ++__phrit) {
868 start_hostname_resolver((*__phrit).first.c_str(), (*__phrit).second);
870 __pending_hostname_resolves.clear();
875 AvahiThread::start_address_resolvers()
877 for (__parit = __pending_address_resolves.begin(); __parit != __pending_address_resolves.end(); ++__parit) {
878 start_address_resolver((*__parit).first, (*__parit).second);
880 __pending_address_resolves.clear();
895 if ( addrlen !=
sizeof(
struct sockaddr_in) ) {
896 throw Exception(
"Only IPv4 is currently supported");
899 struct sockaddr_in *in_addr = (
struct sockaddr_in *)calloc(1,
sizeof(
struct sockaddr_in));
900 memcpy(in_addr, addr,
sizeof(
struct sockaddr_in));
901 AvahiResolverCallbackData *data =
new AvahiResolverCallbackData(
this, handler);
903 __pending_address_resolves[in_addr] = data;
909 AvahiThread::start_address_resolver(
struct sockaddr_in *in_addr, AvahiResolverCallbackData *data)
912 a.proto = AVAHI_PROTO_INET;
913 a.data.ipv4.address = in_addr->sin_addr.s_addr;
915 AvahiAddressResolver *resolver;
916 if ( (resolver = avahi_address_resolver_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
917 &a, AVAHI_LOOKUP_USE_MULTICAST,
918 AvahiThread::address_resolver_callback,
920 Exception e(
"Cannot create Avahi address resolver");
921 e.
append(
"Avahi error: %s", avahi_strerror(avahi_client_errno(client)));
934 AvahiThread::remove_hostname_resolver(AvahiHostNameResolver *r)
945 AvahiThread::remove_address_resolver(AvahiAddressResolver *r)
955 AvahiThread::host_name_resolver_callback(AvahiHostNameResolver *r,
956 AvahiIfIndex interface,
957 AvahiProtocol protocol,
958 AvahiResolverEvent event,
960 const AvahiAddress *a,
961 AvahiLookupResultFlags flags,
964 AvahiResolverCallbackData *cd =
static_cast<AvahiResolverCallbackData *
>(userdata);
966 cd->first->remove_hostname_resolver(r);
967 avahi_host_name_resolver_free(r);
970 case AVAHI_RESOLVER_FOUND:
972 struct sockaddr_in *res = (
struct sockaddr_in *)malloc(
sizeof(
struct sockaddr_in));
973 res->sin_family = (
unsigned short)avahi_proto_to_af(protocol);
974 res->sin_addr.s_addr = a->data.ipv4.address;
976 cd->second->resolved_name(strdup(name), (
struct sockaddr *)res,
sizeof(
struct sockaddr_in));
980 case AVAHI_RESOLVER_FAILURE:
982 cd->second->name_resolution_failed(strdup(name));
994 AvahiThread::address_resolver_callback(AvahiAddressResolver *r,
995 AvahiIfIndex interface,
996 AvahiProtocol protocol,
997 AvahiResolverEvent event,
998 const AvahiAddress *a,
1000 AvahiLookupResultFlags flags,
1003 AvahiResolverCallbackData *cd =
static_cast<AvahiResolverCallbackData *
>(userdata);
1005 cd->first->remove_address_resolver(r);
1006 avahi_address_resolver_free(r);
1008 struct sockaddr_in *res = (
struct sockaddr_in *)malloc(
sizeof(
struct sockaddr_in));
1009 res->sin_family = (
unsigned short)avahi_proto_to_af(protocol);
1010 res->sin_addr.s_addr = a->data.ipv4.address;
1013 case AVAHI_RESOLVER_FOUND:
1014 cd->second->resolved_address((
struct sockaddr_in *)res,
sizeof(
struct sockaddr_in),
1017 case AVAHI_RESOLVER_FAILURE:
1019 cd->second->address_resolution_failed((
struct sockaddr_in *)res,
1020 sizeof(
struct sockaddr_in));
1032 AvahiThread::init_done()
void unlock() const
Unlock list.
Wait until a given condition holds.
void erase_locked(const KeyType &key)
Remove item with lock.
~AvahiThread()
Destructor.
const char * type() const
Get type of service.
virtual void loop()
Avahi thread loop.
Fawkes library namespace.
void wake_all()
Wake up all waiting threads.
thread cannot be cancelled
A NULL pointer was supplied where not allowed.
Thread class encapsulation of pthreads.
void set_prepfin_conc_loop(bool concurrent=true)
Set concurrent execution of prepare_finalize() and loop().
void push_back_locked(const Type &x)
Push element to list at back with lock protection.
void remove_locked(const Type &x)
Remove element from list with lock protection.
static void set_cancel_state(CancelState new_state, CancelState *old_state=0)
Set the cancel state of the current thread.
Interface for class that process browse results.
Avahi resolver handler interface.
void publish_service(NetworkService *service)
Publish service.
void set_name(const char *new_name)
Set name of service.
void resolve_address(struct sockaddr *addr, socklen_t addrlen, AvahiResolverHandler *handler)
Order address resolution.
const char * host() const
Get host of service.
Base class for exceptions in Fawkes.
void resolve_name(const char *name, AvahiResolverHandler *handler)
Order name resolution.
void watch_service(const char *service_type, ServiceBrowseHandler *h)
Add a result handler.
void set_modified_name(const char *new_name) const
Set modified name of service.
unsigned short int port() const
Get port of service.
void wait()
Wait for the condition forever.
const char * name() const
Get name of thread.
Representation of a service announced or found via service discovery (i.e.
const char * domain() const
Get domain of service.
AvahiThread()
Constructor.
void lock() const
Lock queue.
void push_locked(const Type &x)
Push element to queue with lock protection.
const std::list< std::string > & txt() const
Get TXT record list of service.
static std::string to_string(unsigned int i)
Convert unsigned int value to a string.
void unpublish_service(NetworkService *service)
Revoke service publication.
void append(const char *format,...)
Append messages to the message list.
void wait_initialized()
Waits for the AvahiThread to be initialized.
const char * name() const
Get name of service.
void unwatch_service(const char *service_type, ServiceBrowseHandler *h)
Remove a handler.