diff --git a/Base/etc/SystemServer.ini b/Base/etc/SystemServer.ini index eccf102ae3..189e002b36 100644 --- a/Base/etc/SystemServer.ini +++ b/Base/etc/SystemServer.ini @@ -1,3 +1,8 @@ +[ConfigServer] +Socket=/tmp/portal/config +SocketPermissions=600 +User=anon + [RequestServer] Socket=/tmp/portal/request SocketPermissions=600 diff --git a/Userland/Libraries/CMakeLists.txt b/Userland/Libraries/CMakeLists.txt index ac96530081..8c0ec81ef2 100644 --- a/Userland/Libraries/CMakeLists.txt +++ b/Userland/Libraries/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(LibC) add_subdirectory(LibCards) add_subdirectory(LibChess) add_subdirectory(LibCompress) +add_subdirectory(LibConfig) add_subdirectory(LibCore) add_subdirectory(LibCoredump) add_subdirectory(LibCpp) diff --git a/Userland/Libraries/LibConfig/CMakeLists.txt b/Userland/Libraries/LibConfig/CMakeLists.txt new file mode 100644 index 0000000000..04527241bd --- /dev/null +++ b/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) diff --git a/Userland/Libraries/LibConfig/Client.cpp b/Userland/Libraries/LibConfig/Client.cpp new file mode 100644 index 0000000000..2fce3af8dc --- /dev/null +++ b/Userland/Libraries/LibConfig/Client.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Config { + +static RefPtr 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); +} + +} diff --git a/Userland/Libraries/LibConfig/Client.h b/Userland/Libraries/LibConfig/Client.h new file mode 100644 index 0000000000..6018145b03 --- /dev/null +++ b/Userland/Libraries/LibConfig/Client.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Config { + +class Client final + : public IPC::ServerConnection + , 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(*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); +} + +} diff --git a/Userland/Services/CMakeLists.txt b/Userland/Services/CMakeLists.txt index 0fcf7a417b..6e4237f7cd 100644 --- a/Userland/Services/CMakeLists.txt +++ b/Userland/Services/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(AudioServer) add_subdirectory(ChessEngine) add_subdirectory(Clipboard) +add_subdirectory(ConfigServer) add_subdirectory(CrashDaemon) add_subdirectory(DHCPClient) add_subdirectory(EchoServer) diff --git a/Userland/Services/ConfigServer/CMakeLists.txt b/Userland/Services/ConfigServer/CMakeLists.txt new file mode 100644 index 0000000000..febcc964ac --- /dev/null +++ b/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) diff --git a/Userland/Services/ConfigServer/ClientConnection.cpp b/Userland/Services/ConfigServer/ClientConnection.cpp new file mode 100644 index 0000000000..e55879c74c --- /dev/null +++ b/Userland/Services/ConfigServer/ClientConnection.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "ClientConnection.h" +#include +#include + +namespace ConfigServer { + +static HashMap> s_connections; + +ClientConnection::ClientConnection(NonnullRefPtr client_socket, int client_id) + : IPC::ClientConnection(*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 {}; + return Optional { 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 {}; + return Optional { 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 {}; + return Optional { 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); +} + +} diff --git a/Userland/Services/ConfigServer/ClientConnection.h b/Userland/Services/ConfigServer/ClientConnection.h new file mode 100644 index 0000000000..df41d9647e --- /dev/null +++ b/Userland/Services/ConfigServer/ClientConnection.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +#include +#include + +namespace ConfigServer { + +class ClientConnection final : public IPC::ClientConnection { + C_OBJECT(ClientConnection) +public: + ~ClientConnection() override; + + virtual void die() override; + +private: + explicit ClientConnection(NonnullRefPtr, 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; +}; + +} diff --git a/Userland/Services/ConfigServer/ConfigClient.ipc b/Userland/Services/ConfigServer/ConfigClient.ipc new file mode 100644 index 0000000000..bd0318b3ad --- /dev/null +++ b/Userland/Services/ConfigServer/ConfigClient.ipc @@ -0,0 +1,3 @@ +endpoint ConfigClient +{ +} diff --git a/Userland/Services/ConfigServer/ConfigServer.ipc b/Userland/Services/ConfigServer/ConfigServer.ipc new file mode 100644 index 0000000000..7c0b725870 --- /dev/null +++ b/Userland/Services/ConfigServer/ConfigServer.ipc @@ -0,0 +1,10 @@ +endpoint ConfigServer +{ + read_string_value(String domain, String group, String key) => (Optional value) + read_i32_value(String domain, String group, String key) => (Optional value) + read_bool_value(String domain, String group, String key) => (Optional 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) =| +} diff --git a/Userland/Services/ConfigServer/main.cpp b/Userland/Services/ConfigServer/main.cpp new file mode 100644 index 0000000000..6bf07cd020 --- /dev/null +++ b/Userland/Services/ConfigServer/main.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "ClientConnection.h" +#include +#include +#include + +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(client_socket.release_nonnull(), client_id); + }; + + return event_loop.exec(); +}