1/* $Id$ */ 2 3/*** 4 This file is part of avahi. 5 6 avahi is free software; you can redistribute it and/or modify it 7 under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 2.1 of the 9 License, or (at your option) any later version. 10 11 avahi is distributed in the hope that it will be useful, but WITHOUT 12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General 14 Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with avahi; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 19 USA. 20***/ 21 22#ifdef HAVE_CONFIG_H 23#include <config.h> 24#endif 25 26#include <assert.h> 27 28#include <pthread.h> 29 30#include <avahi-common/strlst.h> 31#include <avahi-common/malloc.h> 32#include <avahi-common/domain.h> 33#include <avahi-common/simple-watch.h> 34#include <avahi-common/error.h> 35#include <avahi-common/llist.h> 36 37#include <avahi-client/client.h> 38#include <avahi-client/publish.h> 39#include <avahi-client/lookup.h> 40 41#include "howl.h" 42#include "warn.h" 43 44#define OID_MAX 50 45 46enum { 47 COMMAND_POLL = 'p', 48 COMMAND_QUIT = 'q', 49 COMMAND_POLL_DONE = 'P', 50 COMMAND_POLL_FAILED = 'F' 51}; 52 53typedef enum { 54 OID_UNUSED = 0, 55 OID_SERVICE_BROWSER, 56 OID_SERVICE_RESOLVER, 57 OID_DOMAIN_BROWSER, 58 OID_ENTRY_GROUP 59} oid_type; 60 61typedef struct service_data service_data; 62 63typedef struct oid_data { 64 oid_type type; 65 sw_opaque extra; 66 sw_discovery discovery; 67 void *object; 68 sw_result (*reply)(void); 69 service_data *service_data; 70} oid_data; 71 72 73struct service_data { 74 char *name, *regtype, *domain, *host; 75 uint16_t port; 76 AvahiIfIndex interface; 77 AvahiStringList *txt; 78 AVAHI_LLIST_FIELDS(service_data, services); 79}; 80 81struct _sw_discovery { 82 int n_ref; 83 AvahiSimplePoll *simple_poll; 84 AvahiClient *client; 85 86 oid_data oid_table[OID_MAX]; 87 sw_discovery_oid oid_index; 88 89 int thread_fd, main_fd; 90 91 pthread_t thread; 92 int thread_running; 93 94 pthread_mutex_t mutex, salt_mutex; 95 96 AVAHI_LLIST_HEAD(service_data, services); 97}; 98 99#define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); } 100 101#define OID_GET_INDEX(data) ((sw_discovery_oid) (((data) - ((data)->discovery->oid_table)))) 102 103static sw_discovery discovery_ref(sw_discovery self); 104static void discovery_unref(sw_discovery self); 105 106static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) { 107 if (!s) 108 return NULL; 109 110 if (*s == 0) 111 return s; 112 113 if (s[strlen(s)-1] == '.') 114 return s; 115 116 snprintf(buf, buf_len, "%s.", s); 117 return buf; 118} 119 120static sw_result map_error(int error) { 121 switch (error) { 122 case AVAHI_OK: 123 return SW_OKAY; 124 125 case AVAHI_ERR_NO_MEMORY: 126 return SW_E_MEM; 127 } 128 129 return SW_E_UNKNOWN; 130} 131 132static int read_command(int fd) { 133 ssize_t r; 134 char command; 135 136 assert(fd >= 0); 137 138 if ((r = read(fd, &command, 1)) != 1) { 139 fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); 140 return -1; 141 } 142 143 return command; 144} 145 146static int write_command(int fd, char reply) { 147 assert(fd >= 0); 148 149 if (write(fd, &reply, 1) != 1) { 150 fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); 151 return -1; 152 } 153 154 return 0; 155} 156 157static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) { 158 sw_discovery self = userdata; 159 int ret; 160 161 assert(self); 162 163 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 164 ret = poll(ufds, nfds, timeout); 165 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 166 167 return ret; 168} 169 170static void * thread_func(void *data) { 171 sw_discovery self = data; 172 sigset_t mask; 173 174 sigfillset(&mask); 175 pthread_sigmask(SIG_BLOCK, &mask, NULL); 176 177 self->thread = pthread_self(); 178 self->thread_running = 1; 179 180 for (;;) { 181 char command; 182 183 if ((command = read_command(self->thread_fd)) < 0) 184 break; 185 186/* fprintf(stderr, "Command: %c\n", command); */ 187 188 switch (command) { 189 190 case COMMAND_POLL: { 191 int ret; 192 193 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 194 195 for (;;) { 196 errno = 0; 197 198 if ((ret = avahi_simple_poll_run(self->simple_poll)) < 0) { 199 200 if (errno == EINTR) 201 continue; 202 203 fprintf(stderr, __FILE__": avahi_simple_poll_run() failed: %s\n", strerror(errno)); 204 } 205 206 break; 207 } 208 209 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 210 211 if (write_command(self->thread_fd, ret < 0 ? COMMAND_POLL_FAILED : COMMAND_POLL_DONE) < 0) 212 break; 213 214 break; 215 } 216 217 case COMMAND_QUIT: 218 return NULL; 219 } 220 221 } 222 223 return NULL; 224} 225 226static int oid_alloc(sw_discovery self, oid_type type) { 227 sw_discovery_oid i; 228 assert(self); 229 230 for (i = 0; i < OID_MAX; i++) { 231 232 while (self->oid_index >= OID_MAX) 233 self->oid_index -= OID_MAX; 234 235 if (self->oid_table[self->oid_index].type == OID_UNUSED) { 236 self->oid_table[self->oid_index].type = type; 237 self->oid_table[self->oid_index].discovery = self; 238 239 assert(OID_GET_INDEX(&self->oid_table[self->oid_index]) == self->oid_index); 240 241 return self->oid_index ++; 242 } 243 244 self->oid_index ++; 245 } 246 247 /* No free entry found */ 248 249 return (sw_discovery_oid) -1; 250} 251 252static void oid_release(sw_discovery self, sw_discovery_oid oid) { 253 assert(self); 254 assert(oid < OID_MAX); 255 256 assert(self->oid_table[oid].type != OID_UNUSED); 257 258 self->oid_table[oid].type = OID_UNUSED; 259 self->oid_table[oid].discovery = NULL; 260 self->oid_table[oid].reply = NULL; 261 self->oid_table[oid].object = NULL; 262 self->oid_table[oid].extra = NULL; 263 self->oid_table[oid].service_data = NULL; 264} 265 266static oid_data* oid_get(sw_discovery self, sw_discovery_oid oid) { 267 assert(self); 268 269 if (oid >= OID_MAX) 270 return NULL; 271 272 if (self->oid_table[oid].type == OID_UNUSED) 273 return NULL; 274 275 return &self->oid_table[oid]; 276} 277 278static service_data* service_data_new(sw_discovery self) { 279 service_data *sdata; 280 281 assert(self); 282 283 if (!(sdata = avahi_new0(service_data, 1))) 284 return NULL; 285 286 AVAHI_LLIST_PREPEND(service_data, services, self->services, sdata); 287 288 return sdata; 289 290} 291 292static void service_data_free(sw_discovery self, service_data* sdata) { 293 assert(self); 294 assert(sdata); 295 296 AVAHI_LLIST_REMOVE(service_data, services, self->services, sdata); 297 298 avahi_free(sdata->name); 299 avahi_free(sdata->regtype); 300 avahi_free(sdata->domain); 301 avahi_free(sdata->host); 302 avahi_string_list_free(sdata->txt); 303 avahi_free(sdata); 304} 305 306static void reg_client_callback(oid_data *data, AvahiClientState state); 307 308static void client_callback(AvahiClient *s, AvahiClientState state, void* userdata) { 309 sw_discovery self = userdata; 310 sw_discovery_oid oid; 311 312 assert(s); 313 assert(self); 314 315 discovery_ref(self); 316 317 for (oid = 0; oid < OID_MAX; oid++) { 318 319 switch (self->oid_table[oid].type) { 320 321 case OID_ENTRY_GROUP: 322 reg_client_callback(&self->oid_table[oid], state); 323 break; 324 325 case OID_DOMAIN_BROWSER: 326 case OID_SERVICE_BROWSER: 327 ((sw_discovery_browse_reply) self->oid_table[oid].reply)(self, oid, SW_DISCOVERY_BROWSE_INVALID, 0, NULL, NULL, NULL, self->oid_table[oid].extra); 328 break; 329 330 case OID_SERVICE_RESOLVER: 331 case OID_UNUSED: 332 ; 333 } 334 } 335 336 discovery_unref(self); 337} 338 339sw_result sw_discovery_init(sw_discovery * self) { 340 int fd[2] = { -1, -1}; 341 sw_result result = SW_E_UNKNOWN; 342 pthread_mutexattr_t mutex_attr; 343 int error; 344 345 assert(self); 346 347 AVAHI_WARN_LINKAGE; 348 349 *self = NULL; 350 351 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) 352 goto fail; 353 354 if (!(*self = avahi_new(struct _sw_discovery, 1))) { 355 result = SW_E_MEM; 356 goto fail; 357 } 358 359 (*self)->n_ref = 1; 360 (*self)->thread_fd = fd[0]; 361 (*self)->main_fd = fd[1]; 362 363 (*self)->client = NULL; 364 (*self)->simple_poll = NULL; 365 366 memset((*self)->oid_table, 0, sizeof((*self)->oid_table)); 367 (*self)->oid_index = 0; 368 369 (*self)->thread_running = 0; 370 371 AVAHI_LLIST_HEAD_INIT(service_info, (*self)->services); 372 373 ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr)); 374 pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE); 375 ASSERT_SUCCESS(pthread_mutex_init(&(*self)->mutex, &mutex_attr)); 376 ASSERT_SUCCESS(pthread_mutex_init(&(*self)->salt_mutex, &mutex_attr)); 377 378 if (!((*self)->simple_poll = avahi_simple_poll_new())) 379 goto fail; 380 381 avahi_simple_poll_set_func((*self)->simple_poll, poll_func, *self); 382 383 if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), 0, client_callback, *self, &error))) { 384 result = map_error(error); 385 goto fail; 386 } 387 388 /* Start simple poll */ 389 if (avahi_simple_poll_prepare((*self)->simple_poll, -1) < 0) 390 goto fail; 391 392 /* Queue an initial POLL command for the thread */ 393 if (write_command((*self)->main_fd, COMMAND_POLL) < 0) 394 goto fail; 395 396 if (pthread_create(&(*self)->thread, NULL, thread_func, *self) != 0) 397 goto fail; 398 399 (*self)->thread_running = 1; 400 401 return SW_OKAY; 402 403fail: 404 405 if (*self) 406 sw_discovery_fina(*self); 407 408 return result; 409} 410 411static int stop_thread(sw_discovery self) { 412 assert(self); 413 414 if (!self->thread_running) 415 return 0; 416 417 if (write_command(self->main_fd, COMMAND_QUIT) < 0) 418 return -1; 419 420 avahi_simple_poll_wakeup(self->simple_poll); 421 422 ASSERT_SUCCESS(pthread_join(self->thread, NULL)); 423 self->thread_running = 0; 424 return 0; 425} 426 427static sw_discovery discovery_ref(sw_discovery self) { 428 assert(self); 429 assert(self->n_ref >= 1); 430 431 self->n_ref++; 432 433 return self; 434} 435 436static void discovery_unref(sw_discovery self) { 437 assert(self); 438 assert(self->n_ref >= 1); 439 440 if (--self->n_ref > 0) 441 return; 442 443 stop_thread(self); 444 445 if (self->client) 446 avahi_client_free(self->client); 447 448 if (self->simple_poll) 449 avahi_simple_poll_free(self->simple_poll); 450 451 if (self->thread_fd >= 0) 452 close(self->thread_fd); 453 454 if (self->main_fd >= 0) 455 close(self->main_fd); 456 457 ASSERT_SUCCESS(pthread_mutex_destroy(&self->mutex)); 458 ASSERT_SUCCESS(pthread_mutex_destroy(&self->salt_mutex)); 459 460 while (self->services) 461 service_data_free(self, self->services); 462 463 avahi_free(self); 464} 465 466sw_result sw_discovery_fina(sw_discovery self) { 467 assert(self); 468 469 AVAHI_WARN_LINKAGE; 470 471 stop_thread(self); 472 discovery_unref(self); 473 474 return SW_OKAY; 475} 476 477sw_result sw_discovery_run(sw_discovery self) { 478 assert(self); 479 480 AVAHI_WARN_LINKAGE; 481 482 return sw_salt_run((sw_salt) self); 483} 484 485sw_result sw_discovery_stop_run(sw_discovery self) { 486 assert(self); 487 488 AVAHI_WARN_LINKAGE; 489 490 return sw_salt_stop_run((sw_salt) self); 491} 492 493int sw_discovery_socket(sw_discovery self) { 494 assert(self); 495 496 AVAHI_WARN_LINKAGE; 497 498 return self->main_fd; 499} 500 501sw_result sw_discovery_read_socket(sw_discovery self) { 502 sw_result result = SW_E_UNKNOWN; 503 504 assert(self); 505 506 discovery_ref(self); 507 508 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 509 510 /* Cleanup notification socket */ 511 if (read_command(self->main_fd) != COMMAND_POLL_DONE) 512 goto finish; 513 514 if (avahi_simple_poll_dispatch(self->simple_poll) < 0) 515 goto finish; 516 517 if (self->n_ref > 1) /* Perhaps we should die */ 518 519 /* Dispatch events */ 520 if (avahi_simple_poll_prepare(self->simple_poll, -1) < 0) 521 goto finish; 522 523 if (self->n_ref > 1) 524 525 /* Request the poll */ 526 if (write_command(self->main_fd, COMMAND_POLL) < 0) 527 goto finish; 528 529 result = SW_OKAY; 530 531finish: 532 533 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 534 535 discovery_unref(self); 536 537 return result; 538} 539 540sw_result sw_discovery_salt(sw_discovery self, sw_salt *salt) { 541 assert(self); 542 assert(salt); 543 544 AVAHI_WARN_LINKAGE; 545 546 *salt = (sw_salt) self; 547 548 return SW_OKAY; 549} 550 551sw_result sw_salt_step(sw_salt self, sw_uint32 * msec) { 552 struct pollfd p; 553 int r; 554 sw_result result; 555 556 AVAHI_WARN_LINKAGE; 557 558 if (!((sw_discovery) self)->thread_running) 559 return SW_E_UNKNOWN; 560 561 memset(&p, 0, sizeof(p)); 562 p.fd = ((sw_discovery) self)->main_fd; 563 p.events = POLLIN; 564 565 if ((r = poll(&p, 1, msec ? (int) *msec : -1)) < 0) { 566 567 /* Don't treat EINTR as error */ 568 if (errno == EINTR) 569 return SW_OKAY; 570 571 return SW_E_UNKNOWN; 572 573 } else if (r == 0) { 574 575 /* Timeoout */ 576 return SW_OKAY; 577 578 } else { 579 /* Success */ 580 581 if (p.revents != POLLIN) 582 return SW_E_UNKNOWN; 583 584 if ((result = sw_discovery_read_socket((sw_discovery) self)) != SW_OKAY) 585 return result; 586 } 587 588 return SW_OKAY; 589} 590 591sw_result sw_salt_run(sw_salt self) { 592 sw_result ret; 593 594 AVAHI_WARN_LINKAGE; 595 596 assert(self); 597 598 for (;;) 599 if ((ret = sw_salt_step(self, NULL)) != SW_OKAY) 600 return ret; 601} 602 603sw_result sw_salt_stop_run(sw_salt self) { 604 AVAHI_WARN_LINKAGE; 605 606 assert(self); 607 608 if (stop_thread((sw_discovery) self) < 0) 609 return SW_E_UNKNOWN; 610 611 return SW_OKAY; 612} 613 614sw_result sw_salt_lock(sw_salt self) { 615 AVAHI_WARN_LINKAGE; 616 617 assert(self); 618 ASSERT_SUCCESS(pthread_mutex_lock(&((sw_discovery) self)->salt_mutex)); 619 620 return SW_OKAY; 621} 622 623sw_result sw_salt_unlock(sw_salt self) { 624 assert(self); 625 626 AVAHI_WARN_LINKAGE; 627 628 ASSERT_SUCCESS(pthread_mutex_unlock(&((sw_discovery) self)->salt_mutex)); 629 630 return SW_OKAY; 631} 632 633static void reg_report_status(oid_data *data, sw_discovery_publish_status status) { 634 sw_discovery_publish_reply reply; 635 636 assert(data); 637 638 reply = (sw_discovery_publish_reply) data->reply; 639 640 reply(data->discovery, 641 OID_GET_INDEX(data), 642 status, 643 data->extra); 644} 645 646static int reg_create_service(oid_data *data) { 647 int ret; 648 const char *real_type; 649 650 assert(data); 651 652 real_type = avahi_get_type_from_subtype(data->service_data->regtype); 653 654 if ((ret = avahi_entry_group_add_service_strlst( 655 data->object, 656 data->service_data->interface, 657 AVAHI_PROTO_INET, 658 0, 659 data->service_data->name, 660 real_type ? real_type : data->service_data->regtype, 661 data->service_data->domain, 662 data->service_data->host, 663 data->service_data->port, 664 data->service_data->txt)) < 0) 665 return ret; 666 667 if (real_type) { 668 /* Create a subtype entry */ 669 670 if (avahi_entry_group_add_service_subtype( 671 data->object, 672 data->service_data->interface, 673 AVAHI_PROTO_INET, 674 0, 675 data->service_data->name, 676 real_type, 677 data->service_data->domain, 678 data->service_data->regtype) < 0) 679 return ret; 680 681 } 682 683 if ((ret = avahi_entry_group_commit(data->object)) < 0) 684 return ret; 685 686 return 0; 687} 688 689static void reg_client_callback(oid_data *data, AvahiClientState state) { 690 assert(data); 691 692 /* We've not been setup completely */ 693 if (!data->object) 694 return; 695 696 switch (state) { 697 case AVAHI_CLIENT_FAILURE: 698 reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID); 699 break; 700 701 case AVAHI_CLIENT_S_RUNNING: { 702 int ret; 703 704 /* Register the service */ 705 if ((ret = reg_create_service(data)) < 0) { 706 reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID); 707 return; 708 } 709 710 break; 711 } 712 713 case AVAHI_CLIENT_S_COLLISION: 714 case AVAHI_CLIENT_S_REGISTERING: 715 716 /* Remove our entry */ 717 avahi_entry_group_reset(data->object); 718 break; 719 720 case AVAHI_CLIENT_CONNECTING: 721 /* Ignore */ 722 break; 723 } 724 725} 726 727static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { 728 oid_data *data = userdata; 729 730 assert(g); 731 assert(data); 732 733 switch (state) { 734 case AVAHI_ENTRY_GROUP_ESTABLISHED: 735 736 reg_report_status(data, SW_DISCOVERY_PUBLISH_STARTED); 737 break; 738 739 case AVAHI_ENTRY_GROUP_COLLISION: 740 741 reg_report_status(data, SW_DISCOVERY_PUBLISH_NAME_COLLISION); 742 break; 743 744 case AVAHI_ENTRY_GROUP_REGISTERING: 745 case AVAHI_ENTRY_GROUP_UNCOMMITED: 746 /* Ignore */ 747 break; 748 749 case AVAHI_ENTRY_GROUP_FAILURE: 750 reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID); 751 break; 752 753 } 754} 755 756sw_result sw_discovery_publish( 757 sw_discovery self, 758 sw_uint32 interface_index, 759 sw_const_string name, 760 sw_const_string type, 761 sw_const_string domain, 762 sw_const_string host, 763 sw_port port, 764 sw_octets text_record, 765 sw_uint32 text_record_len, 766 sw_discovery_publish_reply reply, 767 sw_opaque extra, 768 sw_discovery_oid * oid) { 769 770 oid_data *data; 771 sw_result result = SW_E_UNKNOWN; 772 service_data *sdata; 773 AvahiStringList *txt = NULL; 774 775 assert(self); 776 assert(name); 777 assert(type); 778 assert(reply); 779 assert(oid); 780 781 AVAHI_WARN_LINKAGE; 782 783 if (text_record && text_record_len > 0) 784 if (avahi_string_list_parse(text_record, text_record_len, &txt) < 0) 785 return SW_E_UNKNOWN; 786 787 if ((*oid = oid_alloc(self, OID_ENTRY_GROUP)) == (sw_discovery_oid) -1) { 788 avahi_string_list_free(txt); 789 return SW_E_UNKNOWN; 790 } 791 792 if (!(sdata = service_data_new(self))) { 793 avahi_string_list_free(txt); 794 oid_release(self, *oid); 795 return SW_E_MEM; 796 } 797 798 data = oid_get(self, *oid); 799 assert(data); 800 data->reply = (sw_result (*)(void)) reply; 801 data->extra = extra; 802 data->service_data = sdata; 803 804 sdata->interface = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index; 805 sdata->name = avahi_strdup(name); 806 sdata->regtype = type ? avahi_normalize_name_strdup(type) : NULL; 807 sdata->domain = domain ? avahi_normalize_name_strdup(domain) : NULL; 808 sdata->host = host ? avahi_normalize_name_strdup(host) : NULL; 809 sdata->port = port; 810 sdata->txt = txt; 811 812 /* Some OOM checking would be cool here */ 813 814 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 815 816 if (!(data->object = avahi_entry_group_new(self->client, reg_entry_group_callback, data))) { 817 result = map_error(avahi_client_errno(self->client)); 818 goto finish; 819 } 820 821 if (avahi_client_get_state(self->client) == AVAHI_CLIENT_S_RUNNING) { 822 int error; 823 824 if ((error = reg_create_service(data)) < 0) { 825 result = map_error(error); 826 goto finish; 827 } 828 } 829 830 result = SW_OKAY; 831 832finish: 833 834 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 835 836 if (result != SW_OKAY) 837 if (*oid != (sw_discovery_oid) -1) 838 sw_discovery_cancel(self, *oid); 839 840 return result; 841} 842 843static void domain_browser_callback( 844 AvahiDomainBrowser *b, 845 AvahiIfIndex interface, 846 AVAHI_GCC_UNUSED AvahiProtocol protocol, 847 AvahiBrowserEvent event, 848 const char *domain, 849 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 850 void *userdata) { 851 852 oid_data* data = userdata; 853 sw_discovery_browse_reply reply; 854 static char domain_fixed[AVAHI_DOMAIN_NAME_MAX]; 855 856 assert(b); 857 assert(data); 858 859 reply = (sw_discovery_browse_reply) data->reply; 860 861 domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed)); 862 863 switch (event) { 864 case AVAHI_BROWSER_NEW: 865 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_DOMAIN, interface, NULL, NULL, domain, data->extra); 866 break; 867 868 case AVAHI_BROWSER_REMOVE: 869 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_DOMAIN, interface, NULL, NULL, domain, data->extra); 870 break; 871 872 case AVAHI_BROWSER_FAILURE: 873 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, NULL, NULL, domain, data->extra); 874 break; 875 876 case AVAHI_BROWSER_CACHE_EXHAUSTED: 877 case AVAHI_BROWSER_ALL_FOR_NOW: 878 break; 879 } 880} 881 882sw_result sw_discovery_browse_domains( 883 sw_discovery self, 884 sw_uint32 interface_index, 885 sw_discovery_browse_reply reply, 886 sw_opaque extra, 887 sw_discovery_oid * oid) { 888 889 oid_data *data; 890 AvahiIfIndex ifindex; 891 sw_result result = SW_E_UNKNOWN; 892 893 assert(self); 894 assert(reply); 895 assert(oid); 896 897 AVAHI_WARN_LINKAGE; 898 899 if ((*oid = oid_alloc(self, OID_DOMAIN_BROWSER)) == (sw_discovery_oid) -1) 900 return SW_E_UNKNOWN; 901 902 data = oid_get(self, *oid); 903 assert(data); 904 data->reply = (sw_result (*)(void)) reply; 905 data->extra = extra; 906 907 ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index; 908 909 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 910 911 if (!(data->object = avahi_domain_browser_new(self->client, ifindex, AVAHI_PROTO_INET, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browser_callback, data))) { 912 result = map_error(avahi_client_errno(self->client)); 913 goto finish; 914 } 915 916 result = SW_OKAY; 917 918finish: 919 920 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 921 922 if (result != SW_OKAY) 923 if (*oid != (sw_discovery_oid) -1) 924 sw_discovery_cancel(self, *oid); 925 926 return result; 927} 928 929static void service_resolver_callback( 930 AvahiServiceResolver *r, 931 AvahiIfIndex interface, 932 AVAHI_GCC_UNUSED AvahiProtocol protocol, 933 AvahiResolverEvent event, 934 const char *name, 935 const char *type, 936 const char *domain, 937 const char *host_name, 938 const AvahiAddress *a, 939 uint16_t port, 940 AvahiStringList *txt, 941 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 942 void *userdata) { 943 944 oid_data* data = userdata; 945 sw_discovery_resolve_reply reply; 946 947 assert(r); 948 assert(data); 949 950 reply = (sw_discovery_resolve_reply) data->reply; 951 952 switch (event) { 953 case AVAHI_RESOLVER_FOUND: { 954 955 char host_name_fixed[AVAHI_DOMAIN_NAME_MAX]; 956 uint8_t *p = NULL; 957 size_t l = 0; 958 sw_ipv4_address addr; 959 960 sw_ipv4_address_init_from_saddr(&addr, a->data.ipv4.address); 961 962 host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed)); 963 964 if ((p = avahi_new0(uint8_t, (l = avahi_string_list_serialize(txt, NULL, 0))+1))) 965 avahi_string_list_serialize(txt, p, l); 966 967 reply(data->discovery, OID_GET_INDEX(data), interface, name, type, domain, addr, port, p, l, data->extra); 968 969 avahi_free(p); 970 break; 971 } 972 973 case AVAHI_RESOLVER_FAILURE: 974 975 /* Apparently there is no way in HOWL to inform about failed resolvings ... */ 976 977 avahi_warn("A service failed to resolve in the HOWL compatiblity layer of Avahi which is used by '%s'. " 978 "Since the HOWL API doesn't offer any means to inform the application about this, we have to ignore the failure. " 979 "Please fix your application to use the native API of Avahi!", 980 avahi_exe_name()); 981 982 break; 983 } 984} 985 986sw_result sw_discovery_resolve( 987 sw_discovery self, 988 sw_uint32 interface_index, 989 sw_const_string name, 990 sw_const_string type, 991 sw_const_string domain, 992 sw_discovery_resolve_reply reply, 993 sw_opaque extra, 994 sw_discovery_oid * oid) { 995 996 oid_data *data; 997 AvahiIfIndex ifindex; 998 sw_result result = SW_E_UNKNOWN; 999 1000 assert(self); 1001 assert(name); 1002 assert(type); 1003 assert(reply); 1004 assert(oid); 1005 1006 AVAHI_WARN_LINKAGE; 1007 1008 if ((*oid = oid_alloc(self, OID_SERVICE_RESOLVER)) == (sw_discovery_oid) -1) 1009 return SW_E_UNKNOWN; 1010 1011 data = oid_get(self, *oid); 1012 assert(data); 1013 data->reply = (sw_result (*)(void)) reply; 1014 data->extra = extra; 1015 1016 ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index; 1017 1018 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 1019 1020 if (!(data->object = avahi_service_resolver_new(self->client, ifindex, AVAHI_PROTO_INET, name, type, domain, AVAHI_PROTO_INET, 0, service_resolver_callback, data))) { 1021 result = map_error(avahi_client_errno(self->client)); 1022 goto finish; 1023 } 1024 1025 result = SW_OKAY; 1026 1027finish: 1028 1029 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 1030 1031 if (result != SW_OKAY) 1032 if (*oid != (sw_discovery_oid) -1) 1033 sw_discovery_cancel(self, *oid); 1034 1035 return result; 1036} 1037 1038static void service_browser_callback( 1039 AvahiServiceBrowser *b, 1040 AvahiIfIndex interface, 1041 AVAHI_GCC_UNUSED AvahiProtocol protocol, 1042 AvahiBrowserEvent event, 1043 const char *name, 1044 const char *type, 1045 const char *domain, 1046 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 1047 void *userdata) { 1048 1049 oid_data* data = userdata; 1050 char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX]; 1051 sw_discovery_browse_reply reply; 1052 1053 assert(b); 1054 assert(data); 1055 1056 reply = (sw_discovery_browse_reply) data->reply; 1057 1058 type = add_trailing_dot(type, type_fixed, sizeof(type_fixed)); 1059 domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed)); 1060 1061 switch (event) { 1062 case AVAHI_BROWSER_NEW: 1063 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_SERVICE, interface, name, type, domain, data->extra); 1064 break; 1065 1066 case AVAHI_BROWSER_REMOVE: 1067 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_SERVICE, interface, name, type, domain, data->extra); 1068 break; 1069 1070 case AVAHI_BROWSER_FAILURE: 1071 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, name, type, domain, data->extra); 1072 break; 1073 1074 case AVAHI_BROWSER_CACHE_EXHAUSTED: 1075 case AVAHI_BROWSER_ALL_FOR_NOW: 1076 break; 1077 } 1078} 1079 1080sw_result sw_discovery_browse( 1081 sw_discovery self, 1082 sw_uint32 interface_index, 1083 sw_const_string type, 1084 sw_const_string domain, 1085 sw_discovery_browse_reply reply, 1086 sw_opaque extra, 1087 sw_discovery_oid * oid) { 1088 1089 oid_data *data; 1090 AvahiIfIndex ifindex; 1091 sw_result result = SW_E_UNKNOWN; 1092 1093 assert(self); 1094 assert(type); 1095 assert(reply); 1096 assert(oid); 1097 1098 AVAHI_WARN_LINKAGE; 1099 1100 if ((*oid = oid_alloc(self, OID_SERVICE_BROWSER)) == (sw_discovery_oid) -1) 1101 return SW_E_UNKNOWN; 1102 1103 data = oid_get(self, *oid); 1104 assert(data); 1105 data->reply = (sw_result (*)(void)) reply; 1106 data->extra = extra; 1107 1108 ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index; 1109 1110 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 1111 1112 if (!(data->object = avahi_service_browser_new(self->client, ifindex, AVAHI_PROTO_INET, type, domain, 0, service_browser_callback, data))) { 1113 result = map_error(avahi_client_errno(self->client)); 1114 goto finish; 1115 } 1116 1117 result = SW_OKAY; 1118 1119finish: 1120 1121 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 1122 1123 if (result != SW_OKAY) 1124 if (*oid != (sw_discovery_oid) -1) 1125 sw_discovery_cancel(self, *oid); 1126 1127 return result; 1128} 1129 1130sw_result sw_discovery_cancel(sw_discovery self, sw_discovery_oid oid) { 1131 oid_data *data; 1132 assert(self); 1133 1134 AVAHI_WARN_LINKAGE; 1135 1136 if (!(data = oid_get(self, oid))) 1137 return SW_E_UNKNOWN; 1138 1139 if (data->object) { 1140 switch (data->type) { 1141 case OID_SERVICE_BROWSER: 1142 avahi_service_browser_free(data->object); 1143 break; 1144 1145 case OID_SERVICE_RESOLVER: 1146 avahi_service_resolver_free(data->object); 1147 break; 1148 1149 case OID_DOMAIN_BROWSER: 1150 avahi_domain_browser_free(data->object); 1151 break; 1152 1153 case OID_ENTRY_GROUP: 1154 avahi_entry_group_free(data->object); 1155 break; 1156 1157 case OID_UNUSED: 1158 ; 1159 } 1160 } 1161 1162 if (data->service_data) { 1163 assert(data->type == OID_ENTRY_GROUP); 1164 service_data_free(self, data->service_data); 1165 } 1166 1167 oid_release(self, oid); 1168 1169 return SW_OKAY; 1170} 1171 1172sw_result sw_discovery_init_with_flags( 1173 sw_discovery * self, 1174 sw_discovery_init_flags flags) { 1175 1176 assert(self); 1177 1178 AVAHI_WARN_LINKAGE; 1179 1180 if (flags != SW_DISCOVERY_USE_SHARED_SERVICE) 1181 return SW_E_NO_IMPL; 1182 1183 return sw_discovery_init(self); 1184} 1185