diff options
Diffstat (limited to 'avahi-compat-howl/compat.c')
-rw-r--r-- | avahi-compat-howl/compat.c | 661 |
1 files changed, 661 insertions, 0 deletions
diff --git a/avahi-compat-howl/compat.c b/avahi-compat-howl/compat.c new file mode 100644 index 0000000..fc50458 --- /dev/null +++ b/avahi-compat-howl/compat.c @@ -0,0 +1,661 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> + +#include <pthread.h> + +#include <avahi-common/strlst.h> +#include <avahi-common/malloc.h> +#include <avahi-common/domain.h> +#include <avahi-common/simple-watch.h> +#include <avahi-common/error.h> +#include <avahi-client/client.h> + +#include "howl.h" +#include "warn.h" + +#define OID_MAX 50 + +enum { + COMMAND_POLL = 'p', + COMMAND_QUIT = 'q', + COMMAND_POLL_DONE = 'P' +}; + +typedef enum { + OID_UNUSED = 0, + OID_SERVICE_BROWSER, + OID_SERVICE_RESOLVER, + OID_DOMAIN_BROWSER, + OID_ENTRY_GROUP +} oid_type; + +typedef struct oid_data { + oid_type type; + sw_opaque extra; + sw_discovery discovery; + void *object; + sw_result (*reply)(void); +} oid_data; + +struct _sw_discovery { + int n_ref; + AvahiSimplePoll *simple_poll; + AvahiClient *client; + + oid_data oid_table[OID_MAX]; + sw_discovery_oid oid_index; + + int thread_fd, main_fd; + + pthread_t thread; + int thread_running; + + pthread_mutex_t mutex; + +}; + +#define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); } + +static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) { + if (!s) + return NULL; + + if (*s == 0) + return s; + + if (s[strlen(s)-1] == '.') + return s; + + snprintf(buf, buf_len, "%s.", s); + return buf; +} + +static sw_result map_error(int error) { + switch (error) { + case AVAHI_OK: + return SW_OKAY; + + case AVAHI_ERR_NO_MEMORY: + return SW_E_MEM; + } + + return SW_E_UNKNOWN; +} + +static int read_command(int fd) { + ssize_t r; + char command; + + assert(fd >= 0); + + if ((r = read(fd, &command, 1)) != 1) { + fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); + return -1; + } + + return command; +} + +static int write_command(int fd, char reply) { + assert(fd >= 0); + + if (write(fd, &reply, 1) != 1) { + fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) { + sw_discovery self = userdata; + int ret; + + assert(self); + + ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); + ret = poll(ufds, nfds, timeout); + ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); + + return ret; +} + +static void * thread_func(void *data) { + sw_discovery self = data; + sigset_t mask; + + sigfillset(&mask); + pthread_sigmask(SIG_BLOCK, &mask, NULL); + + self->thread = pthread_self(); + self->thread_running = 1; + + for (;;) { + char command; + + if ((command = read_command(self->thread_fd)) < 0) + break; + +/* fprintf(stderr, "Command: %c\n", command); */ + + switch (command) { + + case COMMAND_POLL: + + ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); + + if (avahi_simple_poll_run(self->simple_poll) < 0) { + fprintf(stderr, __FILE__": avahi_simple_poll_run() failed.\n"); + ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); + break; + } + + ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); + + if (write_command(self->thread_fd, COMMAND_POLL_DONE) < 0) + break; + + break; + + case COMMAND_QUIT: + return NULL; + } + + } + + return NULL; +} + +static int oid_alloc(sw_discovery self, oid_type type) { + sw_discovery_oid i; + assert(self); + + for (i = 0; i < OID_MAX; i++) { + + while (self->oid_index >= OID_MAX) + self->oid_index -= OID_MAX; + + if (self->oid_table[self->oid_index].type == OID_UNUSED) { + self->oid_table[self->oid_index].type = type; + self->oid_table[self->oid_index].discovery = self; + return self->oid_index ++; + } + + self->oid_index ++; + } + + /* No free entry found */ + + return (sw_discovery_oid) -1; +} + +static void oid_release(sw_discovery self, sw_discovery_oid oid) { + assert(self); + assert(oid < OID_MAX); + + assert(self->oid_table[oid].type != OID_UNUSED); + + self->oid_table[oid].type = OID_UNUSED; + self->oid_table[oid].discovery = NULL; + self->oid_table[oid].reply = NULL; + self->oid_table[oid].object = NULL; + self->oid_table[oid].extra = NULL; +} + +static oid_data* oid_get(sw_discovery self, sw_discovery_oid oid) { + assert(self); + + if (oid >= OID_MAX) + return NULL; + + if (self->oid_table[oid].type == OID_UNUSED) + return NULL; + + return &self->oid_table[oid]; +} + +sw_result sw_discovery_init(sw_discovery * self) { + int fd[2] = { -1, -1}; + sw_result result = SW_E_UNKNOWN; + pthread_mutexattr_t mutex_attr; + int error; + + assert(self); + + AVAHI_WARN_LINKAGE; + + *self = NULL; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) + goto fail; + + if (!(*self = avahi_new(struct _sw_discovery, 1))) { + result = SW_E_MEM; + goto fail; + } + + (*self)->n_ref = 1; + (*self)->thread_fd = fd[0]; + (*self)->main_fd = fd[1]; + + (*self)->client = NULL; + (*self)->simple_poll = NULL; + + memset((*self)->oid_table, 0, sizeof((*self)->oid_table)); + (*self)->oid_index = 0; + + (*self)->thread_running = 0; + + ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr)); + pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE); + ASSERT_SUCCESS(pthread_mutex_init(&(*self)->mutex, NULL)); + + if (!((*self)->simple_poll = avahi_simple_poll_new())) + goto fail; + + avahi_simple_poll_set_func((*self)->simple_poll, poll_func, *self); + + if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), NULL, *self, &error))) { + result = map_error(error); + goto fail; + } + + /* Start simple poll */ + if (avahi_simple_poll_prepare((*self)->simple_poll, -1) < 0) + goto fail; + + /* Queue an initial POLL command for the thread */ + if (write_command((*self)->main_fd, COMMAND_POLL) < 0) + goto fail; + + if (pthread_create(&(*self)->thread, NULL, thread_func, *self) != 0) + goto fail; + + (*self)->thread_running = 1; + + return SW_OKAY; + +fail: + + if (*self) + sw_discovery_fina(*self); + + return result; +} + +static int stop_thread(sw_discovery self) { + assert(self); + + if (!self->thread_running) + return 0; + + if (write_command(self->main_fd, COMMAND_QUIT) < 0) + return -1; + + avahi_simple_poll_wakeup(self->simple_poll); + + ASSERT_SUCCESS(pthread_join(self->thread, NULL)); + self->thread_running = 0; + return 0; +} + +static sw_discovery discover_ref(sw_discovery self) { + assert(self); + assert(self->n_ref >= 1); + + self->n_ref++; + + return self; +} + +static void discover_unref(sw_discovery self) { + assert(self); + assert(self->n_ref >= 1); + + if (--self->n_ref > 0) + return; + + stop_thread(self); + + if (self->client) + avahi_client_free(self->client); + + if (self->simple_poll) + avahi_simple_poll_free(self->simple_poll); + + if (self->thread_fd >= 0) + close(self->thread_fd); + + if (self->main_fd >= 0) + close(self->main_fd); + + ASSERT_SUCCESS(pthread_mutex_destroy(&self->mutex)); + + avahi_free(self); +} + +sw_result sw_discovery_fina(sw_discovery self) { + assert(self); + + AVAHI_WARN_LINKAGE; + + stop_thread(self); + discover_unref(self); + + return SW_OKAY; +} + +sw_result sw_discovery_run(sw_discovery self) { + assert(self); + + AVAHI_WARN_LINKAGE; + + return sw_salt_run((sw_salt) self); +} + +sw_result sw_discovery_stop_run(sw_discovery self) { + assert(self); + + AVAHI_WARN_LINKAGE; + + return sw_salt_stop_run((sw_salt) self); +} + +int sw_discovery_socket(sw_discovery self) { + assert(self); + + AVAHI_WARN_LINKAGE; + + return self->main_fd; +} + +sw_result sw_discovery_read_socket(sw_discovery self) { + sw_result result = SW_E_UNKNOWN; + + assert(self); + + discover_ref(self); + + ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); + + /* Cleanup notification socket */ + if (read_command(self->main_fd) != COMMAND_POLL_DONE) + goto finish; + + if (avahi_simple_poll_dispatch(self->simple_poll) < 0) + goto finish; + + if (self->n_ref > 1) /* Perhaps we should die */ + + /* Dispatch events */ + if (avahi_simple_poll_prepare(self->simple_poll, -1) < 0) + goto finish; + + if (self->n_ref > 1) + + /* Request the poll */ + if (write_command(self->main_fd, COMMAND_POLL) < 0) + goto finish; + + result = SW_OKAY; + +finish: + + ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); + + discover_unref(self); + + return result; +} + +sw_result sw_discovery_salt(sw_discovery self, sw_salt *salt) { + assert(self); + assert(salt); + + AVAHI_WARN_LINKAGE; + + *salt = (sw_salt) self; + + return SW_OKAY; +} + +sw_result sw_salt_step(sw_salt self, sw_uint32 * msec) { + struct pollfd p; + int r; + sw_result result; + + AVAHI_WARN_LINKAGE; + + if (!((sw_discovery) self)->thread_running) + return SW_E_UNKNOWN; + + memset(&p, 0, sizeof(p)); + p.fd = ((sw_discovery) self)->main_fd; + p.events = POLLIN; + + if ((r = poll(&p, 1, msec ? (int) *msec : -1)) < 0) { + + /* Don't treat EINTR as error */ + if (errno == EINTR) + return SW_OKAY; + + return SW_E_UNKNOWN; + + } else if (r == 0) { + + /* Timeoout */ + return SW_OKAY; + + } else { + /* Success */ + + if (p.revents != POLLIN) + return SW_E_UNKNOWN; + + if ((result = sw_discovery_read_socket((sw_discovery) self)) != SW_OKAY) + return result; + } + + return SW_OKAY; +} + +sw_result sw_salt_run(sw_salt self) { + sw_result ret; + + AVAHI_WARN_LINKAGE; + + for (;;) + if ((ret = sw_salt_step(self, NULL)) != SW_OKAY) + return ret; +} + +sw_result sw_salt_stop_run(sw_salt self) { + AVAHI_WARN_LINKAGE; + + if (stop_thread((sw_discovery) self) < 0) + return SW_E_UNKNOWN; + + return SW_OKAY; +} + +sw_result sw_discovery_publish( + sw_discovery self, + sw_uint32 interface_index, + sw_const_string name, + sw_const_string type, + sw_const_string domain, + sw_const_string host, + sw_port port, + sw_octets text_record, + sw_uint32 text_record_len, + sw_discovery_publish_reply reply, + sw_opaque extra, + sw_discovery_oid * oid) { + AVAHI_WARN_UNSUPPORTED; + return SW_E_NO_IMPL; +} + +sw_result sw_discovery_browse_domains( + sw_discovery self, + sw_uint32 interface_index, + sw_discovery_browse_reply reply, + sw_opaque extra, + sw_discovery_oid * oid) { + AVAHI_WARN_UNSUPPORTED; + return SW_E_NO_IMPL; +} + +sw_result sw_discovery_resolve( + sw_discovery self, + sw_uint32 interface_index, + sw_const_string name, + sw_const_string type, + sw_const_string domain, + sw_discovery_resolve_reply reply, + sw_opaque extra, + sw_discovery_oid * oid) { + AVAHI_WARN_UNSUPPORTED; + return SW_E_NO_IMPL; +} + +#define OID_GET_INDEX(data) ((sw_discovery_oid) (((data) - ((data)->discovery->oid_table))/sizeof(oid_data))) + +static void service_browser_callback( + AvahiServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, + void *userdata) { + + oid_data* data = userdata; + char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX]; + sw_discovery_browse_reply reply; + + assert(b); + assert(data); + + reply = (sw_discovery_browse_reply) data->reply; + + type = add_trailing_dot(type, type_fixed, sizeof(type_fixed)); + domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed)); + + switch (event) { + case AVAHI_BROWSER_NEW: + reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_SERVICE, interface, name, type, domain, data->extra); + break; + + case AVAHI_BROWSER_REMOVE: + reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_SERVICE, interface, name, type, domain, data->extra); + break; + + case AVAHI_BROWSER_FAILURE: + reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, name, type, domain, data->extra); + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_ALL_FOR_NOW: + break; + } +} + + +sw_result sw_discovery_browse( + sw_discovery self, + sw_uint32 interface_index, + sw_const_string type, + sw_const_string domain, + sw_discovery_browse_reply reply, + sw_opaque extra, + sw_discovery_oid * oid) { + + oid_data *data; + AvahiIfIndex ifindex; + sw_result result = SW_E_UNKNOWN; + + assert(self); + assert(type); + assert(reply); + assert(oid); + + AVAHI_WARN_LINKAGE; + + if ((*oid = oid_alloc(self, OID_SERVICE_BROWSER)) == (sw_discovery_oid) -1) + return SW_E_UNKNOWN; + + data = oid_get(self, *oid); + assert(data); + data->reply = (sw_result (*)(void)) reply; + data->extra = extra; + + ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index; + + ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); + + if (!(data->object = avahi_service_browser_new(self->client, ifindex, AVAHI_PROTO_UNSPEC, type, domain, 0, service_browser_callback, data))) { + result = map_error(avahi_client_errno(self->client)); + goto finish; + } + + result = SW_OKAY; + +finish: + + ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); + + if (result != SW_OKAY) + if (*oid != (sw_discovery_oid) -1) + oid_release(self, *oid); + + return result; +} + +sw_result sw_discovery_cancel(sw_discovery self, sw_discovery_oid oid) { + oid_data *data; + assert(self); + + AVAHI_WARN_LINKAGE; + + if (!(data = oid_get(self, oid))) + return SW_E_UNKNOWN; + + switch (data->type) { + case OID_SERVICE_BROWSER: + avahi_service_browser_free(data->object); + break; + + case OID_UNUSED: + ; + } + + oid_release(self, oid); + + return SW_OKAY; +} |