main.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "RemoteObject.h"
  7. #include "RemoteObjectGraphModel.h"
  8. #include "RemoteObjectPropertyModel.h"
  9. #include "RemoteProcess.h"
  10. #include <AK/URL.h>
  11. #include <LibCore/ProcessStatisticsReader.h>
  12. #include <LibDesktop/Launcher.h>
  13. #include <LibGUI/Application.h>
  14. #include <LibGUI/BoxLayout.h>
  15. #include <LibGUI/Clipboard.h>
  16. #include <LibGUI/Menu.h>
  17. #include <LibGUI/Menubar.h>
  18. #include <LibGUI/MessageBox.h>
  19. #include <LibGUI/ModelEditingDelegate.h>
  20. #include <LibGUI/ProcessChooser.h>
  21. #include <LibGUI/Splitter.h>
  22. #include <LibGUI/TreeView.h>
  23. #include <LibGUI/Window.h>
  24. #include <stdio.h>
  25. using namespace Inspector;
  26. [[noreturn]] static void print_usage_and_exit()
  27. {
  28. outln("usage: Inspector <pid>");
  29. exit(0);
  30. }
  31. int main(int argc, char** argv)
  32. {
  33. if (pledge("stdio recvfd sendfd rpath unix", nullptr) < 0) {
  34. perror("pledge");
  35. return 1;
  36. }
  37. if (unveil("/res", "r") < 0) {
  38. perror("unveil");
  39. return 1;
  40. }
  41. if (unveil("/bin", "r") < 0) {
  42. perror("unveil");
  43. return 1;
  44. }
  45. if (unveil("/tmp", "rwc") < 0) {
  46. perror("unveil");
  47. return 1;
  48. }
  49. if (unveil("/proc/all", "r") < 0) {
  50. perror("unveil");
  51. return 1;
  52. }
  53. if (unveil("/etc/passwd", "r") < 0) {
  54. perror("unveil");
  55. return 1;
  56. }
  57. unveil(nullptr, nullptr);
  58. pid_t pid;
  59. auto app = GUI::Application::construct(argc, argv);
  60. auto app_icon = GUI::Icon::default_icon("app-inspector");
  61. if (argc != 2) {
  62. auto process_chooser = GUI::ProcessChooser::construct("Inspector", "Inspect", app_icon.bitmap_for_size(16));
  63. if (process_chooser->exec() == GUI::Dialog::ExecCancel)
  64. return 0;
  65. pid = process_chooser->pid();
  66. } else {
  67. auto pid_opt = String(argv[1]).to_int();
  68. if (!pid_opt.has_value())
  69. print_usage_and_exit();
  70. pid = pid_opt.value();
  71. }
  72. auto window = GUI::Window::construct();
  73. if (!Desktop::Launcher::add_allowed_handler_with_only_specific_urls(
  74. "/bin/Help",
  75. { URL::create_with_file_protocol("/usr/share/man/man1/Inspector.md") })
  76. || !Desktop::Launcher::seal_allowlist()) {
  77. warnln("Failed to set up allowed launch URLs");
  78. return 1;
  79. }
  80. if (pid == getpid()) {
  81. GUI::MessageBox::show(window, "Cannot inspect Inspector itself!", "Error", GUI::MessageBox::Type::Error);
  82. return 1;
  83. }
  84. auto all_processes = Core::ProcessStatisticsReader::get_all();
  85. for (auto& it : all_processes.value()) {
  86. if (it.value.pid != pid)
  87. continue;
  88. if (it.value.pledge.is_empty())
  89. break;
  90. if (!it.value.pledge.contains("accept")) {
  91. GUI::MessageBox::show(window, String::formatted("{} ({}) has not pledged accept!", it.value.name, pid), "Error", GUI::MessageBox::Type::Error);
  92. return 1;
  93. }
  94. break;
  95. }
  96. window->set_title("Inspector");
  97. window->resize(685, 500);
  98. window->set_icon(app_icon.bitmap_for_size(16));
  99. auto menubar = GUI::Menubar::construct();
  100. auto& file_menu = menubar->add_menu("&File");
  101. file_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); }));
  102. auto& help_menu = menubar->add_menu("&Help");
  103. help_menu.add_action(GUI::CommonActions::make_help_action([](auto&) {
  104. Desktop::Launcher::open(URL::create_with_file_protocol("/usr/share/man/man1/Inspector.md"), "/bin/Help");
  105. }));
  106. help_menu.add_action(GUI::CommonActions::make_about_action("Inspector", app_icon, window));
  107. auto& widget = window->set_main_widget<GUI::Widget>();
  108. widget.set_fill_with_background_color(true);
  109. widget.set_layout<GUI::VerticalBoxLayout>();
  110. auto& splitter = widget.add<GUI::HorizontalSplitter>();
  111. RemoteProcess remote_process(pid);
  112. remote_process.on_update = [&] {
  113. if (!remote_process.process_name().is_null())
  114. window->set_title(String::formatted("{} ({}) - Inspector", remote_process.process_name(), remote_process.pid()));
  115. };
  116. auto& tree_view = splitter.add<GUI::TreeView>();
  117. tree_view.set_model(remote_process.object_graph_model());
  118. tree_view.set_activates_on_selection(true);
  119. tree_view.set_fixed_width(286);
  120. auto& properties_tree_view = splitter.add<GUI::TreeView>();
  121. properties_tree_view.set_should_fill_selected_rows(true);
  122. properties_tree_view.set_editable(true);
  123. properties_tree_view.aid_create_editing_delegate = [](auto&) {
  124. return make<GUI::StringModelEditingDelegate>();
  125. };
  126. tree_view.on_activation = [&](auto& index) {
  127. auto* remote_object = static_cast<RemoteObject*>(index.internal_data());
  128. properties_tree_view.set_model(remote_object->property_model());
  129. remote_process.set_inspected_object(remote_object->address);
  130. };
  131. auto properties_tree_view_context_menu = GUI::Menu::construct("Properties Tree View");
  132. auto copy_bitmap = Gfx::Bitmap::load_from_file("/res/icons/16x16/edit-copy.png");
  133. auto copy_property_name_action = GUI::Action::create("Copy Property Name", copy_bitmap, [&](auto&) {
  134. GUI::Clipboard::the().set_plain_text(properties_tree_view.selection().first().data().to_string());
  135. });
  136. auto copy_property_value_action = GUI::Action::create("Copy Property Value", copy_bitmap, [&](auto&) {
  137. GUI::Clipboard::the().set_plain_text(properties_tree_view.selection().first().sibling_at_column(1).data().to_string());
  138. });
  139. properties_tree_view_context_menu->add_action(copy_property_name_action);
  140. properties_tree_view_context_menu->add_action(copy_property_value_action);
  141. properties_tree_view.on_context_menu_request = [&](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) {
  142. if (index.is_valid()) {
  143. properties_tree_view_context_menu->popup(event.screen_position());
  144. }
  145. };
  146. window->set_menubar(move(menubar));
  147. window->show();
  148. remote_process.update();
  149. if (pledge("stdio recvfd sendfd rpath", nullptr) < 0) {
  150. perror("pledge");
  151. return 1;
  152. }
  153. return app->exec();
  154. }