mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 18:27:35 +00:00
Services: Add InspectorServer to reverse the direction of Inspector
This service daemon will act as an intermediary between the Inspector program and the inspectable programs it wants to inspect. Programs can make themselves available for inspection by connecting to /tmp/portal/inspectables using the Core::EventLoop RPC protocol.
This commit is contained in:
parent
3c3b384c80
commit
3d3a5b431f
11 changed files with 332 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
13
Userland/Services/InspectorServer/CMakeLists.txt
Normal file
13
Userland/Services/InspectorServer/CMakeLists.txt
Normal file
|
@ -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)
|
87
Userland/Services/InspectorServer/ClientConnection.cpp
Normal file
87
Userland/Services/InspectorServer/ClientConnection.cpp
Normal file
|
@ -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 };
|
||||
}
|
||||
|
||||
}
|
34
Userland/Services/InspectorServer/ClientConnection.h
Normal file
34
Userland/Services/InspectorServer/ClientConnection.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
13
Userland/Services/InspectorServer/Forward.h
Normal file
13
Userland/Services/InspectorServer/Forward.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace SymbolServer {
|
||||
|
||||
class ClientConnection;
|
||||
|
||||
}
|
77
Userland/Services/InspectorServer/InspectableProcess.cpp
Normal file
77
Userland/Services/InspectorServer/InspectableProcess.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
30
Userland/Services/InspectorServer/InspectableProcess.h
Normal file
30
Userland/Services/InspectorServer/InspectableProcess.h
Normal file
|
@ -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;
|
||||
|
||||
}
|
4
Userland/Services/InspectorServer/InspectorClient.ipc
Normal file
4
Userland/Services/InspectorServer/InspectorClient.ipc
Normal file
|
@ -0,0 +1,4 @@
|
|||
endpoint InspectorClient
|
||||
{
|
||||
dummy() =|
|
||||
}
|
10
Userland/Services/InspectorServer/InspectorServer.ipc
Normal file
10
Userland/Services/InspectorServer/InspectorServer.ipc
Normal file
|
@ -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)
|
||||
|
||||
}
|
57
Userland/Services/InspectorServer/main.cpp
Normal file
57
Userland/Services/InspectorServer/main.cpp
Normal file
|
@ -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();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue