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