瀏覽代碼

Userland: Introduce ConfigServer and LibConfig

ConfigServer is an IPC service that provides access to application
configuration and settings. The idea is to replace all uses of
Core::ConfigFile with IPC requests to ConfigServer.

This first cut of the API is pretty similar to Core::ConfigFile.

The old:

    auto config = Core::ConfigFile::open_for_app("App");
    auto value = config->read_entry("Group", "Key");

The new:

    auto value = Config::read_string("App", "Group", "Key");

ConfigServer uses the ~/.config directory as its backing store
and all the files remain human-editable. :^)
Andreas Kling 3 年之前
父節點
當前提交
bdcd0abf9d

+ 5 - 0
Base/etc/SystemServer.ini

@@ -1,3 +1,8 @@
+[ConfigServer]
+Socket=/tmp/portal/config
+SocketPermissions=600
+User=anon
+
 [RequestServer]
 [RequestServer]
 Socket=/tmp/portal/request
 Socket=/tmp/portal/request
 SocketPermissions=600
 SocketPermissions=600

+ 1 - 0
Userland/Libraries/CMakeLists.txt

@@ -4,6 +4,7 @@ add_subdirectory(LibC)
 add_subdirectory(LibCards)
 add_subdirectory(LibCards)
 add_subdirectory(LibChess)
 add_subdirectory(LibChess)
 add_subdirectory(LibCompress)
 add_subdirectory(LibCompress)
+add_subdirectory(LibConfig)
 add_subdirectory(LibCore)
 add_subdirectory(LibCore)
 add_subdirectory(LibCoredump)
 add_subdirectory(LibCoredump)
 add_subdirectory(LibCpp)
 add_subdirectory(LibCpp)

+ 11 - 0
Userland/Libraries/LibConfig/CMakeLists.txt

@@ -0,0 +1,11 @@
+set(SOURCES
+    Client.cpp
+)
+
+set(GENERATED_SOURCES
+    ../../Services/ConfigServer/ConfigServerEndpoint.h
+    ../../Services/ConfigServer/ConfigClientEndpoint.h
+)
+
+serenity_lib(LibConfig config)
+target_link_libraries(LibConfig LibIPC)

+ 50 - 0
Userland/Libraries/LibConfig/Client.cpp

@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibConfig/Client.h>
+
+namespace Config {
+
+static RefPtr<Client> s_the = nullptr;
+
+Client& Client::the()
+{
+    if (!s_the || !s_the->is_open())
+        s_the = Client::construct();
+    return *s_the;
+}
+
+String Client::read_string(StringView domain, StringView group, StringView key, StringView fallback)
+{
+    return read_string_value(domain, group, key).value_or(fallback);
+}
+
+i32 Client::read_i32(StringView domain, StringView group, StringView key, i32 fallback)
+{
+    return read_i32_value(domain, group, key).value_or(fallback);
+}
+
+bool Client::read_bool(StringView domain, StringView group, StringView key, bool fallback)
+{
+    return read_bool_value(domain, group, key).value_or(fallback);
+}
+
+void Client::write_string(StringView domain, StringView group, StringView key, StringView value)
+{
+    async_write_string_value(domain, group, key, value);
+}
+
+void Client::write_i32(StringView domain, StringView group, StringView key, i32 value)
+{
+    async_write_i32_value(domain, group, key, value);
+}
+
+void Client::write_bool(StringView domain, StringView group, StringView key, bool value)
+{
+    async_write_bool_value(domain, group, key, value);
+}
+
+}

+ 71 - 0
Userland/Libraries/LibConfig/Client.h

@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <ConfigServer/ConfigClientEndpoint.h>
+#include <ConfigServer/ConfigServerEndpoint.h>
+#include <LibCore/File.h>
+#include <LibCore/Promise.h>
+#include <LibCore/StandardPaths.h>
+#include <LibIPC/ServerConnection.h>
+
+namespace Config {
+
+class Client final
+    : public IPC::ServerConnection<ConfigClientEndpoint, ConfigServerEndpoint>
+    , public ConfigClientEndpoint {
+    C_OBJECT(Client);
+
+public:
+    String read_string(StringView domain, StringView group, StringView key, StringView fallback);
+    i32 read_i32(StringView domain, StringView group, StringView key, i32 fallback);
+    bool read_bool(StringView domain, StringView group, StringView key, bool fallback);
+
+    void write_string(StringView domain, StringView group, StringView key, StringView value);
+    void write_i32(StringView domain, StringView group, StringView key, i32 value);
+    void write_bool(StringView domain, StringView group, StringView key, bool value);
+
+    static Client& the();
+
+private:
+    explicit Client()
+        : IPC::ServerConnection<ConfigClientEndpoint, ConfigServerEndpoint>(*this, "/tmp/portal/config")
+    {
+    }
+};
+
+inline String read_string(StringView domain, StringView group, StringView key, StringView fallback = {})
+{
+    return Client::the().read_string(domain, group, key, fallback);
+}
+
+inline i32 read_i32(StringView domain, StringView group, StringView key, i32 fallback = 0)
+{
+    return Client::the().read_i32(domain, group, key, fallback);
+}
+
+inline bool read_bool(StringView domain, StringView group, StringView key, bool fallback = false)
+{
+    return Client::the().read_bool(domain, group, key, fallback);
+}
+
+inline void write_string(StringView domain, StringView group, StringView key, StringView value)
+{
+    Client::the().write_string(domain, group, key, value);
+}
+
+inline void write_i32(StringView domain, StringView group, StringView key, i32 value)
+{
+    Client::the().write_i32(domain, group, key, value);
+}
+
+inline void write_bool(StringView domain, StringView group, StringView key, bool value)
+{
+    Client::the().write_bool(domain, group, key, value);
+}
+
+}

+ 1 - 0
Userland/Services/CMakeLists.txt

@@ -1,6 +1,7 @@
 add_subdirectory(AudioServer)
 add_subdirectory(AudioServer)
 add_subdirectory(ChessEngine)
 add_subdirectory(ChessEngine)
 add_subdirectory(Clipboard)
 add_subdirectory(Clipboard)
+add_subdirectory(ConfigServer)
 add_subdirectory(CrashDaemon)
 add_subdirectory(CrashDaemon)
 add_subdirectory(DHCPClient)
 add_subdirectory(DHCPClient)
 add_subdirectory(EchoServer)
 add_subdirectory(EchoServer)

+ 18 - 0
Userland/Services/ConfigServer/CMakeLists.txt

@@ -0,0 +1,18 @@
+serenity_component(
+    ConfigServer
+    REQUIRED
+    TARGETS ConfigServer
+)
+
+compile_ipc(ConfigServer.ipc ConfigServerEndpoint.h)
+compile_ipc(ConfigClient.ipc ConfigClientEndpoint.h)
+
+set(SOURCES
+    ClientConnection.cpp
+    main.cpp
+    ConfigServerEndpoint.h
+    ConfigClientEndpoint.h
+)
+
+serenity_bin(ConfigServer)
+target_link_libraries(ConfigServer LibIPC)

+ 72 - 0
Userland/Services/ConfigServer/ClientConnection.cpp

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "ClientConnection.h"
+#include <ConfigServer/ConfigClientEndpoint.h>
+#include <LibCore/ConfigFile.h>
+
+namespace ConfigServer {
+
+static HashMap<int, RefPtr<ClientConnection>> s_connections;
+
+ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socket, int client_id)
+    : IPC::ClientConnection<ConfigClientEndpoint, ConfigServerEndpoint>(*this, move(client_socket), client_id)
+{
+    s_connections.set(client_id, *this);
+}
+
+ClientConnection::~ClientConnection()
+{
+}
+
+void ClientConnection::die()
+{
+    s_connections.remove(client_id());
+}
+
+Messages::ConfigServer::ReadStringValueResponse ClientConnection::read_string_value(String const& domain, String const& group, String const& key)
+{
+    auto config = Core::ConfigFile::open_for_app(domain);
+    if (!config->has_key(group, key))
+        return Optional<String> {};
+    return Optional<String> { config->read_entry(group, key) };
+}
+
+Messages::ConfigServer::ReadI32ValueResponse ClientConnection::read_i32_value(String const& domain, String const& group, String const& key)
+{
+    auto config = Core::ConfigFile::open_for_app(domain);
+    if (!config->has_key(group, key))
+        return Optional<i32> {};
+    return Optional<i32> { config->read_num_entry(group, key) };
+}
+
+Messages::ConfigServer::ReadBoolValueResponse ClientConnection::read_bool_value(String const& domain, String const& group, String const& key)
+{
+    auto config = Core::ConfigFile::open_for_app(domain);
+    if (!config->has_key(group, key))
+        return Optional<bool> {};
+    return Optional<bool> { config->read_bool_entry(group, key) };
+}
+
+void ClientConnection::write_string_value(String const& domain, String const& group, String const& key, String const& value)
+{
+    auto config = Core::ConfigFile::open_for_app(domain, Core::ConfigFile::AllowWriting::Yes);
+    config->write_entry(group, key, value);
+}
+
+void ClientConnection::write_i32_value(String const& domain, String const& group, String const& key, i32 value)
+{
+    auto config = Core::ConfigFile::open_for_app(domain, Core::ConfigFile::AllowWriting::Yes);
+    config->write_num_entry(group, key, value);
+}
+
+void ClientConnection::write_bool_value(String const& domain, String const& group, String const& key, bool value)
+{
+    auto config = Core::ConfigFile::open_for_app(domain, Core::ConfigFile::AllowWriting::Yes);
+    config->write_bool_entry(group, key, value);
+}
+
+}

+ 34 - 0
Userland/Services/ConfigServer/ClientConnection.h

@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibIPC/ClientConnection.h>
+
+#include <ConfigServer/ConfigClientEndpoint.h>
+#include <ConfigServer/ConfigServerEndpoint.h>
+
+namespace ConfigServer {
+
+class ClientConnection final : public IPC::ClientConnection<ConfigClientEndpoint, ConfigServerEndpoint> {
+    C_OBJECT(ClientConnection)
+public:
+    ~ClientConnection() override;
+
+    virtual void die() override;
+
+private:
+    explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id);
+
+    virtual Messages::ConfigServer::ReadStringValueResponse read_string_value([[maybe_unused]] String const& domain, [[maybe_unused]] String const& group, [[maybe_unused]] String const& key) override;
+    virtual Messages::ConfigServer::ReadI32ValueResponse read_i32_value([[maybe_unused]] String const& domain, [[maybe_unused]] String const& group, [[maybe_unused]] String const& key) override;
+    virtual Messages::ConfigServer::ReadBoolValueResponse read_bool_value([[maybe_unused]] String const& domain, [[maybe_unused]] String const& group, [[maybe_unused]] String const& key) override;
+    virtual void write_string_value([[maybe_unused]] String const& domain, [[maybe_unused]] String const& group, [[maybe_unused]] String const& key, [[maybe_unused]] String const& value) override;
+    virtual void write_i32_value([[maybe_unused]] String const& domain, [[maybe_unused]] String const& group, [[maybe_unused]] String const& key, [[maybe_unused]] i32 value) override;
+    virtual void write_bool_value([[maybe_unused]] String const& domain, [[maybe_unused]] String const& group, [[maybe_unused]] String const& key, [[maybe_unused]] bool value) override;
+};
+
+}

+ 3 - 0
Userland/Services/ConfigServer/ConfigClient.ipc

@@ -0,0 +1,3 @@
+endpoint ConfigClient
+{
+}

+ 10 - 0
Userland/Services/ConfigServer/ConfigServer.ipc

@@ -0,0 +1,10 @@
+endpoint ConfigServer
+{
+    read_string_value(String domain, String group, String key) => (Optional<String> value)
+    read_i32_value(String domain, String group, String key) => (Optional<i32> value)
+    read_bool_value(String domain, String group, String key) => (Optional<bool> value)
+
+    write_string_value(String domain, String group, String key, String value) =|
+    write_i32_value(String domain, String group, String key, i32 value) =|
+    write_bool_value(String domain, String group, String key, bool value)  =|
+}

+ 43 - 0
Userland/Services/ConfigServer/main.cpp

@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "ClientConnection.h"
+#include <LibCore/LocalServer.h>
+#include <LibCore/StandardPaths.h>
+#include <unistd.h>
+
+int main(int, char**)
+{
+    if (pledge("stdio accept rpath wpath cpath", nullptr) < 0) {
+        perror("pledge");
+        return 1;
+    }
+
+    if (unveil(Core::StandardPaths::config_directory().characters(), "rwc") < 0) {
+        perror("unveil");
+        return 1;
+    }
+
+    unveil(nullptr, nullptr);
+
+    Core::EventLoop event_loop;
+    auto server = Core::LocalServer::construct();
+
+    bool ok = server->take_over_from_system_server();
+    VERIFY(ok);
+    server->on_ready_to_accept = [&] {
+        auto client_socket = server->accept();
+        if (!client_socket) {
+            dbgln("ConfigServer: accept failed.");
+            return;
+        }
+        static int s_next_client_id = 0;
+        int client_id = ++s_next_client_id;
+        IPC::new_client_connection<ConfigServer::ClientConnection>(client_socket.release_nonnull(), client_id);
+    };
+
+    return event_loop.exec();
+}