Fawkes API  Fawkes Development Version
avahi_thread.cpp
1 
2 /***************************************************************************
3  * avahi_thread.cpp - Avahi thread
4  *
5  * Created: Wed Nov 08 11:19:25 2006
6  * Copyright 2006-2011 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <netcomm/dns-sd/avahi_thread.h>
25 #include <netcomm/dns-sd/avahi_resolver_handler.h>
26 
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>
31 
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>
39 
40 #include <sys/socket.h>
41 #include <sys/types.h>
42 #include <netinet/in.h>
43 #include <cstdlib>
44 #include <cstddef>
45 #include <cstring>
46 
47 namespace fawkes {
48 
49 /** @class AvahiThread netcomm/dns-sd/avahi_thread.h
50  * Avahi main thread.
51  * This thread handles all tasks related to avahi. This is the single
52  * interaction point with the Avahi adapter.
53  *
54  * @ingroup NetComm
55  * @author Tim Niemueller
56  */
57 
58 /** Constructor. */
60  : Thread("AvahiThread")
61 {
62  simple_poll = NULL;
63  client = NULL;
64 
65  need_recover = false;
66  do_reset_groups = false;
67 
68  init_wc = new WaitCondition();
69 
71 }
72 
73 
74 /** Destructor. */
76 {
77  delete init_wc;
78 
79  remove_pending_services();
80  remove_pending_browsers();
81 
82  erase_groups();
83  erase_browsers();
84 
85  if ( client )
86  avahi_client_free( client );
87 
88  if ( simple_poll )
89  avahi_simple_poll_free( simple_poll );
90 
91 }
92 
93 
94 /** Avahi thread loop.
95  * The avahi thread calls the simple poll iterate to poll with an infinite
96  * timeout. This way the loop blocks until an event occurs.
97  */
98 void
100 {
101  if ( need_recover ) {
102  if ( client ) {
103  avahi_client_free( client );
104  client = NULL;
105  }
106 
107  if ( simple_poll ) {
108  avahi_simple_poll_free( simple_poll );
109  simple_poll = NULL;
110  }
111  }
112 
113  if ( ! simple_poll ) {
114  // Init
115  int error;
116 
117  if ( (simple_poll = avahi_simple_poll_new()) ) {
118 
119  client = avahi_client_new( avahi_simple_poll_get(simple_poll), AVAHI_CLIENT_NO_FAIL,
120  AvahiThread::client_callback, this, &error );
121 
122  if ( ! client ) {
123  avahi_simple_poll_free( simple_poll );
124  }
125  }
126  }
127 
128  if ( client ) {
129  if ( do_reset_groups ) {
130  reset_groups();
131  recreate_services();
132  }
133  if ( need_recover ) {
134  erase_groups();
135  erase_browsers();
136  recreate_services();
137  recreate_browsers();
138  }
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();
146  }
147 
148  need_recover = false;
149 
150  avahi_simple_poll_iterate( simple_poll, -1);
151  }
152 }
153 
154 
155 /** Recover froma broken Avahi connection.
156  * This will erase all service browsers and announced service groups
157  * and will try to reconnect in the next loop.
158  */
159 void
160 AvahiThread::recover()
161 {
162  need_recover = true;
163  wake_poller();
164 }
165 
166 void
167 AvahiThread::wake_poller()
168 {
169  if ( simple_poll ) {
170  avahi_simple_poll_wakeup( simple_poll );
171  }
172 }
173 
174 
175 /** Called whenever the client or server state changes.
176  * @param c Avahi client
177  * @param state new state
178  * @param instance Instance of AvahiThread that triggered the event.
179  */
180 void
181 AvahiThread::client_callback(AvahiClient *c, AvahiClientState state, void *instance)
182 {
183  AvahiThread *at = static_cast<AvahiThread *>(instance);
184  at->client_state = state;
185 
186  switch (state) {
187  case AVAHI_CLIENT_S_RUNNING:
188  /* The server has startup successfully and registered its host
189  * name on the network, so it's time to create our services */
190  //printf("(Client): RUNNING\n");
191  //at->create_browsers();
192  //at->set_available( true );
193  at->init_done();
194  break;
195 
196  case AVAHI_CLIENT_S_COLLISION:
197  //printf("(Client): COLLISION\n");
198  /* Let's drop our registered services. When the server is back
199  * in AVAHI_SERVER_RUNNING state we will register them
200  * again with the new host name. */
201  at->do_reset_groups = true;
202  break;
203 
204  case AVAHI_CLIENT_FAILURE:
205  // Doh!
206  //printf("(Client): FAILURE\n");
207  at->recover();
208  break;
209 
210  case AVAHI_CLIENT_CONNECTING:
211  //printf("(Client): CONNECTING\n");
212  break;
213 
214  case AVAHI_CLIENT_S_REGISTERING:
215  // Ignored
216  //printf("(Client): REGISTERING\n");
217  break;
218  }
219 }
220 
221 /* **********************************************************************************
222  * Avahi Service Publisher methods
223  * **********************************************************************************/
224 
225 
226 /** Publish service.
227  * @param service service to publish.
228  */
229 void
231 {
232  if ( __services.find(service) == __services.end() ) {
233  __pending_services.push_locked(service);
234  } else {
235  throw Exception("Service already registered");
236  }
237 
238  wake_poller();
239 }
240 
241 
242 void
244 {
245  if ( __services.find(service) != __services.end() ) {
246  __pending_remove_services.push_locked(service);
247  } else {
248  throw Exception("Service not registered");
249  }
250 
251  wake_poller();
252 }
253 
254 
255 /** Create services. */
256 AvahiEntryGroup *
257 AvahiThread::create_service(const NetworkService &service, AvahiEntryGroup *exgroup)
258 {
259  // the following errors are non-fatal, they can happen since Avahi is started
260  // asynchronously, just ignore them by bailing out
261  if ( ! client ) return NULL;
262 
263  AvahiEntryGroup *group;
264  if ( exgroup ) {
265  group = exgroup;
266  } else {
267  if ( ! (group = avahi_entry_group_new(client,
268  AvahiThread::entry_group_callback,
269  this))) {
270  throw NullPointerException("Cannot create service group");
271  }
272  }
273 
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());
278  }
279 
280  // only IPv4 for now
281  int rv = AVAHI_ERR_COLLISION;
282  for (int i = 1; (i <= 100) && (rv == AVAHI_ERR_COLLISION); ++i) {
283  std::string name = service.name();
284  if (i > 1) {
285  name += " ";
286  name += StringConversions::to_string(i);
287  }
288 
289  rv = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC,
290  AVAHI_PROTO_INET,
291  AVAHI_PUBLISH_USE_MULTICAST,
292  name.c_str(), service.type(),
293  service.domain(),
294  service.host(),
295  service.port(), al);
296 
297  if ((i > 1) && (rv >= 0)) {
298  service.set_modified_name(name.c_str());
299  }
300  }
301 
302  avahi_string_list_free(al);
303 
304  if (rv < 0) {
305  throw Exception("Adding Avahi/mDNS-SD service failed: %s", avahi_strerror(rv));
306  }
307 
308  /*
309  if (service.modified_name() != 0) {
310  LibLogger::log_warn("FawkesNetworkManager", "Network service name collision, "
311  "modified to '%s' (from '%s')", service.modified_name(),
312  service.name());
313  }
314  */
315 
316  /* Tell the server to register the service */
317  if (avahi_entry_group_commit(group) < 0) {
318  throw Exception("Registering Avahi services failed");
319  }
320 
321  return group;
322 }
323 
324 void
325 AvahiThread::recreate_services()
326 {
327  for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
328  (*__sit).second = create_service(__sit->first, __sit->second);
329  }
330 }
331 
332 
333 void
334 AvahiThread::create_pending_services()
335 {
336  __pending_services.lock();
337  while ( ! __pending_services.empty()) {
338  NetworkService &s = __pending_services.front();
339  __services[s] = create_service(s, NULL);
340  __pending_services.pop();
341  }
342  __pending_services.unlock();
343 }
344 
345 
346 void
347 AvahiThread::remove_pending_services()
348 {
349  Thread::CancelState old_state;
350  set_cancel_state(CANCEL_DISABLED, &old_state);
351  __pending_remove_services.lock();
352  while ( ! __pending_remove_services.empty()) {
353  NetworkService &s = __pending_remove_services.front();
354  if ( __services.find(s) != __services.end() ) {
355  group_erase(__services[s]);
356  __services.erase_locked(s);
357  }
358  __pending_remove_services.pop();
359  }
360  __pending_remove_services.unlock();
361  set_cancel_state(old_state);
362 }
363 
364 
365 /** Drop our registered services.
366  * When the server is back in AVAHI_SERVER_RUNNING state we will register them
367  * again with the new host name (triggered by AvahiThread).
368  */
369 void
370 AvahiThread::group_reset(AvahiEntryGroup *g)
371 {
372  if ( g ) {
373  avahi_entry_group_reset(g);
374  }
375 }
376 
377 
378 /** Erase service group. */
379 void
380 AvahiThread::group_erase(AvahiEntryGroup *g)
381 {
382  if ( g ) {
383  avahi_entry_group_reset( g );
384  avahi_entry_group_free( g );
385  }
386 }
387 
388 
389 void
390 AvahiThread::erase_groups()
391 {
392  for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
393  if (__sit->second) group_erase(__sit->second);
394  __sit->second = NULL;
395  }
396 }
397 
398 
399 void
400 AvahiThread::reset_groups()
401 {
402  for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
403  group_reset((*__sit).second);
404  }
405 }
406 
407 
408 /** Called if there was a name collision. */
409 void
410 AvahiThread::name_collision(AvahiEntryGroup *g)
411 {
412  for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
413  if ( (*__sit).second == g ) {
414  NetworkService alternate_service((*__sit).first);
415 
416  /* A service name collision happened. Let's pick a new name */
417  char *n = avahi_alternative_service_name((*__sit).first.name());
418  alternate_service.set_name(n);
419  avahi_free(n);
420 
421  __pending_remove_services.push_locked((*__sit).first);
422  __pending_services.push_locked(alternate_service);
423  }
424  }
425 }
426 
427 
428 /** Callback for Avahi.
429  * @param g entry group
430  * @param state new state
431  * @param instance instance of AvahiThread that triggered the event.
432  */
433 void
434 AvahiThread::entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
435  void *instance)
436 {
437  AvahiThread *at = static_cast<AvahiThread *>(instance);
438 
439  switch (state) {
440  case AVAHI_ENTRY_GROUP_ESTABLISHED :
441  /* The entry group has been established successfully */
442  //fprintf(stderr, "Service '%s' successfully established.\n", name);
443  break;
444 
445  case AVAHI_ENTRY_GROUP_COLLISION : {
446  at->name_collision(g);
447  break;
448  }
449 
450  case AVAHI_ENTRY_GROUP_FAILURE :
451  /* Some kind of failure happened while we were registering our services */
452  at->recover();
453  break;
454 
455  case AVAHI_ENTRY_GROUP_UNCOMMITED:
456  case AVAHI_ENTRY_GROUP_REGISTERING:
457  break;
458  }
459 }
460 
461 
462 /* **********************************************************************************
463  * Avahi Browser Publisher methods
464  * **********************************************************************************/
465 
466 
467 /** Add a result handler.
468  * A handler is added for the given service type. A search is initiated
469  * for the given service and the given handler is called for added or
470  * removed services or if an error occurs.
471  * @param service_type string of the service type
472  * @param h The ServiceBrowseHandler
473  */
474 void
476 {
477  __handlers[service_type].push_back(h);
478  __pending_browsers.push_locked(service_type);
479 
480  wake_poller();
481 }
482 
483 
484 /** Remove a handler.
485  * The handler is removed and no further events will be emitted to the
486  * handler.
487  * @param service_type service type to de-register the handler for
488  * @param h the handler
489  */
490 void
492 {
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);
498  //avahi_service_browser_free(__browsers[service_type]);
499  //__browsers.erase(service_type);
500  }
501  __handlers.erase(service_type);
502  }
503  }
504 
505  wake_poller();
506 }
507 
508 
509 /** Create browser for a given service.
510  * @param service_type service type
511  */
512 void
513 AvahiThread::create_browser(const char *service_type)
514 {
515  if ( __browsers.find(service_type) == __browsers.end() ) {
516  if ( client ) {
517  AvahiServiceBrowser *b = avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
518  AVAHI_PROTO_UNSPEC,
519  service_type, NULL, (AvahiLookupFlags)0,
520  AvahiThread::browse_callback, this);
521 
522  if ( ! b ) {
523  __handlers[service_type].pop_back();
524  throw NullPointerException("Could not instantiate AvahiServiceBrowser");
525  }
526  __browsers[service_type] = b;
527  }
528  }
529 }
530 
531 
532 /** Create browsers.
533  * Creates browser for all services.
534  */
535 void
536 AvahiThread::recreate_browsers()
537 {
539  for (i = __handlers.begin(); i != __handlers.end(); ++i) {
540  create_browser( (*i).first.c_str() );
541  }
542 }
543 
544 
545 void
546 AvahiThread::create_pending_browsers()
547 {
548  __pending_browsers.lock();
549  while ( ! __pending_browsers.empty() ) {
550  //printf("Creating browser for %s\n", __pending_browsers.front().c_str());
551  create_browser(__pending_browsers.front().c_str());
552  __pending_browsers.pop();
553  }
554  __pending_browsers.unlock();
555 }
556 
557 
558 void
559 AvahiThread::remove_pending_browsers()
560 {
561  Thread::CancelState old_state;
562  set_cancel_state(CANCEL_DISABLED, &old_state);
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]);
567  __browsers.erase_locked(s);
568  __pending_browser_removes.pop();
569  }
570  __pending_browser_removes.unlock();
571  set_cancel_state(old_state);
572 }
573 
574 
575 /** Erase all browsers. */
576 void
577 AvahiThread::erase_browsers()
578 {
579  std::map< std::string, AvahiServiceBrowser * >::iterator i;
580  for (i = __browsers.begin(); i != __browsers.end(); ++i) {
581  avahi_service_browser_free((*i).second);
582  }
583  __browsers.clear();
584 }
585 
586 
587 /** Call handler for a removed service.
588  * @param name name
589  * @param type type
590  * @param domain domain
591  */
592 void
593 AvahiThread::call_handler_service_removed( const char *name,
594  const char *type,
595  const char *domain)
596 {
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);
601  }
602  }
603 }
604 
605 
606 /** Call handler for an added service.
607  * @param name name
608  * @param type type
609  * @param domain domain
610  * @param host_name host name
611  * @param address address of host
612  * @param port port of service
613  * @þaram txt list of TXT records
614  * @param flags flags
615  */
616 void
617 AvahiThread::call_handler_service_added( const char *name,
618  const char *type,
619  const char *domain,
620  const char *host_name,
621  const AvahiAddress *address,
622  uint16_t port,
623  std::list<std::string> &txt,
624  AvahiLookupResultFlags flags)
625 {
626  struct sockaddr_in *s = NULL;
627  socklen_t slen;
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;
632  } else {
633  // ignore
634  return;
635  }
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);
641  }
642  }
643  free(s);
644 }
645 
646 
647 /** Call handler for failure.
648  * @param name name
649  * @param type type
650  * @param domain domain
651  */
652 void
653 AvahiThread::call_handler_failed( const char *name,
654  const char *type,
655  const char *domain)
656 {
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);
661  }
662  }
663 }
664 
665 
666 /** Call handler "all for now".
667  * @param type type
668  */
669 void
670 AvahiThread::call_handler_all_for_now(const char *type)
671 {
672  if ( __handlers.find(type) != __handlers.end() ) {
673  std::list<ServiceBrowseHandler *>::iterator i;
674  for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
675  (*i)->all_for_now();
676  }
677  }
678 }
679 
680 
681 /** Call handler "cache exhausted".
682  * @param type type
683  */
684 void
685 AvahiThread::call_handler_cache_exhausted(const char *type)
686 {
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();
691  }
692  }
693 }
694 
695 
696 /** Callback for Avahi.
697  * Callback called by Avahi.
698  * @param b service browser
699  * @param interface interface index
700  * @param protocol protocol
701  * @param event event
702  * @param name name
703  * @param type type
704  * @param domain domain
705  * @param flags flags
706  * @param instance pointer to the AvahiThread instance that initiated
707  * the search
708  */
709 void
710 AvahiThread::browse_callback( AvahiServiceBrowser *b,
711  AvahiIfIndex interface,
712  AvahiProtocol protocol,
713  AvahiBrowserEvent event,
714  const char *name,
715  const char *type,
716  const char *domain,
717  AvahiLookupResultFlags flags,
718  void *instance)
719 {
720  AvahiThread *at = static_cast<AvahiThread *>(instance);
721 
722  switch (event) {
723  case AVAHI_BROWSER_FAILURE:
724  //printf("(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
725  return;
726 
727  case AVAHI_BROWSER_NEW:
728  //printf("(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
729  // We ignore the returned resolver object. In the callback
730  // function we free it. If the server is terminated before
731  // the callback function is called the server will free
732  // the resolver for us.
733  if (!(avahi_service_resolver_new(at->client, interface, protocol,
734  name, type, domain, protocol, (AvahiLookupFlags)0,
735  AvahiThread::resolve_callback, instance))) {
736  throw NullPointerException("Could not instantiate resolver");
737  }
738  break;
739 
740  case AVAHI_BROWSER_REMOVE:
741  // handler
742  //printf("(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
743  at->call_handler_service_removed(name, type, domain);
744  break;
745 
746  case AVAHI_BROWSER_ALL_FOR_NOW:
747  // handler
748  //printf("(Browser) ALL_FOR_NOW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
749  at->call_handler_all_for_now(type);
750  break;
751 
752  case AVAHI_BROWSER_CACHE_EXHAUSTED:
753  // handler
754  //printf("(Browser) CACHE_EXHAUSTED: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
755  at->call_handler_cache_exhausted(type);
756  break;
757 
758  }
759 }
760 
761 
762 /** Callback for Avahi.
763  * Callback called by Avahi.
764  * @param r service resolver
765  * @param interface interface index
766  * @param protocol protocol
767  * @param event event
768  * @param name name
769  * @param type type
770  * @param domain domain
771  * @param host_name host name
772  * @param address address
773  * @param port port
774  * @param txt TXT records
775  * @param flags flags
776  * @param instance pointer to the AvahiThread instance that initiated
777  * the search
778  */
779 void
780 AvahiThread::resolve_callback( AvahiServiceResolver *r,
781  AVAHI_GCC_UNUSED AvahiIfIndex interface,
782  AVAHI_GCC_UNUSED AvahiProtocol protocol,
783  AvahiResolverEvent event,
784  const char *name,
785  const char *type,
786  const char *domain,
787  const char *host_name,
788  const AvahiAddress *address,
789  uint16_t port,
790  AvahiStringList *txt,
791  AvahiLookupResultFlags flags,
792  void *instance)
793 {
794  AvahiThread *at = static_cast<AvahiThread *>(instance);
795 
796  switch (event) {
797  case AVAHI_RESOLVER_FAILURE:
798  // handler failure
799  at->call_handler_failed(name, type, domain);
800  break;
801 
802  case AVAHI_RESOLVER_FOUND:
803  // handler add
804  {
805  std::list< std::string > txts;
806  AvahiStringList *l = txt;
807 
808  txts.clear();
809  while ( l ) {
810  txts.push_back((char *)avahi_string_list_get_text(l));
811  l = avahi_string_list_get_next( l );
812  }
813 
814  at->call_handler_service_added(name, type, domain, host_name, address, port, txts, flags);
815  }
816  break;
817  }
818 
819  avahi_service_resolver_free(r);
820 }
821 
822 
823 /* **********************************************************************************
824  * Avahi resolver methods
825  * **********************************************************************************/
826 
827 
828 /** Order name resolution.
829  * This initiates resolution of a name. The method immediately returns and will not
830  * wait for the result.
831  * @param name name to resolve.
832  * @param handler handler to call for the result
833  */
834 void
836 {
837  AvahiResolverCallbackData *data = new AvahiResolverCallbackData(this, handler);
838 
839  if ( __pending_hostname_resolves.find(name) == __pending_hostname_resolves.end()) {
840  __pending_hostname_resolves[name] = data;
841  }
842 
843  wake_poller();
844 }
845 
846 
847 void
848 AvahiThread::start_hostname_resolver(const char *name, AvahiResolverCallbackData *data)
849 {
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,
855  data) ) == NULL ) {
856  throw Exception("Cannot create Avahi name resolver");
857  } else {
858  __running_hostname_resolvers.push_back(resolver);
859  }
860 
861 }
862 
863 
864 void
865 AvahiThread::start_hostname_resolvers()
866 {
867  for (__phrit = __pending_hostname_resolves.begin(); __phrit != __pending_hostname_resolves.end(); ++__phrit) {
868  start_hostname_resolver((*__phrit).first.c_str(), (*__phrit).second);
869  }
870  __pending_hostname_resolves.clear();
871 }
872 
873 
874 void
875 AvahiThread::start_address_resolvers()
876 {
877  for (__parit = __pending_address_resolves.begin(); __parit != __pending_address_resolves.end(); ++__parit) {
878  start_address_resolver((*__parit).first, (*__parit).second);
879  }
880  __pending_address_resolves.clear();
881 }
882 
883 
884 /** Order address resolution.
885  * This initiates resolution of an address. The method immediately returns and will not
886  * wait for the result.
887  * @param addr address to resolve, currently only struct sockaddr_in is supported (IPv4)
888  * @param addrlen length of addr in bytes
889  * @param handler handler to call for the result
890  */
891 void
892 AvahiThread::resolve_address(struct sockaddr *addr, socklen_t addrlen,
893  AvahiResolverHandler *handler)
894 {
895  if ( addrlen != sizeof(struct sockaddr_in) ) {
896  throw Exception("Only IPv4 is currently supported");
897  }
898 
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);
902 
903  __pending_address_resolves[in_addr] = data;
904  wake_poller();
905 }
906 
907 
908 void
909 AvahiThread::start_address_resolver(struct sockaddr_in *in_addr, AvahiResolverCallbackData *data)
910 {
911  AvahiAddress a;
912  a.proto = AVAHI_PROTO_INET;
913  a.data.ipv4.address = in_addr->sin_addr.s_addr;
914 
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,
919  data) ) == NULL ) {
920  Exception e("Cannot create Avahi address resolver");
921  e.append("Avahi error: %s", avahi_strerror(avahi_client_errno(client)));
922  throw e;
923  } else {
924  __running_address_resolvers.push_back_locked(resolver);
925  }
926 }
927 
928 
929 /** Remove hostname resolver.
930  * Used internally by callback.
931  * @param r resolver
932  */
933 void
934 AvahiThread::remove_hostname_resolver(AvahiHostNameResolver *r)
935 {
936  __running_hostname_resolvers.remove_locked(r);
937 }
938 
939 
940 /** Remove address resolver.
941  * Used internally by callback.
942  * @param r resolver
943  */
944 void
945 AvahiThread::remove_address_resolver(AvahiAddressResolver *r)
946 {
947  __running_address_resolvers.remove_locked(r);
948 }
949 
950 
951 /** Internal callback.
952  * Callback for avahi.
953  */
954 void
955 AvahiThread::host_name_resolver_callback(AvahiHostNameResolver *r,
956  AvahiIfIndex interface,
957  AvahiProtocol protocol,
958  AvahiResolverEvent event,
959  const char *name,
960  const AvahiAddress *a,
961  AvahiLookupResultFlags flags,
962  void *userdata)
963 {
964  AvahiResolverCallbackData *cd = static_cast<AvahiResolverCallbackData *>(userdata);
965 
966  cd->first->remove_hostname_resolver(r);
967  avahi_host_name_resolver_free(r);
968 
969  switch (event) {
970  case AVAHI_RESOLVER_FOUND:
971  {
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;
975 
976  cd->second->resolved_name(strdup(name), (struct sockaddr *)res, sizeof(struct sockaddr_in));
977  }
978  break;
979 
980  case AVAHI_RESOLVER_FAILURE:
981  default:
982  cd->second->name_resolution_failed(strdup(name));
983  break;
984  }
985 
986  delete cd;
987 }
988 
989 
990 /** Internal callback.
991  * Callback for avahi.
992  */
993 void
994 AvahiThread::address_resolver_callback(AvahiAddressResolver *r,
995  AvahiIfIndex interface,
996  AvahiProtocol protocol,
997  AvahiResolverEvent event,
998  const AvahiAddress *a,
999  const char *name,
1000  AvahiLookupResultFlags flags,
1001  void *userdata)
1002 {
1003  AvahiResolverCallbackData *cd = static_cast<AvahiResolverCallbackData *>(userdata);
1004 
1005  cd->first->remove_address_resolver(r);
1006  avahi_address_resolver_free(r);
1007 
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;
1011 
1012  switch (event) {
1013  case AVAHI_RESOLVER_FOUND:
1014  cd->second->resolved_address((struct sockaddr_in *)res, sizeof(struct sockaddr_in),
1015  strdup(name));
1016  break;
1017  case AVAHI_RESOLVER_FAILURE:
1018  default:
1019  cd->second->address_resolution_failed((struct sockaddr_in *)res,
1020  sizeof(struct sockaddr_in));
1021  break;
1022  }
1023 
1024  delete cd;
1025 }
1026 
1027 
1028 /** Unlocks init lock.
1029  * Only to be called by client_callback().
1030  */
1031 void
1032 AvahiThread::init_done()
1033 {
1034  wake_poller();
1035  init_wc->wake_all();
1036 }
1037 
1038 
1039 /** Waits for the AvahiThread to be initialized.
1040  * You can use this if you want to wait until the thread has been
1041  * fully initialized and may be used. Since the happens in this thread
1042  * it is in general not immediately ready after start().
1043  * This will block the calling thread until the AvahiThread has
1044  * been initialized. This is done by waiting for a release of an
1045  * initialization mutex.
1046  */
1047 void
1049 {
1050  init_wc->wait();
1051 }
1052 
1053 } // end namespace fawkes
void unlock() const
Unlock list.
Definition: lock_queue.h:131
Wait until a given condition holds.
void erase_locked(const KeyType &key)
Remove item with lock.
Definition: lock_map.h:132
~AvahiThread()
Destructor.
const char * type() const
Get type of service.
Definition: service.cpp:353
virtual void loop()
Avahi thread loop.
Fawkes library namespace.
void wake_all()
Wake up all waiting threads.
thread cannot be cancelled
Definition: thread.h:62
A NULL pointer was supplied where not allowed.
Definition: software.h:34
Thread class encapsulation of pthreads.
Definition: thread.h:42
void set_prepfin_conc_loop(bool concurrent=true)
Set concurrent execution of prepare_finalize() and loop().
Definition: thread.cpp:715
void push_back_locked(const Type &x)
Push element to list at back with lock protection.
Definition: lock_list.h:152
void remove_locked(const Type &x)
Remove element from list with lock protection.
Definition: lock_list.h:172
static void set_cancel_state(CancelState new_state, CancelState *old_state=0)
Set the cancel state of the current thread.
Definition: thread.cpp:1327
Interface for class that process browse results.
Map with a lock.
Definition: lock_map.h:37
Avahi resolver handler interface.
void publish_service(NetworkService *service)
Publish service.
void set_name(const char *new_name)
Set name of service.
Definition: service.cpp:306
void resolve_address(struct sockaddr *addr, socklen_t addrlen, AvahiResolverHandler *handler)
Order address resolution.
const char * host() const
Get host of service.
Definition: service.cpp:373
Base class for exceptions in Fawkes.
Definition: exception.h:36
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.
Definition: service.cpp:329
Avahi main thread.
Definition: avahi_thread.h:55
unsigned short int port() const
Get port of service.
Definition: service.cpp:383
void wait()
Wait for the condition forever.
const char * name() const
Get name of thread.
Definition: thread.h:95
Representation of a service announced or found via service discovery (i.e.
Definition: service.h:37
const char * domain() const
Get domain of service.
Definition: service.cpp:363
AvahiThread()
Constructor.
void lock() const
Lock queue.
Definition: lock_queue.h:115
void push_locked(const Type &x)
Push element to queue with lock protection.
Definition: lock_queue.h:139
const std::list< std::string > & txt() const
Get TXT record list of service.
Definition: service.cpp:406
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.
Definition: exception.cpp:341
void wait_initialized()
Waits for the AvahiThread to be initialized.
const char * name() const
Get name of service.
Definition: service.cpp:317
void unwatch_service(const char *service_type, ServiceBrowseHandler *h)
Remove a handler.
CancelState
Cancel state.
Definition: thread.h:60