Commit 978d2638 authored by Max Kellermann's avatar Max Kellermann

zeroconf/avahi/Client: new class, replacing lots of code from ZeroconfAvahi.cxx

parent cfcafdf8
......@@ -18,7 +18,9 @@
*/
#include "ZeroconfAvahi.hxx"
#include "avahi/Poll.hxx"
#include "avahi/Client.hxx"
#include "avahi/ConnectionListener.hxx"
#include "avahi/ErrorHandler.hxx"
#include "ZeroconfInternal.hxx"
#include "Listen.hxx"
#include "util/Domain.hxx"
......@@ -35,17 +37,34 @@
static constexpr Domain avahi_domain("avahi");
class AvahiGlue final {
class AvahiGlue final : Avahi::ErrorHandler, Avahi::ConnectionListener {
public:
Avahi::Poll poll;
Avahi::Client client;
explicit AvahiGlue(EventLoop &event_loop)
:poll(event_loop) {}
:client(event_loop, *this)
{
client.AddListener(*this);
}
~AvahiGlue() noexcept {
client.RemoveListener(*this);
}
/* virtual methods from class AvahiConnectionListener */
void OnAvahiConnect(AvahiClient *client) noexcept override;
void OnAvahiDisconnect() noexcept override;
void OnAvahiChanged() noexcept override;
/* virtual methods from class Avahi::ErrorHandler */
bool OnAvahiError(std::exception_ptr e) noexcept override {
LogError(e);
return true;
}
};
static char *avahi_name;
static AvahiGlue *avahi_glue;
static AvahiClient *avahi_client;
static AvahiEntryGroup *avahi_group;
static void
......@@ -154,91 +173,32 @@ AvahiRegisterService(AvahiClient *c)
}
}
/* Callback when avahi changes state */
static void
MyAvahiClientCallback(AvahiClient *c, AvahiClientState state,
[[maybe_unused]] void *userdata)
void
AvahiGlue::OnAvahiConnect(AvahiClient *c) noexcept
{
assert(c != nullptr);
/* Called whenever the client or server state changes */
FormatDebug(avahi_domain, "Client changed to state %d", state);
switch (state) {
int reason;
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 (avahi_group == nullptr)
AvahiRegisterService(c);
break;
case AVAHI_CLIENT_FAILURE:
reason = avahi_client_errno(c);
if (reason == AVAHI_ERR_DISCONNECTED) {
LogNotice(avahi_domain,
"Client Disconnected, will reconnect shortly");
if (avahi_group != nullptr) {
avahi_entry_group_free(avahi_group);
avahi_group = nullptr;
}
if (avahi_client != nullptr)
avahi_client_free(avahi_client);
avahi_client =
avahi_client_new(&avahi_glue->poll,
AVAHI_CLIENT_NO_FAIL,
MyAvahiClientCallback, nullptr,
&reason);
if (avahi_client == nullptr)
FormatWarning(avahi_domain,
"Could not reconnect: %s",
avahi_strerror(reason));
} else {
FormatWarning(avahi_domain,
"Client failure: %s (terminal)",
avahi_strerror(reason));
}
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 (avahi_group != nullptr) {
LogDebug(avahi_domain, "Resetting group");
avahi_entry_group_reset(avahi_group);
}
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 (avahi_group != nullptr) {
LogDebug(avahi_domain, "Resetting group");
avahi_entry_group_reset(avahi_group);
}
if (avahi_group == nullptr)
AvahiRegisterService(c);
}
break;
void
AvahiGlue::OnAvahiDisconnect() noexcept
{
if (avahi_group != nullptr) {
avahi_entry_group_free(avahi_group);
avahi_group = nullptr;
}
}
case AVAHI_CLIENT_CONNECTING:
LogDebug(avahi_domain, "Client is CONNECTING");
break;
void
AvahiGlue::OnAvahiChanged() noexcept
{
if (avahi_group != nullptr) {
LogDebug(avahi_domain, "Resetting group");
avahi_entry_group_reset(avahi_group);
}
}
void
AvahiInit(EventLoop &loop, const char *serviceName)
{
......@@ -250,16 +210,6 @@ AvahiInit(EventLoop &loop, const char *serviceName)
avahi_name = avahi_strdup(serviceName);
avahi_glue = new AvahiGlue(loop);
int error;
avahi_client = avahi_client_new(&avahi_glue->poll, AVAHI_CLIENT_NO_FAIL,
MyAvahiClientCallback, nullptr,
&error);
if (avahi_client == nullptr) {
FormatError(avahi_domain, "Failed to create client: %s",
avahi_strerror(error));
AvahiDeinit();
}
}
void
......@@ -272,11 +222,6 @@ AvahiDeinit()
avahi_group = nullptr;
}
if (avahi_client != nullptr) {
avahi_client_free(avahi_client);
avahi_client = nullptr;
}
delete avahi_glue;
avahi_free(avahi_name);
......
/*
* Copyright 2007-2021 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "Client.hxx"
#include "ConnectionListener.hxx"
#include "ErrorHandler.hxx"
#include "Error.hxx"
#include <avahi-common/error.h>
#include <cassert>
namespace Avahi {
Client::Client(EventLoop &event_loop, ErrorHandler &_error_handler) noexcept
:error_handler(_error_handler),
reconnect_timer(event_loop, BIND_THIS_METHOD(OnReconnectTimer)),
poll(event_loop)
{
reconnect_timer.Schedule({});
}
Client::~Client() noexcept
{
Close();
}
void
Client::Close() noexcept
{
if (client != nullptr) {
for (auto *l : listeners)
l->OnAvahiDisconnect();
avahi_client_free(client);
client = nullptr;
}
reconnect_timer.Cancel();
}
void
Client::ClientCallback(AvahiClient *c, AvahiClientState state) noexcept
{
int error;
switch (state) {
case AVAHI_CLIENT_S_RUNNING:
for (auto *l : listeners)
l->OnAvahiConnect(c);
break;
case AVAHI_CLIENT_FAILURE:
error = avahi_client_errno(c);
if (error == AVAHI_ERR_DISCONNECTED) {
Close();
reconnect_timer.Schedule(std::chrono::seconds(10));
} else {
if (!error_handler.OnAvahiError(std::make_exception_ptr(MakeError(error,
"Avahi connection error"))))
return;
reconnect_timer.Schedule(std::chrono::minutes(1));
}
for (auto *l : listeners)
l->OnAvahiDisconnect();
break;
case AVAHI_CLIENT_S_COLLISION:
case AVAHI_CLIENT_S_REGISTERING:
for (auto *l : listeners)
l->OnAvahiChanged();
break;
case AVAHI_CLIENT_CONNECTING:
break;
}
}
void
Client::ClientCallback(AvahiClient *c, AvahiClientState state,
void *userdata) noexcept
{
auto &client = *(Client *)userdata;
client.ClientCallback(c, state);
}
void
Client::OnReconnectTimer() noexcept
{
int error;
client = avahi_client_new(&poll, AVAHI_CLIENT_NO_FAIL,
ClientCallback, this,
&error);
if (client == nullptr) {
if (!error_handler.OnAvahiError(std::make_exception_ptr(MakeError(error,
"Failed to create Avahi client"))))
return;
reconnect_timer.Schedule(std::chrono::minutes(1));
return;
}
}
} // namespace Avahi
/*
* Copyright 2007-2021 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "Poll.hxx"
#include "event/CoarseTimerEvent.hxx"
#include <avahi-client/client.h>
#include <forward_list>
class EventLoop;
namespace Avahi {
class ErrorHandler;
class ConnectionListener;
class Client final {
ErrorHandler &error_handler;
CoarseTimerEvent reconnect_timer;
Poll poll;
AvahiClient *client = nullptr;
std::forward_list<ConnectionListener *> listeners;
public:
Client(EventLoop &event_loop, ErrorHandler &_error_handler) noexcept;
~Client() noexcept;
Client(const Client &) = delete;
Client &operator=(const Client &) = delete;
EventLoop &GetEventLoop() const noexcept {
return poll.GetEventLoop();
}
void Close() noexcept;
AvahiClient *GetClient() noexcept {
return client;
}
void AddListener(ConnectionListener &listener) noexcept {
listeners.push_front(&listener);
}
void RemoveListener(ConnectionListener &listener) noexcept {
listeners.remove(&listener);
}
private:
void ClientCallback(AvahiClient *c, AvahiClientState state) noexcept;
static void ClientCallback(AvahiClient *c, AvahiClientState state,
void *userdata) noexcept;
void OnReconnectTimer() noexcept;
};
} // namespace Avahi
/*
* Copyright 2007-2021 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <avahi-client/client.h>
namespace Avahi {
class ConnectionListener {
public:
/**
* The connection to the Avahi daemon has been established.
*
* Note that this may be called again after a collision
* (AVAHI_CLIENT_S_COLLISION) or a host name change
* (AVAHI_CLIENT_S_REGISTERING).
*/
virtual void OnAvahiConnect(AvahiClient *client) noexcept = 0;
virtual void OnAvahiDisconnect() noexcept = 0;
/**
* Something about the Avahi connection has changed, e.g. a
* collision (AVAHI_CLIENT_S_COLLISION) or a host name change
* (AVAHI_CLIENT_S_REGISTERING). Services shall be
* unpublished now, and will be re-published in the following
* OnAvahiConnect() call.
*/
virtual void OnAvahiChanged() noexcept {}
};
} // namespace Avahi
/*
* Copyright 2007-2021 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "Error.hxx"
#include <avahi-client/client.h>
#include <avahi-common/error.h>
#include <system_error>
namespace Avahi {
ErrorCategory error_category;
std::string
ErrorCategory::message(int condition) const
{
return avahi_strerror(condition);
}
std::system_error
MakeError(AvahiClient &client, const char *msg) noexcept
{
return MakeError(avahi_client_errno(&client), msg);
}
} // namespace Avahi
/*
* Copyright 2007-2021 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <system_error>
struct AvahiClient;
namespace Avahi {
class ErrorCategory final : public std::error_category {
public:
const char *name() const noexcept override {
return "avahi-client";
}
std::string message(int condition) const override;
};
extern ErrorCategory error_category;
inline std::system_error
MakeError(int error, const char *msg) noexcept
{
return std::system_error(error, error_category, msg);
}
std::system_error
MakeError(AvahiClient &client, const char *msg) noexcept;
} // namespace Avahi
/*
* Copyright 2007-2021 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <exception>
struct AvahiClient;
namespace Avahi {
class ErrorHandler {
public:
/**
* @return true to keep retrying, false if the failed object
* has been disposed
*/
virtual bool OnAvahiError(std::exception_ptr e) noexcept = 0;
};
} // namespace Avahi
......@@ -6,6 +6,8 @@ endif
avahi = static_library(
'avahi',
'Client.cxx',
'Error.cxx',
'Poll.cxx',
include_directories: inc,
dependencies: [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment