From 5d79aced8cc587b8b8889647b7cb9da79d500d64 Mon Sep 17 00:00:00 2001
From: Jim Ramsay <i.am@jimramsay.com>
Date: Thu, 11 Jan 2007 20:41:17 +0000
Subject: Added zeroconf service publishing using avahi

git-svn-id: https://svn.musicpd.org/mpd/trunk@5238 09075e82-0dd4-0310-85a5-a0d7c8717e4f
---
 src/Makefile.am |   6 +-
 src/conf.c      |   1 +
 src/conf.h      |   1 +
 src/interface.c |  62 ++++++-
 src/ioops.h     |  47 ++++++
 src/listen.c    |   8 +
 src/listen.h    |   2 +
 src/main.c      |   3 +
 src/zeroconf.c  | 493 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/zeroconf.h  |  27 ++++
 10 files changed, 646 insertions(+), 4 deletions(-)
 create mode 100644 src/ioops.h
 create mode 100644 src/zeroconf.c
 create mode 100644 src/zeroconf.h

(limited to 'src')

diff --git a/src/Makefile.am b/src/Makefile.am
index 38b206d40..be9cd2a31 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -73,7 +73,8 @@ mpd_headers = \
 	utf8.h \
 	utils.h \
 	volume.h \
-	localization.h
+	localization.h \
+	zeroconf.h
 
 
 mpd_SOURCES = \
@@ -123,7 +124,8 @@ mpd_SOURCES = \
 	utils.c \
 	volume.c \
 	utf8.c \
-	localization.c
+	localization.c \
+	zeroconf.c
 
 
 mpd_CFLAGS = $(MPD_CFLAGS)
diff --git a/src/conf.c b/src/conf.c
index d354634ef..a2609710e 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -146,6 +146,7 @@ void initConf(void)
 	registerConfigParam(CONF_BIND_TO_ADDRESS,               1,     0);
 	registerConfigParam(CONF_PORT,                          0,     0);
 	registerConfigParam(CONF_LOG_LEVEL,                     0,     0);
+	registerConfigParam(CONF_ZEROCONF_NAME,                 0,     0);
 	registerConfigParam(CONF_PASSWORD,                      1,     0);
 	registerConfigParam(CONF_DEFAULT_PERMS,                 0,     0);
 	registerConfigParam(CONF_AUDIO_OUTPUT,                  1,     1);
diff --git a/src/conf.h b/src/conf.h
index 7d26cadf7..e2f568525 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -32,6 +32,7 @@
 #define CONF_BIND_TO_ADDRESS            "bind_to_address"
 #define CONF_PORT                       "port"
 #define CONF_LOG_LEVEL                  "log_level"
+#define CONF_ZEROCONF_NAME              "zeroconf_name"
 #define CONF_PASSWORD                   "password"
 #define CONF_DEFAULT_PERMS              "default_permissions"
 #define CONF_AUDIO_OUTPUT               "audio_output"
diff --git a/src/interface.c b/src/interface.c
index 4220935ed..1e6fb4445 100644
--- a/src/interface.c
+++ b/src/interface.c
@@ -26,6 +26,7 @@
 #include "permission.h"
 #include "sllist.h"
 #include "utils.h"
+#include "ioops.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -62,6 +63,9 @@ static size_t interface_max_command_list_size =
 static size_t interface_max_output_buffer_size =
     INTERFACE_MAX_OUTPUT_BUFFER_SIZE_DEFAULT;
 
+/* List of registered external IO handlers */
+static struct ioOps *ioList;
+
 /* maybe make conf option for this, or... 32 might be good enough */
 static long int interface_list_cache_size = 32;
 
@@ -488,6 +492,7 @@ int doIOForInterfaces(void)
 {
 	fd_set rfds;
 	fd_set wfds;
+	fd_set efds;
 	struct timeval tv;
 	int i;
 	int selret;
@@ -499,12 +504,43 @@ int doIOForInterfaces(void)
 	while (1) {
 		fdmax = 0;
 
+		FD_ZERO( &rfds );
+		FD_ZERO( &wfds );
+		FD_ZERO( &efds );
 		addInterfacesReadyToReadAndListenSocketToFdSet(&rfds, &fdmax);
 		addInterfacesForBufferFlushToFdSet(&wfds, &fdmax);
 
-		selret = select(fdmax + 1, &rfds, &wfds, NULL, &tv);
+		// Add fds for all registered IO handlers
+		if( ioList ) {
+			struct ioOps *i = ioList;
+			while( i ) {
+				struct ioOps *current = i;
+				int fdnum;
+				assert( current->fdset );
+				fdnum = current->fdset( &rfds, &wfds, &efds );
+				if( fdmax < fdnum )
+					fdmax = fdnum;
+				i = i->next;
+			}
+		}
+
+		selret = select(fdmax + 1, &rfds, &wfds, &efds, &tv);
+
+		if (selret < 0 && errno == EINTR)
+			break;
+
+		// Consume fds for all registered IO handlers
+		if( ioList ) {
+			struct ioOps *i = ioList;
+			while( i ) {
+				struct ioOps *current = i;
+				assert( current->consume );
+				selret = current->consume( selret, &rfds, &wfds, &efds );
+				i = i->next;
+			}
+		}
 
-		if (selret == 0 || (selret < 0 && errno == EINTR))
+		if (selret == 0)
 			break;
 
 		if (selret < 0) {
@@ -794,3 +830,25 @@ static void printInterfaceOutBuffer(Interface * interface)
 
 	interface->send_buf_used = 0;
 }
+
+// From ioops.h:
+void registerIO( struct ioOps *ops )
+{
+	assert( ops != NULL );
+
+	ops->next = ioList;
+	ioList = ops;
+	ops->prev = NULL;
+	if( ops->next )
+		ops->next->prev = ops;
+}
+
+void deregisterIO( struct ioOps *ops )
+{
+	assert( ops != NULL );
+
+	if( ioList == ops )
+		ioList = ops->next;
+	else if( ops->prev != NULL )
+		ops->prev->next = ops->next;
+}
diff --git a/src/ioops.h b/src/ioops.h
new file mode 100644
index 000000000..a2a6fa841
--- /dev/null
+++ b/src/ioops.h
@@ -0,0 +1,47 @@
+/* the Music Player Daemon (MPD)
+ * (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
+ * This project's homepage is: 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 IOOPS_H
+#define IOOPS_H
+
+#include <sys/select.h>
+
+struct ioOps {
+	struct ioOps *prev, *next;
+
+	// Called before each 'select' statement.
+	//   To register for IO, call FD_SET for each required queue
+	// Return the highest fd number you registered
+	int (*fdset) ( fd_set *rfds, fd_set *wfds, fd_set *efds );
+
+	// Called after each 'select' statement.
+	//   fdCount is the number of fds total in all sets.  It may be 0.
+	//   For each fd you registered for in (fdset), you should FD_CLR it from the
+	//   appropriate queue(s).
+	// Return the total number of fds left in all sets (Ie, return fdCount
+	//   minus the number of times you called FD_CLR).
+	int (*consume) ( int fdCount, fd_set *rfds, fd_set *wfds, fd_set *efds );
+};
+
+// Call this to register your io operation handler struct
+void registerIO( struct ioOps *ops );
+
+// Call this to deregister your io operation handler struct
+void deregisterIO( struct ioOps *ops );
+
+#endif
diff --git a/src/listen.c b/src/listen.c
index 7cc16a2d3..4caf2dc2a 100644
--- a/src/listen.c
+++ b/src/listen.c
@@ -47,6 +47,7 @@
 
 int *listenSockets = NULL;
 int numberOfListenSockets = 0;
+static int boundPort = 0;
 
 static int establishListen(unsigned int port,
                            struct sockaddr *addrp, socklen_t addrlen)
@@ -211,6 +212,8 @@ void listenOnPort(void)
 		}
 	}
 
+	boundPort = port;
+
 	do {
 		parseListenConfigParam(port, param);
 	} while ((param = getNextConfigParam(CONF_BIND_TO_ADDRESS, param)));
@@ -266,3 +269,8 @@ void getConnections(fd_set * fds)
 		}
 	}
 }
+
+int getBoundPort()
+{
+	return boundPort;
+}
diff --git a/src/listen.h b/src/listen.h
index dd2c7dc7e..74c5e6adc 100644
--- a/src/listen.h
+++ b/src/listen.h
@@ -36,4 +36,6 @@ void freeAllListenSockets(void);
 /* fdmax should be initialized to something */
 void addListenSocketsToFdSet(fd_set * fds, int *fdmax);
 
+int getBoundPort(void);
+
 #endif
diff --git a/src/main.c b/src/main.c
index e32146e6c..1166237ec 100644
--- a/src/main.c
+++ b/src/main.c
@@ -42,6 +42,7 @@
 #include "../config.h"
 #include "utils.h"
 #include "normalize.h"
+#include "zeroconf.h"
 #include "localization.h"
 
 #include <stdio.h>
@@ -452,6 +453,7 @@ int main(int argc, char *argv[])
 	initAudioDriver();
 	initVolume();
 	initInterfaces();
+	initZeroconf();
 	initReplayGainState();
 	initNormalization();
 	initInputStream();
@@ -477,6 +479,7 @@ int main(int argc, char *argv[])
 
 	write_state_file();
 	playerKill();
+	finishZeroconf();
 	freeAllInterfaces();
 	closeAllListenSockets();
 	finishPlaylist();
diff --git a/src/zeroconf.c b/src/zeroconf.c
new file mode 100644
index 000000000..0fa8d7398
--- /dev/null
+++ b/src/zeroconf.c
@@ -0,0 +1,493 @@
+/* the Music Player Daemon (MPD)
+ * (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
+ * This project's homepage is: 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 <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "zeroconf.h"
+#include "conf.h"
+#include "log.h"
+#include "listen.h"
+#include "ioops.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)
+ */
+#define SERVICE_NAME		"Music Player"
+
+/* Here is the implementation for Avahi (http://avahi.org) Zeroconf support */
+#if 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 = NULL;
+static char *avahiName = NULL;
+static AvahiClient* avahiClient = NULL;
+static AvahiPoll avahiPoll;
+static int avahiRunning = 0;
+
+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 );
+static struct ioOps avahiIo = {
+	.fdset = avahiFdset,
+	.consume = avahiFdconsume,
+};
+
+/* 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( const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata )
+{
+	struct AvahiWatch* newWatch = malloc( 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( const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata )
+{
+	struct AvahiTimeout* newTimeout = malloc( 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,
+		void *userdata)
+{
+	assert(g);
+
+	DEBUG( "Avahi: Service group changed to state %d\n", state );
+
+	switch (state) {
+		case AVAHI_ENTRY_GROUP_ESTABLISHED :
+			/* The entry group has been established successfully */
+			LOG( "Avahi: Service '%s' successfully established.\n", avahiName );
+			break;
+
+		case AVAHI_ENTRY_GROUP_COLLISION : {
+			char *n;
+			
+			/* A service name collision happened. Let's pick a new name */
+			n = avahi_alternative_service_name(avahiName);
+			avahi_free(avahiName);
+			avahiName = n;
+			
+			LOG( "Avahi: Service name collision, renaming service to '%s'\n", avahiName );
+
+			/* And recreate the services */
+			avahiRegisterService(avahi_entry_group_get_client(g));
+			break;
+		}
+
+		case AVAHI_ENTRY_GROUP_FAILURE :
+			ERROR( "Avahi: Entry group failure: %s\n",
+					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:
+			DEBUG( "Avahi: Service group is UNCOMMITED\n" );
+			break;
+		case AVAHI_ENTRY_GROUP_REGISTERING:
+			DEBUG( "Avahi: Service group is REGISTERING\n" );
+			;
+	}
+}
+
+/* Registers a new service with avahi */
+static void avahiRegisterService(AvahiClient *c)
+{
+	int ret;
+	assert(c);
+	DEBUG( "Avahi: Registering service %s/%s\n", 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 ) {
+			ERROR( "Avahi: Failed to create avahi EntryGroup: %s\n", 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,
+					getBoundPort(),
+					NULL);
+	if( ret < 0 ) {
+		ERROR( "Avahi: Failed to add service %s: %s\n", SERVICE_TYPE, avahi_strerror(ret) );
+		goto fail;
+	}
+
+	/* Tell the server to register the service group */
+	ret = avahi_entry_group_commit(avahiGroup);
+	if( ret < 0 ) {
+		ERROR( "Avahi: Failed to commit service group: %s\n", avahi_strerror(ret) );
+		goto fail;
+	}
+	return;
+
+fail:
+	avahiRunning = 0;
+}
+
+/* Callback when avahi changes state */
+static void avahiClientCallback(AvahiClient *c, AvahiClientState state, void *userdata)
+{
+	assert(c);
+
+	/* Called whenever the client or server state changes */
+	DEBUG( "Avahi: Client changed to state %d\n", state );
+
+	switch (state) {
+		case AVAHI_CLIENT_S_RUNNING:
+			DEBUG( "Avahi: Client is RUNNING\n" );
+		
+			/* 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:
+			{
+				int reason = avahi_client_errno(c);
+				if( reason == AVAHI_ERR_DISCONNECTED ) {
+					LOG( "Avahi: Client Disconnected, will reconnect shortly\n");
+					avahi_entry_group_free( avahiGroup );
+					avahiGroup = NULL;
+					avahi_client_free( avahiClient );
+					avahiClient = NULL;
+					avahiClient = avahi_client_new( &avahiPoll, AVAHI_CLIENT_NO_FAIL,
+						avahiClientCallback, NULL, &reason );
+					if( !avahiClient ) {
+						ERROR( "Avahi: Could not reconnect: %s\n", avahi_strerror(reason) );
+						avahiRunning = 0;
+					}
+				} else {
+					ERROR( "Avahi: Client failure: %s (terminal)\n", avahi_strerror(reason));
+					avahiRunning = 0;
+				}
+			}
+			break;
+
+		case AVAHI_CLIENT_S_COLLISION:
+			DEBUG( "Avahi: Client is COLLISION\n" );
+			/* 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) {
+				DEBUG( "Avahi: Resetting group\n" );
+				avahi_entry_group_reset(avahiGroup);
+			}
+			
+		case AVAHI_CLIENT_S_REGISTERING:
+			DEBUG( "Avahi: Client is REGISTERING\n" );
+			/* 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) {
+				DEBUG( "Avahi: Resetting group\n" );
+				avahi_entry_group_reset(avahiGroup);
+			}
+			
+			break;
+
+		case AVAHI_CLIENT_CONNECTING:
+			DEBUG( "Avahi: Client is CONNECTING\n" );
+			;
+	}
+}
+
+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 ) {
+			ERROR( "Avahi: No support for HUP events! (ignoring)\n" );
+		}
+
+		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;
+
+	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 );
+		}
+	}
+
+	AvahiTimeout* 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;
+}
+
+#endif // HAVE_AVAHI
+
+void initZeroconf(void)
+{
+	const char* serviceName = SERVICE_NAME;
+	ConfigParam *param;
+
+	param = getConfigParam(CONF_ZEROCONF_NAME);
+
+	if (param && strlen(param->value) > 0)
+		serviceName = param->value;
+
+#if HAVE_AVAHI
+	int error;
+	DEBUG( "Avahi: Initializing interface\n" );
+
+	if( avahi_is_valid_service_name( serviceName ) ) {
+		avahiName = avahi_strdup( serviceName );
+	} else {
+		ERROR( "Invalid zeroconf_name \"%s\", defaulting to \"%s\" instead.\n", serviceName, SERVICE_NAME );
+		avahiName = avahi_strdup( SERVICE_NAME );
+	}
+
+	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 ) {
+		ERROR( "Avahi: Failed to create client: %s\n", avahi_strerror(error) );
+		goto fail;
+	}
+
+	avahiIo.fdset = avahiFdset;
+	avahiIo.consume = avahiFdconsume;
+	registerIO( &avahiIo );
+
+	return;
+
+fail:
+	finishZeroconf();
+#endif // HAVE_AVAHI
+}
+
+void finishZeroconf(void)
+{
+#if HAVE_AVAHI
+	DEBUG( "Avahi: Shutting down interface\n" );
+	deregisterIO( &avahiIo );
+
+	if( avahiGroup ) {
+		avahi_entry_group_free( avahiGroup );
+		avahiGroup = NULL;
+	}
+
+	if( avahiClient ) {
+		avahi_client_free( avahiClient );
+		avahiClient = NULL;
+	}
+
+	avahi_free( avahiName );
+	avahiName = NULL;
+#endif // HAVE_AVAHI
+}
diff --git a/src/zeroconf.h b/src/zeroconf.h
new file mode 100644
index 000000000..2301c0d40
--- /dev/null
+++ b/src/zeroconf.h
@@ -0,0 +1,27 @@
+/* the Music Player Daemon (MPD)
+ * (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
+ * This project's homepage is: 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_H
+#define ZEROCONF_H
+
+#include "../config.h"
+
+void initZeroconf(void);
+void finishZeroconf(void);
+
+#endif
-- 
cgit v1.2.3