PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/libs/netcomm/dns-sd/avahi_thread.cpp

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