From 83d3720842d1b31769c05615c7c8eb23471bba79 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 26 Aug 2021 19:31:43 +0200 Subject: [PATCH] ConfigServer: Monitor opened config files for changes After opening a domain configuration and putting into our configuration cache, we now monitor the underlying file for changes and send out notifications to monitoring clients as needed. This patch has three FIXME's: - We create a new Core::FileWatcher for each domain. - We don't yet detect removed keys. - We don't know the type of key, so we assume everything is a string. There are a number of ways we can solve those problems but let's not hold up this patch while we wait for solutions. :^) --- .../ConfigServer/ClientConnection.cpp | 27 ++++++++++++++++++- .../Services/ConfigServer/ClientConnection.h | 2 ++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Userland/Services/ConfigServer/ClientConnection.cpp b/Userland/Services/ConfigServer/ClientConnection.cpp index 246a95e10f..6a2e0194b6 100644 --- a/Userland/Services/ConfigServer/ClientConnection.cpp +++ b/Userland/Services/ConfigServer/ClientConnection.cpp @@ -7,6 +7,7 @@ #include "ClientConnection.h" #include #include +#include namespace ConfigServer { @@ -15,6 +16,7 @@ static HashMap> s_connections; struct CachedDomain { String domain; NonnullRefPtr config; + RefPtr watcher; }; static HashMap> s_cache; @@ -26,7 +28,30 @@ static Core::ConfigFile& ensure_domain_config(String const& domain) return *it->value->config; auto config = Core::ConfigFile::open_for_app(domain, Core::ConfigFile::AllowWriting::Yes); - auto cache_entry = make(domain, config); + // FIXME: Use a single FileWatcher with multiple watches inside. + auto watcher_or_error = Core::FileWatcher::create(InodeWatcherFlags::Nonblock); + VERIFY(!watcher_or_error.is_error()); + auto result = watcher_or_error.value()->add_watch(config->filename(), Core::FileWatcherEvent::Type::ContentModified); + VERIFY(!result.is_error()); + watcher_or_error.value()->on_change = [config, domain](auto&) { + auto new_config = Core::ConfigFile::open(config->filename()); + // FIXME: Detect removed keys. + // FIXME: Detect type of keys. + for (auto& group : new_config->groups()) { + for (auto& key : new_config->keys(group)) { + auto old_value = config->read_entry(group, key); + auto new_value = new_config->read_entry(group, key); + if (old_value != new_value) { + for (auto& it : s_connections) { + if (it.value->is_monitoring_domain(domain)) { + it.value->async_notify_changed_string_value(domain, group, key, new_value); + } + } + } + } + } + }; + auto cache_entry = make(domain, config, watcher_or_error.release_value()); s_cache.set(domain, move(cache_entry)); return *config; } diff --git a/Userland/Services/ConfigServer/ClientConnection.h b/Userland/Services/ConfigServer/ClientConnection.h index 55bd3ebb9c..026f17ff81 100644 --- a/Userland/Services/ConfigServer/ClientConnection.h +++ b/Userland/Services/ConfigServer/ClientConnection.h @@ -20,6 +20,8 @@ public: virtual void die() override; + bool is_monitoring_domain(String const& domain) const { return m_monitored_domains.contains(domain); } + private: explicit ClientConnection(NonnullRefPtr, int client_id);