4 This file is part of avahi.
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.
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.
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
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-client/client.h>
45 COMMAND_POLL_DONE = 'P'
56 typedef struct oid_data {
59 sw_discovery discovery;
61 sw_result (*reply)(void);
64 struct _sw_discovery {
66 AvahiSimplePoll *simple_poll;
69 oid_data oid_table[OID_MAX];
70 sw_discovery_oid oid_index;
72 int thread_fd, main_fd;
77 pthread_mutex_t mutex;
81 #define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); }
83 #define OID_GET_INDEX(data) ((sw_discovery_oid) (((data) - ((data)->discovery->oid_table))))
85 static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) {
92 if (s[strlen(s)-1] == '.')
95 snprintf(buf, buf_len, "%s.", s);
99 static sw_result map_error(int error) {
104 case AVAHI_ERR_NO_MEMORY:
111 static int read_command(int fd) {
117 if ((r = read(fd, &command, 1)) != 1) {
118 fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
125 static int write_command(int fd, char reply) {
128 if (write(fd, &reply, 1) != 1) {
129 fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
136 static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) {
137 sw_discovery self = userdata;
142 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
143 ret = poll(ufds, nfds, timeout);
144 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
149 static void * thread_func(void *data) {
150 sw_discovery self = data;
154 pthread_sigmask(SIG_BLOCK, &mask, NULL);
156 self->thread = pthread_self();
157 self->thread_running = 1;
162 if ((command = read_command(self->thread_fd)) < 0)
165 /* fprintf(stderr, "Command: %c\n", command); */
171 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
173 if (avahi_simple_poll_run(self->simple_poll) < 0) {
174 fprintf(stderr, __FILE__": avahi_simple_poll_run() failed.\n");
175 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
179 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
181 if (write_command(self->thread_fd, COMMAND_POLL_DONE) < 0)
195 static int oid_alloc(sw_discovery self, oid_type type) {
199 for (i = 0; i < OID_MAX; i++) {
201 while (self->oid_index >= OID_MAX)
202 self->oid_index -= OID_MAX;
204 if (self->oid_table[self->oid_index].type == OID_UNUSED) {
205 self->oid_table[self->oid_index].type = type;
206 self->oid_table[self->oid_index].discovery = self;
208 assert(OID_GET_INDEX(&self->oid_table[self->oid_index]) == self->oid_index);
210 return self->oid_index ++;
216 /* No free entry found */
218 return (sw_discovery_oid) -1;
221 static void oid_release(sw_discovery self, sw_discovery_oid oid) {
223 assert(oid < OID_MAX);
225 assert(self->oid_table[oid].type != OID_UNUSED);
227 self->oid_table[oid].type = OID_UNUSED;
228 self->oid_table[oid].discovery = NULL;
229 self->oid_table[oid].reply = NULL;
230 self->oid_table[oid].object = NULL;
231 self->oid_table[oid].extra = NULL;
234 static oid_data* oid_get(sw_discovery self, sw_discovery_oid oid) {
240 if (self->oid_table[oid].type == OID_UNUSED)
243 return &self->oid_table[oid];
246 sw_result sw_discovery_init(sw_discovery * self) {
247 int fd[2] = { -1, -1};
248 sw_result result = SW_E_UNKNOWN;
249 pthread_mutexattr_t mutex_attr;
258 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0)
261 if (!(*self = avahi_new(struct _sw_discovery, 1))) {
267 (*self)->thread_fd = fd[0];
268 (*self)->main_fd = fd[1];
270 (*self)->client = NULL;
271 (*self)->simple_poll = NULL;
273 memset((*self)->oid_table, 0, sizeof((*self)->oid_table));
274 (*self)->oid_index = 0;
276 (*self)->thread_running = 0;
278 ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr));
279 pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
280 ASSERT_SUCCESS(pthread_mutex_init(&(*self)->mutex, &mutex_attr));
282 if (!((*self)->simple_poll = avahi_simple_poll_new()))
285 avahi_simple_poll_set_func((*self)->simple_poll, poll_func, *self);
287 if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), NULL, *self, &error))) {
288 result = map_error(error);
292 /* Start simple poll */
293 if (avahi_simple_poll_prepare((*self)->simple_poll, -1) < 0)
296 /* Queue an initial POLL command for the thread */
297 if (write_command((*self)->main_fd, COMMAND_POLL) < 0)
300 if (pthread_create(&(*self)->thread, NULL, thread_func, *self) != 0)
303 (*self)->thread_running = 1;
310 sw_discovery_fina(*self);
315 static int stop_thread(sw_discovery self) {
318 if (!self->thread_running)
321 if (write_command(self->main_fd, COMMAND_QUIT) < 0)
324 avahi_simple_poll_wakeup(self->simple_poll);
326 ASSERT_SUCCESS(pthread_join(self->thread, NULL));
327 self->thread_running = 0;
331 static sw_discovery discover_ref(sw_discovery self) {
333 assert(self->n_ref >= 1);
340 static void discover_unref(sw_discovery self) {
342 assert(self->n_ref >= 1);
344 if (--self->n_ref > 0)
350 avahi_client_free(self->client);
352 if (self->simple_poll)
353 avahi_simple_poll_free(self->simple_poll);
355 if (self->thread_fd >= 0)
356 close(self->thread_fd);
358 if (self->main_fd >= 0)
359 close(self->main_fd);
361 ASSERT_SUCCESS(pthread_mutex_destroy(&self->mutex));
366 sw_result sw_discovery_fina(sw_discovery self) {
372 discover_unref(self);
377 sw_result sw_discovery_run(sw_discovery self) {
382 return sw_salt_run((sw_salt) self);
385 sw_result sw_discovery_stop_run(sw_discovery self) {
390 return sw_salt_stop_run((sw_salt) self);
393 int sw_discovery_socket(sw_discovery self) {
398 return self->main_fd;
401 sw_result sw_discovery_read_socket(sw_discovery self) {
402 sw_result result = SW_E_UNKNOWN;
408 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
410 /* Cleanup notification socket */
411 if (read_command(self->main_fd) != COMMAND_POLL_DONE)
414 if (avahi_simple_poll_dispatch(self->simple_poll) < 0)
417 if (self->n_ref > 1) /* Perhaps we should die */
419 /* Dispatch events */
420 if (avahi_simple_poll_prepare(self->simple_poll, -1) < 0)
425 /* Request the poll */
426 if (write_command(self->main_fd, COMMAND_POLL) < 0)
433 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
435 discover_unref(self);
440 sw_result sw_discovery_salt(sw_discovery self, sw_salt *salt) {
446 *salt = (sw_salt) self;
451 sw_result sw_salt_step(sw_salt self, sw_uint32 * msec) {
458 if (!((sw_discovery) self)->thread_running)
461 memset(&p, 0, sizeof(p));
462 p.fd = ((sw_discovery) self)->main_fd;
465 if ((r = poll(&p, 1, msec ? (int) *msec : -1)) < 0) {
467 /* Don't treat EINTR as error */
481 if (p.revents != POLLIN)
484 if ((result = sw_discovery_read_socket((sw_discovery) self)) != SW_OKAY)
491 sw_result sw_salt_run(sw_salt self) {
497 if ((ret = sw_salt_step(self, NULL)) != SW_OKAY)
501 sw_result sw_salt_stop_run(sw_salt self) {
504 if (stop_thread((sw_discovery) self) < 0)
510 sw_result sw_discovery_publish(
512 sw_uint32 interface_index,
513 sw_const_string name,
514 sw_const_string type,
515 sw_const_string domain,
516 sw_const_string host,
518 sw_octets text_record,
519 sw_uint32 text_record_len,
520 sw_discovery_publish_reply reply,
522 sw_discovery_oid * oid) {
523 AVAHI_WARN_UNSUPPORTED;
527 static void domain_browser_callback(
528 AvahiDomainBrowser *b,
529 AvahiIfIndex interface,
530 AvahiProtocol protocol,
531 AvahiBrowserEvent event,
533 AvahiLookupResultFlags flags,
536 oid_data* data = userdata;
537 sw_discovery_browse_reply reply;
538 static char domain_fixed[AVAHI_DOMAIN_NAME_MAX];
543 reply = (sw_discovery_browse_reply) data->reply;
545 domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
548 case AVAHI_BROWSER_NEW:
549 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_DOMAIN, interface, NULL, NULL, domain, data->extra);
552 case AVAHI_BROWSER_REMOVE:
553 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_DOMAIN, interface, NULL, NULL, domain, data->extra);
556 case AVAHI_BROWSER_FAILURE:
557 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, NULL, NULL, domain, data->extra);
560 case AVAHI_BROWSER_CACHE_EXHAUSTED:
561 case AVAHI_BROWSER_ALL_FOR_NOW:
566 sw_result sw_discovery_browse_domains(
568 sw_uint32 interface_index,
569 sw_discovery_browse_reply reply,
571 sw_discovery_oid * oid) {
574 AvahiIfIndex ifindex;
575 sw_result result = SW_E_UNKNOWN;
583 if ((*oid = oid_alloc(self, OID_DOMAIN_BROWSER)) == (sw_discovery_oid) -1)
586 data = oid_get(self, *oid);
588 data->reply = (sw_result (*)(void)) reply;
591 ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
593 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
595 if (!(data->object = avahi_domain_browser_new(self->client, ifindex, AVAHI_PROTO_INET, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browser_callback, data))) {
596 result = map_error(avahi_client_errno(self->client));
604 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
606 if (result != SW_OKAY)
607 if (*oid != (sw_discovery_oid) -1)
608 oid_release(self, *oid);
613 static void service_resolver_callback(
614 AvahiServiceResolver *r,
615 AvahiIfIndex interface,
616 AvahiProtocol protocol,
617 AvahiResolverEvent event,
621 const char *host_name,
622 const AvahiAddress *a,
624 AvahiStringList *txt,
625 AvahiLookupResultFlags flags,
628 oid_data* data = userdata;
629 sw_discovery_resolve_reply reply;
634 reply = (sw_discovery_resolve_reply) data->reply;
637 case AVAHI_RESOLVER_FOUND: {
639 char host_name_fixed[AVAHI_DOMAIN_NAME_MAX];
642 sw_ipv4_address addr;
644 sw_ipv4_address_init_from_saddr(&addr, a->data.ipv4.address);
646 host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed));
648 if ((p = avahi_new0(uint8_t, (l = avahi_string_list_serialize(txt, NULL, 0))+1)))
649 avahi_string_list_serialize(txt, p, l);
651 reply(data->discovery, OID_GET_INDEX(data), interface, name, type, domain, addr, port, p, l, data->extra);
657 case AVAHI_RESOLVER_FAILURE:
659 /* Apparently there is no way in HOWL to inform about failed resolvings ... */
661 avahi_warn("A service failed to resolve in the HOWL compatiblity layer of Avahi which is used by '%s'. "
662 "Since the HOWL API doesn't offer any means to inform the application about this, we have to ignore the failure. "
663 "Please fix your application to use the native API of Avahi!",
670 sw_result sw_discovery_resolve(
672 sw_uint32 interface_index,
673 sw_const_string name,
674 sw_const_string type,
675 sw_const_string domain,
676 sw_discovery_resolve_reply reply,
678 sw_discovery_oid * oid) {
681 AvahiIfIndex ifindex;
682 sw_result result = SW_E_UNKNOWN;
692 if ((*oid = oid_alloc(self, OID_SERVICE_RESOLVER)) == (sw_discovery_oid) -1)
695 data = oid_get(self, *oid);
697 data->reply = (sw_result (*)(void)) reply;
700 ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
702 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
704 if (!(data->object = avahi_service_resolver_new(self->client, ifindex, AVAHI_PROTO_INET, name, type, domain, AVAHI_PROTO_INET, 0, service_resolver_callback, data))) {
705 result = map_error(avahi_client_errno(self->client));
713 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
715 if (result != SW_OKAY)
716 if (*oid != (sw_discovery_oid) -1)
717 oid_release(self, *oid);
722 static void service_browser_callback(
723 AvahiServiceBrowser *b,
724 AvahiIfIndex interface,
725 AvahiProtocol protocol,
726 AvahiBrowserEvent event,
730 AvahiLookupResultFlags flags,
733 oid_data* data = userdata;
734 char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
735 sw_discovery_browse_reply reply;
740 reply = (sw_discovery_browse_reply) data->reply;
742 type = add_trailing_dot(type, type_fixed, sizeof(type_fixed));
743 domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
746 case AVAHI_BROWSER_NEW:
747 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_SERVICE, interface, name, type, domain, data->extra);
750 case AVAHI_BROWSER_REMOVE:
751 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_SERVICE, interface, name, type, domain, data->extra);
754 case AVAHI_BROWSER_FAILURE:
755 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, name, type, domain, data->extra);
758 case AVAHI_BROWSER_CACHE_EXHAUSTED:
759 case AVAHI_BROWSER_ALL_FOR_NOW:
764 sw_result sw_discovery_browse(
766 sw_uint32 interface_index,
767 sw_const_string type,
768 sw_const_string domain,
769 sw_discovery_browse_reply reply,
771 sw_discovery_oid * oid) {
774 AvahiIfIndex ifindex;
775 sw_result result = SW_E_UNKNOWN;
784 if ((*oid = oid_alloc(self, OID_SERVICE_BROWSER)) == (sw_discovery_oid) -1)
787 data = oid_get(self, *oid);
789 data->reply = (sw_result (*)(void)) reply;
792 ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
794 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
796 if (!(data->object = avahi_service_browser_new(self->client, ifindex, AVAHI_PROTO_INET, type, domain, 0, service_browser_callback, data))) {
797 result = map_error(avahi_client_errno(self->client));
805 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
807 if (result != SW_OKAY)
808 if (*oid != (sw_discovery_oid) -1)
809 oid_release(self, *oid);
814 sw_result sw_discovery_cancel(sw_discovery self, sw_discovery_oid oid) {
820 if (!(data = oid_get(self, oid)))
823 switch (data->type) {
824 case OID_SERVICE_BROWSER:
825 avahi_service_browser_free(data->object);
828 case OID_SERVICE_RESOLVER:
829 avahi_service_resolver_free(data->object);
832 case OID_DOMAIN_BROWSER:
833 avahi_domain_browser_free(data->object);
836 case OID_ENTRY_GROUP:
837 avahi_entry_group_free(data->object);
844 oid_release(self, oid);