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:
		
				
					committed by
					
						
						Max Kellermann
					
				
			
			
				
	
			
			
			
						parent
						
							b20b773189
						
					
				
				
					commit
					461da92064
				
			@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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().
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user