From 6b70f5e49097c334c461994bb855709977bd8dae Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 24 Jan 2014 00:14:54 +0100 Subject: Zeroconf*: move to zeroconf/ --- Makefile.am | 12 +- src/AvahiPoll.cxx | 146 -------------------- src/AvahiPoll.hxx | 48 ------- src/Main.cxx | 2 +- src/ZeroconfAvahi.cxx | 275 -------------------------------------- src/ZeroconfAvahi.hxx | 31 ----- src/ZeroconfBonjour.cxx | 108 --------------- src/ZeroconfBonjour.hxx | 31 ----- src/ZeroconfGlue.cxx | 83 ------------ src/ZeroconfGlue.hxx | 47 ------- src/ZeroconfInternal.hxx | 26 ---- src/zeroconf/AvahiPoll.cxx | 146 ++++++++++++++++++++ src/zeroconf/AvahiPoll.hxx | 48 +++++++ src/zeroconf/ZeroconfAvahi.cxx | 275 ++++++++++++++++++++++++++++++++++++++ src/zeroconf/ZeroconfAvahi.hxx | 31 +++++ src/zeroconf/ZeroconfBonjour.cxx | 108 +++++++++++++++ src/zeroconf/ZeroconfBonjour.hxx | 31 +++++ src/zeroconf/ZeroconfGlue.cxx | 83 ++++++++++++ src/zeroconf/ZeroconfGlue.hxx | 47 +++++++ src/zeroconf/ZeroconfInternal.hxx | 26 ++++ test/run_avahi.cxx | 2 +- 21 files changed, 803 insertions(+), 803 deletions(-) delete mode 100644 src/AvahiPoll.cxx delete mode 100644 src/AvahiPoll.hxx delete mode 100644 src/ZeroconfAvahi.cxx delete mode 100644 src/ZeroconfAvahi.hxx delete mode 100644 src/ZeroconfBonjour.cxx delete mode 100644 src/ZeroconfBonjour.hxx delete mode 100644 src/ZeroconfGlue.cxx delete mode 100644 src/ZeroconfGlue.hxx delete mode 100644 src/ZeroconfInternal.hxx create mode 100644 src/zeroconf/AvahiPoll.cxx create mode 100644 src/zeroconf/AvahiPoll.hxx create mode 100644 src/zeroconf/ZeroconfAvahi.cxx create mode 100644 src/zeroconf/ZeroconfAvahi.hxx create mode 100644 src/zeroconf/ZeroconfBonjour.cxx create mode 100644 src/zeroconf/ZeroconfBonjour.hxx create mode 100644 src/zeroconf/ZeroconfGlue.cxx create mode 100644 src/zeroconf/ZeroconfGlue.hxx create mode 100644 src/zeroconf/ZeroconfInternal.hxx diff --git a/Makefile.am b/Makefile.am index dbc720a82..22486443d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -807,17 +807,17 @@ endif if HAVE_ZEROCONF src_mpd_SOURCES += \ - src/ZeroconfInternal.hxx \ - src/ZeroconfGlue.cxx src/ZeroconfGlue.hxx + src/zeroconf/ZeroconfInternal.hxx \ + src/zeroconf/ZeroconfGlue.cxx src/zeroconf/ZeroconfGlue.hxx if HAVE_AVAHI src_mpd_SOURCES += \ - src/AvahiPoll.cxx src/AvahiPoll.hxx \ - src/ZeroconfAvahi.cxx src/ZeroconfAvahi.hxx + src/zeroconf/AvahiPoll.cxx src/zeroconf/AvahiPoll.hxx \ + src/zeroconf/ZeroconfAvahi.cxx src/zeroconf/ZeroconfAvahi.hxx endif if HAVE_BONJOUR -src_mpd_SOURCES += src/ZeroconfBonjour.cxx src/ZeroconfBonjour.hxx +src_mpd_SOURCES += src/zeroconf/ZeroconfBonjour.cxx src/zeroconf/ZeroconfBonjour.hxx endif endif @@ -1486,7 +1486,7 @@ test_software_volume_LDADD = \ test_run_avahi_SOURCES = \ src/Log.cxx src/LogBackend.cxx \ - src/ZeroconfAvahi.cxx src/AvahiPoll.cxx \ + src/zeroconf/ZeroconfAvahi.cxx src/zeroconf/AvahiPoll.cxx \ test/ShutdownHandler.cxx test/ShutdownHandler.hxx \ test/run_avahi.cxx test_run_avahi_CPPFLAGS = $(AM_CPPFLAGS) \ diff --git a/src/AvahiPoll.cxx b/src/AvahiPoll.cxx deleted file mode 100644 index 20d5d74e6..000000000 --- a/src/AvahiPoll.cxx +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2003-2014 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "AvahiPoll.hxx" -#include "event/SocketMonitor.hxx" -#include "event/TimeoutMonitor.hxx" - -static unsigned -FromAvahiWatchEvent(AvahiWatchEvent e) -{ - return (e & AVAHI_WATCH_IN ? SocketMonitor::READ : 0) | - (e & AVAHI_WATCH_OUT ? SocketMonitor::WRITE : 0) | - (e & AVAHI_WATCH_ERR ? SocketMonitor::ERROR : 0) | - (e & AVAHI_WATCH_HUP ? SocketMonitor::HANGUP : 0); -} - -static AvahiWatchEvent -ToAvahiWatchEvent(unsigned e) -{ - return AvahiWatchEvent((e & SocketMonitor::READ ? AVAHI_WATCH_IN : 0) | - (e & SocketMonitor::WRITE ? AVAHI_WATCH_OUT : 0) | - (e & SocketMonitor::ERROR ? AVAHI_WATCH_ERR : 0) | - (e & SocketMonitor::HANGUP ? AVAHI_WATCH_HUP : 0)); -} - -struct AvahiWatch final : private SocketMonitor { -private: - const AvahiWatchCallback callback; - void *const userdata; - - AvahiWatchEvent received; - -public: - AvahiWatch(int _fd, AvahiWatchEvent _event, - AvahiWatchCallback _callback, void *_userdata, - EventLoop &_loop) - :SocketMonitor(_fd, _loop), - callback(_callback), userdata(_userdata), - received(AvahiWatchEvent(0)) { - Schedule(FromAvahiWatchEvent(_event)); - } - - static void WatchUpdate(AvahiWatch *w, AvahiWatchEvent event) { - w->Schedule(FromAvahiWatchEvent(event)); - } - - static AvahiWatchEvent WatchGetEvents(AvahiWatch *w) { - return w->received; - } - - static void WatchFree(AvahiWatch *w) { - delete w; - } - -protected: - virtual bool OnSocketReady(unsigned flags) { - received = ToAvahiWatchEvent(flags); - callback(this, Get(), received, userdata); - received = AvahiWatchEvent(0); - return true; - } -}; - -static constexpr unsigned -TimevalToMS(const timeval &tv) -{ - return tv.tv_sec * 1000 + (tv.tv_usec + 500) / 1000; -} - -struct AvahiTimeout final : private TimeoutMonitor { -private: - const AvahiTimeoutCallback callback; - void *const userdata; - -public: - AvahiTimeout(const struct timeval *tv, - AvahiTimeoutCallback _callback, void *_userdata, - EventLoop &_loop) - :TimeoutMonitor(_loop), - callback(_callback), userdata(_userdata) { - if (tv != nullptr) - Schedule(TimevalToMS(*tv)); - } - - static void TimeoutUpdate(AvahiTimeout *t, const struct timeval *tv) { - if (tv != nullptr) - t->Schedule(TimevalToMS(*tv)); - else - t->Cancel(); - } - - static void TimeoutFree(AvahiTimeout *t) { - delete t; - } - -protected: - virtual void OnTimeout() { - callback(this, userdata); - } -}; - -MyAvahiPoll::MyAvahiPoll(EventLoop &_loop):event_loop(_loop) -{ - watch_new = WatchNew; - watch_update = AvahiWatch::WatchUpdate; - watch_get_events = AvahiWatch::WatchGetEvents; - watch_free = AvahiWatch::WatchFree; - timeout_new = TimeoutNew; - timeout_update = AvahiTimeout::TimeoutUpdate; - timeout_free = AvahiTimeout::TimeoutFree; -} - -AvahiWatch * -MyAvahiPoll::WatchNew(const AvahiPoll *api, int fd, AvahiWatchEvent event, - AvahiWatchCallback callback, void *userdata) { - const MyAvahiPoll &poll = *(const MyAvahiPoll *)api; - - return new AvahiWatch(fd, event, callback, userdata, - poll.event_loop); -} - -AvahiTimeout * -MyAvahiPoll::TimeoutNew(const AvahiPoll *api, const struct timeval *tv, - AvahiTimeoutCallback callback, void *userdata) { - const MyAvahiPoll &poll = *(const MyAvahiPoll *)api; - - return new AvahiTimeout(tv, callback, userdata, - poll.event_loop); -} diff --git a/src/AvahiPoll.hxx b/src/AvahiPoll.hxx deleted file mode 100644 index e194d3370..000000000 --- a/src/AvahiPoll.hxx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2003-2014 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_AVAHI_POLL_HXX -#define MPD_AVAHI_POLL_HXX - -#include "check.h" -#include "Compiler.h" - -#include - -class EventLoop; - -class MyAvahiPoll final : public AvahiPoll { - EventLoop &event_loop; - -public: - MyAvahiPoll(EventLoop &_loop); - -private: - static AvahiWatch *WatchNew(const AvahiPoll *api, int fd, - AvahiWatchEvent event, - AvahiWatchCallback callback, - void *userdata); - - static AvahiTimeout *TimeoutNew(const AvahiPoll *api, - const struct timeval *tv, - AvahiTimeoutCallback callback, - void *userdata); -}; - -#endif diff --git a/src/Main.cxx b/src/Main.cxx index 28afd12cf..8f6780661 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -52,7 +52,7 @@ #include "fs/Config.hxx" #include "fs/StandardDirectory.hxx" #include "playlist/PlaylistRegistry.hxx" -#include "ZeroconfGlue.hxx" +#include "zeroconf/ZeroconfGlue.hxx" #include "decoder/DecoderList.hxx" #include "AudioConfig.hxx" #include "pcm/PcmConvert.hxx" diff --git a/src/ZeroconfAvahi.cxx b/src/ZeroconfAvahi.cxx deleted file mode 100644 index 8cd09cbed..000000000 --- a/src/ZeroconfAvahi.cxx +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) 2003-2014 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "ZeroconfAvahi.hxx" -#include "AvahiPoll.hxx" -#include "ZeroconfInternal.hxx" -#include "Listen.hxx" -#include "system/FatalError.hxx" -#include "util/Domain.hxx" -#include "Log.hxx" - -#include -#include - -#include -#include -#include -#include - -static constexpr Domain avahi_domain("avahi"); - -static char *avahiName; -static bool avahi_running; -static MyAvahiPoll *avahi_poll; -static AvahiClient *avahiClient; -static AvahiEntryGroup *avahiGroup; - -static void avahiRegisterService(AvahiClient * c); - -/* Callback when the EntryGroup changes state */ -static void avahiGroupCallback(AvahiEntryGroup * g, - AvahiEntryGroupState state, - gcc_unused void *userdata) -{ - char *n; - assert(g); - - FormatDebug(avahi_domain, - "Service group changed to state %d", state); - - switch (state) { - case AVAHI_ENTRY_GROUP_ESTABLISHED: - /* The entry group has been established successfully */ - FormatDefault(avahi_domain, - "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; - - FormatDefault(avahi_domain, - "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: - FormatError(avahi_domain, - "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 */ - avahi_running = false; - break; - - case AVAHI_ENTRY_GROUP_UNCOMMITED: - LogDebug(avahi_domain, "Service group is UNCOMMITED"); - break; - case AVAHI_ENTRY_GROUP_REGISTERING: - LogDebug(avahi_domain, "Service group is REGISTERING"); - } -} - -/* Registers a new service with avahi */ -static void avahiRegisterService(AvahiClient * c) -{ - FormatDebug(avahi_domain, "Registering service %s/%s", - SERVICE_TYPE, avahiName); - - int ret; - assert(c); - - /* 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, nullptr); - if (!avahiGroup) { - FormatError(avahi_domain, - "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, - AvahiPublishFlags(0), - avahiName, SERVICE_TYPE, nullptr, - nullptr, listen_port, nullptr); - if (ret < 0) { - FormatError(avahi_domain, "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) { - FormatError(avahi_domain, "Failed to commit service group: %s", - avahi_strerror(ret)); - goto fail; - } - return; - -fail: - avahi_running = false; -} - -/* Callback when avahi changes state */ -static void avahiClientCallback(AvahiClient * c, AvahiClientState state, - gcc_unused void *userdata) -{ - int reason; - assert(c); - - /* Called whenever the client or server state changes */ - FormatDebug(avahi_domain, "Client changed to state %d", state); - - switch (state) { - case AVAHI_CLIENT_S_RUNNING: - LogDebug(avahi_domain, "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) { - LogDefault(avahi_domain, - "Client Disconnected, will reconnect shortly"); - if (avahiGroup) { - avahi_entry_group_free(avahiGroup); - avahiGroup = nullptr; - } - if (avahiClient) - avahi_client_free(avahiClient); - avahiClient = - avahi_client_new(avahi_poll, - AVAHI_CLIENT_NO_FAIL, - avahiClientCallback, nullptr, - &reason); - if (!avahiClient) { - FormatWarning(avahi_domain, - "Could not reconnect: %s", - avahi_strerror(reason)); - avahi_running = false; - } - } else { - FormatWarning(avahi_domain, - "Client failure: %s (terminal)", - avahi_strerror(reason)); - avahi_running = false; - } - break; - - case AVAHI_CLIENT_S_COLLISION: - LogDebug(avahi_domain, "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) { - LogDebug(avahi_domain, "Resetting group"); - avahi_entry_group_reset(avahiGroup); - } - - break; - - case AVAHI_CLIENT_S_REGISTERING: - LogDebug(avahi_domain, "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) { - LogDebug(avahi_domain, "Resetting group"); - avahi_entry_group_reset(avahiGroup); - } - - break; - - case AVAHI_CLIENT_CONNECTING: - LogDebug(avahi_domain, "Client is CONNECTING"); - break; - } -} - -void -AvahiInit(EventLoop &loop, const char *serviceName) -{ - LogDebug(avahi_domain, "Initializing interface"); - - if (!avahi_is_valid_service_name(serviceName)) - FormatFatalError("Invalid zeroconf_name \"%s\"", serviceName); - - avahiName = avahi_strdup(serviceName); - - avahi_running = true; - - avahi_poll = new MyAvahiPoll(loop); - - int error; - avahiClient = avahi_client_new(avahi_poll, AVAHI_CLIENT_NO_FAIL, - avahiClientCallback, nullptr, &error); - - if (!avahiClient) { - FormatError(avahi_domain, "Failed to create client: %s", - avahi_strerror(error)); - AvahiDeinit(); - } -} - -void -AvahiDeinit(void) -{ - LogDebug(avahi_domain, "Shutting down interface"); - - if (avahiGroup) { - avahi_entry_group_free(avahiGroup); - avahiGroup = nullptr; - } - - if (avahiClient) { - avahi_client_free(avahiClient); - avahiClient = nullptr; - } - - delete avahi_poll; - avahi_poll = nullptr; - - avahi_free(avahiName); - avahiName = nullptr; -} diff --git a/src/ZeroconfAvahi.hxx b/src/ZeroconfAvahi.hxx deleted file mode 100644 index 09a199f55..000000000 --- a/src/ZeroconfAvahi.hxx +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2003-2014 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_ZEROCONF_AVAHI_HXX -#define MPD_ZEROCONF_AVAHI_HXX - -class EventLoop; - -void -AvahiInit(EventLoop &loop, const char *service_name); - -void -AvahiDeinit(); - -#endif diff --git a/src/ZeroconfBonjour.cxx b/src/ZeroconfBonjour.cxx deleted file mode 100644 index 5b5de1247..000000000 --- a/src/ZeroconfBonjour.cxx +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2003-2014 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "ZeroconfBonjour.hxx" -#include "ZeroconfInternal.hxx" -#include "Listen.hxx" -#include "event/SocketMonitor.hxx" -#include "util/Domain.hxx" -#include "Log.hxx" -#include "Compiler.h" - -#include - -#include - -static constexpr Domain bonjour_domain("bonjour"); - -class BonjourMonitor final : public SocketMonitor { - DNSServiceRef service_ref; - -public: - BonjourMonitor(EventLoop &_loop, DNSServiceRef _service_ref) - :SocketMonitor(DNSServiceRefSockFD(_service_ref), _loop), - service_ref(_service_ref) { - ScheduleRead(); - } - - ~BonjourMonitor() { - DNSServiceRefDeallocate(service_ref); - } - -protected: - virtual bool OnSocketReady(gcc_unused unsigned flags) override { - DNSServiceProcessResult(service_ref); - return false; - } -}; - -static BonjourMonitor *bonjour_monitor; - -static void -dnsRegisterCallback(gcc_unused DNSServiceRef sdRef, - gcc_unused DNSServiceFlags flags, - DNSServiceErrorType errorCode, const char *name, - gcc_unused const char *regtype, - gcc_unused const char *domain, - gcc_unused void *context) -{ - if (errorCode != kDNSServiceErr_NoError) { - LogError(bonjour_domain, - "Failed to register zeroconf service"); - - bonjour_monitor->Cancel(); - } else { - FormatDebug(bonjour_domain, - "Registered zeroconf service with name '%s'", - name); - } -} - -void -BonjourInit(EventLoop &loop, const char *service_name) -{ - DNSServiceRef dnsReference; - DNSServiceErrorType error = DNSServiceRegister(&dnsReference, - 0, 0, service_name, - SERVICE_TYPE, nullptr, nullptr, - g_htons(listen_port), 0, - nullptr, - dnsRegisterCallback, - nullptr); - - if (error != kDNSServiceErr_NoError) { - LogError(bonjour_domain, - "Failed to register zeroconf service"); - - if (dnsReference) { - DNSServiceRefDeallocate(dnsReference); - dnsReference = nullptr; - } - return; - } - - bonjour_monitor = new BonjourMonitor(loop, dnsReference); -} - -void -BonjourDeinit() -{ - delete bonjour_monitor; -} diff --git a/src/ZeroconfBonjour.hxx b/src/ZeroconfBonjour.hxx deleted file mode 100644 index cff52815e..000000000 --- a/src/ZeroconfBonjour.hxx +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2003-2014 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_ZEROCONF_BONJOUR_HXX -#define MPD_ZEROCONF_BONJOUR_HXX - -class EventLoop; - -void -BonjourInit(EventLoop &loop, const char *service_name); - -void -BonjourDeinit(); - -#endif diff --git a/src/ZeroconfGlue.cxx b/src/ZeroconfGlue.cxx deleted file mode 100644 index 05bb3c17c..000000000 --- a/src/ZeroconfGlue.cxx +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2003-2014 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "ZeroconfGlue.hxx" -#include "ZeroconfAvahi.hxx" -#include "ZeroconfBonjour.hxx" -#include "ConfigGlobal.hxx" -#include "ConfigOption.hxx" -#include "Listen.hxx" -#include "util/Domain.hxx" -#include "Log.hxx" -#include "Compiler.h" - -static constexpr Domain zeroconf_domain("zeroconf"); - -/* The default service name to publish - * (overridden by 'zeroconf_name' config parameter) - */ -#define SERVICE_NAME "Music Player" - -#define DEFAULT_ZEROCONF_ENABLED 1 - -static int zeroconfEnabled; - -void -ZeroconfInit(gcc_unused EventLoop &loop) -{ - const char *serviceName; - - zeroconfEnabled = config_get_bool(CONF_ZEROCONF_ENABLED, - DEFAULT_ZEROCONF_ENABLED); - if (!zeroconfEnabled) - return; - - if (listen_port <= 0) { - LogWarning(zeroconf_domain, - "No global port, disabling zeroconf"); - zeroconfEnabled = false; - return; - } - - serviceName = config_get_string(CONF_ZEROCONF_NAME, SERVICE_NAME); - -#ifdef HAVE_AVAHI - AvahiInit(loop, serviceName); -#endif - -#ifdef HAVE_BONJOUR - BonjourInit(loop, serviceName); -#endif -} - -void -ZeroconfDeinit() -{ - if (!zeroconfEnabled) - return; - -#ifdef HAVE_AVAHI - AvahiDeinit(); -#endif /* HAVE_AVAHI */ - -#ifdef HAVE_BONJOUR - BonjourDeinit(); -#endif -} diff --git a/src/ZeroconfGlue.hxx b/src/ZeroconfGlue.hxx deleted file mode 100644 index 5d2f29642..000000000 --- a/src/ZeroconfGlue.hxx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2003-2014 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_ZEROCONF_GLUE_HXX -#define MPD_ZEROCONF_GLUE_HXX - -#include "check.h" - -class EventLoop; - -#ifdef HAVE_ZEROCONF - -void -ZeroconfInit(EventLoop &loop); - -void -ZeroconfDeinit(); - -#else /* ! HAVE_ZEROCONF */ - -static inline void -ZeroconfInit(EventLoop &) -{} - -static inline void -ZeroconfDeinit() -{} - -#endif /* ! HAVE_ZEROCONF */ - -#endif diff --git a/src/ZeroconfInternal.hxx b/src/ZeroconfInternal.hxx deleted file mode 100644 index 4d47d260a..000000000 --- a/src/ZeroconfInternal.hxx +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2003-2014 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef ZEROCONF_INTERNAL_H -#define ZEROCONF_INTERNAL_H - -/* The dns-sd service type qualifier to publish */ -#define SERVICE_TYPE "_mpd._tcp" - -#endif diff --git a/src/zeroconf/AvahiPoll.cxx b/src/zeroconf/AvahiPoll.cxx new file mode 100644 index 000000000..20d5d74e6 --- /dev/null +++ b/src/zeroconf/AvahiPoll.cxx @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2003-2014 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "AvahiPoll.hxx" +#include "event/SocketMonitor.hxx" +#include "event/TimeoutMonitor.hxx" + +static unsigned +FromAvahiWatchEvent(AvahiWatchEvent e) +{ + return (e & AVAHI_WATCH_IN ? SocketMonitor::READ : 0) | + (e & AVAHI_WATCH_OUT ? SocketMonitor::WRITE : 0) | + (e & AVAHI_WATCH_ERR ? SocketMonitor::ERROR : 0) | + (e & AVAHI_WATCH_HUP ? SocketMonitor::HANGUP : 0); +} + +static AvahiWatchEvent +ToAvahiWatchEvent(unsigned e) +{ + return AvahiWatchEvent((e & SocketMonitor::READ ? AVAHI_WATCH_IN : 0) | + (e & SocketMonitor::WRITE ? AVAHI_WATCH_OUT : 0) | + (e & SocketMonitor::ERROR ? AVAHI_WATCH_ERR : 0) | + (e & SocketMonitor::HANGUP ? AVAHI_WATCH_HUP : 0)); +} + +struct AvahiWatch final : private SocketMonitor { +private: + const AvahiWatchCallback callback; + void *const userdata; + + AvahiWatchEvent received; + +public: + AvahiWatch(int _fd, AvahiWatchEvent _event, + AvahiWatchCallback _callback, void *_userdata, + EventLoop &_loop) + :SocketMonitor(_fd, _loop), + callback(_callback), userdata(_userdata), + received(AvahiWatchEvent(0)) { + Schedule(FromAvahiWatchEvent(_event)); + } + + static void WatchUpdate(AvahiWatch *w, AvahiWatchEvent event) { + w->Schedule(FromAvahiWatchEvent(event)); + } + + static AvahiWatchEvent WatchGetEvents(AvahiWatch *w) { + return w->received; + } + + static void WatchFree(AvahiWatch *w) { + delete w; + } + +protected: + virtual bool OnSocketReady(unsigned flags) { + received = ToAvahiWatchEvent(flags); + callback(this, Get(), received, userdata); + received = AvahiWatchEvent(0); + return true; + } +}; + +static constexpr unsigned +TimevalToMS(const timeval &tv) +{ + return tv.tv_sec * 1000 + (tv.tv_usec + 500) / 1000; +} + +struct AvahiTimeout final : private TimeoutMonitor { +private: + const AvahiTimeoutCallback callback; + void *const userdata; + +public: + AvahiTimeout(const struct timeval *tv, + AvahiTimeoutCallback _callback, void *_userdata, + EventLoop &_loop) + :TimeoutMonitor(_loop), + callback(_callback), userdata(_userdata) { + if (tv != nullptr) + Schedule(TimevalToMS(*tv)); + } + + static void TimeoutUpdate(AvahiTimeout *t, const struct timeval *tv) { + if (tv != nullptr) + t->Schedule(TimevalToMS(*tv)); + else + t->Cancel(); + } + + static void TimeoutFree(AvahiTimeout *t) { + delete t; + } + +protected: + virtual void OnTimeout() { + callback(this, userdata); + } +}; + +MyAvahiPoll::MyAvahiPoll(EventLoop &_loop):event_loop(_loop) +{ + watch_new = WatchNew; + watch_update = AvahiWatch::WatchUpdate; + watch_get_events = AvahiWatch::WatchGetEvents; + watch_free = AvahiWatch::WatchFree; + timeout_new = TimeoutNew; + timeout_update = AvahiTimeout::TimeoutUpdate; + timeout_free = AvahiTimeout::TimeoutFree; +} + +AvahiWatch * +MyAvahiPoll::WatchNew(const AvahiPoll *api, int fd, AvahiWatchEvent event, + AvahiWatchCallback callback, void *userdata) { + const MyAvahiPoll &poll = *(const MyAvahiPoll *)api; + + return new AvahiWatch(fd, event, callback, userdata, + poll.event_loop); +} + +AvahiTimeout * +MyAvahiPoll::TimeoutNew(const AvahiPoll *api, const struct timeval *tv, + AvahiTimeoutCallback callback, void *userdata) { + const MyAvahiPoll &poll = *(const MyAvahiPoll *)api; + + return new AvahiTimeout(tv, callback, userdata, + poll.event_loop); +} diff --git a/src/zeroconf/AvahiPoll.hxx b/src/zeroconf/AvahiPoll.hxx new file mode 100644 index 000000000..e194d3370 --- /dev/null +++ b/src/zeroconf/AvahiPoll.hxx @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2003-2014 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_AVAHI_POLL_HXX +#define MPD_AVAHI_POLL_HXX + +#include "check.h" +#include "Compiler.h" + +#include + +class EventLoop; + +class MyAvahiPoll final : public AvahiPoll { + EventLoop &event_loop; + +public: + MyAvahiPoll(EventLoop &_loop); + +private: + static AvahiWatch *WatchNew(const AvahiPoll *api, int fd, + AvahiWatchEvent event, + AvahiWatchCallback callback, + void *userdata); + + static AvahiTimeout *TimeoutNew(const AvahiPoll *api, + const struct timeval *tv, + AvahiTimeoutCallback callback, + void *userdata); +}; + +#endif diff --git a/src/zeroconf/ZeroconfAvahi.cxx b/src/zeroconf/ZeroconfAvahi.cxx new file mode 100644 index 000000000..8cd09cbed --- /dev/null +++ b/src/zeroconf/ZeroconfAvahi.cxx @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2003-2014 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ZeroconfAvahi.hxx" +#include "AvahiPoll.hxx" +#include "ZeroconfInternal.hxx" +#include "Listen.hxx" +#include "system/FatalError.hxx" +#include "util/Domain.hxx" +#include "Log.hxx" + +#include +#include + +#include +#include +#include +#include + +static constexpr Domain avahi_domain("avahi"); + +static char *avahiName; +static bool avahi_running; +static MyAvahiPoll *avahi_poll; +static AvahiClient *avahiClient; +static AvahiEntryGroup *avahiGroup; + +static void avahiRegisterService(AvahiClient * c); + +/* Callback when the EntryGroup changes state */ +static void avahiGroupCallback(AvahiEntryGroup * g, + AvahiEntryGroupState state, + gcc_unused void *userdata) +{ + char *n; + assert(g); + + FormatDebug(avahi_domain, + "Service group changed to state %d", state); + + switch (state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED: + /* The entry group has been established successfully */ + FormatDefault(avahi_domain, + "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; + + FormatDefault(avahi_domain, + "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: + FormatError(avahi_domain, + "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 */ + avahi_running = false; + break; + + case AVAHI_ENTRY_GROUP_UNCOMMITED: + LogDebug(avahi_domain, "Service group is UNCOMMITED"); + break; + case AVAHI_ENTRY_GROUP_REGISTERING: + LogDebug(avahi_domain, "Service group is REGISTERING"); + } +} + +/* Registers a new service with avahi */ +static void avahiRegisterService(AvahiClient * c) +{ + FormatDebug(avahi_domain, "Registering service %s/%s", + SERVICE_TYPE, avahiName); + + int ret; + assert(c); + + /* 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, nullptr); + if (!avahiGroup) { + FormatError(avahi_domain, + "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, + AvahiPublishFlags(0), + avahiName, SERVICE_TYPE, nullptr, + nullptr, listen_port, nullptr); + if (ret < 0) { + FormatError(avahi_domain, "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) { + FormatError(avahi_domain, "Failed to commit service group: %s", + avahi_strerror(ret)); + goto fail; + } + return; + +fail: + avahi_running = false; +} + +/* Callback when avahi changes state */ +static void avahiClientCallback(AvahiClient * c, AvahiClientState state, + gcc_unused void *userdata) +{ + int reason; + assert(c); + + /* Called whenever the client or server state changes */ + FormatDebug(avahi_domain, "Client changed to state %d", state); + + switch (state) { + case AVAHI_CLIENT_S_RUNNING: + LogDebug(avahi_domain, "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) { + LogDefault(avahi_domain, + "Client Disconnected, will reconnect shortly"); + if (avahiGroup) { + avahi_entry_group_free(avahiGroup); + avahiGroup = nullptr; + } + if (avahiClient) + avahi_client_free(avahiClient); + avahiClient = + avahi_client_new(avahi_poll, + AVAHI_CLIENT_NO_FAIL, + avahiClientCallback, nullptr, + &reason); + if (!avahiClient) { + FormatWarning(avahi_domain, + "Could not reconnect: %s", + avahi_strerror(reason)); + avahi_running = false; + } + } else { + FormatWarning(avahi_domain, + "Client failure: %s (terminal)", + avahi_strerror(reason)); + avahi_running = false; + } + break; + + case AVAHI_CLIENT_S_COLLISION: + LogDebug(avahi_domain, "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) { + LogDebug(avahi_domain, "Resetting group"); + avahi_entry_group_reset(avahiGroup); + } + + break; + + case AVAHI_CLIENT_S_REGISTERING: + LogDebug(avahi_domain, "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) { + LogDebug(avahi_domain, "Resetting group"); + avahi_entry_group_reset(avahiGroup); + } + + break; + + case AVAHI_CLIENT_CONNECTING: + LogDebug(avahi_domain, "Client is CONNECTING"); + break; + } +} + +void +AvahiInit(EventLoop &loop, const char *serviceName) +{ + LogDebug(avahi_domain, "Initializing interface"); + + if (!avahi_is_valid_service_name(serviceName)) + FormatFatalError("Invalid zeroconf_name \"%s\"", serviceName); + + avahiName = avahi_strdup(serviceName); + + avahi_running = true; + + avahi_poll = new MyAvahiPoll(loop); + + int error; + avahiClient = avahi_client_new(avahi_poll, AVAHI_CLIENT_NO_FAIL, + avahiClientCallback, nullptr, &error); + + if (!avahiClient) { + FormatError(avahi_domain, "Failed to create client: %s", + avahi_strerror(error)); + AvahiDeinit(); + } +} + +void +AvahiDeinit(void) +{ + LogDebug(avahi_domain, "Shutting down interface"); + + if (avahiGroup) { + avahi_entry_group_free(avahiGroup); + avahiGroup = nullptr; + } + + if (avahiClient) { + avahi_client_free(avahiClient); + avahiClient = nullptr; + } + + delete avahi_poll; + avahi_poll = nullptr; + + avahi_free(avahiName); + avahiName = nullptr; +} diff --git a/src/zeroconf/ZeroconfAvahi.hxx b/src/zeroconf/ZeroconfAvahi.hxx new file mode 100644 index 000000000..09a199f55 --- /dev/null +++ b/src/zeroconf/ZeroconfAvahi.hxx @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2003-2014 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ZEROCONF_AVAHI_HXX +#define MPD_ZEROCONF_AVAHI_HXX + +class EventLoop; + +void +AvahiInit(EventLoop &loop, const char *service_name); + +void +AvahiDeinit(); + +#endif diff --git a/src/zeroconf/ZeroconfBonjour.cxx b/src/zeroconf/ZeroconfBonjour.cxx new file mode 100644 index 000000000..5b5de1247 --- /dev/null +++ b/src/zeroconf/ZeroconfBonjour.cxx @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2003-2014 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ZeroconfBonjour.hxx" +#include "ZeroconfInternal.hxx" +#include "Listen.hxx" +#include "event/SocketMonitor.hxx" +#include "util/Domain.hxx" +#include "Log.hxx" +#include "Compiler.h" + +#include + +#include + +static constexpr Domain bonjour_domain("bonjour"); + +class BonjourMonitor final : public SocketMonitor { + DNSServiceRef service_ref; + +public: + BonjourMonitor(EventLoop &_loop, DNSServiceRef _service_ref) + :SocketMonitor(DNSServiceRefSockFD(_service_ref), _loop), + service_ref(_service_ref) { + ScheduleRead(); + } + + ~BonjourMonitor() { + DNSServiceRefDeallocate(service_ref); + } + +protected: + virtual bool OnSocketReady(gcc_unused unsigned flags) override { + DNSServiceProcessResult(service_ref); + return false; + } +}; + +static BonjourMonitor *bonjour_monitor; + +static void +dnsRegisterCallback(gcc_unused DNSServiceRef sdRef, + gcc_unused DNSServiceFlags flags, + DNSServiceErrorType errorCode, const char *name, + gcc_unused const char *regtype, + gcc_unused const char *domain, + gcc_unused void *context) +{ + if (errorCode != kDNSServiceErr_NoError) { + LogError(bonjour_domain, + "Failed to register zeroconf service"); + + bonjour_monitor->Cancel(); + } else { + FormatDebug(bonjour_domain, + "Registered zeroconf service with name '%s'", + name); + } +} + +void +BonjourInit(EventLoop &loop, const char *service_name) +{ + DNSServiceRef dnsReference; + DNSServiceErrorType error = DNSServiceRegister(&dnsReference, + 0, 0, service_name, + SERVICE_TYPE, nullptr, nullptr, + g_htons(listen_port), 0, + nullptr, + dnsRegisterCallback, + nullptr); + + if (error != kDNSServiceErr_NoError) { + LogError(bonjour_domain, + "Failed to register zeroconf service"); + + if (dnsReference) { + DNSServiceRefDeallocate(dnsReference); + dnsReference = nullptr; + } + return; + } + + bonjour_monitor = new BonjourMonitor(loop, dnsReference); +} + +void +BonjourDeinit() +{ + delete bonjour_monitor; +} diff --git a/src/zeroconf/ZeroconfBonjour.hxx b/src/zeroconf/ZeroconfBonjour.hxx new file mode 100644 index 000000000..cff52815e --- /dev/null +++ b/src/zeroconf/ZeroconfBonjour.hxx @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2003-2014 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ZEROCONF_BONJOUR_HXX +#define MPD_ZEROCONF_BONJOUR_HXX + +class EventLoop; + +void +BonjourInit(EventLoop &loop, const char *service_name); + +void +BonjourDeinit(); + +#endif diff --git a/src/zeroconf/ZeroconfGlue.cxx b/src/zeroconf/ZeroconfGlue.cxx new file mode 100644 index 000000000..05bb3c17c --- /dev/null +++ b/src/zeroconf/ZeroconfGlue.cxx @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2003-2014 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ZeroconfGlue.hxx" +#include "ZeroconfAvahi.hxx" +#include "ZeroconfBonjour.hxx" +#include "ConfigGlobal.hxx" +#include "ConfigOption.hxx" +#include "Listen.hxx" +#include "util/Domain.hxx" +#include "Log.hxx" +#include "Compiler.h" + +static constexpr Domain zeroconf_domain("zeroconf"); + +/* The default service name to publish + * (overridden by 'zeroconf_name' config parameter) + */ +#define SERVICE_NAME "Music Player" + +#define DEFAULT_ZEROCONF_ENABLED 1 + +static int zeroconfEnabled; + +void +ZeroconfInit(gcc_unused EventLoop &loop) +{ + const char *serviceName; + + zeroconfEnabled = config_get_bool(CONF_ZEROCONF_ENABLED, + DEFAULT_ZEROCONF_ENABLED); + if (!zeroconfEnabled) + return; + + if (listen_port <= 0) { + LogWarning(zeroconf_domain, + "No global port, disabling zeroconf"); + zeroconfEnabled = false; + return; + } + + serviceName = config_get_string(CONF_ZEROCONF_NAME, SERVICE_NAME); + +#ifdef HAVE_AVAHI + AvahiInit(loop, serviceName); +#endif + +#ifdef HAVE_BONJOUR + BonjourInit(loop, serviceName); +#endif +} + +void +ZeroconfDeinit() +{ + if (!zeroconfEnabled) + return; + +#ifdef HAVE_AVAHI + AvahiDeinit(); +#endif /* HAVE_AVAHI */ + +#ifdef HAVE_BONJOUR + BonjourDeinit(); +#endif +} diff --git a/src/zeroconf/ZeroconfGlue.hxx b/src/zeroconf/ZeroconfGlue.hxx new file mode 100644 index 000000000..5d2f29642 --- /dev/null +++ b/src/zeroconf/ZeroconfGlue.hxx @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2003-2014 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ZEROCONF_GLUE_HXX +#define MPD_ZEROCONF_GLUE_HXX + +#include "check.h" + +class EventLoop; + +#ifdef HAVE_ZEROCONF + +void +ZeroconfInit(EventLoop &loop); + +void +ZeroconfDeinit(); + +#else /* ! HAVE_ZEROCONF */ + +static inline void +ZeroconfInit(EventLoop &) +{} + +static inline void +ZeroconfDeinit() +{} + +#endif /* ! HAVE_ZEROCONF */ + +#endif diff --git a/src/zeroconf/ZeroconfInternal.hxx b/src/zeroconf/ZeroconfInternal.hxx new file mode 100644 index 000000000..4d47d260a --- /dev/null +++ b/src/zeroconf/ZeroconfInternal.hxx @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2003-2014 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef ZEROCONF_INTERNAL_H +#define ZEROCONF_INTERNAL_H + +/* The dns-sd service type qualifier to publish */ +#define SERVICE_TYPE "_mpd._tcp" + +#endif diff --git a/test/run_avahi.cxx b/test/run_avahi.cxx index b44980bc6..d1c153cfd 100644 --- a/test/run_avahi.cxx +++ b/test/run_avahi.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "event/Loop.hxx" #include "ShutdownHandler.hxx" -#include "ZeroconfAvahi.hxx" +#include "zeroconf/ZeroconfAvahi.hxx" #include -- cgit v1.2.3