diff options
-rw-r--r-- | Base/etc/SystemServer.ini | 6 | ||||
-rw-r--r-- | Userland/Services/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Services/InspectorServer/CMakeLists.txt | 13 | ||||
-rw-r--r-- | Userland/Services/InspectorServer/ClientConnection.cpp | 87 | ||||
-rw-r--r-- | Userland/Services/InspectorServer/ClientConnection.h | 34 | ||||
-rw-r--r-- | Userland/Services/InspectorServer/Forward.h | 13 | ||||
-rw-r--r-- | Userland/Services/InspectorServer/InspectableProcess.cpp | 77 | ||||
-rw-r--r-- | Userland/Services/InspectorServer/InspectableProcess.h | 30 | ||||
-rw-r--r-- | Userland/Services/InspectorServer/InspectorClient.ipc | 4 | ||||
-rw-r--r-- | Userland/Services/InspectorServer/InspectorServer.ipc | 10 | ||||
-rw-r--r-- | Userland/Services/InspectorServer/main.cpp | 57 |
11 files changed, 332 insertions, 0 deletions
diff --git a/Base/etc/SystemServer.ini b/Base/etc/SystemServer.ini index 5095292b25..157bedda11 100644 --- a/Base/etc/SystemServer.ini +++ b/Base/etc/SystemServer.ini @@ -78,6 +78,12 @@ Priority=high KeepAlive=1 User=window +[InspectorServer] +Socket=/tmp/portal/inspector,/tmp/portal/inspectables +SocketPermissions=600,666 +KeepAlive=1 +User=anon + [Clipboard] Socket=/tmp/portal/clipboard SocketPermissions=660 diff --git a/Userland/Services/CMakeLists.txt b/Userland/Services/CMakeLists.txt index 8fa4fd1710..c126e2d09c 100644 --- a/Userland/Services/CMakeLists.txt +++ b/Userland/Services/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(DHCPClient) add_subdirectory(EchoServer) add_subdirectory(FileOperation) add_subdirectory(ImageDecoder) +add_subdirectory(InspectorServer) add_subdirectory(KeyboardPreferenceLoader) add_subdirectory(LaunchServer) add_subdirectory(LookupServer) diff --git a/Userland/Services/InspectorServer/CMakeLists.txt b/Userland/Services/InspectorServer/CMakeLists.txt new file mode 100644 index 0000000000..2adbe4a4d6 --- /dev/null +++ b/Userland/Services/InspectorServer/CMakeLists.txt @@ -0,0 +1,13 @@ +compile_ipc(InspectorServer.ipc InspectorServerEndpoint.h) +compile_ipc(InspectorClient.ipc InspectorClientEndpoint.h) + +set(SOURCES + ClientConnection.cpp + main.cpp + InspectableProcess.cpp + InspectorServerEndpoint.h + InspectorClientEndpoint.h +) + +serenity_bin(InspectorServer) +target_link_libraries(InspectorServer LibIPC) diff --git a/Userland/Services/InspectorServer/ClientConnection.cpp b/Userland/Services/InspectorServer/ClientConnection.cpp new file mode 100644 index 0000000000..0fccb06c84 --- /dev/null +++ b/Userland/Services/InspectorServer/ClientConnection.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "InspectableProcess.h" +#include <InspectorServer/ClientConnection.h> + +namespace InspectorServer { + +static HashMap<int, RefPtr<ClientConnection>> s_connections; + +ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> socket, int client_id) + : IPC::ClientConnection<InspectorClientEndpoint, InspectorServerEndpoint>(*this, move(socket), client_id) +{ + s_connections.set(client_id, *this); +} + +ClientConnection::~ClientConnection() +{ +} + +void ClientConnection::die() +{ + s_connections.remove(client_id()); +} + +void ClientConnection::greet() +{ +} + +Messages::InspectorServer::GetAllObjectsResponse ClientConnection::get_all_objects(pid_t pid) +{ + auto process = InspectableProcess::from_pid(pid); + if (!process) + return { String {} }; + + JsonObject request; + request.set("type", "GetAllObjects"); + process->send_request(request); + auto response = process->wait_for_response(); + return { response }; +} + +Messages::InspectorServer::SetInspectedObjectResponse ClientConnection::set_inspected_object(pid_t pid, u64 object_id) +{ + auto process = InspectableProcess::from_pid(pid); + if (!process) + return { false }; + + JsonObject request; + request.set("type", "SetInspectedObject"); + request.set("address", object_id); + process->send_request(request); + return { true }; +} + +Messages::InspectorServer::SetObjectPropertyResponse ClientConnection::set_object_property(pid_t pid, u64 object_id, String const& name, String const& value) +{ + auto process = InspectableProcess::from_pid(pid); + if (!process) + return { false }; + + JsonObject request; + request.set("type", "SetProperty"); + request.set("address", object_id); + request.set("name", name); + request.set("value", value); + process->send_request(request); + return { true }; +} + +Messages::InspectorServer::IdentifyResponse ClientConnection::identify(pid_t pid) +{ + auto process = InspectableProcess::from_pid(pid); + if (!process) + return { String {} }; + + JsonObject request; + request.set("type", "Identify"); + process->send_request(request); + auto response = process->wait_for_response(); + return { response }; +} + +} diff --git a/Userland/Services/InspectorServer/ClientConnection.h b/Userland/Services/InspectorServer/ClientConnection.h new file mode 100644 index 0000000000..812f725081 --- /dev/null +++ b/Userland/Services/InspectorServer/ClientConnection.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/HashMap.h> +#include <InspectorServer/InspectorClientEndpoint.h> +#include <InspectorServer/InspectorServerEndpoint.h> +#include <LibIPC/ClientConnection.h> + +namespace InspectorServer { + +class ClientConnection final + : public IPC::ClientConnection<InspectorClientEndpoint, InspectorServerEndpoint> { + C_OBJECT(ClientConnection); + +public: + explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id); + ~ClientConnection() override; + + virtual void die() override; + +private: + virtual void greet() override; + virtual Messages::InspectorServer::GetAllObjectsResponse get_all_objects(pid_t) override; + virtual Messages::InspectorServer::SetInspectedObjectResponse set_inspected_object(pid_t, u64 object_id) override; + virtual Messages::InspectorServer::SetObjectPropertyResponse set_object_property(pid_t, u64 object_id, String const& name, String const& value) override; + virtual Messages::InspectorServer::IdentifyResponse identify(pid_t) override; +}; + +} diff --git a/Userland/Services/InspectorServer/Forward.h b/Userland/Services/InspectorServer/Forward.h new file mode 100644 index 0000000000..97cdfba43c --- /dev/null +++ b/Userland/Services/InspectorServer/Forward.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +namespace SymbolServer { + +class ClientConnection; + +} diff --git a/Userland/Services/InspectorServer/InspectableProcess.cpp b/Userland/Services/InspectorServer/InspectableProcess.cpp new file mode 100644 index 0000000000..a064acd415 --- /dev/null +++ b/Userland/Services/InspectorServer/InspectableProcess.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "InspectableProcess.h" + +namespace InspectorServer { + +HashMap<pid_t, NonnullOwnPtr<InspectableProcess>> g_processes; + +InspectableProcess* InspectableProcess::from_pid(pid_t pid) +{ + return g_processes.get(pid).value_or(nullptr); +} + +InspectableProcess::InspectableProcess(pid_t pid, NonnullRefPtr<Core::LocalSocket> socket) + : m_pid(pid) + , m_socket(move(socket)) +{ + m_socket->set_blocking(true); + m_socket->on_ready_to_read = [this] { + auto buffer = m_socket->read(1); + if (m_socket->eof()) { + g_processes.remove(m_pid); + return; + } + }; +} + +InspectableProcess::~InspectableProcess() +{ +} + +String InspectableProcess::wait_for_response() +{ + if (m_socket->eof()) { + dbgln("InspectableProcess disconnected: PID {}", m_pid); + m_socket->close(); + return {}; + } + + u32 length {}; + auto nread = m_socket->read((u8*)&length, sizeof(length)); + if (nread != sizeof(length)) { + dbgln("InspectableProcess got malformed data: PID {}", m_pid); + m_socket->close(); + return {}; + } + + ByteBuffer data; + size_t remaining_bytes = length; + + while (remaining_bytes) { + auto packet = m_socket->read(remaining_bytes); + if (packet.size() == 0) + break; + data.append(packet.data(), packet.size()); + remaining_bytes -= packet.size(); + } + + VERIFY(data.size() == length); + dbgln("Got data size {} and read that many bytes", length); + + return String::copy(data); +} + +void InspectableProcess::send_request(JsonObject const& request) +{ + auto serialized = request.to_string(); + auto length = serialized.length(); + m_socket->write((u8 const*)&length, sizeof(length)); + m_socket->write(serialized); +} + +} diff --git a/Userland/Services/InspectorServer/InspectableProcess.h b/Userland/Services/InspectorServer/InspectableProcess.h new file mode 100644 index 0000000000..afefa0ba87 --- /dev/null +++ b/Userland/Services/InspectorServer/InspectableProcess.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibCore/LocalSocket.h> + +namespace InspectorServer { + +class InspectableProcess { +public: + InspectableProcess(pid_t, NonnullRefPtr<Core::LocalSocket>); + ~InspectableProcess(); + + void send_request(JsonObject const& request); + String wait_for_response(); + + static InspectableProcess* from_pid(pid_t); + +private: + pid_t m_pid { 0 }; + NonnullRefPtr<Core::LocalSocket> m_socket; +}; + +extern HashMap<pid_t, NonnullOwnPtr<InspectorServer::InspectableProcess>> g_processes; + +} diff --git a/Userland/Services/InspectorServer/InspectorClient.ipc b/Userland/Services/InspectorServer/InspectorClient.ipc new file mode 100644 index 0000000000..bd0021d632 --- /dev/null +++ b/Userland/Services/InspectorServer/InspectorClient.ipc @@ -0,0 +1,4 @@ +endpoint InspectorClient +{ + dummy() =| +} diff --git a/Userland/Services/InspectorServer/InspectorServer.ipc b/Userland/Services/InspectorServer/InspectorServer.ipc new file mode 100644 index 0000000000..ed1696274d --- /dev/null +++ b/Userland/Services/InspectorServer/InspectorServer.ipc @@ -0,0 +1,10 @@ +endpoint InspectorServer +{ + greet() => () + + get_all_objects(i32 pid) => (String json) + set_inspected_object(i32 pid, u64 object_id) => (bool success) + set_object_property(i32 pid, u64 object_id, String name, String value) => (bool success) + identify(i32 pid) => (String json) + +} diff --git a/Userland/Services/InspectorServer/main.cpp b/Userland/Services/InspectorServer/main.cpp new file mode 100644 index 0000000000..de1e7d1704 --- /dev/null +++ b/Userland/Services/InspectorServer/main.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "InspectableProcess.h" +#include <InspectorServer/ClientConnection.h> +#include <LibCore/EventLoop.h> +#include <LibCore/LocalServer.h> +#include <LibIPC/ClientConnection.h> + +int main(int, char**) +{ + Core::EventLoop event_loop(Core::EventLoop::MakeInspectable::No); + auto server = Core::LocalServer::construct(); + + if (pledge("stdio unix accept", nullptr) < 0) { + perror("pledge"); + return 1; + } + + bool ok = server->take_over_from_system_server("/tmp/portal/inspector"); + VERIFY(ok); + server->on_ready_to_accept = [&] { + auto client_socket = server->accept(); + if (!client_socket) { + dbgln("accept failed."); + return; + } + static int s_next_client_id = 0; + int client_id = ++s_next_client_id; + IPC::new_client_connection<InspectorServer::ClientConnection>(client_socket.release_nonnull(), client_id); + }; + + auto inspectables_server = Core::LocalServer::construct(); + if (!inspectables_server->take_over_from_system_server("/tmp/portal/inspectables")) + VERIFY_NOT_REACHED(); + + inspectables_server->on_ready_to_accept = [&] { + auto client_socket = inspectables_server->accept(); + if (!client_socket) { + dbgln("backdoor accept failed."); + return; + } + struct ucred creds = {}; + socklen_t creds_size = sizeof(creds); + if (getsockopt(client_socket->fd(), SOL_SOCKET, SO_PEERCRED, &creds, &creds_size) < 0) { + dbgln("SO_PEERCRED failed"); + return; + } + + InspectorServer::g_processes.set(creds.pid, make<InspectorServer::InspectableProcess>(creds.pid, client_socket.release_nonnull())); + }; + + return event_loop.exec(); +} |