Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * avahi_thread.cpp - Avahi thread 00004 * 00005 * Created: Wed Nov 08 11:19:25 2006 00006 * Copyright 2006-2011 Tim Niemueller [www.niemueller.de] 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. A runtime exception applies to 00014 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU Library General Public License for more details. 00020 * 00021 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00022 */ 00023 00024 #include <netcomm/dns-sd/avahi_thread.h> 00025 #include <netcomm/dns-sd/avahi_resolver_handler.h> 00026 00027 #include <core/threading/mutex.h> 00028 #include <core/threading/wait_condition.h> 00029 #include <core/exceptions/software.h> 00030 #include <utils/misc/string_conversions.h> 00031 00032 #include <avahi-client/lookup.h> 00033 #include <avahi-client/publish.h> 00034 #include <avahi-common/alternative.h> 00035 #include <avahi-common/simple-watch.h> 00036 #include <avahi-common/malloc.h> 00037 #include <avahi-common/error.h> 00038 #include <avahi-common/timeval.h> 00039 00040 #include <sys/socket.h> 00041 #include <sys/types.h> 00042 #include <netinet/in.h> 00043 #include <cstdlib> 00044 #include <cstddef> 00045 #include <cstring> 00046 00047 namespace fawkes { 00048 00049 /** @class AvahiThread netcomm/dns-sd/avahi_thread.h 00050 * Avahi main thread. 00051 * This thread handles all tasks related to avahi. This is the single 00052 * interaction point with the Avahi adapter. 00053 * 00054 * @ingroup NetComm 00055 * @author Tim Niemueller 00056 */ 00057 00058 /** Constructor. */ 00059 AvahiThread::AvahiThread() 00060 : Thread("AvahiThread") 00061 { 00062 simple_poll = NULL; 00063 client = NULL; 00064 00065 need_recover = false; 00066 do_reset_groups = false; 00067 00068 init_wc = new WaitCondition(); 00069 00070 set_prepfin_conc_loop(true); 00071 } 00072 00073 00074 /** Destructor. */ 00075 AvahiThread::~AvahiThread() 00076 { 00077 delete init_wc; 00078 00079 remove_pending_services(); 00080 remove_pending_browsers(); 00081 00082 erase_groups(); 00083 erase_browsers(); 00084 00085 if ( client ) 00086 avahi_client_free( client ); 00087 00088 if ( simple_poll ) 00089 avahi_simple_poll_free( simple_poll ); 00090 00091 } 00092 00093 00094 /** Avahi thread loop. 00095 * The avahi thread calls the simple poll iterate to poll with an infinite 00096 * timeout. This way the loop blocks until an event occurs. 00097 */ 00098 void 00099 AvahiThread::loop() 00100 { 00101 if ( need_recover ) { 00102 if ( client ) { 00103 avahi_client_free( client ); 00104 client = NULL; 00105 } 00106 00107 if ( simple_poll ) { 00108 avahi_simple_poll_free( simple_poll ); 00109 simple_poll = NULL; 00110 } 00111 } 00112 00113 if ( ! simple_poll ) { 00114 // Init 00115 int error; 00116 00117 if ( (simple_poll = avahi_simple_poll_new()) ) { 00118 00119 client = avahi_client_new( avahi_simple_poll_get(simple_poll), AVAHI_CLIENT_NO_FAIL, 00120 AvahiThread::client_callback, this, &error ); 00121 00122 if ( ! client ) { 00123 avahi_simple_poll_free( simple_poll ); 00124 } 00125 } 00126 } 00127 00128 if ( client ) { 00129 if ( do_reset_groups ) { 00130 reset_groups(); 00131 recreate_services(); 00132 } 00133 if ( need_recover ) { 00134 erase_groups(); 00135 erase_browsers(); 00136 recreate_services(); 00137 recreate_browsers(); 00138 } 00139 if ( client_state == AVAHI_CLIENT_S_RUNNING ) { 00140 remove_pending_services(); 00141 remove_pending_browsers(); 00142 create_pending_services(); 00143 create_pending_browsers(); 00144 start_hostname_resolvers(); 00145 start_address_resolvers(); 00146 } 00147 00148 need_recover = false; 00149 00150 avahi_simple_poll_iterate( simple_poll, -1); 00151 } 00152 } 00153 00154 00155 /** Recover froma broken Avahi connection. 00156 * This will erase all service browsers and announced service groups 00157 * and will try to reconnect in the next loop. 00158 */ 00159 void 00160 AvahiThread::recover() 00161 { 00162 need_recover = true; 00163 wake_poller(); 00164 } 00165 00166 void 00167 AvahiThread::wake_poller() 00168 { 00169 if ( simple_poll ) { 00170 avahi_simple_poll_wakeup( simple_poll ); 00171 } 00172 } 00173 00174 00175 /** Called whenever the client or server state changes. 00176 * @param c Avahi client 00177 * @param state new state 00178 * @param instance Instance of AvahiThread that triggered the event. 00179 */ 00180 void 00181 AvahiThread::client_callback(AvahiClient *c, AvahiClientState state, void *instance) 00182 { 00183 AvahiThread *at = static_cast<AvahiThread *>(instance); 00184 at->client_state = state; 00185 00186 switch (state) { 00187 case AVAHI_CLIENT_S_RUNNING: 00188 /* The server has startup successfully and registered its host 00189 * name on the network, so it's time to create our services */ 00190 //printf("(Client): RUNNING\n"); 00191 //at->create_browsers(); 00192 //at->set_available( true ); 00193 at->init_done(); 00194 break; 00195 00196 case AVAHI_CLIENT_S_COLLISION: 00197 //printf("(Client): COLLISION\n"); 00198 /* Let's drop our registered services. When the server is back 00199 * in AVAHI_SERVER_RUNNING state we will register them 00200 * again with the new host name. */ 00201 at->do_reset_groups = true; 00202 break; 00203 00204 case AVAHI_CLIENT_FAILURE: 00205 // Doh! 00206 //printf("(Client): FAILURE\n"); 00207 at->recover(); 00208 break; 00209 00210 case AVAHI_CLIENT_CONNECTING: 00211 //printf("(Client): CONNECTING\n"); 00212 break; 00213 00214 case AVAHI_CLIENT_S_REGISTERING: 00215 // Ignored 00216 //printf("(Client): REGISTERING\n"); 00217 break; 00218 } 00219 } 00220 00221 /* ********************************************************************************** 00222 * Avahi Service Publisher methods 00223 * **********************************************************************************/ 00224 00225 00226 /** Publish service. 00227 * @param service service to publish. 00228 */ 00229 void 00230 AvahiThread::publish_service(NetworkService *service) 00231 { 00232 if ( __services.find(service) == __services.end() ) { 00233 __pending_services.push_locked(service); 00234 } else { 00235 throw Exception("Service already registered"); 00236 } 00237 00238 wake_poller(); 00239 } 00240 00241 00242 void 00243 AvahiThread::unpublish_service(NetworkService *service) 00244 { 00245 if ( __services.find(service) != __services.end() ) { 00246 __pending_remove_services.push_locked(service); 00247 } else { 00248 throw Exception("Service not registered"); 00249 } 00250 00251 wake_poller(); 00252 } 00253 00254 00255 /** Create services. */ 00256 AvahiEntryGroup * 00257 AvahiThread::create_service(const NetworkService &service, AvahiEntryGroup *exgroup) 00258 { 00259 // the following errors are non-fatal, they can happen since Avahi is started 00260 // asynchronously, just ignore them by bailing out 00261 if ( ! client ) return NULL; 00262 00263 AvahiEntryGroup *group; 00264 if ( exgroup ) { 00265 group = exgroup; 00266 } else { 00267 if ( ! (group = avahi_entry_group_new(client, 00268 AvahiThread::entry_group_callback, 00269 this))) { 00270 throw NullPointerException("Cannot create service group"); 00271 } 00272 } 00273 00274 AvahiStringList *al = NULL; 00275 const std::list<std::string> &l = service.txt(); 00276 for (std::list<std::string>::const_iterator j = l.begin(); j != l.end(); ++j) { 00277 al = avahi_string_list_add(al, j->c_str()); 00278 } 00279 00280 // only IPv4 for now 00281 int rv = AVAHI_ERR_COLLISION; 00282 for (int i = 1; (i <= 100) && (rv == AVAHI_ERR_COLLISION); ++i) { 00283 std::string name = service.name(); 00284 if (i > 1) { 00285 name += " "; 00286 name += StringConversions::to_string(i); 00287 } 00288 00289 rv = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, 00290 AVAHI_PROTO_INET, 00291 AVAHI_PUBLISH_USE_MULTICAST, 00292 name.c_str(), service.type(), 00293 service.domain(), 00294 service.host(), 00295 service.port(), al); 00296 00297 if ((i > 1) && (rv >= 0)) { 00298 service.set_modified_name(name.c_str()); 00299 } 00300 } 00301 00302 avahi_string_list_free(al); 00303 00304 if (rv < 0) { 00305 throw Exception("Adding Avahi/mDNS-SD service failed: %s", avahi_strerror(rv)); 00306 } 00307 00308 /* 00309 if (service.modified_name() != 0) { 00310 LibLogger::log_warn("FawkesNetworkManager", "Network service name collision, " 00311 "modified to '%s' (from '%s')", service.modified_name(), 00312 service.name()); 00313 } 00314 */ 00315 00316 /* Tell the server to register the service */ 00317 if (avahi_entry_group_commit(group) < 0) { 00318 throw Exception("Registering Avahi services failed"); 00319 } 00320 00321 return group; 00322 } 00323 00324 void 00325 AvahiThread::recreate_services() 00326 { 00327 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) { 00328 (*__sit).second = create_service(__sit->first, __sit->second); 00329 } 00330 } 00331 00332 00333 void 00334 AvahiThread::create_pending_services() 00335 { 00336 __pending_services.lock(); 00337 while ( ! __pending_services.empty()) { 00338 NetworkService &s = __pending_services.front(); 00339 __services[s] = create_service(s, NULL); 00340 __pending_services.pop(); 00341 } 00342 __pending_services.unlock(); 00343 } 00344 00345 00346 void 00347 AvahiThread::remove_pending_services() 00348 { 00349 Thread::CancelState old_state; 00350 set_cancel_state(CANCEL_DISABLED, &old_state); 00351 __pending_remove_services.lock(); 00352 while ( ! __pending_remove_services.empty()) { 00353 NetworkService &s = __pending_remove_services.front(); 00354 if ( __services.find(s) != __services.end() ) { 00355 group_erase(__services[s]); 00356 __services.erase_locked(s); 00357 } 00358 __pending_remove_services.pop(); 00359 } 00360 __pending_remove_services.unlock(); 00361 set_cancel_state(old_state); 00362 } 00363 00364 00365 /** Drop our registered services. 00366 * When the server is back in AVAHI_SERVER_RUNNING state we will register them 00367 * again with the new host name (triggered by AvahiThread). 00368 */ 00369 void 00370 AvahiThread::group_reset(AvahiEntryGroup *g) 00371 { 00372 if ( g ) { 00373 avahi_entry_group_reset(g); 00374 } 00375 } 00376 00377 00378 /** Erase service group. */ 00379 void 00380 AvahiThread::group_erase(AvahiEntryGroup *g) 00381 { 00382 if ( g ) { 00383 avahi_entry_group_reset( g ); 00384 avahi_entry_group_free( g ); 00385 } 00386 } 00387 00388 00389 void 00390 AvahiThread::erase_groups() 00391 { 00392 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) { 00393 if (__sit->second) group_erase(__sit->second); 00394 __sit->second = NULL; 00395 } 00396 } 00397 00398 00399 void 00400 AvahiThread::reset_groups() 00401 { 00402 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) { 00403 group_reset((*__sit).second); 00404 } 00405 } 00406 00407 00408 /** Called if there was a name collision. */ 00409 void 00410 AvahiThread::name_collision(AvahiEntryGroup *g) 00411 { 00412 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) { 00413 if ( (*__sit).second == g ) { 00414 NetworkService alternate_service((*__sit).first); 00415 00416 /* A service name collision happened. Let's pick a new name */ 00417 char *n = avahi_alternative_service_name((*__sit).first.name()); 00418 alternate_service.set_name(n); 00419 avahi_free(n); 00420 00421 __pending_remove_services.push_locked((*__sit).first); 00422 __pending_services.push_locked(alternate_service); 00423 } 00424 } 00425 } 00426 00427 00428 /** Callback for Avahi. 00429 * @param g entry group 00430 * @param state new state 00431 * @param instance instance of AvahiThread that triggered the event. 00432 */ 00433 void 00434 AvahiThread::entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, 00435 void *instance) 00436 { 00437 AvahiThread *at = static_cast<AvahiThread *>(instance); 00438 00439 switch (state) { 00440 case AVAHI_ENTRY_GROUP_ESTABLISHED : 00441 /* The entry group has been established successfully */ 00442 //fprintf(stderr, "Service '%s' successfully established.\n", name); 00443 break; 00444 00445 case AVAHI_ENTRY_GROUP_COLLISION : { 00446 at->name_collision(g); 00447 break; 00448 } 00449 00450 case AVAHI_ENTRY_GROUP_FAILURE : 00451 /* Some kind of failure happened while we were registering our services */ 00452 at->recover(); 00453 break; 00454 00455 case AVAHI_ENTRY_GROUP_UNCOMMITED: 00456 case AVAHI_ENTRY_GROUP_REGISTERING: 00457 break; 00458 } 00459 } 00460 00461 00462 /* ********************************************************************************** 00463 * Avahi Browser Publisher methods 00464 * **********************************************************************************/ 00465 00466 00467 /** Add a result handler. 00468 * A handler is added for the given service type. A search is initiated 00469 * for the given service and the given handler is called for added or 00470 * removed services or if an error occurs. 00471 * @param service_type string of the service type 00472 * @param h The ServiceBrowseHandler 00473 */ 00474 void 00475 AvahiThread::watch_service(const char *service_type, ServiceBrowseHandler *h) 00476 { 00477 __handlers[service_type].push_back(h); 00478 __pending_browsers.push_locked(service_type); 00479 00480 wake_poller(); 00481 } 00482 00483 00484 /** Remove a handler. 00485 * The handler is removed and no further events will be emitted to the 00486 * handler. 00487 * @param service_type service type to de-register the handler for 00488 * @param h the handler 00489 */ 00490 void 00491 AvahiThread::unwatch_service(const char *service_type, ServiceBrowseHandler *h) 00492 { 00493 if ( __handlers.find(service_type) != __handlers.end() ) { 00494 __handlers[service_type].remove(h); 00495 if ( __handlers[service_type].size() == 0 ) { 00496 if ( __browsers.find(service_type) != __browsers.end() ) { 00497 __pending_browser_removes.push_locked(service_type); 00498 //avahi_service_browser_free(__browsers[service_type]); 00499 //__browsers.erase(service_type); 00500 } 00501 __handlers.erase(service_type); 00502 } 00503 } 00504 00505 wake_poller(); 00506 } 00507 00508 00509 /** Create browser for a given service. 00510 * @param service_type service type 00511 */ 00512 void 00513 AvahiThread::create_browser(const char *service_type) 00514 { 00515 if ( __browsers.find(service_type) == __browsers.end() ) { 00516 if ( client ) { 00517 AvahiServiceBrowser *b = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, 00518 AVAHI_PROTO_UNSPEC, 00519 service_type, NULL, (AvahiLookupFlags)0, 00520 AvahiThread::browse_callback, this); 00521 00522 if ( ! b ) { 00523 __handlers[service_type].pop_back(); 00524 throw NullPointerException("Could not instantiate AvahiServiceBrowser"); 00525 } 00526 __browsers[service_type] = b; 00527 } 00528 } 00529 } 00530 00531 00532 /** Create browsers. 00533 * Creates browser for all services. 00534 */ 00535 void 00536 AvahiThread::recreate_browsers() 00537 { 00538 LockMap< std::string, std::list<ServiceBrowseHandler *> >::iterator i; 00539 for (i = __handlers.begin(); i != __handlers.end(); ++i) { 00540 create_browser( (*i).first.c_str() ); 00541 } 00542 } 00543 00544 00545 void 00546 AvahiThread::create_pending_browsers() 00547 { 00548 __pending_browsers.lock(); 00549 while ( ! __pending_browsers.empty() ) { 00550 //printf("Creating browser for %s\n", __pending_browsers.front().c_str()); 00551 create_browser(__pending_browsers.front().c_str()); 00552 __pending_browsers.pop(); 00553 } 00554 __pending_browsers.unlock(); 00555 } 00556 00557 00558 void 00559 AvahiThread::remove_pending_browsers() 00560 { 00561 Thread::CancelState old_state; 00562 set_cancel_state(CANCEL_DISABLED, &old_state); 00563 __pending_browser_removes.lock(); 00564 while ( ! __pending_browser_removes.empty()) { 00565 std::string &s = __pending_browser_removes.front(); 00566 avahi_service_browser_free(__browsers[s]); 00567 __browsers.erase_locked(s); 00568 __pending_browser_removes.pop(); 00569 } 00570 __pending_browser_removes.unlock(); 00571 set_cancel_state(old_state); 00572 } 00573 00574 00575 /** Erase all browsers. */ 00576 void 00577 AvahiThread::erase_browsers() 00578 { 00579 std::map< std::string, AvahiServiceBrowser * >::iterator i; 00580 for (i = __browsers.begin(); i != __browsers.end(); ++i) { 00581 avahi_service_browser_free((*i).second); 00582 } 00583 __browsers.clear(); 00584 } 00585 00586 00587 /** Call handler for a removed service. 00588 * @param name name 00589 * @param type type 00590 * @param domain domain 00591 */ 00592 void 00593 AvahiThread::call_handler_service_removed( const char *name, 00594 const char *type, 00595 const char *domain) 00596 { 00597 if ( __handlers.find(type) != __handlers.end() ) { 00598 std::list<ServiceBrowseHandler *>::iterator i; 00599 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) { 00600 (*i)->service_removed(name, type, domain); 00601 } 00602 } 00603 } 00604 00605 00606 /** Call handler for an added service. 00607 * @param name name 00608 * @param type type 00609 * @param domain domain 00610 * @param host_name host name 00611 * @param address address of host 00612 * @param port port of service 00613 * @þaram txt list of TXT records 00614 * @param flags flags 00615 */ 00616 void 00617 AvahiThread::call_handler_service_added( const char *name, 00618 const char *type, 00619 const char *domain, 00620 const char *host_name, 00621 const AvahiAddress *address, 00622 uint16_t port, 00623 std::list<std::string> &txt, 00624 AvahiLookupResultFlags flags) 00625 { 00626 struct sockaddr_in *s = NULL; 00627 socklen_t slen; 00628 if ( address->proto == AVAHI_PROTO_INET ) { 00629 slen = sizeof(struct sockaddr_in); 00630 s = (struct sockaddr_in *)malloc(slen); 00631 s->sin_addr.s_addr = address->data.ipv4.address; 00632 } else { 00633 // ignore 00634 return; 00635 } 00636 if ( __handlers.find(type) != __handlers.end() ) { 00637 std::list<ServiceBrowseHandler *>::iterator i; 00638 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) { 00639 (*i)->service_added(name, type, domain, host_name, 00640 (struct sockaddr *)s, slen, port, txt, (int)flags); 00641 } 00642 } 00643 free(s); 00644 } 00645 00646 00647 /** Call handler for failure. 00648 * @param name name 00649 * @param type type 00650 * @param domain domain 00651 */ 00652 void 00653 AvahiThread::call_handler_failed( const char *name, 00654 const char *type, 00655 const char *domain) 00656 { 00657 if ( __handlers.find(type) != __handlers.end() ) { 00658 std::list<ServiceBrowseHandler *>::iterator i; 00659 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) { 00660 (*i)->browse_failed(name, type, domain); 00661 } 00662 } 00663 } 00664 00665 00666 /** Call handler "all for now". 00667 * @param type type 00668 */ 00669 void 00670 AvahiThread::call_handler_all_for_now(const char *type) 00671 { 00672 if ( __handlers.find(type) != __handlers.end() ) { 00673 std::list<ServiceBrowseHandler *>::iterator i; 00674 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) { 00675 (*i)->all_for_now(); 00676 } 00677 } 00678 } 00679 00680 00681 /** Call handler "cache exhausted". 00682 * @param type type 00683 */ 00684 void 00685 AvahiThread::call_handler_cache_exhausted(const char *type) 00686 { 00687 if ( __handlers.find(type) != __handlers.end() ) { 00688 std::list<ServiceBrowseHandler *>::iterator i; 00689 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) { 00690 (*i)->cache_exhausted(); 00691 } 00692 } 00693 } 00694 00695 00696 /** Callback for Avahi. 00697 * Callback called by Avahi. 00698 * @param b service browser 00699 * @param interface interface index 00700 * @param protocol protocol 00701 * @param event event 00702 * @param name name 00703 * @param type type 00704 * @param domain domain 00705 * @param flags flags 00706 * @param instance pointer to the AvahiThread instance that initiated 00707 * the search 00708 */ 00709 void 00710 AvahiThread::browse_callback( AvahiServiceBrowser *b, 00711 AvahiIfIndex interface, 00712 AvahiProtocol protocol, 00713 AvahiBrowserEvent event, 00714 const char *name, 00715 const char *type, 00716 const char *domain, 00717 AvahiLookupResultFlags flags, 00718 void *instance) 00719 { 00720 AvahiThread *at = static_cast<AvahiThread *>(instance); 00721 00722 switch (event) { 00723 case AVAHI_BROWSER_FAILURE: 00724 //printf("(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); 00725 return; 00726 00727 case AVAHI_BROWSER_NEW: 00728 //printf("(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); 00729 // We ignore the returned resolver object. In the callback 00730 // function we free it. If the server is terminated before 00731 // the callback function is called the server will free 00732 // the resolver for us. 00733 if (!(avahi_service_resolver_new(at->client, interface, protocol, 00734 name, type, domain, protocol, (AvahiLookupFlags)0, 00735 AvahiThread::resolve_callback, instance))) { 00736 throw NullPointerException("Could not instantiate resolver"); 00737 } 00738 break; 00739 00740 case AVAHI_BROWSER_REMOVE: 00741 // handler 00742 //printf("(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); 00743 at->call_handler_service_removed(name, type, domain); 00744 break; 00745 00746 case AVAHI_BROWSER_ALL_FOR_NOW: 00747 // handler 00748 //printf("(Browser) ALL_FOR_NOW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); 00749 at->call_handler_all_for_now(type); 00750 break; 00751 00752 case AVAHI_BROWSER_CACHE_EXHAUSTED: 00753 // handler 00754 //printf("(Browser) CACHE_EXHAUSTED: service '%s' of type '%s' in domain '%s'\n", name, type, domain); 00755 at->call_handler_cache_exhausted(type); 00756 break; 00757 00758 } 00759 } 00760 00761 00762 /** Callback for Avahi. 00763 * Callback called by Avahi. 00764 * @param r service resolver 00765 * @param interface interface index 00766 * @param protocol protocol 00767 * @param event event 00768 * @param name name 00769 * @param type type 00770 * @param domain domain 00771 * @param host_name host name 00772 * @param address address 00773 * @param port port 00774 * @param txt TXT records 00775 * @param flags flags 00776 * @param instance pointer to the AvahiThread instance that initiated 00777 * the search 00778 */ 00779 void 00780 AvahiThread::resolve_callback( AvahiServiceResolver *r, 00781 AVAHI_GCC_UNUSED AvahiIfIndex interface, 00782 AVAHI_GCC_UNUSED AvahiProtocol protocol, 00783 AvahiResolverEvent event, 00784 const char *name, 00785 const char *type, 00786 const char *domain, 00787 const char *host_name, 00788 const AvahiAddress *address, 00789 uint16_t port, 00790 AvahiStringList *txt, 00791 AvahiLookupResultFlags flags, 00792 void *instance) 00793 { 00794 AvahiThread *at = static_cast<AvahiThread *>(instance); 00795 00796 switch (event) { 00797 case AVAHI_RESOLVER_FAILURE: 00798 // handler failure 00799 at->call_handler_failed(name, type, domain); 00800 break; 00801 00802 case AVAHI_RESOLVER_FOUND: 00803 // handler add 00804 { 00805 std::list< std::string > txts; 00806 AvahiStringList *l = txt; 00807 00808 txts.clear(); 00809 while ( l ) { 00810 txts.push_back((char *)avahi_string_list_get_text(l)); 00811 l = avahi_string_list_get_next( l ); 00812 } 00813 00814 at->call_handler_service_added(name, type, domain, host_name, address, port, txts, flags); 00815 } 00816 break; 00817 } 00818 00819 avahi_service_resolver_free(r); 00820 } 00821 00822 00823 /* ********************************************************************************** 00824 * Avahi resolver methods 00825 * **********************************************************************************/ 00826 00827 00828 /** Order name resolution. 00829 * This initiates resolution of a name. The method immediately returns and will not 00830 * wait for the result. 00831 * @param name name to resolve. 00832 * @param handler handler to call for the result 00833 */ 00834 void 00835 AvahiThread::resolve_name(const char *name, AvahiResolverHandler *handler) 00836 { 00837 AvahiResolverCallbackData *data = new AvahiResolverCallbackData(this, handler); 00838 00839 if ( __pending_hostname_resolves.find(name) == __pending_hostname_resolves.end()) { 00840 __pending_hostname_resolves[name] = data; 00841 } 00842 00843 wake_poller(); 00844 } 00845 00846 00847 void 00848 AvahiThread::start_hostname_resolver(const char *name, AvahiResolverCallbackData *data) 00849 { 00850 AvahiHostNameResolver *resolver; 00851 if ( (resolver = avahi_host_name_resolver_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 00852 name, AVAHI_PROTO_INET, 00853 AVAHI_LOOKUP_USE_MULTICAST, 00854 AvahiThread::host_name_resolver_callback, 00855 data) ) == NULL ) { 00856 throw Exception("Cannot create Avahi name resolver"); 00857 } else { 00858 __running_hostname_resolvers.push_back(resolver); 00859 } 00860 00861 } 00862 00863 00864 void 00865 AvahiThread::start_hostname_resolvers() 00866 { 00867 for (__phrit = __pending_hostname_resolves.begin(); __phrit != __pending_hostname_resolves.end(); ++__phrit) { 00868 start_hostname_resolver((*__phrit).first.c_str(), (*__phrit).second); 00869 } 00870 __pending_hostname_resolves.clear(); 00871 } 00872 00873 00874 void 00875 AvahiThread::start_address_resolvers() 00876 { 00877 for (__parit = __pending_address_resolves.begin(); __parit != __pending_address_resolves.end(); ++__parit) { 00878 start_address_resolver((*__parit).first, (*__parit).second); 00879 } 00880 __pending_address_resolves.clear(); 00881 } 00882 00883 00884 /** Order address resolution. 00885 * This initiates resolution of an address. The method immediately returns and will not 00886 * wait for the result. 00887 * @param addr address to resolve, currently only struct sockaddr_in is supported (IPv4) 00888 * @param addrlen length of addr in bytes 00889 * @param handler handler to call for the result 00890 */ 00891 void 00892 AvahiThread::resolve_address(struct sockaddr *addr, socklen_t addrlen, 00893 AvahiResolverHandler *handler) 00894 { 00895 if ( addrlen != sizeof(struct sockaddr_in) ) { 00896 throw Exception("Only IPv4 is currently supported"); 00897 } 00898 00899 struct sockaddr_in *in_addr = (struct sockaddr_in *)calloc(1, sizeof(struct sockaddr_in)); 00900 memcpy(in_addr, addr, sizeof(struct sockaddr_in)); 00901 AvahiResolverCallbackData *data = new AvahiResolverCallbackData(this, handler); 00902 00903 __pending_address_resolves[in_addr] = data; 00904 wake_poller(); 00905 } 00906 00907 00908 void 00909 AvahiThread::start_address_resolver(struct sockaddr_in *in_addr, AvahiResolverCallbackData *data) 00910 { 00911 AvahiAddress a; 00912 a.proto = AVAHI_PROTO_INET; 00913 a.data.ipv4.address = in_addr->sin_addr.s_addr; 00914 00915 AvahiAddressResolver *resolver; 00916 if ( (resolver = avahi_address_resolver_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 00917 &a, AVAHI_LOOKUP_USE_MULTICAST, 00918 AvahiThread::address_resolver_callback, 00919 data) ) == NULL ) { 00920 Exception e("Cannot create Avahi address resolver"); 00921 e.append("Avahi error: %s", avahi_strerror(avahi_client_errno(client))); 00922 throw e; 00923 } else { 00924 __running_address_resolvers.push_back_locked(resolver); 00925 } 00926 } 00927 00928 00929 /** Remove hostname resolver. 00930 * Used internally by callback. 00931 * @param r resolver 00932 */ 00933 void 00934 AvahiThread::remove_hostname_resolver(AvahiHostNameResolver *r) 00935 { 00936 __running_hostname_resolvers.remove_locked(r); 00937 } 00938 00939 00940 /** Remove address resolver. 00941 * Used internally by callback. 00942 * @param r resolver 00943 */ 00944 void 00945 AvahiThread::remove_address_resolver(AvahiAddressResolver *r) 00946 { 00947 __running_address_resolvers.remove_locked(r); 00948 } 00949 00950 00951 /** Internal callback. 00952 * Callback for avahi. 00953 */ 00954 void 00955 AvahiThread::host_name_resolver_callback(AvahiHostNameResolver *r, 00956 AvahiIfIndex interface, 00957 AvahiProtocol protocol, 00958 AvahiResolverEvent event, 00959 const char *name, 00960 const AvahiAddress *a, 00961 AvahiLookupResultFlags flags, 00962 void *userdata) 00963 { 00964 AvahiResolverCallbackData *cd = static_cast<AvahiResolverCallbackData *>(userdata); 00965 00966 cd->first->remove_hostname_resolver(r); 00967 avahi_host_name_resolver_free(r); 00968 00969 switch (event) { 00970 case AVAHI_RESOLVER_FOUND: 00971 { 00972 struct sockaddr_in *res = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in)); 00973 res->sin_family = (unsigned short)avahi_proto_to_af(protocol); 00974 res->sin_addr.s_addr = a->data.ipv4.address; 00975 00976 cd->second->resolved_name(strdup(name), (struct sockaddr *)res, sizeof(struct sockaddr_in)); 00977 } 00978 break; 00979 00980 case AVAHI_RESOLVER_FAILURE: 00981 default: 00982 cd->second->name_resolution_failed(strdup(name)); 00983 break; 00984 } 00985 00986 delete cd; 00987 } 00988 00989 00990 /** Internal callback. 00991 * Callback for avahi. 00992 */ 00993 void 00994 AvahiThread::address_resolver_callback(AvahiAddressResolver *r, 00995 AvahiIfIndex interface, 00996 AvahiProtocol protocol, 00997 AvahiResolverEvent event, 00998 const AvahiAddress *a, 00999 const char *name, 01000 AvahiLookupResultFlags flags, 01001 void *userdata) 01002 { 01003 AvahiResolverCallbackData *cd = static_cast<AvahiResolverCallbackData *>(userdata); 01004 01005 cd->first->remove_address_resolver(r); 01006 avahi_address_resolver_free(r); 01007 01008 struct sockaddr_in *res = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in)); 01009 res->sin_family = (unsigned short)avahi_proto_to_af(protocol); 01010 res->sin_addr.s_addr = a->data.ipv4.address; 01011 01012 switch (event) { 01013 case AVAHI_RESOLVER_FOUND: 01014 cd->second->resolved_address((struct sockaddr_in *)res, sizeof(struct sockaddr_in), 01015 strdup(name)); 01016 break; 01017 case AVAHI_RESOLVER_FAILURE: 01018 default: 01019 cd->second->address_resolution_failed((struct sockaddr_in *)res, 01020 sizeof(struct sockaddr_in)); 01021 break; 01022 } 01023 01024 delete cd; 01025 } 01026 01027 01028 /** Unlocks init lock. 01029 * Only to be called by client_callback(). 01030 */ 01031 void 01032 AvahiThread::init_done() 01033 { 01034 wake_poller(); 01035 init_wc->wake_all(); 01036 } 01037 01038 01039 /** Waits for the AvahiThread to be initialized. 01040 * You can use this if you want to wait until the thread has been 01041 * fully initialized and may be used. Since the happens in this thread 01042 * it is in general not immediately ready after start(). 01043 * This will block the calling thread until the AvahiThread has 01044 * been initialized. This is done by waiting for a release of an 01045 * initialization mutex. 01046 */ 01047 void 01048 AvahiThread::wait_initialized() 01049 { 01050 init_wc->wait(); 01051 } 01052 01053 } // end namespace fawkes