Userland: Remove "Inspector" program and related utilities

This program has never lived up to its original idea, and has been
broken for years (property editing, etc). It's also unmaintained and
off-by-default since forever.

At this point, Inspector is more of a maintenance burden than a feature,
so this commit removes it from the system, along with the mechanism in
Core::EventLoop that enables it.

If we decide we want the feature again in the future, it can be
reimplemented better. :^)
This commit is contained in:
Andreas Kling 2023-04-24 10:31:49 +02:00
parent 203e84c378
commit c756e021a7
Notes: sideshowbarker 2024-07-17 09:47:09 +09:00
39 changed files with 11 additions and 1386 deletions

View File

@ -11,7 +11,6 @@ alias iv=ImageViewer
alias pi=Piano
alias calc=Calculator
alias calendar=Calendar
alias ins=Inspector
alias sp=SoundPlayer
alias help=Help
alias br=Browser

View File

@ -11,7 +11,6 @@ alias iv=ImageViewer
alias pi=Piano
alias calc=Calculator
alias calendar=Calendar
alias ins=Inspector
alias sp=SoundPlayer
alias help=Help
alias br=Browser

View File

@ -57,11 +57,6 @@ Lazy=true
Priority=low
KeepAlive=true
[InspectorServer]
Socket=/tmp/session/%sid/portal/inspector,/tmp/session/%sid/portal/inspectables
SocketPermissions=600,666
KeepAlive=true
[AudioServer]
Socket=/tmp/session/%sid/portal/audio
Priority=high

View File

@ -23,6 +23,5 @@ Holding Ctrl accelerates mouse wheel interaction with sliders and spin boxes.
Selected files can be renamed by pressing F2.
Assistant can help you quickly find files and applications by pressing Super+Space.
Holding Ctrl while activating a menu item prevents that menu from closing.
Applications can be viewed with Inspector by including 'MAKE_INSPECTABLE=1' in their environment.
Pressing Ctrl+Shift+A on a focused widget or application activates the command palette, a searchable list of available actions.
Workspaces can be switched by pressing Ctrl+Alt+Arrows. Shift brings the active window along.

View File

@ -1,4 +0,0 @@
[App]
Name=Inspector
Executable=/bin/Inspector
Category=Development

View File

@ -26,7 +26,6 @@ Note that many applications can be disabled at SerenityOS build time if desired.
- [GML Playground](help://man/1/Applications/GMLPlayground)
- [Hex Editor](help://man/1/Applications/HexEditor)
- [Image Viewer](help://man/1/Applications/ImageViewer)
- [Inspector](help://man/1/Applications/Inspector)
- [Magnifier](help://man/1/Applications/Magnifier)
- [Mail](help://man/1/Applications/Mail)
- [Mouse Settings](help://man/1/Applications/MouseSettings)

View File

@ -1,31 +0,0 @@
## Name
![Icon](/res/icons/16x16/app-inspector.png) Inspector - Serenity process inspector
[Open](file:///bin/Inspector)
## Synopsis
```**sh
$ Inspector [pid]
```
## Arguments
* `pid`: Process ID to inspect
## Description
Inspector facilitates process inspection via RPC.
To inspect a process, it must have `MAKE_INSPECTABLE=1` in its environment,
and it must have previously allowed the
[`accept`(2)](help://man/2/accept) system call with
[`pledge`(2)](help://man/2/pledge) to allow inspection
via UNIX socket.
## Examples
```sh
$ Inspector $(pidof Shell)
```

View File

@ -41,8 +41,6 @@ $ watch -n 1 -- bt 124
## See also
* [`Inspector`(1)](help://man/1/Applications/Inspector)
* [`Profiler`(1)](help://man/1/Applications/Profiler)
* [`watch`(1)](help://man/1/watch)

View File

@ -52,11 +52,6 @@ This is a list of useful tips and tricks to help you make the most out of Sereni
## Development
* Supplying `# profile` with a process identifier (PID) of `-1` as root enables systemwide profiling.
* Make applications inspectable by including `MAKE_INSPECTABLE=1` in their environment. Inspectable processes can be examined with [Inspector](help://man/1/Applications/Inspector) via remote procedure calls (RPCs). For example, to make the Eyes application inspectable, enter the following Shell commands:
```sh
$ export MAKE_INSPECTABLE=1
$ Eyes &
```
## See also
* [Keyboard Shortcuts](help://man/7/KeyboardShortcuts)
* [Keyboard Shortcuts](help://man/7/KeyboardShortcuts)

View File

@ -300,7 +300,7 @@ index 0000000000000000000000000000000000000000..8a5abd58967dee12619c45d0e0d6cfc8
+
+ // We need to create our audio connection and event loop here, in order to register them with SDL's audio thread
+ if (!h->event_loop)
+ h->event_loop = make<Core::EventLoop>(Core::EventLoop::MakeInspectable::No);
+ h->event_loop = make<Core::EventLoop>();
+ if (!h->client)
+ h->client = MUST(Audio::ConnectionToServer::try_create());
+

View File

@ -1,7 +1,7 @@
serenity_component(
SystemMonitor
REQUIRED
TARGETS SystemMonitor Profiler Inspector
TARGETS SystemMonitor Profiler
)
compile_gml(SystemMonitor.gml SystemMonitorGML.h system_monitor_gml)

View File

@ -263,7 +263,6 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
return result.release_error();
TRY(Core::System::unveil("/bin/Profiler", "rx"));
TRY(Core::System::unveil("/bin/Inspector", "rx"));
TRY(Core::System::unveil("/bin/HackStudio", "rx"));
TRY(Core::System::unveil(nullptr, nullptr));

View File

@ -15,7 +15,7 @@
ErrorOr<int> serenity_main(Main::Arguments arguments)
{
TRY(Core::System::pledge("stdio recvfd sendfd rpath unix thread"));
auto app = TRY(GUI::Application::try_create(arguments, Core::EventLoop::MakeInspectable::Yes));
auto app = TRY(GUI::Application::try_create(arguments));
TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw"));
TRY(Core::System::unveil("/res", "r"));

View File

@ -1,5 +1,4 @@
add_subdirectory(GMLPlayground)
add_subdirectory(Inspector)
add_subdirectory(Profiler)
add_subdirectory(HackStudio)
add_subdirectory(SQLStudio)

View File

@ -1,16 +0,0 @@
serenity_component(
Inspector
RECOMMENDED
TARGETS Inspector
)
set(SOURCES
main.cpp
RemoteObject.cpp
RemoteObjectGraphModel.cpp
RemoteObjectPropertyModel.cpp
RemoteProcess.cpp
)
serenity_app(Inspector ICON app-inspector)
target_link_libraries(Inspector PRIVATE LibCore LibDesktop LibGfx LibGUI LibIPC LibMain)

View File

@ -1,30 +0,0 @@
/*
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <InspectorServer/InspectorClientEndpoint.h>
#include <InspectorServer/InspectorServerEndpoint.h>
#include <LibIPC/ConnectionToServer.h>
namespace Inspector {
class InspectorServerClient final
: public IPC::ConnectionToServer<InspectorClientEndpoint, InspectorServerEndpoint>
, public InspectorClientEndpoint {
IPC_CLIENT_CONNECTION(InspectorServerClient, "/tmp/session/%sid/portal/inspector"sv)
public:
virtual ~InspectorServerClient() override = default;
private:
InspectorServerClient(NonnullOwnPtr<Core::LocalSocket> socket)
: IPC::ConnectionToServer<InspectorClientEndpoint, InspectorServerEndpoint>(*this, move(socket))
{
}
};
}

View File

@ -1,23 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "RemoteObject.h"
#include "RemoteObjectPropertyModel.h"
namespace Inspector {
RemoteObject::RemoteObject()
: m_property_model(RemoteObjectPropertyModel::create(*this))
{
}
RemoteObjectPropertyModel& RemoteObject::property_model()
{
m_property_model->invalidate();
return *m_property_model;
}
}

View File

@ -1,35 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/DeprecatedString.h>
#include <AK/JsonObject.h>
namespace Inspector {
class RemoteObjectPropertyModel;
class RemoteObject {
public:
RemoteObject();
RemoteObjectPropertyModel& property_model();
RemoteObject* parent { nullptr };
Vector<NonnullOwnPtr<RemoteObject>> children;
FlatPtr address { 0 };
FlatPtr parent_address { 0 };
DeprecatedString class_name;
DeprecatedString name;
JsonObject json;
NonnullRefPtr<RemoteObjectPropertyModel> m_property_model;
};
}

View File

@ -1,95 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "RemoteObjectGraphModel.h"
#include "RemoteObject.h"
#include "RemoteProcess.h"
#include <AK/JsonValue.h>
#include <LibGUI/Application.h>
#include <stdio.h>
namespace Inspector {
RemoteObjectGraphModel::RemoteObjectGraphModel(RemoteProcess& process)
: m_process(process)
{
m_object_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png"sv).release_value_but_fixme_should_propagate_errors());
m_window_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/window.png"sv).release_value_but_fixme_should_propagate_errors());
m_layout_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/layout.png"sv).release_value_but_fixme_should_propagate_errors());
m_timer_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/timer.png"sv).release_value_but_fixme_should_propagate_errors());
}
GUI::ModelIndex RemoteObjectGraphModel::index(int row, int column, const GUI::ModelIndex& parent) const
{
if (!parent.is_valid()) {
if (m_process.roots().is_empty())
return {};
return create_index(row, column, &m_process.roots().at(row));
}
auto& remote_parent = *static_cast<RemoteObject*>(parent.internal_data());
return create_index(row, column, &remote_parent.children.at(row));
}
GUI::ModelIndex RemoteObjectGraphModel::parent_index(const GUI::ModelIndex& index) const
{
if (!index.is_valid())
return {};
auto& remote_object = *static_cast<RemoteObject*>(index.internal_data());
if (!remote_object.parent)
return {};
// NOTE: If the parent has no parent, it's a root, so we have to look among the remote roots.
if (!remote_object.parent->parent) {
for (size_t row = 0; row < m_process.roots().size(); ++row) {
if (m_process.roots()[row] == remote_object.parent)
return create_index(row, 0, remote_object.parent);
}
VERIFY_NOT_REACHED();
return {};
}
for (size_t row = 0; row < remote_object.parent->parent->children.size(); ++row) {
if (remote_object.parent->parent->children[row] == remote_object.parent)
return create_index(row, 0, remote_object.parent);
}
VERIFY_NOT_REACHED();
return {};
}
int RemoteObjectGraphModel::row_count(const GUI::ModelIndex& index) const
{
if (!index.is_valid())
return m_process.roots().size();
auto& remote_object = *static_cast<RemoteObject*>(index.internal_data());
return remote_object.children.size();
}
int RemoteObjectGraphModel::column_count(const GUI::ModelIndex&) const
{
return 1;
}
GUI::Variant RemoteObjectGraphModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
{
auto* remote_object = static_cast<RemoteObject*>(index.internal_data());
if (role == GUI::ModelRole::Icon) {
if (remote_object->class_name == "Window")
return m_window_icon;
if (remote_object->class_name == "Timer")
return m_timer_icon;
if (remote_object->class_name.ends_with("Layout"sv))
return m_layout_icon;
return m_object_icon;
}
if (role == GUI::ModelRole::Display)
return DeprecatedString::formatted("{}({:p})", remote_object->class_name, remote_object->address);
return {};
}
}

View File

@ -1,44 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <LibGUI/Model.h>
namespace Inspector {
class RemoteProcess;
class RemoteObjectGraphModel final : public GUI::Model {
public:
static NonnullRefPtr<RemoteObjectGraphModel> create(RemoteProcess& process)
{
return adopt_ref(*new RemoteObjectGraphModel(process));
}
virtual ~RemoteObjectGraphModel() override = default;
virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override;
virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override;
private:
explicit RemoteObjectGraphModel(RemoteProcess&);
RemoteProcess& m_process;
GUI::Icon m_object_icon;
GUI::Icon m_window_icon;
GUI::Icon m_layout_icon;
GUI::Icon m_timer_icon;
};
}

View File

@ -1,216 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "RemoteObjectPropertyModel.h"
#include "RemoteObject.h"
#include "RemoteProcess.h"
namespace Inspector {
RemoteObjectPropertyModel::RemoteObjectPropertyModel(RemoteObject& object)
: m_object(object)
{
}
int RemoteObjectPropertyModel::row_count(const GUI::ModelIndex& index) const
{
Function<int(JsonValue const&)> do_count = [&](JsonValue const& value) {
if (value.is_array())
return value.as_array().size();
else if (value.is_object())
return value.as_object().size();
return (size_t)0;
};
if (index.is_valid()) {
auto* path = static_cast<JsonPath const*>(index.internal_data());
return do_count(path->resolve(m_object.json));
} else {
return do_count(m_object.json);
}
}
DeprecatedString RemoteObjectPropertyModel::column_name(int column) const
{
switch (column) {
case Column::Name:
return "Name";
case Column::Value:
return "Value";
}
VERIFY_NOT_REACHED();
}
GUI::Variant RemoteObjectPropertyModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
{
auto* path = static_cast<JsonPath const*>(index.internal_data());
if (!path)
return {};
if (role == GUI::ModelRole::Display) {
switch (index.column()) {
case Column::Name:
return path->last().to_deprecated_string();
case Column::Value: {
auto data = path->resolve(m_object.json);
if (data.is_array())
return DeprecatedString::formatted("<Array with {} element{}", data.as_array().size(), data.as_array().size() == 1 ? ">" : "s>");
if (data.is_object())
return DeprecatedString::formatted("<Object with {} entr{}", data.as_object().size(), data.as_object().size() == 1 ? "y>" : "ies>");
return data;
}
}
}
return {};
}
void RemoteObjectPropertyModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& new_value)
{
if (!index.is_valid())
return;
auto* path = static_cast<JsonPath const*>(index.internal_data());
if (path->size() != 1)
return;
FlatPtr address = m_object.address;
RemoteProcess::the().set_property(address, path->first().to_deprecated_string(), new_value.to_deprecated_string());
did_update();
}
GUI::ModelIndex RemoteObjectPropertyModel::index(int row, int column, const GUI::ModelIndex& parent) const
{
auto const& parent_path = parent.is_valid() ? *static_cast<JsonPath const*>(parent.internal_data()) : JsonPath {};
auto nth_child = [&](int n, JsonValue const& value) -> JsonPath const* {
auto path = make<JsonPath>();
path->extend(parent_path);
int row_index = n;
if (value.is_object()) {
DeprecatedString property_name;
auto& object = value.as_object();
object.for_each_member([&](auto& name, auto&) {
if (row_index > 0) {
--row_index;
} else if (row_index == 0) {
property_name = name;
--row_index;
}
});
if (property_name.is_null())
return nullptr;
path->append({ property_name });
m_paths.append(move(path));
} else if (value.is_array()) {
path->append(JsonPathElement { (size_t)n });
m_paths.append(move(path));
} else {
return nullptr;
}
return m_paths.last();
};
if (!parent.is_valid()) {
if (m_object.json.is_empty())
return {};
}
auto index_path = cached_path_at(row, parent_path);
if (!index_path)
index_path = nth_child(row, parent_path.resolve(m_object.json));
if (!index_path)
return {};
return create_index(row, column, index_path);
}
GUI::ModelIndex RemoteObjectPropertyModel::parent_index(const GUI::ModelIndex& index) const
{
if (!index.is_valid())
return index;
auto path = *static_cast<JsonPath const*>(index.internal_data());
if (path.is_empty())
return {};
path.take_last();
if (path.is_empty())
return {};
auto* cpath = find_cached_path(path);
if (cpath) {
int index_in_parent = 0;
if (cpath->last().kind() == JsonPathElement::Kind::Index)
index_in_parent = cpath->last().index();
else if (cpath->last().kind() == JsonPathElement::Kind::Key) {
auto path_copy = path;
auto last = path_copy.take_last();
bool found = false;
path_copy.resolve(m_object.json).as_object().for_each_member([&](auto& name, auto&) {
if (!found) {
if (last.key() == name)
found = true;
else
index_in_parent++;
}
});
}
return create_index(index_in_parent, 0, cpath);
}
dbgln("No cached path found for path {}", path.to_deprecated_string());
return {};
}
JsonPath const* RemoteObjectPropertyModel::cached_path_at(int n, Vector<JsonPathElement> const& prefix) const
{
// FIXME: ModelIndex wants a void*, so we have to keep these
// indices alive, but allocating a new path every time
// we're asked for an index is silly, so we have to look for existing ones first.
JsonPath const* index_path = nullptr;
int row_index = n;
for (auto& path : m_paths) {
if (path->size() != prefix.size() + 1)
continue;
for (size_t i = 0; i < prefix.size(); ++i) {
if ((*path)[i] != prefix[i])
goto do_continue;
}
if (row_index == 0) {
index_path = path;
break;
}
--row_index;
do_continue:;
}
return index_path;
};
JsonPath const* RemoteObjectPropertyModel::find_cached_path(Vector<JsonPathElement> const& path) const
{
for (auto& cpath : m_paths) {
if (cpath->size() != path.size())
continue;
for (size_t i = 0; i < cpath->size(); ++i) {
if ((*cpath)[i] != path[i])
goto do_continue;
}
return cpath;
do_continue:;
}
return nullptr;
}
}

View File

@ -1,50 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/JsonPath.h>
#include <AK/JsonValue.h>
#include <LibGUI/Model.h>
namespace Inspector {
class RemoteObject;
class RemoteObjectPropertyModel final : public GUI::Model {
public:
virtual ~RemoteObjectPropertyModel() override { }
static NonnullRefPtr<RemoteObjectPropertyModel> create(RemoteObject& object)
{
return adopt_ref(*new RemoteObjectPropertyModel(object));
}
enum Column {
Name,
Value,
__Count,
};
virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return Column::__Count; }
virtual DeprecatedString column_name(int) const override;
virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
virtual void set_data(const GUI::ModelIndex&, const GUI::Variant&) override;
virtual bool is_editable(const GUI::ModelIndex& index) const override { return index.column() == Column::Value; }
virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override;
virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override;
private:
explicit RemoteObjectPropertyModel(RemoteObject&);
JsonPath const* cached_path_at(int n, Vector<JsonPathElement> const& prefix) const;
JsonPath const* find_cached_path(Vector<JsonPathElement> const& path) const;
RemoteObject& m_object;
mutable Vector<NonnullOwnPtr<JsonPath>> m_paths;
};
}

View File

@ -1,108 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "RemoteProcess.h"
#include "RemoteObject.h"
#include "RemoteObjectGraphModel.h"
#include "RemoteObjectPropertyModel.h"
namespace Inspector {
RemoteProcess* s_the;
RemoteProcess& RemoteProcess::the()
{
return *s_the;
}
RemoteProcess::RemoteProcess(pid_t pid)
: m_pid(pid)
, m_object_graph_model(RemoteObjectGraphModel::create(*this))
{
s_the = this;
m_client = InspectorServerClient::try_create().release_value_but_fixme_should_propagate_errors();
}
void RemoteProcess::handle_identify_response(JsonObject const& response)
{
int pid = response.get_i32("pid"sv).value_or(0);
VERIFY(pid == m_pid);
m_process_name = response.get_deprecated_string("process_name"sv).value_or({});
if (on_update)
on_update();
}
void RemoteProcess::handle_get_all_objects_response(JsonObject const& response)
{
// FIXME: It would be good if we didn't have to make a local copy of the array value here!
auto& object_array = response.get_array("objects"sv).value();
Vector<NonnullOwnPtr<RemoteObject>> remote_objects;
HashMap<FlatPtr, RemoteObject*> objects_by_address;
for (auto& value : object_array.values()) {
VERIFY(value.is_object());
auto& object = value.as_object();
auto remote_object = make<RemoteObject>();
remote_object->address = object.get_addr("address"sv).value_or(0);
remote_object->parent_address = object.get_addr("parent"sv).value_or(0);
remote_object->name = object.get_deprecated_string("name"sv).value_or({});
remote_object->class_name = object.get_deprecated_string("class_name"sv).value_or({});
remote_object->json = object;
objects_by_address.set(remote_object->address, remote_object);
remote_objects.append(move(remote_object));
}
for (size_t i = 0; i < remote_objects.size(); ++i) {
auto& remote_object = remote_objects[i];
auto* parent = objects_by_address.get(remote_object->parent_address).value_or(nullptr);
if (!parent) {
m_roots.append(move(remote_object));
} else {
remote_object->parent = parent;
parent->children.append(move(remote_object));
}
}
m_object_graph_model->invalidate();
if (on_update)
on_update();
}
void RemoteProcess::set_inspected_object(FlatPtr address)
{
m_client->async_set_inspected_object(m_pid, address);
}
void RemoteProcess::set_property(FlatPtr object, StringView name, JsonValue const& value)
{
m_client->async_set_object_property(m_pid, object, name, value.to_deprecated_string());
}
bool RemoteProcess::is_inspectable()
{
return m_client->is_inspectable(m_pid);
}
void RemoteProcess::update()
{
{
auto raw_json = m_client->identify(m_pid);
auto json = JsonValue::from_string(raw_json);
handle_identify_response(json.value().as_object());
}
{
auto raw_json = m_client->get_all_objects(m_pid);
auto json = JsonValue::from_string(raw_json);
handle_get_all_objects_response(json.value().as_object());
}
}
}

View File

@ -1,48 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "InspectorServerClient.h"
namespace Inspector {
class RemoteObjectGraphModel;
class RemoteObject;
class RemoteProcess {
public:
static RemoteProcess& the();
explicit RemoteProcess(pid_t);
void update();
pid_t pid() const { return m_pid; }
DeprecatedString const& process_name() const { return m_process_name; }
RemoteObjectGraphModel& object_graph_model() { return *m_object_graph_model; }
Vector<NonnullOwnPtr<RemoteObject>> const& roots() const { return m_roots; }
void set_inspected_object(FlatPtr);
void set_property(FlatPtr object, StringView name, JsonValue const& value);
bool is_inspectable();
Function<void()> on_update;
private:
void handle_get_all_objects_response(JsonObject const&);
void handle_identify_response(JsonObject const&);
pid_t m_pid { -1 };
DeprecatedString m_process_name;
NonnullRefPtr<RemoteObjectGraphModel> m_object_graph_model;
Vector<NonnullOwnPtr<RemoteObject>> m_roots;
RefPtr<InspectorServerClient> m_client;
};
}

View File

@ -1,155 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "RemoteObject.h"
#include "RemoteObjectGraphModel.h"
#include "RemoteObjectPropertyModel.h"
#include "RemoteProcess.h"
#include <AK/URL.h>
#include <LibCore/System.h>
#include <LibDesktop/Launcher.h>
#include <LibGUI/Application.h>
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Clipboard.h>
#include <LibGUI/Menu.h>
#include <LibGUI/Menubar.h>
#include <LibGUI/MessageBox.h>
#include <LibGUI/ModelEditingDelegate.h>
#include <LibGUI/ProcessChooser.h>
#include <LibGUI/Splitter.h>
#include <LibGUI/TreeView.h>
#include <LibGUI/Window.h>
#include <LibMain/Main.h>
#include <stdio.h>
#include <unistd.h>
using namespace Inspector;
[[noreturn]] static void print_usage_and_exit()
{
outln("usage: Inspector <pid>");
exit(0);
}
ErrorOr<int> serenity_main(Main::Arguments arguments)
{
TRY(Core::System::pledge("stdio recvfd sendfd rpath unix"));
TRY(Core::System::unveil("/res", "r"));
TRY(Core::System::unveil("/bin", "r"));
TRY(Core::System::unveil("/tmp", "rwc"));
TRY(Core::System::unveil("/sys/kernel/processes", "r"));
TRY(Core::System::unveil("/etc/passwd", "r"));
TRY(Core::System::unveil(nullptr, nullptr));
bool gui_mode = arguments.argc != 2;
pid_t pid;
auto app = TRY(GUI::Application::try_create(arguments));
auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-inspector"sv));
if (gui_mode) {
choose_pid:
auto process_chooser = TRY(GUI::ProcessChooser::try_create("Inspector"sv, "Inspect"_short_string, app_icon.bitmap_for_size(16)));
if (process_chooser->exec() == GUI::Dialog::ExecResult::Cancel)
return 0;
pid = process_chooser->pid();
} else {
auto pid_opt = DeprecatedString(arguments.strings[1]).to_int();
if (!pid_opt.has_value())
print_usage_and_exit();
pid = pid_opt.value();
}
auto window = TRY(GUI::Window::try_create());
if (pid == getpid()) {
GUI::MessageBox::show(window, "Cannot inspect Inspector itself!"sv, "Error"sv, GUI::MessageBox::Type::Error);
if (gui_mode)
goto choose_pid;
else
return 1;
}
RemoteProcess remote_process(pid);
if (!remote_process.is_inspectable()) {
GUI::MessageBox::show(window, DeprecatedString::formatted("Process pid={} is not inspectable", remote_process.pid()), "Error"sv, GUI::MessageBox::Type::Error);
if (gui_mode) {
goto choose_pid;
} else {
return 1;
}
}
TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man1/Applications/Inspector.md") }));
TRY(Desktop::Launcher::seal_allowlist());
window->set_title("Inspector");
window->resize(685, 500);
window->set_icon(app_icon.bitmap_for_size(16));
auto& file_menu = window->add_menu("&File"_short_string);
file_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); }));
auto& help_menu = window->add_menu("&Help"_short_string);
help_menu.add_action(GUI::CommonActions::make_command_palette_action(window));
help_menu.add_action(GUI::CommonActions::make_help_action([](auto&) {
Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man1/Applications/Inspector.md"), "/bin/Help");
}));
help_menu.add_action(GUI::CommonActions::make_about_action("Inspector", app_icon, window));
auto widget = TRY(window->set_main_widget<GUI::Widget>());
widget->set_fill_with_background_color(true);
widget->set_layout<GUI::VerticalBoxLayout>();
auto& splitter = widget->add<GUI::HorizontalSplitter>();
remote_process.on_update = [&] {
if (!remote_process.process_name().is_null())
window->set_title(DeprecatedString::formatted("{} ({}) - Inspector", remote_process.process_name(), remote_process.pid()));
};
auto& tree_view = splitter.add<GUI::TreeView>();
tree_view.set_model(remote_process.object_graph_model());
tree_view.set_activates_on_selection(true);
tree_view.set_preferred_width(286);
auto& properties_tree_view = splitter.add<GUI::TreeView>();
properties_tree_view.set_should_fill_selected_rows(true);
properties_tree_view.set_editable(true);
properties_tree_view.aid_create_editing_delegate = [](auto&) {
return make<GUI::StringModelEditingDelegate>();
};
tree_view.on_activation = [&](auto& index) {
auto* remote_object = static_cast<RemoteObject*>(index.internal_data());
properties_tree_view.set_model(remote_object->property_model());
remote_process.set_inspected_object(remote_object->address);
};
auto properties_tree_view_context_menu = TRY(GUI::Menu::try_create(TRY("Properties Tree View"_string)));
auto copy_bitmap = Gfx::Bitmap::load_from_file("/res/icons/16x16/edit-copy.png"sv).release_value_but_fixme_should_propagate_errors();
auto copy_property_name_action = GUI::Action::create("Copy Property Name", copy_bitmap, [&](auto&) {
GUI::Clipboard::the().set_plain_text(properties_tree_view.selection().first().data().to_deprecated_string());
});
auto copy_property_value_action = GUI::Action::create("Copy Property Value", copy_bitmap, [&](auto&) {
GUI::Clipboard::the().set_plain_text(properties_tree_view.selection().first().sibling_at_column(1).data().to_deprecated_string());
});
properties_tree_view_context_menu->add_action(copy_property_name_action);
properties_tree_view_context_menu->add_action(copy_property_value_action);
properties_tree_view.on_context_menu_request = [&](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) {
if (index.is_valid()) {
properties_tree_view_context_menu->popup(event.screen_position());
}
};
window->show();
remote_process.update();
TRY(Core::System::pledge("stdio recvfd sendfd rpath"));
return app->exec();
}

View File

@ -48,10 +48,6 @@ extern bool s_global_initializers_ran;
namespace Core {
class InspectorServerConnection;
[[maybe_unused]] static bool connect_to_inspector_server();
struct EventLoopTimer {
int timer_id { 0 };
Time interval;
@ -74,7 +70,6 @@ struct EventLoop::Private {
};
static Threading::MutexProtected<NeverDestroyed<IDAllocator>> s_id_allocator;
static Threading::MutexProtected<RefPtr<InspectorServerConnection>> s_inspector_server_connection;
// Each thread has its own event loop stack, its own timers, notifiers and a wake pipe.
static thread_local Vector<EventLoop&>* s_event_loop_stack;
@ -162,145 +157,7 @@ inline SignalHandlersInfo* signals_info()
pid_t EventLoop::s_pid;
class InspectorServerConnection : public Object {
C_OBJECT(InspectorServerConnection)
private:
explicit InspectorServerConnection(NonnullOwnPtr<LocalSocket> socket)
: m_socket(move(socket))
{
#ifdef AK_OS_SERENITY
m_socket->on_ready_to_read = [this] {
u32 length;
auto maybe_bytes_read = m_socket->read_some({ (u8*)&length, sizeof(length) });
if (maybe_bytes_read.is_error()) {
dbgln("InspectorServerConnection: Failed to read message length from inspector server connection: {}", maybe_bytes_read.error());
return;
}
auto bytes_read = maybe_bytes_read.release_value();
if (bytes_read.is_empty()) {
dbgln_if(EVENTLOOP_DEBUG, "RPC client disconnected");
return;
}
VERIFY(bytes_read.size() == sizeof(length));
auto request_buffer = ByteBuffer::create_uninitialized(length).release_value();
maybe_bytes_read = m_socket->read_some(request_buffer.bytes());
if (maybe_bytes_read.is_error()) {
dbgln("InspectorServerConnection: Failed to read message content from inspector server connection: {}", maybe_bytes_read.error());
return;
}
bytes_read = maybe_bytes_read.release_value();
auto request_json = JsonValue::from_string(request_buffer);
if (request_json.is_error() || !request_json.value().is_object()) {
dbgln("RPC client sent invalid request");
return;
}
handle_request(request_json.value().as_object());
};
#else
warnln("RPC Client constructed outside serenity, this is very likely a bug!");
#endif
}
virtual ~InspectorServerConnection() override
{
if (auto inspected_object = m_inspected_object.strong_ref())
inspected_object->decrement_inspector_count({});
}
public:
void send_response(JsonObject const& response)
{
auto serialized = response.to_deprecated_string();
auto bytes_to_send = serialized.bytes();
u32 length = bytes_to_send.size();
// FIXME: Propagate errors
MUST(m_socket->write_value(length));
while (!bytes_to_send.is_empty()) {
size_t bytes_sent = MUST(m_socket->write_some(bytes_to_send));
bytes_to_send = bytes_to_send.slice(bytes_sent);
}
}
void handle_request(JsonObject const& request)
{
auto type = request.get_deprecated_string("type"sv);
if (!type.has_value()) {
dbgln("RPC client sent request without type field");
return;
}
if (type == "Identify") {
JsonObject response;
response.set("type", type.value());
response.set("pid", getpid());
#ifdef AK_OS_SERENITY
char buffer[1024];
if (get_process_name(buffer, sizeof(buffer)) >= 0) {
response.set("process_name", buffer);
} else {
response.set("process_name", JsonValue());
}
#endif
send_response(response);
return;
}
if (type == "GetAllObjects") {
JsonObject response;
response.set("type", type.value());
JsonArray objects;
for (auto& object : Object::all_objects()) {
JsonObject json_object;
object.save_to(json_object);
objects.must_append(move(json_object));
}
response.set("objects", move(objects));
send_response(response);
return;
}
if (type == "SetInspectedObject") {
auto address = request.get_addr("address"sv);
for (auto& object : Object::all_objects()) {
if ((FlatPtr)&object == address) {
if (auto inspected_object = m_inspected_object.strong_ref())
inspected_object->decrement_inspector_count({});
m_inspected_object = object;
object.increment_inspector_count({});
break;
}
}
return;
}
if (type == "SetProperty") {
auto address = request.get_addr("address"sv);
for (auto& object : Object::all_objects()) {
if ((FlatPtr)&object == address) {
bool success = object.set_property(request.get_deprecated_string("name"sv).value(), request.get("value"sv).value());
JsonObject response;
response.set("type", "SetProperty");
response.set("success", success);
send_response(response);
break;
}
}
return;
}
}
private:
NonnullOwnPtr<LocalSocket> m_socket;
WeakPtr<Object> m_inspected_object;
};
EventLoop::EventLoop([[maybe_unused]] MakeInspectable make_inspectable)
EventLoop::EventLoop()
: m_wake_pipe_fds(&s_wake_pipe_fds)
, m_private(make<Private>())
{
@ -325,19 +182,6 @@ EventLoop::EventLoop([[maybe_unused]] MakeInspectable make_inspectable)
if (s_event_loop_stack->is_empty()) {
s_pid = getpid();
s_event_loop_stack->append(*this);
#ifdef AK_OS_SERENITY
if (getuid() != 0) {
if (getenv("MAKE_INSPECTABLE") == "1"sv)
make_inspectable = Core::EventLoop::MakeInspectable::Yes;
if (make_inspectable == MakeInspectable::Yes
&& !s_inspector_server_connection.with_locked([](auto inspector_server_connection) { return inspector_server_connection; })) {
if (!connect_to_inspector_server())
dbgln("Core::EventLoop: Failed to connect to InspectorServer");
}
}
#endif
}
initialize_wake_pipes();
@ -351,29 +195,6 @@ EventLoop::~EventLoop()
s_event_loop_stack->take_last();
}
bool connect_to_inspector_server()
{
#ifdef AK_OS_SERENITY
auto maybe_path = SessionManagement::parse_path_with_sid("/tmp/session/%sid/portal/inspectables"sv);
if (maybe_path.is_error()) {
dbgln("connect_to_inspector_server: {}", maybe_path.error());
return false;
}
auto inspector_server_path = maybe_path.value();
auto maybe_socket = LocalSocket::connect(inspector_server_path, Socket::PreventSIGPIPE::Yes);
if (maybe_socket.is_error()) {
dbgln("connect_to_inspector_server: Failed to connect: {}", maybe_socket.error());
return false;
}
s_inspector_server_connection.with_locked([&](auto& inspector_server_connection) {
inspector_server_connection = InspectorServerConnection::construct(maybe_socket.release_value());
});
return true;
#else
VERIFY_NOT_REACHED();
#endif
}
#define VERIFY_EVENT_LOOP_INITIALIZED() \
do { \
if (!s_event_loop_stack) { \

View File

@ -36,23 +36,16 @@ namespace Core {
// - Fork events, because the child process event loop needs to clear its events and handlers.
// - Quit events, i.e. the event loop should exit.
// Any event that the event loop needs to wait on or needs to repeatedly handle is stored in a handle, e.g. s_timers.
//
// EventLoop has one final responsibility: Handling the InspectorServer connection and processing requests to the Object hierarchy.
class EventLoop {
friend struct EventLoopPusher;
public:
enum class MakeInspectable {
No,
Yes,
};
enum class WaitMode {
WaitForEvents,
PollForEvents,
};
explicit EventLoop(MakeInspectable = MakeInspectable::No);
EventLoop();
~EventLoop();
static void initialize_wake_pipes();

View File

@ -70,11 +70,11 @@ Application* Application::the()
return *s_the;
}
Application::Application(int argc, char** argv, Core::EventLoop::MakeInspectable make_inspectable)
Application::Application(int argc, char** argv)
{
VERIFY(!*s_the);
*s_the = *this;
m_event_loop = make<Core::EventLoop>(make_inspectable);
m_event_loop = make<Core::EventLoop>();
ConnectionToWindowServer::the();
Clipboard::initialize({});
if (argc > 0)

View File

@ -97,9 +97,9 @@ public:
void register_recent_file_actions(Badge<GUI::Menu>, Vector<NonnullRefPtr<GUI::Action>>);
private:
Application(int argc, char** argv, Core::EventLoop::MakeInspectable = Core::EventLoop::MakeInspectable::No);
Application(Main::Arguments const& arguments, Core::EventLoop::MakeInspectable inspectable = Core::EventLoop::MakeInspectable::No)
: Application(arguments.argc, arguments.argv, inspectable)
Application(int argc, char** argv);
Application(Main::Arguments const& arguments)
: Application(arguments.argc, arguments.argv)
{
}

View File

@ -2,7 +2,6 @@ add_subdirectory(ConfigServer)
add_subdirectory(EchoServer)
add_subdirectory(FileOperation)
add_subdirectory(ImageDecoder)
add_subdirectory(InspectorServer)
add_subdirectory(LookupServer)
add_subdirectory(RequestServer)
add_subdirectory(WebServer)

View File

@ -1,22 +0,0 @@
serenity_component(
InspectorServer
REQUIRED
TARGETS InspectorServer
)
compile_ipc(InspectorServer.ipc InspectorServerEndpoint.h)
compile_ipc(InspectorClient.ipc InspectorClientEndpoint.h)
set(SOURCES
ConnectionFromClient.cpp
main.cpp
InspectableProcess.cpp
)
set(GENERATED_SOURCES
InspectorServerEndpoint.h
InspectorClientEndpoint.h
)
serenity_bin(InspectorServer)
target_link_libraries(InspectorServer PRIVATE LibCore LibIPC LibMain)

View File

@ -1,89 +0,0 @@
/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "InspectableProcess.h"
#include <AK/JsonObject.h>
#include <InspectorServer/ConnectionFromClient.h>
namespace InspectorServer {
static HashMap<int, RefPtr<ConnectionFromClient>> s_connections;
ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket> socket, int client_id)
: IPC::ConnectionFromClient<InspectorClientEndpoint, InspectorServerEndpoint>(*this, move(socket), client_id)
{
s_connections.set(client_id, *this);
}
void ConnectionFromClient::die()
{
s_connections.remove(client_id());
}
Messages::InspectorServer::GetAllObjectsResponse ConnectionFromClient::get_all_objects(pid_t pid)
{
auto process = InspectableProcess::from_pid(pid);
if (!process)
return DeprecatedString {};
JsonObject request;
request.set("type", "GetAllObjects");
process->send_request(request);
auto response = process->wait_for_response();
return response;
}
Messages::InspectorServer::SetInspectedObjectResponse ConnectionFromClient::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 ConnectionFromClient::set_object_property(pid_t pid, u64 object_id, DeprecatedString const& name, DeprecatedString 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 ConnectionFromClient::identify(pid_t pid)
{
auto process = InspectableProcess::from_pid(pid);
if (!process)
return DeprecatedString {};
JsonObject request;
request.set("type", "Identify");
process->send_request(request);
auto response = process->wait_for_response();
return response;
}
Messages::InspectorServer::IsInspectableResponse ConnectionFromClient::is_inspectable(pid_t pid)
{
auto process = InspectableProcess::from_pid(pid);
if (!process)
return false;
return true;
}
}

View File

@ -1,35 +0,0 @@
/*
* 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/ConnectionFromClient.h>
namespace InspectorServer {
class ConnectionFromClient final
: public IPC::ConnectionFromClient<InspectorClientEndpoint, InspectorServerEndpoint> {
C_OBJECT(ConnectionFromClient);
public:
~ConnectionFromClient() override = default;
virtual void die() override;
private:
explicit ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket>, int client_id);
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, DeprecatedString const& name, DeprecatedString const& value) override;
virtual Messages::InspectorServer::IdentifyResponse identify(pid_t) override;
virtual Messages::InspectorServer::IsInspectableResponse is_inspectable(pid_t) override;
};
}

View File

@ -1,13 +0,0 @@
/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
namespace SymbolServer {
class ConnectionFromClient;
}

View File

@ -1,81 +0,0 @@
/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "InspectableProcess.h"
#include <AK/JsonObject.h>
#include <LibCore/EventLoop.h>
#include <LibCore/Socket.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, NonnullOwnPtr<Core::LocalSocket> socket)
: m_pid(pid)
, m_socket(move(socket))
{
// FIXME: Propagate errors
MUST(m_socket->set_blocking(true));
m_socket->on_ready_to_read = [this] {
[[maybe_unused]] auto c = m_socket->read_value<char>().release_value_but_fixme_should_propagate_errors();
if (m_socket->is_eof()) {
Core::deferred_invoke([pid = this->m_pid] { g_processes.remove(pid); });
return;
}
};
}
DeprecatedString InspectableProcess::wait_for_response()
{
if (m_socket->is_eof()) {
dbgln("InspectableProcess disconnected: PID {}", m_pid);
m_socket->close();
return {};
}
auto length = m_socket->read_value<u32>().release_value_but_fixme_should_propagate_errors();
auto data_buffer = ByteBuffer::create_uninitialized(length).release_value_but_fixme_should_propagate_errors();
auto remaining_data_buffer = data_buffer.bytes();
while (!remaining_data_buffer.is_empty()) {
auto maybe_bytes_read = m_socket->read_some(remaining_data_buffer);
if (maybe_bytes_read.is_error()) {
dbgln("InspectableProcess::wait_for_response: Failed to read data: {}", maybe_bytes_read.error());
break;
}
auto bytes_read = maybe_bytes_read.release_value();
if (bytes_read.is_empty())
break;
remaining_data_buffer = remaining_data_buffer.slice(bytes_read.size());
}
VERIFY(data_buffer.size() == length);
dbgln("Got data size {} and read that many bytes", length);
return DeprecatedString::copy(data_buffer);
}
void InspectableProcess::send_request(JsonObject const& request)
{
auto serialized = request.to_deprecated_string();
u32 length = serialized.length();
// FIXME: Propagate errors
MUST(m_socket->write_value(length));
MUST(m_socket->write_until_depleted(serialized.bytes()));
}
}

View File

@ -1,31 +0,0 @@
/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibCore/Socket.h>
#include <sys/types.h>
namespace InspectorServer {
class InspectableProcess {
public:
InspectableProcess(pid_t, NonnullOwnPtr<Core::LocalSocket>);
~InspectableProcess() = default;
void send_request(JsonObject const& request);
DeprecatedString wait_for_response();
static InspectableProcess* from_pid(pid_t);
private:
pid_t m_pid { 0 };
NonnullOwnPtr<Core::LocalSocket> m_socket;
};
extern HashMap<pid_t, NonnullOwnPtr<InspectorServer::InspectableProcess>> g_processes;
}

View File

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

View File

@ -1,8 +0,0 @@
endpoint InspectorServer
{
get_all_objects(i32 pid) => (DeprecatedString json)
set_inspected_object(i32 pid, u64 object_id) => (bool success)
set_object_property(i32 pid, u64 object_id, DeprecatedString name, DeprecatedString value) => (bool success)
identify(i32 pid) => (DeprecatedString json)
is_inspectable(i32 pid) => (bool inspectable)
}

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "InspectableProcess.h"
#include <InspectorServer/ConnectionFromClient.h>
#include <LibCore/EventLoop.h>
#include <LibCore/LocalServer.h>
#include <LibCore/System.h>
#include <LibIPC/ConnectionFromClient.h>
#include <LibIPC/MultiServer.h>
#include <LibMain/Main.h>
ErrorOr<int> serenity_main(Main::Arguments)
{
Core::EventLoop event_loop;
TRY(Core::System::pledge("stdio unix accept rpath"));
auto server = TRY(IPC::MultiServer<InspectorServer::ConnectionFromClient>::try_create("/tmp/session/%sid/portal/inspector"));
auto inspectables_server = TRY(Core::LocalServer::try_create());
TRY(inspectables_server->take_over_from_system_server("/tmp/session/%sid/portal/inspectables"));
inspectables_server->on_accept = [&](auto client_socket) {
auto pid = client_socket->peer_pid().release_value_but_fixme_should_propagate_errors();
InspectorServer::g_processes.set(pid, make<InspectorServer::InspectableProcess>(pid, move(client_socket)));
};
return event_loop.exec();
}