/* * Copyright (c) 2018-2020, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include "RemoteObject.h" #include "RemoteObjectGraphModel.h" #include "RemoteObjectPropertyModel.h" #include "RemoteProcess.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Inspector; [[noreturn]] static void print_usage_and_exit() { outln("usage: Inspector "); exit(0); } ErrorOr 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, String::from_utf8_short_string("Inspect"sv), 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/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"); file_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); auto& help_menu = window->add_menu("&Help"); 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/Inspector.md"), "/bin/Help"); })); help_menu.add_action(GUI::CommonActions::make_about_action("Inspector", app_icon, window)); auto widget = TRY(window->set_main_widget()); widget->set_fill_with_background_color(true); widget->set_layout(); auto& splitter = widget->add(); 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(); 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(); 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(); }; tree_view.on_activation = [&](auto& index) { auto* remote_object = static_cast(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("Properties Tree View")); 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(); }