lib/avahi/Publisher: make the service list dynamic

This allows editing the list of services at any time instead of
passing a constant list at construction time.  To do this, Service
instances are now caller-owned and managed in an IntrusiveList instead
of Publisher-owned in a std::forward_list.
This commit is contained in:
Max Kellermann 2024-01-17 18:10:28 +01:00 committed by Max Kellermann
parent b20b773189
commit 461da92064
5 changed files with 91 additions and 30 deletions

View File

@ -38,28 +38,41 @@ MakePidName(const char *prefix) noexcept
}
Publisher::Publisher(Client &_client, const char *_name,
std::forward_list<Service> _services,
ErrorHandler &_error_handler) noexcept
:error_handler(_error_handler),
name(MakePidName(_name)),
client(_client),
defer_register_services(client.GetEventLoop(),
BIND_THIS_METHOD(DeferredRegisterServices)),
services(std::move(_services))
BIND_THIS_METHOD(DeferredRegisterServices))
{
assert(!services.empty());
client.AddListener(*this);
if (client.IsConnected())
defer_register_services.Schedule();
}
Publisher::~Publisher() noexcept
{
assert(services.empty());
client.RemoveListener(*this);
}
void
Publisher::AddService(Service &service) noexcept
{
services.push_back(service);
if (visible && client.IsConnected())
defer_register_services.Schedule();
}
void
Publisher::RemoveService(Service &service) noexcept
{
services.erase(services.iterator_to(service));
if (visible && client.IsConnected())
defer_register_services.Schedule();
}
inline void
Publisher::GroupCallback(AvahiEntryGroup *g,
AvahiEntryGroupState state) noexcept
@ -82,6 +95,7 @@ try {
}
/* And recreate the services */
should_reset_group = false;
RegisterServices(*g);
break;
@ -122,7 +136,7 @@ AddService(AvahiEntryGroup &group, const Service &service,
static void
AddServices(AvahiEntryGroup &group,
const std::forward_list<Service> &services, const char *name)
const IntrusiveList<Service> &services, const char *name)
{
for (const auto &i : services)
AddService(group, i, name);
@ -131,11 +145,20 @@ AddServices(AvahiEntryGroup &group,
void
Publisher::RegisterServices(AvahiEntryGroup &g)
{
if (should_reset_group) {
should_reset_group = false;
avahi_entry_group_reset(&g);
}
AddServices(g, services, name.c_str());
if (int error = avahi_entry_group_commit(&g);
error != AVAHI_OK)
throw MakeError(error, "Failed to commit Avahi service group");
if (!services.empty()) {
should_reset_group = true;
if (int error = avahi_entry_group_commit(&g);
error != AVAHI_OK)
throw MakeError(error, "Failed to commit Avahi service group");
}
}
void
@ -144,6 +167,8 @@ Publisher::RegisterServices(AvahiClient *c)
assert(visible);
if (!group) {
assert(!should_reset_group);
group.reset(avahi_entry_group_new(c, GroupCallback, this));
if (!group)
throw MakeError(*c, "Failed to create Avahi service group");
@ -175,8 +200,12 @@ Publisher::HideServices() noexcept
defer_register_services.Cancel();
if (group)
if (group) {
should_reset_group = false;
avahi_entry_group_reset(group.get());
} else {
assert(!should_reset_group);
}
}
void
@ -194,7 +223,10 @@ Publisher::ShowServices() noexcept
void
Publisher::OnAvahiConnect(AvahiClient *c) noexcept
{
if (visible)
assert(!group);
assert(!should_reset_group);
if (visible && !services.empty())
try {
RegisterServices(c);
} catch (...) {
@ -206,12 +238,14 @@ void
Publisher::OnAvahiDisconnect() noexcept
{
group.reset();
should_reset_group = false;
}
void
Publisher::OnAvahiChanged() noexcept
{
group.reset();
should_reset_group = false;
}
} // namespace Avahi

View File

@ -7,11 +7,11 @@
#include "EntryGroup.hxx"
#include "ConnectionListener.hxx"
#include "event/DeferEvent.hxx"
#include "util/IntrusiveList.hxx"
#include <avahi-client/publish.h>
#include <string>
#include <forward_list>
class SocketAddress;
@ -36,7 +36,14 @@ class Publisher final : ConnectionListener {
EntryGroupPtr group;
const std::forward_list<Service> services;
IntrusiveList<Service> services;
/**
* Should avahi_entry_group_reset() be called by the next
* RegisterServices() call? This is true if the #gorup is
* non-empty.
*/
bool should_reset_group = false;
/**
* Shall the published services be visible? This is controlled by
@ -46,13 +53,28 @@ class Publisher final : ConnectionListener {
public:
Publisher(Client &client, const char *_name,
std::forward_list<Service> _services,
ErrorHandler &_error_handler) noexcept;
~Publisher() noexcept;
Publisher(const Publisher &) = delete;
Publisher &operator=(const Publisher &) = delete;
/**
* Publish another service.
*
* @param service a #Service instance owned by the caller (it
* must remain valid until you call RemoveService())
*/
void AddService(Service &service) noexcept;
/**
* Unpublish a service.
*
* @param service a #Service instance which was previously
* passed to AddService()
*/
void RemoveService(Service &service) noexcept;
/**
* Temporarily hide all registered services. You can undo this
* with ShowServices().

View File

@ -4,6 +4,8 @@
#pragma once
#include "util/IntrusiveList.hxx"
#include <avahi-common/address.h>
#include <cstdint>
@ -14,7 +16,7 @@ namespace Avahi {
/**
* A service that will be published by class #Publisher.
*/
struct Service {
struct Service : IntrusiveListHook<> {
AvahiIfIndex interface = AVAHI_IF_UNSPEC;
AvahiProtocol protocol = AVAHI_PROTO_UNSPEC;
std::string type;

View File

@ -4,7 +4,6 @@
#include "Helper.hxx"
#include "lib/avahi/Client.hxx"
#include "lib/avahi/ErrorHandler.hxx"
#include "lib/avahi/Service.hxx"
#include "lib/fmt/RuntimeError.hxx"
#include "Log.hxx"
@ -29,12 +28,19 @@ static std::weak_ptr<SharedAvahiClient> shared_avahi_client;
inline
AvahiHelper::AvahiHelper(std::shared_ptr<SharedAvahiClient> _client,
const char *service_name,
std::forward_list<Avahi::Service> &&services)
const char *service_type, unsigned port)
:client(std::move(_client)),
publisher(client->client, service_name,
std::move(services), *client) {}
publisher(client->client, service_name, *client),
service(AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
service_type, port)
{
publisher.AddService(service);
}
AvahiHelper::~AvahiHelper() noexcept = default;
AvahiHelper::~AvahiHelper() noexcept
{
publisher.RemoveService(service);
}
std::unique_ptr<AvahiHelper>
AvahiInit(EventLoop &event_loop, const char *service_name,
@ -49,11 +55,6 @@ AvahiInit(EventLoop &event_loop, const char *service_name,
shared_avahi_client = client =
std::make_shared<SharedAvahiClient>(event_loop);
std::forward_list<Avahi::Service> services;
services.emplace_front(AVAHI_IF_UNSPEC,
AVAHI_PROTO_UNSPEC,
service_type, port);
return std::make_unique<AvahiHelper>(std::move(client), service_name,
std::move(services));
service_type, port);
}

View File

@ -4,6 +4,7 @@
#pragma once
#include "lib/avahi/Publisher.hxx"
#include "lib/avahi/Service.hxx"
#include <memory>
@ -15,11 +16,12 @@ class SharedAvahiClient;
class AvahiHelper final {
std::shared_ptr<SharedAvahiClient> client;
Avahi::Publisher publisher;
Avahi::Service service;
public:
AvahiHelper(std::shared_ptr<SharedAvahiClient> _client,
const char *service_name,
std::forward_list<Avahi::Service> &&services);
const char *service_type, unsigned port);
~AvahiHelper() noexcept;
};