diff options
author | Max Kellermann <max@duempel.org> | 2008-12-30 16:43:58 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2008-12-30 16:43:58 +0100 |
commit | 49fa9708d51b907f0a372a1fc68a1e27c9edae5e (patch) | |
tree | d943b16f8943190cc4225040d67761a61d0394df | |
parent | bbed35822e52a204bf3d82e5df4bb8d00b67af39 (diff) | |
download | mpd-49fa9708d51b907f0a372a1fc68a1e27c9edae5e.tar.gz mpd-49fa9708d51b907f0a372a1fc68a1e27c9edae5e.tar.xz mpd-49fa9708d51b907f0a372a1fc68a1e27c9edae5e.zip |
zeroconf: moved code to zeroconf-avahi.c and zeroconf-bonjour.c
Moved implementation specific code to their own sources, internal
declarations in zeroconf-internal.h.
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | src/Makefile.am | 10 | ||||
-rw-r--r-- | src/zeroconf-avahi.c | 480 | ||||
-rw-r--r-- | src/zeroconf-bonjour.c | 122 | ||||
-rw-r--r-- | src/zeroconf-internal.h | 33 | ||||
-rw-r--r-- | src/zeroconf.c | 558 |
6 files changed, 649 insertions, 556 deletions
diff --git a/configure.ac b/configure.ac index 2ba64c678..dfcd15832 100644 --- a/configure.ac +++ b/configure.ac @@ -944,6 +944,8 @@ if test x$with_zeroconf != xno; then fi AM_CONDITIONAL(HAVE_ZEROCONF, test x$with_zeroconf != xno) +AM_CONDITIONAL(HAVE_AVAHI, test x$with_zeroconf = xavahi) +AM_CONDITIONAL(HAVE_BONJOUR, test x$with_zeroconf = xbonjour) dnl diff --git a/src/Makefile.am b/src/Makefile.am index f9fe0b0cf..6a8894cbe 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -79,7 +79,7 @@ mpd_headers = \ utils.h \ volume.h \ ioops.h \ - zeroconf.h \ + zeroconf.h zeroconf-internal.h \ locate.h \ stored_playlist.h \ timer.h \ @@ -256,6 +256,14 @@ endif if HAVE_ZEROCONF mpd_SOURCES += zeroconf.c + +if HAVE_AVAHI +mpd_SOURCES += zeroconf-avahi.c +endif + +if HAVE_BONJOUR +mpd_SOURCES += zeroconf-bonjour.c +endif endif diff --git a/src/zeroconf-avahi.c b/src/zeroconf-avahi.c new file mode 100644 index 000000000..688e2ab24 --- /dev/null +++ b/src/zeroconf-avahi.c @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2003-2008 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "zeroconf-internal.h" +#include "listen.h" +#include "utils.h" +#include "ioops.h" + +#include <glib.h> + +#include <avahi-client/client.h> +#include <avahi-client/publish.h> + +#include <avahi-common/alternative.h> +#include <avahi-common/domain.h> +#include <avahi-common/malloc.h> +#include <avahi-common/error.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "avahi" + +static struct ioOps zeroConfIo; + +static char *avahiName; +static int avahiRunning; +static AvahiPoll avahiPoll; +static AvahiClient *avahiClient; +static AvahiEntryGroup *avahiGroup; + +static int avahiFdset(fd_set * rfds, fd_set * wfds, fd_set * efds); +static int avahiFdconsume(int fdCount, fd_set * rfds, fd_set * wfds, + fd_set * efds); + +struct AvahiWatch { + struct AvahiWatch *prev; + struct AvahiWatch *next; + int fd; + AvahiWatchEvent requestedEvent; + AvahiWatchEvent observedEvent; + AvahiWatchCallback callback; + void *userdata; +}; + +struct AvahiTimeout { + struct AvahiTimeout *prev; + struct AvahiTimeout *next; + struct timeval expiry; + int enabled; + AvahiTimeoutCallback callback; + void *userdata; +}; + +static AvahiWatch *avahiWatchList; +static AvahiTimeout *avahiTimeoutList; + +static AvahiWatch *avahiWatchNew(G_GNUC_UNUSED const AvahiPoll * api, int fd, + AvahiWatchEvent event, + AvahiWatchCallback callback, void *userdata) +{ + struct AvahiWatch *newWatch = xmalloc(sizeof(struct AvahiWatch)); + + newWatch->fd = fd; + newWatch->requestedEvent = event; + newWatch->observedEvent = 0; + newWatch->callback = callback; + newWatch->userdata = userdata; + + /* Insert at front of list */ + newWatch->next = avahiWatchList; + avahiWatchList = newWatch; + newWatch->prev = NULL; + if (newWatch->next) + newWatch->next->prev = newWatch; + + return newWatch; +} + +static void avahiWatchUpdate(AvahiWatch * w, AvahiWatchEvent event) +{ + assert(w != NULL); + w->requestedEvent = event; +} + +static AvahiWatchEvent avahiWatchGetEvents(AvahiWatch * w) +{ + assert(w != NULL); + return w->observedEvent; +} + +static void avahiWatchFree(AvahiWatch * w) +{ + assert(w != NULL); + + if (avahiWatchList == w) + avahiWatchList = w->next; + else if (w->prev != NULL) + w->prev->next = w->next; + + free(w); +} + +static void avahiCheckExpiry(AvahiTimeout * t) +{ + assert(t != NULL); + if (t->enabled) { + struct timeval now; + gettimeofday(&now, NULL); + if (timercmp(&now, &(t->expiry), >)) { + t->enabled = 0; + t->callback(t, t->userdata); + } + } +} + +static void avahiTimeoutUpdate(AvahiTimeout * t, const struct timeval *tv) +{ + assert(t != NULL); + if (tv) { + t->enabled = 1; + t->expiry.tv_sec = tv->tv_sec; + t->expiry.tv_usec = tv->tv_usec; + } else { + t->enabled = 0; + } +} + +static void avahiTimeoutFree(AvahiTimeout * t) +{ + assert(t != NULL); + + if (avahiTimeoutList == t) + avahiTimeoutList = t->next; + else if (t->prev != NULL) + t->prev->next = t->next; + + free(t); +} + +static AvahiTimeout *avahiTimeoutNew(G_GNUC_UNUSED const AvahiPoll * api, + const struct timeval *tv, + AvahiTimeoutCallback callback, + void *userdata) +{ + struct AvahiTimeout *newTimeout = xmalloc(sizeof(struct AvahiTimeout)); + + newTimeout->callback = callback; + newTimeout->userdata = userdata; + + avahiTimeoutUpdate(newTimeout, tv); + + /* Insert at front of list */ + newTimeout->next = avahiTimeoutList; + avahiTimeoutList = newTimeout; + newTimeout->prev = NULL; + if (newTimeout->next) + newTimeout->next->prev = newTimeout; + + return newTimeout; +} + +static void avahiRegisterService(AvahiClient * c); + +/* Callback when the EntryGroup changes state */ +static void avahiGroupCallback(AvahiEntryGroup * g, + AvahiEntryGroupState state, + G_GNUC_UNUSED void *userdata) +{ + char *n; + assert(g); + + g_debug("Service group changed to state %d", state); + + switch (state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED: + /* The entry group has been established successfully */ + g_message("Service '%s' successfully established.", + avahiName); + break; + + case AVAHI_ENTRY_GROUP_COLLISION: + /* A service name collision happened. Let's pick a new name */ + n = avahi_alternative_service_name(avahiName); + avahi_free(avahiName); + avahiName = n; + + g_message("Service name collision, renaming service to '%s'", + avahiName); + + /* And recreate the services */ + avahiRegisterService(avahi_entry_group_get_client(g)); + break; + + case AVAHI_ENTRY_GROUP_FAILURE: + g_warning("Entry group failure: %s", + avahi_strerror(avahi_client_errno + (avahi_entry_group_get_client(g)))); + /* Some kind of failure happened while we were registering our services */ + avahiRunning = 0; + break; + + case AVAHI_ENTRY_GROUP_UNCOMMITED: + g_debug("Service group is UNCOMMITED"); + break; + case AVAHI_ENTRY_GROUP_REGISTERING: + g_debug("Service group is REGISTERING"); + } +} + +/* Registers a new service with avahi */ +static void avahiRegisterService(AvahiClient * c) +{ + int ret; + assert(c); + g_debug("Registering service %s/%s", SERVICE_TYPE, avahiName); + + /* If this is the first time we're called, + * let's create a new entry group */ + if (!avahiGroup) { + avahiGroup = avahi_entry_group_new(c, avahiGroupCallback, NULL); + if (!avahiGroup) { + g_warning("Failed to create avahi EntryGroup: %s", + avahi_strerror(avahi_client_errno(c))); + goto fail; + } + } + + /* Add the service */ + /* TODO: This currently binds to ALL interfaces. + * We could maybe add a service per actual bound interface, + * if that's better. */ + ret = avahi_entry_group_add_service(avahiGroup, + AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + 0, avahiName, SERVICE_TYPE, NULL, + NULL, boundPort, NULL); + if (ret < 0) { + g_warning("Failed to add service %s: %s", SERVICE_TYPE, + avahi_strerror(ret)); + goto fail; + } + + /* Tell the server to register the service group */ + ret = avahi_entry_group_commit(avahiGroup); + if (ret < 0) { + g_warning("Failed to commit service group: %s", + avahi_strerror(ret)); + goto fail; + } + return; + +fail: + avahiRunning = 0; +} + +/* Callback when avahi changes state */ +static void avahiClientCallback(AvahiClient * c, AvahiClientState state, + G_GNUC_UNUSED void *userdata) +{ + int reason; + assert(c); + + /* Called whenever the client or server state changes */ + g_debug("Client changed to state %d", state); + + switch (state) { + case AVAHI_CLIENT_S_RUNNING: + g_debug("Client is RUNNING"); + + /* The server has startup successfully and registered its host + * name on the network, so it's time to create our services */ + if (!avahiGroup) + avahiRegisterService(c); + break; + + case AVAHI_CLIENT_FAILURE: + reason = avahi_client_errno(c); + if (reason == AVAHI_ERR_DISCONNECTED) { + g_message("Client Disconnected, will reconnect shortly"); + if (avahiGroup) { + avahi_entry_group_free(avahiGroup); + avahiGroup = NULL; + } + if (avahiClient) + avahi_client_free(avahiClient); + avahiClient = + avahi_client_new(&avahiPoll, + AVAHI_CLIENT_NO_FAIL, + avahiClientCallback, NULL, + &reason); + if (!avahiClient) { + g_warning("Could not reconnect: %s", + avahi_strerror(reason)); + avahiRunning = 0; + } + } else { + g_warning("Client failure: %s (terminal)", + avahi_strerror(reason)); + avahiRunning = 0; + } + break; + + case AVAHI_CLIENT_S_COLLISION: + g_debug("Client is COLLISION"); + /* Let's drop our registered services. When the server is back + * in AVAHI_SERVER_RUNNING state we will register them + * again with the new host name. */ + if (avahiGroup) { + g_debug("Resetting group"); + avahi_entry_group_reset(avahiGroup); + } + + case AVAHI_CLIENT_S_REGISTERING: + g_debug("Client is REGISTERING"); + /* The server records are now being established. This + * might be caused by a host name change. We need to wait + * for our own records to register until the host name is + * properly esatblished. */ + + if (avahiGroup) { + g_debug("Resetting group"); + avahi_entry_group_reset(avahiGroup); + } + + break; + + case AVAHI_CLIENT_CONNECTING: + g_debug("Client is CONNECTING"); + } +} + +static int avahiFdset(fd_set * rfds, fd_set * wfds, fd_set * efds) +{ + AvahiWatch *w; + int maxfd = -1; + if (!avahiRunning) + return maxfd; + for (w = avahiWatchList; w != NULL; w = w->next) { + if (w->requestedEvent & AVAHI_WATCH_IN) { + FD_SET(w->fd, rfds); + } + if (w->requestedEvent & AVAHI_WATCH_OUT) { + FD_SET(w->fd, wfds); + } + if (w->requestedEvent & AVAHI_WATCH_ERR) { + FD_SET(w->fd, efds); + } + if (w->requestedEvent & AVAHI_WATCH_HUP) { + g_warning("No support for HUP events! (ignoring)"); + } + + if (w->fd > maxfd) + maxfd = w->fd; + } + return maxfd; +} + +static int avahiFdconsume(int fdCount, fd_set * rfds, fd_set * wfds, + fd_set * efds) +{ + int retval = fdCount; + AvahiTimeout *t; + AvahiWatch *w = avahiWatchList; + + while (w != NULL && retval > 0) { + AvahiWatch *current = w; + current->observedEvent = 0; + if (FD_ISSET(current->fd, rfds)) { + current->observedEvent |= AVAHI_WATCH_IN; + FD_CLR(current->fd, rfds); + retval--; + } + if (FD_ISSET(current->fd, wfds)) { + current->observedEvent |= AVAHI_WATCH_OUT; + FD_CLR(current->fd, wfds); + retval--; + } + if (FD_ISSET(current->fd, efds)) { + current->observedEvent |= AVAHI_WATCH_ERR; + FD_CLR(current->fd, efds); + retval--; + } + + /* Advance to the next one right now, in case the callback + * removes itself + */ + w = w->next; + + if (current->observedEvent && avahiRunning) { + current->callback(current, current->fd, + current->observedEvent, + current->userdata); + } + } + + t = avahiTimeoutList; + while (t != NULL && avahiRunning) { + AvahiTimeout *current = t; + + /* Advance to the next one right now, in case the callback + * removes itself + */ + t = t->next; + avahiCheckExpiry(current); + } + + return retval; +} + +void init_avahi(const char *serviceName) +{ + int error; + g_debug("Initializing interface"); + + if (!avahi_is_valid_service_name(serviceName)) + g_error("Invalid zeroconf_name \"%s\"", serviceName); + + avahiName = avahi_strdup(serviceName); + + avahiRunning = 1; + + avahiPoll.userdata = NULL; + avahiPoll.watch_new = avahiWatchNew; + avahiPoll.watch_update = avahiWatchUpdate; + avahiPoll.watch_get_events = avahiWatchGetEvents; + avahiPoll.watch_free = avahiWatchFree; + avahiPoll.timeout_new = avahiTimeoutNew; + avahiPoll.timeout_update = avahiTimeoutUpdate; + avahiPoll.timeout_free = avahiTimeoutFree; + + avahiClient = avahi_client_new(&avahiPoll, AVAHI_CLIENT_NO_FAIL, + avahiClientCallback, NULL, &error); + + if (!avahiClient) { + g_warning("Failed to create client: %s", + avahi_strerror(error)); + goto fail; + } + + zeroConfIo.fdset = avahiFdset; + zeroConfIo.consume = avahiFdconsume; + registerIO(&zeroConfIo); + + return; + +fail: + avahi_finish(); +} + +void avahi_finish(void) +{ + g_debug("Shutting down interface"); + deregisterIO(&zeroConfIo); + + if (avahiGroup) { + avahi_entry_group_free(avahiGroup); + avahiGroup = NULL; + } + + if (avahiClient) { + avahi_client_free(avahiClient); + avahiClient = NULL; + } + + avahi_free(avahiName); + avahiName = NULL; +} diff --git a/src/zeroconf-bonjour.c b/src/zeroconf-bonjour.c new file mode 100644 index 000000000..03be18de9 --- /dev/null +++ b/src/zeroconf-bonjour.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2003-2008 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "zeroconf-internal.h" +#include "listen.h" +#include "ioops.h" + +#include <glib.h> + +#include <dns_sd.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "bonjour" + +static struct ioOps zeroConfIo; + +static DNSServiceRef dnsReference; + +static int dnsRegisterFdset(fd_set * rfds, fd_set * wfds, fd_set * efds) +{ + int fd; + + if (dnsReference == NULL) + return -1; + + fd = DNSServiceRefSockFD(dnsReference); + if (fd == -1) + return -1; + + FD_SET(fd, rfds); + + return fd; +} + +static int dnsRegisterFdconsume(int fdCount, fd_set * rfds, fd_set * wfds, + fd_set * efds) +{ + int fd; + + if (dnsReference == NULL) + return -1; + + fd = DNSServiceRefSockFD(dnsReference); + if (fd == -1) + return -1; + + if (FD_ISSET(fd, rfds)) { + FD_CLR(fd, rfds); + + DNSServiceProcessResult(dnsReference); + + return fdCount - 1; + } + + return fdCount; +} + +static void dnsRegisterCallback(DNSServiceRef sdRef, DNSServiceFlags flags, + DNSServiceErrorType errorCode, const char *name, + const char *regtype, const char *domain, + void *context) +{ + if (errorCode != kDNSServiceErr_NoError) { + g_warning("Failed to register zeroconf service."); + + DNSServiceRefDeallocate(dnsReference); + dnsReference = NULL; + deregisterIO(&zeroConfIo); + } else { + g_debug("Registered zeroconf service with name '%s'", name); + } +} + +void init_zeroconf_osx(const char *serviceName) +{ + DNSServiceErrorType error = DNSServiceRegister(&dnsReference, + 0, 0, serviceName, + SERVICE_TYPE, NULL, NULL, + htons(boundPort), 0, + NULL, + dnsRegisterCallback, + NULL); + + if (error != kDNSServiceErr_NoError) { + g_warning("Failed to register zeroconf service."); + + if (dnsReference) { + DNSServiceRefDeallocate(dnsReference); + dnsReference = NULL; + } + return; + } + + zeroConfIo.fdset = dnsRegisterFdset; + zeroConfIo.consume = dnsRegisterFdconsume; + registerIO(&zeroConfIo); +} + +void bonjour_finish(void) +{ + deregisterIO(&zeroConfIo); + if (dnsReference != NULL) { + DNSServiceRefDeallocate(dnsReference); + dnsReference = NULL; + g_debug("Deregistered Zeroconf service."); + } +} diff --git a/src/zeroconf-internal.h b/src/zeroconf-internal.h new file mode 100644 index 000000000..ed27c66a6 --- /dev/null +++ b/src/zeroconf-internal.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2003-2008 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ZEROCONF_INTERNAL_H +#define ZEROCONF_INTERNAL_H + +/* The dns-sd service type qualifier to publish */ +#define SERVICE_TYPE "_mpd._tcp" + +void init_avahi(const char *service_name); + +void avahi_finish(void); + +void init_zeroconf_osx(const char *service_name); + +void bonjour_finish(void); + +#endif diff --git a/src/zeroconf.c b/src/zeroconf.c index 23d7a36e9..e51e5f045 100644 --- a/src/zeroconf.c +++ b/src/zeroconf.c @@ -17,18 +17,13 @@ */ #include "zeroconf.h" +#include "zeroconf-internal.h" #include "conf.h" -#include "listen.h" -#include "ioops.h" -#include "utils.h" #include <glib.h> #include <string.h> -/* The dns-sd service type qualifier to publish */ -#define SERVICE_TYPE "_mpd._tcp" - /* The default service name to publish * (overridden by 'zeroconf_name' config parameter) */ @@ -37,534 +32,6 @@ #define DEFAULT_ZEROCONF_ENABLED 1 static int zeroconfEnabled; -static struct ioOps zeroConfIo; - -#ifdef HAVE_BONJOUR -#include <dns_sd.h> - -static DNSServiceRef dnsReference; -#endif - -/* Here is the implementation for Avahi (http://avahi.org) Zeroconf support */ -#ifdef HAVE_AVAHI - -#include <avahi-client/client.h> -#include <avahi-client/publish.h> - -#include <avahi-common/alternative.h> -#include <avahi-common/domain.h> -#include <avahi-common/malloc.h> -#include <avahi-common/error.h> - -/* Static avahi data */ -static AvahiEntryGroup *avahiGroup; -static char *avahiName; -static AvahiClient *avahiClient; -static AvahiPoll avahiPoll; -static int avahiRunning; - -static int avahiFdset(fd_set * rfds, fd_set * wfds, fd_set * efds); -static int avahiFdconsume(int fdCount, fd_set * rfds, fd_set * wfds, - fd_set * efds); - -/* Forward Declaration */ -static void avahiRegisterService(AvahiClient * c); - -struct AvahiWatch { - struct AvahiWatch *prev; - struct AvahiWatch *next; - int fd; - AvahiWatchEvent requestedEvent; - AvahiWatchEvent observedEvent; - AvahiWatchCallback callback; - void *userdata; -}; - -struct AvahiTimeout { - struct AvahiTimeout *prev; - struct AvahiTimeout *next; - struct timeval expiry; - int enabled; - AvahiTimeoutCallback callback; - void *userdata; -}; - -static AvahiWatch *avahiWatchList; -static AvahiTimeout *avahiTimeoutList; - -static AvahiWatch *avahiWatchNew(G_GNUC_UNUSED const AvahiPoll * api, int fd, - AvahiWatchEvent event, - AvahiWatchCallback callback, void *userdata) -{ - struct AvahiWatch *newWatch = xmalloc(sizeof(struct AvahiWatch)); - - newWatch->fd = fd; - newWatch->requestedEvent = event; - newWatch->observedEvent = 0; - newWatch->callback = callback; - newWatch->userdata = userdata; - - /* Insert at front of list */ - newWatch->next = avahiWatchList; - avahiWatchList = newWatch; - newWatch->prev = NULL; - if (newWatch->next) - newWatch->next->prev = newWatch; - - return newWatch; -} - -static void avahiWatchUpdate(AvahiWatch * w, AvahiWatchEvent event) -{ - assert(w != NULL); - w->requestedEvent = event; -} - -static AvahiWatchEvent avahiWatchGetEvents(AvahiWatch * w) -{ - assert(w != NULL); - return w->observedEvent; -} - -static void avahiWatchFree(AvahiWatch * w) -{ - assert(w != NULL); - - if (avahiWatchList == w) - avahiWatchList = w->next; - else if (w->prev != NULL) - w->prev->next = w->next; - - free(w); -} - -static void avahiCheckExpiry(AvahiTimeout * t) -{ - assert(t != NULL); - if (t->enabled) { - struct timeval now; - gettimeofday(&now, NULL); - if (timercmp(&now, &(t->expiry), >)) { - t->enabled = 0; - t->callback(t, t->userdata); - } - } -} - -static void avahiTimeoutUpdate(AvahiTimeout * t, const struct timeval *tv) -{ - assert(t != NULL); - if (tv) { - t->enabled = 1; - t->expiry.tv_sec = tv->tv_sec; - t->expiry.tv_usec = tv->tv_usec; - } else { - t->enabled = 0; - } -} - -static void avahiTimeoutFree(AvahiTimeout * t) -{ - assert(t != NULL); - - if (avahiTimeoutList == t) - avahiTimeoutList = t->next; - else if (t->prev != NULL) - t->prev->next = t->next; - - free(t); -} - -static AvahiTimeout *avahiTimeoutNew(G_GNUC_UNUSED const AvahiPoll * api, - const struct timeval *tv, - AvahiTimeoutCallback callback, - void *userdata) -{ - struct AvahiTimeout *newTimeout = xmalloc(sizeof(struct AvahiTimeout)); - - newTimeout->callback = callback; - newTimeout->userdata = userdata; - - avahiTimeoutUpdate(newTimeout, tv); - - /* Insert at front of list */ - newTimeout->next = avahiTimeoutList; - avahiTimeoutList = newTimeout; - newTimeout->prev = NULL; - if (newTimeout->next) - newTimeout->next->prev = newTimeout; - - return newTimeout; -} - -/* Callback when the EntryGroup changes state */ -static void avahiGroupCallback(AvahiEntryGroup * g, - AvahiEntryGroupState state, - G_GNUC_UNUSED void *userdata) -{ - char *n; - assert(g); - - g_debug("Avahi: Service group changed to state %d", state); - - switch (state) { - case AVAHI_ENTRY_GROUP_ESTABLISHED: - /* The entry group has been established successfully */ - g_message("Avahi: Service '%s' successfully established.", - avahiName); - break; - - case AVAHI_ENTRY_GROUP_COLLISION: - /* A service name collision happened. Let's pick a new name */ - n = avahi_alternative_service_name(avahiName); - avahi_free(avahiName); - avahiName = n; - - g_message("Avahi: Service name collision, renaming service to '%s'", - avahiName); - - /* And recreate the services */ - avahiRegisterService(avahi_entry_group_get_client(g)); - break; - - case AVAHI_ENTRY_GROUP_FAILURE: - g_warning("Avahi: Entry group failure: %s", - avahi_strerror(avahi_client_errno - (avahi_entry_group_get_client(g)))); - /* Some kind of failure happened while we were registering our services */ - avahiRunning = 0; - break; - - case AVAHI_ENTRY_GROUP_UNCOMMITED: - g_debug("Avahi: Service group is UNCOMMITED"); - break; - case AVAHI_ENTRY_GROUP_REGISTERING: - g_debug("Avahi: Service group is REGISTERING"); - } -} - -/* Registers a new service with avahi */ -static void avahiRegisterService(AvahiClient * c) -{ - int ret; - assert(c); - g_debug("Avahi: Registering service %s/%s", SERVICE_TYPE, avahiName); - - /* If this is the first time we're called, - * let's create a new entry group */ - if (!avahiGroup) { - avahiGroup = avahi_entry_group_new(c, avahiGroupCallback, NULL); - if (!avahiGroup) { - g_warning("Avahi: Failed to create avahi EntryGroup: %s", - avahi_strerror(avahi_client_errno(c))); - goto fail; - } - } - - /* Add the service */ - /* TODO: This currently binds to ALL interfaces. - * We could maybe add a service per actual bound interface, - * if that's better. */ - ret = avahi_entry_group_add_service(avahiGroup, - AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, - 0, avahiName, SERVICE_TYPE, NULL, - NULL, boundPort, NULL); - if (ret < 0) { - g_warning("Avahi: Failed to add service %s: %s", SERVICE_TYPE, - avahi_strerror(ret)); - goto fail; - } - - /* Tell the server to register the service group */ - ret = avahi_entry_group_commit(avahiGroup); - if (ret < 0) { - g_warning("Avahi: Failed to commit service group: %s", - avahi_strerror(ret)); - goto fail; - } - return; - -fail: - avahiRunning = 0; -} - -/* Callback when avahi changes state */ -static void avahiClientCallback(AvahiClient * c, AvahiClientState state, - G_GNUC_UNUSED void *userdata) -{ - int reason; - assert(c); - - /* Called whenever the client or server state changes */ - g_debug("Avahi: Client changed to state %d", state); - - switch (state) { - case AVAHI_CLIENT_S_RUNNING: - g_debug("Avahi: Client is RUNNING"); - - /* The server has startup successfully and registered its host - * name on the network, so it's time to create our services */ - if (!avahiGroup) - avahiRegisterService(c); - break; - - case AVAHI_CLIENT_FAILURE: - reason = avahi_client_errno(c); - if (reason == AVAHI_ERR_DISCONNECTED) { - g_message("Avahi: Client Disconnected, " - "will reconnect shortly"); - if (avahiGroup) { - avahi_entry_group_free(avahiGroup); - avahiGroup = NULL; - } - if (avahiClient) - avahi_client_free(avahiClient); - avahiClient = - avahi_client_new(&avahiPoll, - AVAHI_CLIENT_NO_FAIL, - avahiClientCallback, NULL, - &reason); - if (!avahiClient) { - g_warning("Avahi: Could not reconnect: %s", - avahi_strerror(reason)); - avahiRunning = 0; - } - } else { - g_warning("Avahi: Client failure: %s (terminal)", - avahi_strerror(reason)); - avahiRunning = 0; - } - break; - - case AVAHI_CLIENT_S_COLLISION: - g_debug("Avahi: Client is COLLISION"); - /* Let's drop our registered services. When the server is back - * in AVAHI_SERVER_RUNNING state we will register them - * again with the new host name. */ - if (avahiGroup) { - g_debug("Avahi: Resetting group"); - avahi_entry_group_reset(avahiGroup); - } - - case AVAHI_CLIENT_S_REGISTERING: - g_debug("Avahi: Client is REGISTERING"); - /* The server records are now being established. This - * might be caused by a host name change. We need to wait - * for our own records to register until the host name is - * properly esatblished. */ - - if (avahiGroup) { - g_debug("Avahi: Resetting group"); - avahi_entry_group_reset(avahiGroup); - } - - break; - - case AVAHI_CLIENT_CONNECTING: - g_debug("Avahi: Client is CONNECTING"); - } -} - -static int avahiFdset(fd_set * rfds, fd_set * wfds, fd_set * efds) -{ - AvahiWatch *w; - int maxfd = -1; - if (!avahiRunning) - return maxfd; - for (w = avahiWatchList; w != NULL; w = w->next) { - if (w->requestedEvent & AVAHI_WATCH_IN) { - FD_SET(w->fd, rfds); - } - if (w->requestedEvent & AVAHI_WATCH_OUT) { - FD_SET(w->fd, wfds); - } - if (w->requestedEvent & AVAHI_WATCH_ERR) { - FD_SET(w->fd, efds); - } - if (w->requestedEvent & AVAHI_WATCH_HUP) { - g_warning("Avahi: No support for HUP events! (ignoring)"); - } - - if (w->fd > maxfd) - maxfd = w->fd; - } - return maxfd; -} - -static int avahiFdconsume(int fdCount, fd_set * rfds, fd_set * wfds, - fd_set * efds) -{ - int retval = fdCount; - AvahiTimeout *t; - AvahiWatch *w = avahiWatchList; - - while (w != NULL && retval > 0) { - AvahiWatch *current = w; - current->observedEvent = 0; - if (FD_ISSET(current->fd, rfds)) { - current->observedEvent |= AVAHI_WATCH_IN; - FD_CLR(current->fd, rfds); - retval--; - } - if (FD_ISSET(current->fd, wfds)) { - current->observedEvent |= AVAHI_WATCH_OUT; - FD_CLR(current->fd, wfds); - retval--; - } - if (FD_ISSET(current->fd, efds)) { - current->observedEvent |= AVAHI_WATCH_ERR; - FD_CLR(current->fd, efds); - retval--; - } - - /* Advance to the next one right now, in case the callback - * removes itself - */ - w = w->next; - - if (current->observedEvent && avahiRunning) { - current->callback(current, current->fd, - current->observedEvent, - current->userdata); - } - } - - t = avahiTimeoutList; - while (t != NULL && avahiRunning) { - AvahiTimeout *current = t; - - /* Advance to the next one right now, in case the callback - * removes itself - */ - t = t->next; - avahiCheckExpiry(current); - } - - return retval; -} - -static void init_avahi(const char *serviceName) -{ - int error; - g_debug("Avahi: Initializing interface"); - - if (!avahi_is_valid_service_name(serviceName)) - g_error("Invalid zeroconf_name \"%s\"", serviceName); - - avahiName = avahi_strdup(serviceName); - - avahiRunning = 1; - - avahiPoll.userdata = NULL; - avahiPoll.watch_new = avahiWatchNew; - avahiPoll.watch_update = avahiWatchUpdate; - avahiPoll.watch_get_events = avahiWatchGetEvents; - avahiPoll.watch_free = avahiWatchFree; - avahiPoll.timeout_new = avahiTimeoutNew; - avahiPoll.timeout_update = avahiTimeoutUpdate; - avahiPoll.timeout_free = avahiTimeoutFree; - - avahiClient = avahi_client_new(&avahiPoll, AVAHI_CLIENT_NO_FAIL, - avahiClientCallback, NULL, &error); - - if (!avahiClient) { - g_warning("Avahi: Failed to create client: %s", - avahi_strerror(error)); - goto fail; - } - - zeroConfIo.fdset = avahiFdset; - zeroConfIo.consume = avahiFdconsume; - registerIO(&zeroConfIo); - - return; - -fail: - finishZeroconf(); -} -#endif /* HAVE_AVAHI */ - -#ifdef HAVE_BONJOUR -static int dnsRegisterFdset(fd_set * rfds, fd_set * wfds, fd_set * efds) -{ - int fd; - - if (dnsReference == NULL) - return -1; - - fd = DNSServiceRefSockFD(dnsReference); - if (fd == -1) - return -1; - - FD_SET(fd, rfds); - - return fd; -} - -static int dnsRegisterFdconsume(int fdCount, fd_set * rfds, fd_set * wfds, - fd_set * efds) -{ - int fd; - - if (dnsReference == NULL) - return -1; - - fd = DNSServiceRefSockFD(dnsReference); - if (fd == -1) - return -1; - - if (FD_ISSET(fd, rfds)) { - FD_CLR(fd, rfds); - - DNSServiceProcessResult(dnsReference); - - return fdCount - 1; - } - - return fdCount; -} - -static void dnsRegisterCallback(DNSServiceRef sdRef, DNSServiceFlags flags, - DNSServiceErrorType errorCode, const char *name, - const char *regtype, const char *domain, - void *context) -{ - if (errorCode != kDNSServiceErr_NoError) { - g_warning("Failed to register zeroconf service."); - - DNSServiceRefDeallocate(dnsReference); - dnsReference = NULL; - deregisterIO(&zeroConfIo); - } else { - g_debug("Registered zeroconf service with name '%s'", name); - } -} - -static void init_zeroconf_osx(const char *serviceName) -{ - DNSServiceErrorType error = DNSServiceRegister(&dnsReference, - 0, 0, serviceName, - SERVICE_TYPE, NULL, NULL, - htons(boundPort), 0, - NULL, - dnsRegisterCallback, - NULL); - - if (error != kDNSServiceErr_NoError) { - g_warning("Failed to register zeroconf service."); - - if (dnsReference) { - DNSServiceRefDeallocate(dnsReference); - dnsReference = NULL; - } - return; - } - - zeroConfIo.fdset = dnsRegisterFdset; - zeroConfIo.consume = dnsRegisterFdconsume; - registerIO(&zeroConfIo); -} -#endif void initZeroconf(void) { @@ -598,29 +65,10 @@ void finishZeroconf(void) return; #ifdef HAVE_AVAHI - g_debug("Avahi: Shutting down interface"); - deregisterIO(&zeroConfIo); - - if (avahiGroup) { - avahi_entry_group_free(avahiGroup); - avahiGroup = NULL; - } - - if (avahiClient) { - avahi_client_free(avahiClient); - avahiClient = NULL; - } - - avahi_free(avahiName); - avahiName = NULL; + avahi_finish(); #endif /* HAVE_AVAHI */ #ifdef HAVE_BONJOUR - deregisterIO(&zeroConfIo); - if (dnsReference != NULL) { - DNSServiceRefDeallocate(dnsReference); - dnsReference = NULL; - g_debug("Deregistered Zeroconf service."); - } + bonjour_finish(); #endif } |