From 47149e625faf4c970155ec923a2701ac2756e2cd Mon Sep 17 00:00:00 2001 From: Liav A Date: Wed, 23 Jun 2021 00:24:25 +0300 Subject: [PATCH] Kernel/ProcFS: Split code into more separate files Instead of using one file for the entire "backend" of the ProcFS data and metadata, we could split that file into two files that represent 2 logical chunks of the ProcFS exposed objects: 1. Global and inter-process information. This includes all fixed data in the root folder of the ProcFS, networking information and the bus folder. 2. Per-process information. This includes all dynamic data about a process that resides in the /proc/PID/ folder. This change makes it more easier to read the code and to change it, hence we do it although there's no technical benefit from it now :) --- Kernel/CMakeLists.txt | 2 + Kernel/GlobalProcessExposed.cpp | 925 +++++++++++++++++++ Kernel/ProcessExposed.cpp | 1383 ----------------------------- Kernel/ProcessSpecificExposed.cpp | 492 ++++++++++ 4 files changed, 1419 insertions(+), 1383 deletions(-) create mode 100644 Kernel/GlobalProcessExposed.cpp create mode 100644 Kernel/ProcessSpecificExposed.cpp diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index d073b6c8de..c3d5360e6b 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -58,6 +58,7 @@ set(KERNEL_SOURCES Devices/HID/PS2KeyboardDevice.cpp Devices/HID/PS2MouseDevice.cpp Devices/HID/VMWareMouseDevice.cpp + GlobalProcessExposed.cpp Graphics/Console/GenericFramebufferConsole.cpp Graphics/Console/ContiguousFramebufferConsole.cpp Graphics/Console/TextModeConsole.cpp @@ -149,6 +150,7 @@ set(KERNEL_SOURCES PerformanceEventBuffer.cpp Process.cpp ProcessExposed.cpp + ProcessSpecificExposed.cpp ProcessGroup.cpp RTC.cpp Random.cpp diff --git a/Kernel/GlobalProcessExposed.cpp b/Kernel/GlobalProcessExposed.cpp new file mode 100644 index 0000000000..f8f06af79c --- /dev/null +++ b/Kernel/GlobalProcessExposed.cpp @@ -0,0 +1,925 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class ProcFSAdapters final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSAdapters(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + NetworkingManagement::the().for_each([&array](auto& adapter) { + auto obj = array.add_object(); + obj.add("name", adapter.name()); + obj.add("class_name", adapter.class_name()); + obj.add("mac_address", adapter.mac_address().to_string()); + if (!adapter.ipv4_address().is_zero()) { + obj.add("ipv4_address", adapter.ipv4_address().to_string()); + obj.add("ipv4_netmask", adapter.ipv4_netmask().to_string()); + } + if (!adapter.ipv4_gateway().is_zero()) + obj.add("ipv4_gateway", adapter.ipv4_gateway().to_string()); + obj.add("packets_in", adapter.packets_in()); + obj.add("bytes_in", adapter.bytes_in()); + obj.add("packets_out", adapter.packets_out()); + obj.add("bytes_out", adapter.bytes_out()); + obj.add("link_up", adapter.link_up()); + obj.add("mtu", adapter.mtu()); + }); + array.finish(); + return true; + } +}; + +class ProcFSARP final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSARP(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + Locker locker(arp_table().lock(), Lock::Mode::Shared); + for (auto& it : arp_table().resource()) { + auto obj = array.add_object(); + obj.add("mac_address", it.value.to_string()); + obj.add("ip_address", it.key.to_string()); + } + array.finish(); + return true; + } +}; + +class ProcFSTCP final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSTCP(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + TCPSocket::for_each([&array](auto& socket) { + auto obj = array.add_object(); + obj.add("local_address", socket.local_address().to_string()); + obj.add("local_port", socket.local_port()); + obj.add("peer_address", socket.peer_address().to_string()); + obj.add("peer_port", socket.peer_port()); + obj.add("state", TCPSocket::to_string(socket.state())); + obj.add("ack_number", socket.ack_number()); + obj.add("sequence_number", socket.sequence_number()); + obj.add("packets_in", socket.packets_in()); + obj.add("bytes_in", socket.bytes_in()); + obj.add("packets_out", socket.packets_out()); + obj.add("bytes_out", socket.bytes_out()); + }); + array.finish(); + return true; + } +}; + +class ProcFSLocalNet final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSLocalNet(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + LocalSocket::for_each([&array](auto& socket) { + auto obj = array.add_object(); + obj.add("path", String(socket.socket_path())); + obj.add("origin_pid", socket.origin_pid()); + obj.add("origin_uid", socket.origin_uid()); + obj.add("origin_gid", socket.origin_gid()); + obj.add("acceptor_pid", socket.acceptor_pid()); + obj.add("acceptor_uid", socket.acceptor_uid()); + obj.add("acceptor_gid", socket.acceptor_gid()); + }); + array.finish(); + return true; + } +}; + +class ProcFSUDP final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSUDP(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + UDPSocket::for_each([&array](auto& socket) { + auto obj = array.add_object(); + obj.add("local_address", socket.local_address().to_string()); + obj.add("local_port", socket.local_port()); + obj.add("peer_address", socket.peer_address().to_string()); + obj.add("peer_port", socket.peer_port()); + }); + array.finish(); + return true; + } +}; + +class ProcFSNetworkDirectory : public ProcFSExposedFolder { +public: + static NonnullRefPtr must_create(const ProcFSRootFolder& parent_folder); + +private: + ProcFSNetworkDirectory(const ProcFSRootFolder& parent_folder); +}; + +class ProcFSSystemDirectory : public ProcFSExposedFolder { +public: + static NonnullRefPtr must_create(const ProcFSRootFolder& parent_folder); + +private: + ProcFSSystemDirectory(const ProcFSRootFolder& parent_folder); +}; + +UNMAP_AFTER_INIT NonnullRefPtr ProcFSAdapters::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSAdapters).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSARP::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSARP).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSTCP::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSTCP).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSLocalNet::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSLocalNet).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSUDP::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSUDP).release_nonnull(); +} + +UNMAP_AFTER_INIT NonnullRefPtr ProcFSNetworkDirectory::must_create(const ProcFSRootFolder& parent_folder) +{ + auto folder = adopt_ref(*new (nothrow) ProcFSNetworkDirectory(parent_folder)); + folder->m_components.append(ProcFSAdapters::must_create()); + folder->m_components.append(ProcFSARP::must_create()); + folder->m_components.append(ProcFSTCP::must_create()); + folder->m_components.append(ProcFSLocalNet::must_create()); + folder->m_components.append(ProcFSUDP::must_create()); + return folder; +} + +UNMAP_AFTER_INIT ProcFSAdapters::ProcFSAdapters() + : ProcFSGlobalInformation("adapters"sv) +{ +} +UNMAP_AFTER_INIT ProcFSARP::ProcFSARP() + : ProcFSGlobalInformation("arp"sv) +{ +} +UNMAP_AFTER_INIT ProcFSTCP::ProcFSTCP() + : ProcFSGlobalInformation("tcp"sv) +{ +} +UNMAP_AFTER_INIT ProcFSLocalNet::ProcFSLocalNet() + : ProcFSGlobalInformation("local"sv) +{ +} +UNMAP_AFTER_INIT ProcFSUDP::ProcFSUDP() + : ProcFSGlobalInformation("udp"sv) +{ +} +UNMAP_AFTER_INIT ProcFSNetworkDirectory::ProcFSNetworkDirectory(const ProcFSRootFolder& parent_folder) + : ProcFSExposedFolder("net"sv, parent_folder) +{ +} + +class ProcFSSystemDirectory; + +class ProcFSDumpKmallocStacks : public ProcFSSystemBoolean { +public: + static NonnullRefPtr must_create(const ProcFSSystemDirectory&); + virtual bool value() const override + { + Locker locker(m_lock); + return g_dump_kmalloc_stacks; + } + virtual void set_value(bool new_value) override + { + Locker locker(m_lock); + g_dump_kmalloc_stacks = new_value; + } + +private: + ProcFSDumpKmallocStacks(); + mutable Lock m_lock; +}; + +class ProcFSUBSanDeadly : public ProcFSSystemBoolean { +public: + static NonnullRefPtr must_create(const ProcFSSystemDirectory&); + virtual bool value() const override + { + Locker locker(m_lock); + return AK::UBSanitizer::g_ubsan_is_deadly; + } + virtual void set_value(bool new_value) override + { + Locker locker(m_lock); + AK::UBSanitizer::g_ubsan_is_deadly = new_value; + } + +private: + ProcFSUBSanDeadly(); + mutable Lock m_lock; +}; + +class ProcFSCapsLockRemap : public ProcFSSystemBoolean { +public: + static NonnullRefPtr must_create(const ProcFSSystemDirectory&); + virtual bool value() const override + { + Locker locker(m_lock); + return g_caps_lock_remapped_to_ctrl.load(); + } + virtual void set_value(bool new_value) override + { + Locker locker(m_lock); + g_caps_lock_remapped_to_ctrl.exchange(new_value); + } + +private: + ProcFSCapsLockRemap(); + mutable Lock m_lock; +}; + +UNMAP_AFTER_INIT NonnullRefPtr ProcFSDumpKmallocStacks::must_create(const ProcFSSystemDirectory&) +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSDumpKmallocStacks).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSUBSanDeadly::must_create(const ProcFSSystemDirectory&) +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSUBSanDeadly).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSCapsLockRemap::must_create(const ProcFSSystemDirectory&) +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSCapsLockRemap).release_nonnull(); +} + +UNMAP_AFTER_INIT ProcFSDumpKmallocStacks::ProcFSDumpKmallocStacks() + : ProcFSSystemBoolean("kmalloc_stacks"sv) +{ +} + +UNMAP_AFTER_INIT ProcFSUBSanDeadly::ProcFSUBSanDeadly() + : ProcFSSystemBoolean("ubsan_is_deadly"sv) +{ +} + +UNMAP_AFTER_INIT ProcFSCapsLockRemap::ProcFSCapsLockRemap() + : ProcFSSystemBoolean("caps_lock_to_ctrl"sv) +{ +} + +class ProcFSSelfProcessFolder final : public ProcFSExposedLink { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSSelfProcessFolder(); + virtual bool acquire_link(KBufferBuilder& builder) override + { + builder.appendff("{}", Process::current()->pid().value()); + return true; + } +}; + +class ProcFSDiskUsage final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSDiskUsage(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + VFS::the().for_each_mount([&array](auto& mount) { + auto& fs = mount.guest_fs(); + auto fs_object = array.add_object(); + fs_object.add("class_name", fs.class_name()); + fs_object.add("total_block_count", fs.total_block_count()); + fs_object.add("free_block_count", fs.free_block_count()); + fs_object.add("total_inode_count", fs.total_inode_count()); + fs_object.add("free_inode_count", fs.free_inode_count()); + fs_object.add("mount_point", mount.absolute_path()); + fs_object.add("block_size", static_cast(fs.block_size())); + fs_object.add("readonly", fs.is_readonly()); + fs_object.add("mount_flags", mount.flags()); + + if (fs.is_file_backed()) + fs_object.add("source", static_cast(fs).file_description().absolute_path()); + else + fs_object.add("source", "none"); + }); + array.finish(); + return true; + } +}; + +class ProcFSMemoryStatus final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSMemoryStatus(); + virtual bool output(KBufferBuilder& builder) override + { + InterruptDisabler disabler; + + kmalloc_stats stats; + get_kmalloc_stats(stats); + + ScopedSpinLock mm_lock(s_mm_lock); + auto user_physical_pages_total = MM.user_physical_pages(); + auto user_physical_pages_used = MM.user_physical_pages_used(); + auto user_physical_pages_committed = MM.user_physical_pages_committed(); + auto user_physical_pages_uncommitted = MM.user_physical_pages_uncommitted(); + + auto super_physical_total = MM.super_physical_pages(); + auto super_physical_used = MM.super_physical_pages_used(); + mm_lock.unlock(); + + JsonObjectSerializer json { builder }; + json.add("kmalloc_allocated", stats.bytes_allocated); + json.add("kmalloc_available", stats.bytes_free); + json.add("kmalloc_eternal_allocated", stats.bytes_eternal); + json.add("user_physical_allocated", user_physical_pages_used); + json.add("user_physical_available", user_physical_pages_total - user_physical_pages_used); + json.add("user_physical_committed", user_physical_pages_committed); + json.add("user_physical_uncommitted", user_physical_pages_uncommitted); + json.add("super_physical_allocated", super_physical_used); + json.add("super_physical_available", super_physical_total - super_physical_used); + json.add("kmalloc_call_count", stats.kmalloc_call_count); + json.add("kfree_call_count", stats.kfree_call_count); + slab_alloc_stats([&json](size_t slab_size, size_t num_allocated, size_t num_free) { + auto prefix = String::formatted("slab_{}", slab_size); + json.add(String::formatted("{}_num_allocated", prefix), num_allocated); + json.add(String::formatted("{}_num_free", prefix), num_free); + }); + json.finish(); + return true; + } +}; + +class ProcFSOverallProcesses final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSOverallProcesses(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + + // Keep this in sync with CProcessStatistics. + auto build_process = [&](const Process& process) { + auto process_object = array.add_object(); + + if (process.is_user_process()) { + StringBuilder pledge_builder; + +#define __ENUMERATE_PLEDGE_PROMISE(promise) \ + if (process.has_promised(Pledge::promise)) { \ + pledge_builder.append(#promise " "); \ + } + ENUMERATE_PLEDGE_PROMISES +#undef __ENUMERATE_PLEDGE_PROMISE + + process_object.add("pledge", pledge_builder.to_string()); + + switch (process.veil_state()) { + case VeilState::None: + process_object.add("veil", "None"); + break; + case VeilState::Dropped: + process_object.add("veil", "Dropped"); + break; + case VeilState::Locked: + process_object.add("veil", "Locked"); + break; + } + } else { + process_object.add("pledge", String()); + process_object.add("veil", String()); + } + + process_object.add("pid", process.pid().value()); + process_object.add("pgid", process.tty() ? process.tty()->pgid().value() : 0); + process_object.add("pgp", process.pgid().value()); + process_object.add("sid", process.sid().value()); + process_object.add("uid", process.uid()); + process_object.add("gid", process.gid()); + process_object.add("ppid", process.ppid().value()); + process_object.add("nfds", process.fds().open_count()); + process_object.add("name", process.name()); + process_object.add("executable", process.executable() ? process.executable()->absolute_path() : ""); + process_object.add("tty", process.tty() ? process.tty()->tty_name() : "notty"); + process_object.add("amount_virtual", process.space().amount_virtual()); + process_object.add("amount_resident", process.space().amount_resident()); + process_object.add("amount_dirty_private", process.space().amount_dirty_private()); + process_object.add("amount_clean_inode", process.space().amount_clean_inode()); + process_object.add("amount_shared", process.space().amount_shared()); + process_object.add("amount_purgeable_volatile", process.space().amount_purgeable_volatile()); + process_object.add("amount_purgeable_nonvolatile", process.space().amount_purgeable_nonvolatile()); + process_object.add("dumpable", process.is_dumpable()); + process_object.add("kernel", process.is_kernel_process()); + auto thread_array = process_object.add_array("threads"); + process.for_each_thread([&](const Thread& thread) { + auto thread_object = thread_array.add_object(); +#if LOCK_DEBUG + thread_object.add("lock_count", thread.lock_count()); +#endif + thread_object.add("tid", thread.tid().value()); + thread_object.add("name", thread.name()); + thread_object.add("times_scheduled", thread.times_scheduled()); + thread_object.add("ticks_user", thread.ticks_in_user()); + thread_object.add("ticks_kernel", thread.ticks_in_kernel()); + thread_object.add("state", thread.state_string()); + thread_object.add("cpu", thread.cpu()); + thread_object.add("priority", thread.priority()); + thread_object.add("syscall_count", thread.syscall_count()); + thread_object.add("inode_faults", thread.inode_faults()); + thread_object.add("zero_faults", thread.zero_faults()); + thread_object.add("cow_faults", thread.cow_faults()); + thread_object.add("file_read_bytes", thread.file_read_bytes()); + thread_object.add("file_write_bytes", thread.file_write_bytes()); + thread_object.add("unix_socket_read_bytes", thread.unix_socket_read_bytes()); + thread_object.add("unix_socket_write_bytes", thread.unix_socket_write_bytes()); + thread_object.add("ipv4_socket_read_bytes", thread.ipv4_socket_read_bytes()); + thread_object.add("ipv4_socket_write_bytes", thread.ipv4_socket_write_bytes()); + }); + }; + + ScopedSpinLock lock(g_scheduler_lock); + auto processes = Process::all_processes(); + build_process(*Scheduler::colonel()); + for (auto& process : processes) + build_process(process); + array.finish(); + return true; + } +}; +class ProcFSCPUInformation final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSCPUInformation(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + Processor::for_each( + [&](Processor& proc) { + auto& info = proc.info(); + auto obj = array.add_object(); + JsonArray features; + for (auto& feature : info.features().split(' ')) + features.append(feature); + obj.add("processor", proc.get_id()); + obj.add("cpuid", info.cpuid()); + obj.add("family", info.display_family()); + obj.add("features", features); + obj.add("model", info.display_model()); + obj.add("stepping", info.stepping()); + obj.add("type", info.type()); + obj.add("brandstr", info.brandstr()); + }); + array.finish(); + return true; + } +}; +class ProcFSDmesg final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSDmesg(); + virtual bool output(KBufferBuilder& builder) override + { + InterruptDisabler disabler; + for (char ch : ConsoleDevice::the().logbuffer()) + builder.append(ch); + return true; + } +}; +class ProcFSInterrupts final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSInterrupts(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + InterruptManagement::the().enumerate_interrupt_handlers([&array](GenericInterruptHandler& handler) { + auto obj = array.add_object(); + obj.add("purpose", handler.purpose()); + obj.add("interrupt_line", handler.interrupt_number()); + obj.add("controller", handler.controller()); + obj.add("cpu_handler", 0); // FIXME: Determine the responsible CPU for each interrupt handler. + obj.add("device_sharing", (unsigned)handler.sharing_devices_count()); + obj.add("call_count", (unsigned)handler.get_invoking_count()); + }); + array.finish(); + return true; + } +}; +class ProcFSKeymap final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSKeymap(); + virtual bool output(KBufferBuilder& builder) override + { + JsonObjectSerializer json { builder }; + json.add("keymap", HIDManagement::the().keymap_name()); + json.finish(); + return true; + } +}; + +// FIXME: Remove this after we enumerate the SysFS from lspci and SystemMonitor +class ProcFSPCI final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSPCI(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + PCI::enumerate([&array](PCI::Address address, PCI::ID id) { + auto obj = array.add_object(); + obj.add("seg", address.seg()); + obj.add("bus", address.bus()); + obj.add("device", address.device()); + obj.add("function", address.function()); + obj.add("vendor_id", id.vendor_id); + obj.add("device_id", id.device_id); + obj.add("revision_id", PCI::get_revision_id(address)); + obj.add("subclass", PCI::get_subclass(address)); + obj.add("class", PCI::get_class(address)); + obj.add("subsystem_id", PCI::get_subsystem_id(address)); + obj.add("subsystem_vendor_id", PCI::get_subsystem_vendor_id(address)); + }); + array.finish(); + return true; + } +}; + +class ProcFSDevices final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSDevices(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + Device::for_each([&array](auto& device) { + auto obj = array.add_object(); + obj.add("major", device.major()); + obj.add("minor", device.minor()); + obj.add("class_name", device.class_name()); + + if (device.is_block_device()) + obj.add("type", "block"); + else if (device.is_character_device()) + obj.add("type", "character"); + else + VERIFY_NOT_REACHED(); + }); + array.finish(); + return true; + } +}; +class ProcFSUptime final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSUptime(); + virtual bool output(KBufferBuilder& builder) override + { + builder.appendff("{}\n", TimeManagement::the().uptime_ms() / 1000); + return true; + } +}; +class ProcFSCommandLine final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSCommandLine(); + virtual bool output(KBufferBuilder& builder) override + { + builder.append(kernel_command_line().string()); + builder.append('\n'); + return true; + } +}; +class ProcFSModules final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSModules(); + virtual bool output(KBufferBuilder& builder) override + { + extern HashMap>* g_modules; + JsonArraySerializer array { builder }; + for (auto& it : *g_modules) { + auto obj = array.add_object(); + obj.add("name", it.value->name); + obj.add("module_init", it.value->module_init); + obj.add("module_fini", it.value->module_fini); + u32 size = 0; + for (auto& section : it.value->sections) { + size += section.capacity(); + } + obj.add("size", size); + } + array.finish(); + return true; + } +}; +class ProcFSProfile final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSProfile(); + virtual bool output(KBufferBuilder& builder) override + { + extern PerformanceEventBuffer* g_global_perf_events; + if (!g_global_perf_events) + return false; + + return g_global_perf_events->to_json(builder); + } +}; + +UNMAP_AFTER_INIT NonnullRefPtr ProcFSSelfProcessFolder::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSSelfProcessFolder()).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSDiskUsage::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSDiskUsage).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSMemoryStatus::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSMemoryStatus).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSOverallProcesses::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSOverallProcesses).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSCPUInformation::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSCPUInformation).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSDmesg::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSDmesg).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSInterrupts::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSInterrupts).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSKeymap::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSKeymap).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSPCI::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSPCI).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSDevices::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSDevices).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSUptime::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSUptime).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSCommandLine::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSCommandLine).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSModules::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSModules).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSProfile::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSProfile).release_nonnull(); +} + +UNMAP_AFTER_INIT ProcFSSelfProcessFolder::ProcFSSelfProcessFolder() + : ProcFSExposedLink("self"sv) +{ +} +UNMAP_AFTER_INIT ProcFSDiskUsage::ProcFSDiskUsage() + : ProcFSGlobalInformation("df"sv) +{ +} +UNMAP_AFTER_INIT ProcFSMemoryStatus::ProcFSMemoryStatus() + : ProcFSGlobalInformation("memstat"sv) +{ +} +UNMAP_AFTER_INIT ProcFSOverallProcesses::ProcFSOverallProcesses() + : ProcFSGlobalInformation("all"sv) +{ +} +UNMAP_AFTER_INIT ProcFSCPUInformation::ProcFSCPUInformation() + : ProcFSGlobalInformation("cpuinfo"sv) +{ +} +UNMAP_AFTER_INIT ProcFSDmesg::ProcFSDmesg() + : ProcFSGlobalInformation("dmesg"sv) +{ +} +UNMAP_AFTER_INIT ProcFSInterrupts::ProcFSInterrupts() + : ProcFSGlobalInformation("interrupts"sv) +{ +} +UNMAP_AFTER_INIT ProcFSKeymap::ProcFSKeymap() + : ProcFSGlobalInformation("keymap"sv) +{ +} +UNMAP_AFTER_INIT ProcFSPCI::ProcFSPCI() + : ProcFSGlobalInformation("pci"sv) +{ +} +UNMAP_AFTER_INIT ProcFSDevices::ProcFSDevices() + : ProcFSGlobalInformation("devices"sv) +{ +} +UNMAP_AFTER_INIT ProcFSUptime::ProcFSUptime() + : ProcFSGlobalInformation("uptime"sv) +{ +} +UNMAP_AFTER_INIT ProcFSCommandLine::ProcFSCommandLine() + : ProcFSGlobalInformation("cmdline"sv) +{ +} +UNMAP_AFTER_INIT ProcFSModules::ProcFSModules() + : ProcFSGlobalInformation("modules"sv) +{ +} +UNMAP_AFTER_INIT ProcFSProfile::ProcFSProfile() + : ProcFSGlobalInformation("profile"sv) +{ +} + +UNMAP_AFTER_INIT NonnullRefPtr ProcFSBusDirectory::must_create(const ProcFSRootFolder& parent_folder) +{ + auto folder = adopt_ref(*new (nothrow) ProcFSBusDirectory(parent_folder)); + return folder; +} + +UNMAP_AFTER_INIT NonnullRefPtr ProcFSSystemDirectory::must_create(const ProcFSRootFolder& parent_folder) +{ + auto folder = adopt_ref(*new (nothrow) ProcFSSystemDirectory(parent_folder)); + folder->m_components.append(ProcFSDumpKmallocStacks::must_create(folder)); + folder->m_components.append(ProcFSUBSanDeadly::must_create(folder)); + folder->m_components.append(ProcFSCapsLockRemap::must_create(folder)); + return folder; +} + +UNMAP_AFTER_INIT ProcFSBusDirectory::ProcFSBusDirectory(const ProcFSRootFolder& parent_folder) + : ProcFSExposedFolder("bus"sv, parent_folder) +{ +} +UNMAP_AFTER_INIT ProcFSSystemDirectory::ProcFSSystemDirectory(const ProcFSRootFolder& parent_folder) + : ProcFSExposedFolder("sys"sv, parent_folder) +{ +} + +UNMAP_AFTER_INIT NonnullRefPtr ProcFSRootFolder::must_create() +{ + auto folder = adopt_ref(*new (nothrow) ProcFSRootFolder); + folder->m_components.append(ProcFSSelfProcessFolder::must_create()); + folder->m_components.append(ProcFSDiskUsage::must_create()); + folder->m_components.append(ProcFSMemoryStatus::must_create()); + folder->m_components.append(ProcFSOverallProcesses::must_create()); + folder->m_components.append(ProcFSCPUInformation::must_create()); + folder->m_components.append(ProcFSDmesg::must_create()); + folder->m_components.append(ProcFSInterrupts::must_create()); + folder->m_components.append(ProcFSKeymap::must_create()); + folder->m_components.append(ProcFSPCI::must_create()); + folder->m_components.append(ProcFSDevices::must_create()); + folder->m_components.append(ProcFSUptime::must_create()); + folder->m_components.append(ProcFSCommandLine::must_create()); + folder->m_components.append(ProcFSModules::must_create()); + folder->m_components.append(ProcFSProfile::must_create()); + + folder->m_components.append(ProcFSNetworkDirectory::must_create(*folder)); + auto buses_folder = ProcFSBusDirectory::must_create(*folder); + folder->m_components.append(buses_folder); + folder->m_buses_folder = buses_folder; + folder->m_components.append(ProcFSSystemDirectory::must_create(*folder)); + return folder; +} + +KResult ProcFSRootFolder::traverse_as_directory(unsigned fsid, Function callback) const +{ + Locker locker(ProcFSComponentsRegistrar::the().m_lock); + callback({ ".", { fsid, component_index() }, 0 }); + callback({ "..", { fsid, 0 }, 0 }); + + for (auto& component : m_components) { + InodeIdentifier identifier = { fsid, component.component_index() }; + callback({ component.name(), identifier, 0 }); + } + for (auto& component : m_process_folders) { + InodeIdentifier identifier = { fsid, component.component_index() }; + callback({ component.name(), identifier, 0 }); + } + return KSuccess; +} + +RefPtr ProcFSRootFolder::lookup(StringView name) +{ + if (auto candidate = ProcFSExposedFolder::lookup(name); !candidate.is_null()) + return candidate; + + for (auto& component : m_process_folders) { + if (component.name() == name) { + return component; + } + } + return {}; +} + +UNMAP_AFTER_INIT ProcFSRootFolder::ProcFSRootFolder() + : ProcFSExposedFolder("."sv) +{ +} + +UNMAP_AFTER_INIT ProcFSRootFolder::~ProcFSRootFolder() +{ +} + +RefPtr ProcFSRootFolder::process_folder_for(Process& process) +{ + RefPtr checked_process = process; + for (auto& folder : m_process_folders) { + if (folder.associated_process().ptr() == checked_process.ptr()) + return folder; + } + return {}; +} + +} diff --git a/Kernel/ProcessExposed.cpp b/Kernel/ProcessExposed.cpp index 54ba651d24..fb883cad7b 100644 --- a/Kernel/ProcessExposed.cpp +++ b/Kernel/ProcessExposed.cpp @@ -4,42 +4,14 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include -#include -#include -#include #include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include -#include -#include -#include namespace Kernel { @@ -153,16 +125,6 @@ KResult ProcFSGlobalInformation::refresh_data(FileDescription& description) cons return KSuccess; } -RefPtr ProcFSRootFolder::process_folder_for(Process& process) -{ - RefPtr checked_process = process; - for (auto& folder : m_process_folders) { - if (folder.associated_process().ptr() == checked_process.ptr()) - return folder; - } - return {}; -} - KResultOr ProcFSProcessInformation::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const { dbgln_if(PROCFS_DEBUG, "ProcFSProcessInformation @ {}: read_bytes offset: {} count: {}", name(), offset, count); @@ -228,1321 +190,6 @@ KResult ProcFSProcessInformation::refresh_data(FileDescription& description) con return KSuccess; } -class ProcFSSystemDirectory; - -class ProcFSDumpKmallocStacks : public ProcFSSystemBoolean { -public: - static NonnullRefPtr must_create(const ProcFSSystemDirectory&); - virtual bool value() const override - { - Locker locker(m_lock); - return g_dump_kmalloc_stacks; - } - virtual void set_value(bool new_value) override - { - Locker locker(m_lock); - g_dump_kmalloc_stacks = new_value; - } - -private: - ProcFSDumpKmallocStacks(); - mutable Lock m_lock; -}; - -class ProcFSUBSanDeadly : public ProcFSSystemBoolean { -public: - static NonnullRefPtr must_create(const ProcFSSystemDirectory&); - virtual bool value() const override - { - Locker locker(m_lock); - return AK::UBSanitizer::g_ubsan_is_deadly; - } - virtual void set_value(bool new_value) override - { - Locker locker(m_lock); - AK::UBSanitizer::g_ubsan_is_deadly = new_value; - } - -private: - ProcFSUBSanDeadly(); - mutable Lock m_lock; -}; - -class ProcFSCapsLockRemap : public ProcFSSystemBoolean { -public: - static NonnullRefPtr must_create(const ProcFSSystemDirectory&); - virtual bool value() const override - { - Locker locker(m_lock); - return g_caps_lock_remapped_to_ctrl.load(); - } - virtual void set_value(bool new_value) override - { - Locker locker(m_lock); - g_caps_lock_remapped_to_ctrl.exchange(new_value); - } - -private: - ProcFSCapsLockRemap(); - mutable Lock m_lock; -}; - -UNMAP_AFTER_INIT NonnullRefPtr ProcFSDumpKmallocStacks::must_create(const ProcFSSystemDirectory&) -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSDumpKmallocStacks).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSUBSanDeadly::must_create(const ProcFSSystemDirectory&) -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSUBSanDeadly).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSCapsLockRemap::must_create(const ProcFSSystemDirectory&) -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSCapsLockRemap).release_nonnull(); -} - -UNMAP_AFTER_INIT ProcFSDumpKmallocStacks::ProcFSDumpKmallocStacks() - : ProcFSSystemBoolean("kmalloc_stacks"sv) -{ -} - -UNMAP_AFTER_INIT ProcFSUBSanDeadly::ProcFSUBSanDeadly() - : ProcFSSystemBoolean("ubsan_is_deadly"sv) -{ -} - -UNMAP_AFTER_INIT ProcFSCapsLockRemap::ProcFSCapsLockRemap() - : ProcFSSystemBoolean("caps_lock_to_ctrl"sv) -{ -} - -class ProcFSSelfProcessFolder final : public ProcFSExposedLink { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSSelfProcessFolder(); - virtual bool acquire_link(KBufferBuilder& builder) override - { - builder.appendff("{}", Process::current()->pid().value()); - return true; - } -}; - -class ProcFSDiskUsage final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSDiskUsage(); - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - VFS::the().for_each_mount([&array](auto& mount) { - auto& fs = mount.guest_fs(); - auto fs_object = array.add_object(); - fs_object.add("class_name", fs.class_name()); - fs_object.add("total_block_count", fs.total_block_count()); - fs_object.add("free_block_count", fs.free_block_count()); - fs_object.add("total_inode_count", fs.total_inode_count()); - fs_object.add("free_inode_count", fs.free_inode_count()); - fs_object.add("mount_point", mount.absolute_path()); - fs_object.add("block_size", static_cast(fs.block_size())); - fs_object.add("readonly", fs.is_readonly()); - fs_object.add("mount_flags", mount.flags()); - - if (fs.is_file_backed()) - fs_object.add("source", static_cast(fs).file_description().absolute_path()); - else - fs_object.add("source", "none"); - }); - array.finish(); - return true; - } -}; - -class ProcFSMemoryStatus final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSMemoryStatus(); - virtual bool output(KBufferBuilder& builder) override - { - InterruptDisabler disabler; - - kmalloc_stats stats; - get_kmalloc_stats(stats); - - ScopedSpinLock mm_lock(s_mm_lock); - auto user_physical_pages_total = MM.user_physical_pages(); - auto user_physical_pages_used = MM.user_physical_pages_used(); - auto user_physical_pages_committed = MM.user_physical_pages_committed(); - auto user_physical_pages_uncommitted = MM.user_physical_pages_uncommitted(); - - auto super_physical_total = MM.super_physical_pages(); - auto super_physical_used = MM.super_physical_pages_used(); - mm_lock.unlock(); - - JsonObjectSerializer json { builder }; - json.add("kmalloc_allocated", stats.bytes_allocated); - json.add("kmalloc_available", stats.bytes_free); - json.add("kmalloc_eternal_allocated", stats.bytes_eternal); - json.add("user_physical_allocated", user_physical_pages_used); - json.add("user_physical_available", user_physical_pages_total - user_physical_pages_used); - json.add("user_physical_committed", user_physical_pages_committed); - json.add("user_physical_uncommitted", user_physical_pages_uncommitted); - json.add("super_physical_allocated", super_physical_used); - json.add("super_physical_available", super_physical_total - super_physical_used); - json.add("kmalloc_call_count", stats.kmalloc_call_count); - json.add("kfree_call_count", stats.kfree_call_count); - slab_alloc_stats([&json](size_t slab_size, size_t num_allocated, size_t num_free) { - auto prefix = String::formatted("slab_{}", slab_size); - json.add(String::formatted("{}_num_allocated", prefix), num_allocated); - json.add(String::formatted("{}_num_free", prefix), num_free); - }); - json.finish(); - return true; - } -}; - -class ProcFSOverallProcesses final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSOverallProcesses(); - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - - // Keep this in sync with CProcessStatistics. - auto build_process = [&](const Process& process) { - auto process_object = array.add_object(); - - if (process.is_user_process()) { - StringBuilder pledge_builder; - -#define __ENUMERATE_PLEDGE_PROMISE(promise) \ - if (process.has_promised(Pledge::promise)) { \ - pledge_builder.append(#promise " "); \ - } - ENUMERATE_PLEDGE_PROMISES -#undef __ENUMERATE_PLEDGE_PROMISE - - process_object.add("pledge", pledge_builder.to_string()); - - switch (process.veil_state()) { - case VeilState::None: - process_object.add("veil", "None"); - break; - case VeilState::Dropped: - process_object.add("veil", "Dropped"); - break; - case VeilState::Locked: - process_object.add("veil", "Locked"); - break; - } - } else { - process_object.add("pledge", String()); - process_object.add("veil", String()); - } - - process_object.add("pid", process.pid().value()); - process_object.add("pgid", process.tty() ? process.tty()->pgid().value() : 0); - process_object.add("pgp", process.pgid().value()); - process_object.add("sid", process.sid().value()); - process_object.add("uid", process.uid()); - process_object.add("gid", process.gid()); - process_object.add("ppid", process.ppid().value()); - process_object.add("nfds", process.fds().open_count()); - process_object.add("name", process.name()); - process_object.add("executable", process.executable() ? process.executable()->absolute_path() : ""); - process_object.add("tty", process.tty() ? process.tty()->tty_name() : "notty"); - process_object.add("amount_virtual", process.space().amount_virtual()); - process_object.add("amount_resident", process.space().amount_resident()); - process_object.add("amount_dirty_private", process.space().amount_dirty_private()); - process_object.add("amount_clean_inode", process.space().amount_clean_inode()); - process_object.add("amount_shared", process.space().amount_shared()); - process_object.add("amount_purgeable_volatile", process.space().amount_purgeable_volatile()); - process_object.add("amount_purgeable_nonvolatile", process.space().amount_purgeable_nonvolatile()); - process_object.add("dumpable", process.is_dumpable()); - process_object.add("kernel", process.is_kernel_process()); - auto thread_array = process_object.add_array("threads"); - process.for_each_thread([&](const Thread& thread) { - auto thread_object = thread_array.add_object(); -#if LOCK_DEBUG - thread_object.add("lock_count", thread.lock_count()); -#endif - thread_object.add("tid", thread.tid().value()); - thread_object.add("name", thread.name()); - thread_object.add("times_scheduled", thread.times_scheduled()); - thread_object.add("ticks_user", thread.ticks_in_user()); - thread_object.add("ticks_kernel", thread.ticks_in_kernel()); - thread_object.add("state", thread.state_string()); - thread_object.add("cpu", thread.cpu()); - thread_object.add("priority", thread.priority()); - thread_object.add("syscall_count", thread.syscall_count()); - thread_object.add("inode_faults", thread.inode_faults()); - thread_object.add("zero_faults", thread.zero_faults()); - thread_object.add("cow_faults", thread.cow_faults()); - thread_object.add("file_read_bytes", thread.file_read_bytes()); - thread_object.add("file_write_bytes", thread.file_write_bytes()); - thread_object.add("unix_socket_read_bytes", thread.unix_socket_read_bytes()); - thread_object.add("unix_socket_write_bytes", thread.unix_socket_write_bytes()); - thread_object.add("ipv4_socket_read_bytes", thread.ipv4_socket_read_bytes()); - thread_object.add("ipv4_socket_write_bytes", thread.ipv4_socket_write_bytes()); - }); - }; - - ScopedSpinLock lock(g_scheduler_lock); - auto processes = Process::all_processes(); - build_process(*Scheduler::colonel()); - for (auto& process : processes) - build_process(process); - array.finish(); - return true; - } -}; -class ProcFSCPUInformation final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSCPUInformation(); - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - Processor::for_each( - [&](Processor& proc) { - auto& info = proc.info(); - auto obj = array.add_object(); - JsonArray features; - for (auto& feature : info.features().split(' ')) - features.append(feature); - obj.add("processor", proc.get_id()); - obj.add("cpuid", info.cpuid()); - obj.add("family", info.display_family()); - obj.add("features", features); - obj.add("model", info.display_model()); - obj.add("stepping", info.stepping()); - obj.add("type", info.type()); - obj.add("brandstr", info.brandstr()); - }); - array.finish(); - return true; - } -}; -class ProcFSDmesg final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSDmesg(); - virtual bool output(KBufferBuilder& builder) override - { - InterruptDisabler disabler; - for (char ch : ConsoleDevice::the().logbuffer()) - builder.append(ch); - return true; - } -}; -class ProcFSInterrupts final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSInterrupts(); - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - InterruptManagement::the().enumerate_interrupt_handlers([&array](GenericInterruptHandler& handler) { - auto obj = array.add_object(); - obj.add("purpose", handler.purpose()); - obj.add("interrupt_line", handler.interrupt_number()); - obj.add("controller", handler.controller()); - obj.add("cpu_handler", 0); // FIXME: Determine the responsible CPU for each interrupt handler. - obj.add("device_sharing", (unsigned)handler.sharing_devices_count()); - obj.add("call_count", (unsigned)handler.get_invoking_count()); - }); - array.finish(); - return true; - } -}; -class ProcFSKeymap final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSKeymap(); - virtual bool output(KBufferBuilder& builder) override - { - JsonObjectSerializer json { builder }; - json.add("keymap", HIDManagement::the().keymap_name()); - json.finish(); - return true; - } -}; - -// FIXME: Remove this after we enumerate the SysFS from lspci and SystemMonitor -class ProcFSPCI final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSPCI(); - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - PCI::enumerate([&array](PCI::Address address, PCI::ID id) { - auto obj = array.add_object(); - obj.add("seg", address.seg()); - obj.add("bus", address.bus()); - obj.add("device", address.device()); - obj.add("function", address.function()); - obj.add("vendor_id", id.vendor_id); - obj.add("device_id", id.device_id); - obj.add("revision_id", PCI::get_revision_id(address)); - obj.add("subclass", PCI::get_subclass(address)); - obj.add("class", PCI::get_class(address)); - obj.add("subsystem_id", PCI::get_subsystem_id(address)); - obj.add("subsystem_vendor_id", PCI::get_subsystem_vendor_id(address)); - }); - array.finish(); - return true; - } -}; - -class ProcFSDevices final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSDevices(); - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - Device::for_each([&array](auto& device) { - auto obj = array.add_object(); - obj.add("major", device.major()); - obj.add("minor", device.minor()); - obj.add("class_name", device.class_name()); - - if (device.is_block_device()) - obj.add("type", "block"); - else if (device.is_character_device()) - obj.add("type", "character"); - else - VERIFY_NOT_REACHED(); - }); - array.finish(); - return true; - } -}; -class ProcFSUptime final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSUptime(); - virtual bool output(KBufferBuilder& builder) override - { - builder.appendff("{}\n", TimeManagement::the().uptime_ms() / 1000); - return true; - } -}; -class ProcFSCommandLine final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSCommandLine(); - virtual bool output(KBufferBuilder& builder) override - { - builder.append(kernel_command_line().string()); - builder.append('\n'); - return true; - } -}; -class ProcFSModules final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSModules(); - virtual bool output(KBufferBuilder& builder) override - { - extern HashMap>* g_modules; - JsonArraySerializer array { builder }; - for (auto& it : *g_modules) { - auto obj = array.add_object(); - obj.add("name", it.value->name); - obj.add("module_init", it.value->module_init); - obj.add("module_fini", it.value->module_fini); - u32 size = 0; - for (auto& section : it.value->sections) { - size += section.capacity(); - } - obj.add("size", size); - } - array.finish(); - return true; - } -}; -class ProcFSProfile final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSProfile(); - virtual bool output(KBufferBuilder& builder) override - { - extern PerformanceEventBuffer* g_global_perf_events; - if (!g_global_perf_events) - return false; - - return g_global_perf_events->to_json(builder); - } -}; - -UNMAP_AFTER_INIT NonnullRefPtr ProcFSSelfProcessFolder::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSSelfProcessFolder()).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSDiskUsage::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSDiskUsage).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSMemoryStatus::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSMemoryStatus).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSOverallProcesses::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSOverallProcesses).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSCPUInformation::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSCPUInformation).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSDmesg::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSDmesg).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSInterrupts::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSInterrupts).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSKeymap::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSKeymap).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSPCI::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSPCI).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSDevices::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSDevices).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSUptime::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSUptime).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSCommandLine::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSCommandLine).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSModules::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSModules).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSProfile::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSProfile).release_nonnull(); -} - -UNMAP_AFTER_INIT ProcFSSelfProcessFolder::ProcFSSelfProcessFolder() - : ProcFSExposedLink("self"sv) -{ -} -UNMAP_AFTER_INIT ProcFSDiskUsage::ProcFSDiskUsage() - : ProcFSGlobalInformation("df"sv) -{ -} -UNMAP_AFTER_INIT ProcFSMemoryStatus::ProcFSMemoryStatus() - : ProcFSGlobalInformation("memstat"sv) -{ -} -UNMAP_AFTER_INIT ProcFSOverallProcesses::ProcFSOverallProcesses() - : ProcFSGlobalInformation("all"sv) -{ -} -UNMAP_AFTER_INIT ProcFSCPUInformation::ProcFSCPUInformation() - : ProcFSGlobalInformation("cpuinfo"sv) -{ -} -UNMAP_AFTER_INIT ProcFSDmesg::ProcFSDmesg() - : ProcFSGlobalInformation("dmesg"sv) -{ -} -UNMAP_AFTER_INIT ProcFSInterrupts::ProcFSInterrupts() - : ProcFSGlobalInformation("interrupts"sv) -{ -} -UNMAP_AFTER_INIT ProcFSKeymap::ProcFSKeymap() - : ProcFSGlobalInformation("keymap"sv) -{ -} -UNMAP_AFTER_INIT ProcFSPCI::ProcFSPCI() - : ProcFSGlobalInformation("pci"sv) -{ -} -UNMAP_AFTER_INIT ProcFSDevices::ProcFSDevices() - : ProcFSGlobalInformation("devices"sv) -{ -} -UNMAP_AFTER_INIT ProcFSUptime::ProcFSUptime() - : ProcFSGlobalInformation("uptime"sv) -{ -} -UNMAP_AFTER_INIT ProcFSCommandLine::ProcFSCommandLine() - : ProcFSGlobalInformation("cmdline"sv) -{ -} -UNMAP_AFTER_INIT ProcFSModules::ProcFSModules() - : ProcFSGlobalInformation("modules"sv) -{ -} -UNMAP_AFTER_INIT ProcFSProfile::ProcFSProfile() - : ProcFSGlobalInformation("profile"sv) -{ -} - -class ProcFSAdapters final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSAdapters(); - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - NetworkingManagement::the().for_each([&array](auto& adapter) { - auto obj = array.add_object(); - obj.add("name", adapter.name()); - obj.add("class_name", adapter.class_name()); - obj.add("mac_address", adapter.mac_address().to_string()); - if (!adapter.ipv4_address().is_zero()) { - obj.add("ipv4_address", adapter.ipv4_address().to_string()); - obj.add("ipv4_netmask", adapter.ipv4_netmask().to_string()); - } - if (!adapter.ipv4_gateway().is_zero()) - obj.add("ipv4_gateway", adapter.ipv4_gateway().to_string()); - obj.add("packets_in", adapter.packets_in()); - obj.add("bytes_in", adapter.bytes_in()); - obj.add("packets_out", adapter.packets_out()); - obj.add("bytes_out", adapter.bytes_out()); - obj.add("link_up", adapter.link_up()); - obj.add("mtu", adapter.mtu()); - }); - array.finish(); - return true; - } -}; - -class ProcFSARP final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSARP(); - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - Locker locker(arp_table().lock(), Lock::Mode::Shared); - for (auto& it : arp_table().resource()) { - auto obj = array.add_object(); - obj.add("mac_address", it.value.to_string()); - obj.add("ip_address", it.key.to_string()); - } - array.finish(); - return true; - } -}; - -class ProcFSTCP final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSTCP(); - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - TCPSocket::for_each([&array](auto& socket) { - auto obj = array.add_object(); - obj.add("local_address", socket.local_address().to_string()); - obj.add("local_port", socket.local_port()); - obj.add("peer_address", socket.peer_address().to_string()); - obj.add("peer_port", socket.peer_port()); - obj.add("state", TCPSocket::to_string(socket.state())); - obj.add("ack_number", socket.ack_number()); - obj.add("sequence_number", socket.sequence_number()); - obj.add("packets_in", socket.packets_in()); - obj.add("bytes_in", socket.bytes_in()); - obj.add("packets_out", socket.packets_out()); - obj.add("bytes_out", socket.bytes_out()); - }); - array.finish(); - return true; - } -}; - -class ProcFSLocalNet final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSLocalNet(); - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - LocalSocket::for_each([&array](auto& socket) { - auto obj = array.add_object(); - obj.add("path", String(socket.socket_path())); - obj.add("origin_pid", socket.origin_pid()); - obj.add("origin_uid", socket.origin_uid()); - obj.add("origin_gid", socket.origin_gid()); - obj.add("acceptor_pid", socket.acceptor_pid()); - obj.add("acceptor_uid", socket.acceptor_uid()); - obj.add("acceptor_gid", socket.acceptor_gid()); - }); - array.finish(); - return true; - } -}; - -class ProcFSUDP final : public ProcFSGlobalInformation { -public: - static NonnullRefPtr must_create(); - -private: - ProcFSUDP(); - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - UDPSocket::for_each([&array](auto& socket) { - auto obj = array.add_object(); - obj.add("local_address", socket.local_address().to_string()); - obj.add("local_port", socket.local_port()); - obj.add("peer_address", socket.peer_address().to_string()); - obj.add("peer_port", socket.peer_port()); - }); - array.finish(); - return true; - } -}; - -class ProcFSNetworkDirectory : public ProcFSExposedFolder { -public: - static NonnullRefPtr must_create(const ProcFSRootFolder& parent_folder); - -private: - ProcFSNetworkDirectory(const ProcFSRootFolder& parent_folder); -}; - -class ProcFSSystemDirectory : public ProcFSExposedFolder { -public: - static NonnullRefPtr must_create(const ProcFSRootFolder& parent_folder); - -private: - ProcFSSystemDirectory(const ProcFSRootFolder& parent_folder); -}; - -UNMAP_AFTER_INIT NonnullRefPtr ProcFSAdapters::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSAdapters).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSARP::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSARP).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSTCP::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSTCP).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSLocalNet::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSLocalNet).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSUDP::must_create() -{ - return adopt_ref_if_nonnull(new (nothrow) ProcFSUDP).release_nonnull(); -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSNetworkDirectory::must_create(const ProcFSRootFolder& parent_folder) -{ - auto folder = adopt_ref(*new (nothrow) ProcFSNetworkDirectory(parent_folder)); - folder->m_components.append(ProcFSAdapters::must_create()); - folder->m_components.append(ProcFSARP::must_create()); - folder->m_components.append(ProcFSTCP::must_create()); - folder->m_components.append(ProcFSLocalNet::must_create()); - folder->m_components.append(ProcFSUDP::must_create()); - return folder; -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSBusDirectory::must_create(const ProcFSRootFolder& parent_folder) -{ - auto folder = adopt_ref(*new (nothrow) ProcFSBusDirectory(parent_folder)); - return folder; -} -UNMAP_AFTER_INIT NonnullRefPtr ProcFSSystemDirectory::must_create(const ProcFSRootFolder& parent_folder) -{ - auto folder = adopt_ref(*new (nothrow) ProcFSSystemDirectory(parent_folder)); - folder->m_components.append(ProcFSDumpKmallocStacks::must_create(folder)); - folder->m_components.append(ProcFSUBSanDeadly::must_create(folder)); - folder->m_components.append(ProcFSCapsLockRemap::must_create(folder)); - return folder; -} - -UNMAP_AFTER_INIT ProcFSAdapters::ProcFSAdapters() - : ProcFSGlobalInformation("adapters"sv) -{ -} -UNMAP_AFTER_INIT ProcFSARP::ProcFSARP() - : ProcFSGlobalInformation("arp"sv) -{ -} -UNMAP_AFTER_INIT ProcFSTCP::ProcFSTCP() - : ProcFSGlobalInformation("tcp"sv) -{ -} -UNMAP_AFTER_INIT ProcFSLocalNet::ProcFSLocalNet() - : ProcFSGlobalInformation("local"sv) -{ -} -UNMAP_AFTER_INIT ProcFSUDP::ProcFSUDP() - : ProcFSGlobalInformation("udp"sv) -{ -} -UNMAP_AFTER_INIT ProcFSNetworkDirectory::ProcFSNetworkDirectory(const ProcFSRootFolder& parent_folder) - : ProcFSExposedFolder("net"sv, parent_folder) -{ -} -UNMAP_AFTER_INIT ProcFSBusDirectory::ProcFSBusDirectory(const ProcFSRootFolder& parent_folder) - : ProcFSExposedFolder("bus"sv, parent_folder) -{ -} -UNMAP_AFTER_INIT ProcFSSystemDirectory::ProcFSSystemDirectory(const ProcFSRootFolder& parent_folder) - : ProcFSExposedFolder("sys"sv, parent_folder) -{ -} - -UNMAP_AFTER_INIT NonnullRefPtr ProcFSRootFolder::must_create() -{ - auto folder = adopt_ref(*new (nothrow) ProcFSRootFolder); - folder->m_components.append(ProcFSSelfProcessFolder::must_create()); - folder->m_components.append(ProcFSDiskUsage::must_create()); - folder->m_components.append(ProcFSMemoryStatus::must_create()); - folder->m_components.append(ProcFSOverallProcesses::must_create()); - folder->m_components.append(ProcFSCPUInformation::must_create()); - folder->m_components.append(ProcFSDmesg::must_create()); - folder->m_components.append(ProcFSInterrupts::must_create()); - folder->m_components.append(ProcFSKeymap::must_create()); - folder->m_components.append(ProcFSPCI::must_create()); - folder->m_components.append(ProcFSDevices::must_create()); - folder->m_components.append(ProcFSUptime::must_create()); - folder->m_components.append(ProcFSCommandLine::must_create()); - folder->m_components.append(ProcFSModules::must_create()); - folder->m_components.append(ProcFSProfile::must_create()); - - folder->m_components.append(ProcFSNetworkDirectory::must_create(*folder)); - auto buses_folder = ProcFSBusDirectory::must_create(*folder); - folder->m_components.append(buses_folder); - folder->m_buses_folder = buses_folder; - folder->m_components.append(ProcFSSystemDirectory::must_create(*folder)); - return folder; -} - -UNMAP_AFTER_INIT ProcFSRootFolder::ProcFSRootFolder() - : ProcFSExposedFolder("."sv) -{ -} - -UNMAP_AFTER_INIT ProcFSRootFolder::~ProcFSRootFolder() -{ -} - -class ProcFSProcessStacks; -class ProcFSThreadStack final : public ProcFSProcessInformation { -public: - // Note: We pass const ProcFSProcessStacks& to enforce creation with this type of folder - static NonnullRefPtr create(const ProcFSProcessFolder& process_folder, const ProcFSProcessStacks&, const Thread& thread) - { - return adopt_ref(*new (nothrow) ProcFSThreadStack(process_folder, thread)); - } - -private: - explicit ProcFSThreadStack(const ProcFSProcessFolder& process_folder, const Thread& thread) - : ProcFSProcessInformation(String::formatted("{}", thread.tid()), process_folder) - , m_associated_thread(thread) - { - } - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - bool show_kernel_addresses = Process::current()->is_superuser(); - bool kernel_address_added = false; - for (auto address : Processor::capture_stack_trace(*m_associated_thread, 1024)) { - if (!show_kernel_addresses && !is_user_address(VirtualAddress { address })) { - if (kernel_address_added) - continue; - address = 0xdeadc0de; - kernel_address_added = true; - } - array.add(JsonValue(address)); - } - - array.finish(); - return true; - } - - NonnullRefPtr m_associated_thread; -}; - -class ProcFSProcessStacks final : public ProcFSExposedFolder { - // Note: This folder is special, because everything that is created here is dynamic! - // This means we don't register anything in the m_components Vector, and every inode - // is created in runtime when called to get it - // Every ProcFSThreadStack (that represents a thread stack) is created only as a temporary object - // therefore, we don't use m_components so when we are done with the ProcFSThreadStack object, - // It should be deleted (as soon as possible) -public: - virtual KResultOr entries_count() const override; - virtual KResult traverse_as_directory(unsigned, Function) const override; - virtual RefPtr lookup(StringView name) override; - - static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) - { - auto folder = adopt_ref(*new (nothrow) ProcFSProcessStacks(parent_folder)); - return folder; - } - - virtual void prepare_for_deletion() override - { - ProcFSExposedFolder::prepare_for_deletion(); - m_process_folder.clear(); - } - -private: - ProcFSProcessStacks(const ProcFSProcessFolder& parent_folder) - : ProcFSExposedFolder("stacks"sv, parent_folder) - , m_process_folder(parent_folder) - { - } - RefPtr m_process_folder; - mutable Lock m_lock; -}; - -KResultOr ProcFSProcessStacks::entries_count() const -{ - Locker locker(m_lock); - auto process = m_process_folder->m_associated_process; - return process->thread_count(); -} - -KResult ProcFSProcessStacks::traverse_as_directory(unsigned fsid, Function callback) const -{ - Locker locker(m_lock); - callback({ ".", { fsid, component_index() }, 0 }); - callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); - - auto process = m_process_folder->m_associated_process; - process->for_each_thread([&](const Thread& thread) { - int tid = thread.tid().value(); - InodeIdentifier identifier = { fsid, thread.global_procfs_inode_index() }; - callback({ String::number(tid), identifier, 0 }); - }); - return KSuccess; -} - -RefPtr ProcFSProcessStacks::lookup(StringView name) -{ - Locker locker(m_lock); - auto process = m_process_folder->m_associated_process; - RefPtr procfd_stack; - // FIXME: Try to exit the loop earlier - process->for_each_thread([&](const Thread& thread) { - int tid = thread.tid().value(); - if (name == String::number(tid)) { - procfd_stack = ProcFSThreadStack::create(*m_process_folder, *this, thread); - } - }); - return procfd_stack; -} - -class ProcFSProcessFileDescriptions; -class ProcFSProcessFileDescription final : public ProcFSExposedLink { -public: - // Note: we pass const ProcFSProcessFileDescriptions& just to enforce creation of this in the correct folder. - static NonnullRefPtr create(unsigned fd_number, const FileDescription& fd, InodeIndex preallocated_index, const ProcFSProcessFileDescriptions&) - { - return adopt_ref(*new (nothrow) ProcFSProcessFileDescription(fd_number, fd, preallocated_index)); - } - -private: - explicit ProcFSProcessFileDescription(unsigned fd_number, const FileDescription& fd, InodeIndex preallocated_index) - : ProcFSExposedLink(String::formatted("{}", fd_number), preallocated_index) - , m_associated_file_description(fd) - { - } - virtual bool acquire_link(KBufferBuilder& builder) override - { - builder.append_bytes(m_associated_file_description->absolute_path().bytes()); - return true; - } - - NonnullRefPtr m_associated_file_description; -}; - -class ProcFSProcessFileDescriptions final : public ProcFSExposedFolder { - // Note: This folder is special, because everything that is created here is dynamic! - // This means we don't register anything in the m_components Vector, and every inode - // is created in runtime when called to get it - // Every ProcFSProcessFileDescription (that represents a file descriptor) is created only as a temporary object - // therefore, we don't use m_components so when we are done with the ProcFSProcessFileDescription object, - // It should be deleted (as soon as possible) -public: - virtual KResultOr entries_count() const override; - virtual KResult traverse_as_directory(unsigned, Function) const override; - virtual RefPtr lookup(StringView name) override; - - static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) - { - return adopt_ref(*new (nothrow) ProcFSProcessFileDescriptions(parent_folder)); - } - - virtual void prepare_for_deletion() override - { - ProcFSExposedFolder::prepare_for_deletion(); - m_process_folder.clear(); - } - -private: - explicit ProcFSProcessFileDescriptions(const ProcFSProcessFolder& parent_folder) - : ProcFSExposedFolder("fd"sv, parent_folder) - , m_process_folder(parent_folder) - { - } - RefPtr m_process_folder; - mutable Lock m_lock; -}; - -KResultOr ProcFSProcessFileDescriptions::entries_count() const -{ - Locker locker(m_lock); - return m_process_folder->m_associated_process->fds().open_count(); -} -KResult ProcFSProcessFileDescriptions::traverse_as_directory(unsigned fsid, Function callback) const -{ - Locker locker(m_lock); - callback({ ".", { fsid, component_index() }, 0 }); - callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); - - auto process = m_process_folder->m_associated_process; - size_t count = 0; - m_process_folder->m_associated_process->fds().enumerate([&](auto& file_description_metadata) { - if (!file_description_metadata.is_valid()) { - count++; - return; - } - InodeIdentifier identifier = { fsid, file_description_metadata.global_procfs_inode_index() }; - callback({ String::number(count), identifier, 0 }); - count++; - }); - return KSuccess; -} -RefPtr ProcFSProcessFileDescriptions::lookup(StringView name) -{ - Locker locker(m_lock); - auto process = m_process_folder->m_associated_process; - RefPtr procfd_fd; - // FIXME: Try to exit the loop earlier - size_t count = 0; - m_process_folder->m_associated_process->fds().enumerate([&](auto& file_description_metadata) { - if (!file_description_metadata.is_valid()) { - count++; - return; - } - if (name == String::number(count)) { - procfd_fd = ProcFSProcessFileDescription::create(count, *file_description_metadata.description(), file_description_metadata.global_procfs_inode_index(), *this); - } - count++; - }); - return procfd_fd; -} - -class ProcFSProcessUnveil final : public ProcFSProcessInformation { -public: - static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) - { - return adopt_ref(*new (nothrow) ProcFSProcessUnveil(parent_folder)); - } - -private: - explicit ProcFSProcessUnveil(const ProcFSProcessFolder& parent_folder) - : ProcFSProcessInformation("unveil"sv, parent_folder) - { - } - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - for (auto& unveiled_path : m_parent_folder->m_associated_process->unveiled_paths()) { - if (!unveiled_path.was_explicitly_unveiled()) - continue; - auto obj = array.add_object(); - obj.add("path", unveiled_path.path()); - StringBuilder permissions_builder; - if (unveiled_path.permissions() & UnveilAccess::Read) - permissions_builder.append('r'); - if (unveiled_path.permissions() & UnveilAccess::Write) - permissions_builder.append('w'); - if (unveiled_path.permissions() & UnveilAccess::Execute) - permissions_builder.append('x'); - if (unveiled_path.permissions() & UnveilAccess::CreateOrRemove) - permissions_builder.append('c'); - if (unveiled_path.permissions() & UnveilAccess::Browse) - permissions_builder.append('b'); - obj.add("permissions", permissions_builder.to_string()); - } - array.finish(); - return true; - } -}; - -class ProcFSProcessPerformanceEvents final : public ProcFSProcessInformation { -public: - static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) - { - return adopt_ref(*new (nothrow) ProcFSProcessPerformanceEvents(parent_folder)); - } - -private: - explicit ProcFSProcessPerformanceEvents(const ProcFSProcessFolder& parent_folder) - : ProcFSProcessInformation("perf_events"sv, parent_folder) - { - } - virtual bool output(KBufferBuilder& builder) override - { - InterruptDisabler disabler; - if (!m_parent_folder->m_associated_process->perf_events()) { - dbgln("ProcFS: No perf events for {}", m_parent_folder->m_associated_process->pid()); - return false; - } - return m_parent_folder->m_associated_process->perf_events()->to_json(builder); - } -}; - -class ProcFSProcessOverallFileDescriptions final : public ProcFSProcessInformation { -public: - static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) - { - return adopt_ref(*new (nothrow) ProcFSProcessOverallFileDescriptions(parent_folder)); - } - -private: - explicit ProcFSProcessOverallFileDescriptions(const ProcFSProcessFolder& parent_folder) - : ProcFSProcessInformation("fds"sv, parent_folder) - { - } - virtual bool output(KBufferBuilder& builder) override - { - JsonArraySerializer array { builder }; - auto process = m_parent_folder->m_associated_process; - if (process->fds().open_count() == 0) { - array.finish(); - return true; - } - - size_t count = 0; - process->fds().enumerate([&](auto& file_description_metadata) { - if (!file_description_metadata.is_valid()) { - count++; - return; - } - bool cloexec = file_description_metadata.flags() & FD_CLOEXEC; - RefPtr description = file_description_metadata.description(); - auto description_object = array.add_object(); - description_object.add("fd", count); - description_object.add("absolute_path", description->absolute_path()); - description_object.add("seekable", description->file().is_seekable()); - description_object.add("class", description->file().class_name()); - description_object.add("offset", description->offset()); - description_object.add("cloexec", cloexec); - description_object.add("blocking", description->is_blocking()); - description_object.add("can_read", description->can_read()); - description_object.add("can_write", description->can_write()); - count++; - }); - - array.finish(); - return true; - } -}; - -class ProcFSProcessRoot final : public ProcFSExposedLink { -public: - static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) - { - return adopt_ref(*new (nothrow) ProcFSProcessRoot(parent_folder)); - } - -private: - explicit ProcFSProcessRoot(const ProcFSProcessFolder& parent_folder) - : ProcFSExposedLink("root"sv) - , m_parent_process_directory(parent_folder) - { - } - virtual bool acquire_link(KBufferBuilder& builder) override - { - builder.append_bytes(m_parent_process_directory->m_associated_process->root_directory_relative_to_global_root().absolute_path().to_byte_buffer()); - return true; - } - NonnullRefPtr m_parent_process_directory; -}; - -class ProcFSProcessVirtualMemory final : public ProcFSProcessInformation { -public: - static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) - { - return adopt_ref(*new (nothrow) ProcFSProcessVirtualMemory(parent_folder)); - } - -private: - explicit ProcFSProcessVirtualMemory(const ProcFSProcessFolder& parent_folder) - : ProcFSProcessInformation("vm"sv, parent_folder) - { - } - virtual bool output(KBufferBuilder& builder) override - { - auto process = m_parent_folder->m_associated_process; - JsonArraySerializer array { builder }; - { - ScopedSpinLock lock(process->space().get_lock()); - for (auto& region : process->space().regions()) { - if (!region->is_user() && !Process::current()->is_superuser()) - continue; - auto region_object = array.add_object(); - region_object.add("readable", region->is_readable()); - region_object.add("writable", region->is_writable()); - region_object.add("executable", region->is_executable()); - region_object.add("stack", region->is_stack()); - region_object.add("shared", region->is_shared()); - region_object.add("syscall", region->is_syscall_region()); - region_object.add("purgeable", region->vmobject().is_anonymous()); - if (region->vmobject().is_anonymous()) { - region_object.add("volatile", static_cast(region->vmobject()).is_any_volatile()); - } - region_object.add("cacheable", region->is_cacheable()); - region_object.add("address", region->vaddr().get()); - region_object.add("size", region->size()); - region_object.add("amount_resident", region->amount_resident()); - region_object.add("amount_dirty", region->amount_dirty()); - region_object.add("cow_pages", region->cow_pages()); - region_object.add("name", region->name()); - region_object.add("vmobject", region->vmobject().class_name()); - - StringBuilder pagemap_builder; - for (size_t i = 0; i < region->page_count(); ++i) { - auto* page = region->physical_page(i); - if (!page) - pagemap_builder.append('N'); - else if (page->is_shared_zero_page() || page->is_lazy_committed_page()) - pagemap_builder.append('Z'); - else - pagemap_builder.append('P'); - } - region_object.add("pagemap", pagemap_builder.to_string()); - } - } - array.finish(); - return true; - } -}; - -class ProcFSProcessCurrentWorkDirectory final : public ProcFSExposedLink { -public: - static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) - { - return adopt_ref(*new (nothrow) ProcFSProcessCurrentWorkDirectory(parent_folder)); - } - -private: - explicit ProcFSProcessCurrentWorkDirectory(const ProcFSProcessFolder& parent_folder) - : ProcFSExposedLink("cwd"sv) - , m_parent_process_directory(parent_folder) - { - } - virtual bool acquire_link(KBufferBuilder& builder) override - { - builder.append_bytes(m_parent_process_directory->m_associated_process->current_directory().absolute_path().bytes()); - return true; - } - - NonnullRefPtr m_parent_process_directory; -}; - -class ProcFSProcessBinary final : public ProcFSExposedLink { -public: - static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) - { - return adopt_ref(*new (nothrow) ProcFSProcessBinary(parent_folder)); - } - - virtual mode_t required_mode() const override - { - if (!m_parent_process_directory->m_associated_process->executable()) - return 0; - return ProcFSExposedComponent::required_mode(); - } - -private: - explicit ProcFSProcessBinary(const ProcFSProcessFolder& parent_folder) - : ProcFSExposedLink("exe"sv) - , m_parent_process_directory(parent_folder) - { - } - virtual bool acquire_link(KBufferBuilder& builder) override - { - auto* custody = m_parent_process_directory->m_associated_process->executable(); - if (!custody) - return false; - builder.append(custody->absolute_path().bytes()); - return true; - } - - NonnullRefPtr m_parent_process_directory; -}; - -NonnullRefPtr ProcFSProcessFolder::create(const Process& process) -{ - auto folder = adopt_ref_if_nonnull(new (nothrow) ProcFSProcessFolder(process)).release_nonnull(); - folder->m_components.append(ProcFSProcessUnveil::create(folder)); - folder->m_components.append(ProcFSProcessPerformanceEvents::create(folder)); - folder->m_components.append(ProcFSProcessFileDescriptions::create(folder)); - folder->m_components.append(ProcFSProcessOverallFileDescriptions::create(folder)); - folder->m_components.append(ProcFSProcessRoot::create(folder)); - folder->m_components.append(ProcFSProcessVirtualMemory::create(folder)); - folder->m_components.append(ProcFSProcessCurrentWorkDirectory::create(folder)); - folder->m_components.append(ProcFSProcessBinary::create(folder)); - folder->m_components.append(ProcFSProcessStacks::create(folder)); - return folder; -} - -ProcFSProcessFolder::ProcFSProcessFolder(const Process& process) - : ProcFSExposedFolder(String::formatted("{:d}", process.pid().value()), ProcFSComponentsRegistrar::the().root_folder()) - , m_associated_process(process) -{ -} - KResultOr ProcFSExposedLink::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription*) const { VERIFY(offset == 0); @@ -1604,34 +251,4 @@ KResult ProcFSExposedFolder::traverse_as_directory(unsigned fsid, Function ProcFSRootFolder::lookup(StringView name) -{ - if (auto candidate = ProcFSExposedFolder::lookup(name); !candidate.is_null()) - return candidate; - - for (auto& component : m_process_folders) { - if (component.name() == name) { - return component; - } - } - return {}; -} - -KResult ProcFSRootFolder::traverse_as_directory(unsigned fsid, Function callback) const -{ - Locker locker(ProcFSComponentsRegistrar::the().m_lock); - callback({ ".", { fsid, component_index() }, 0 }); - callback({ "..", { fsid, 0 }, 0 }); - - for (auto& component : m_components) { - InodeIdentifier identifier = { fsid, component.component_index() }; - callback({ component.name(), identifier, 0 }); - } - for (auto& component : m_process_folders) { - InodeIdentifier identifier = { fsid, component.component_index() }; - callback({ component.name(), identifier, 0 }); - } - return KSuccess; -} - } diff --git a/Kernel/ProcessSpecificExposed.cpp b/Kernel/ProcessSpecificExposed.cpp new file mode 100644 index 0000000000..45b244649c --- /dev/null +++ b/Kernel/ProcessSpecificExposed.cpp @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class ProcFSProcessStacks; +class ProcFSThreadStack final : public ProcFSProcessInformation { +public: + // Note: We pass const ProcFSProcessStacks& to enforce creation with this type of folder + static NonnullRefPtr create(const ProcFSProcessFolder& process_folder, const ProcFSProcessStacks&, const Thread& thread) + { + return adopt_ref(*new (nothrow) ProcFSThreadStack(process_folder, thread)); + } + +private: + explicit ProcFSThreadStack(const ProcFSProcessFolder& process_folder, const Thread& thread) + : ProcFSProcessInformation(String::formatted("{}", thread.tid()), process_folder) + , m_associated_thread(thread) + { + } + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + bool show_kernel_addresses = Process::current()->is_superuser(); + bool kernel_address_added = false; + for (auto address : Processor::capture_stack_trace(*m_associated_thread, 1024)) { + if (!show_kernel_addresses && !is_user_address(VirtualAddress { address })) { + if (kernel_address_added) + continue; + address = 0xdeadc0de; + kernel_address_added = true; + } + array.add(JsonValue(address)); + } + + array.finish(); + return true; + } + + NonnullRefPtr m_associated_thread; +}; + +class ProcFSProcessStacks final : public ProcFSExposedFolder { + // Note: This folder is special, because everything that is created here is dynamic! + // This means we don't register anything in the m_components Vector, and every inode + // is created in runtime when called to get it + // Every ProcFSThreadStack (that represents a thread stack) is created only as a temporary object + // therefore, we don't use m_components so when we are done with the ProcFSThreadStack object, + // It should be deleted (as soon as possible) +public: + virtual KResultOr entries_count() const override; + virtual KResult traverse_as_directory(unsigned, Function) const override; + virtual RefPtr lookup(StringView name) override; + + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + auto folder = adopt_ref(*new (nothrow) ProcFSProcessStacks(parent_folder)); + return folder; + } + + virtual void prepare_for_deletion() override + { + ProcFSExposedFolder::prepare_for_deletion(); + m_process_folder.clear(); + } + +private: + ProcFSProcessStacks(const ProcFSProcessFolder& parent_folder) + : ProcFSExposedFolder("stacks"sv, parent_folder) + , m_process_folder(parent_folder) + { + } + RefPtr m_process_folder; + mutable Lock m_lock; +}; + +KResultOr ProcFSProcessStacks::entries_count() const +{ + Locker locker(m_lock); + auto process = m_process_folder->m_associated_process; + return process->thread_count(); +} + +KResult ProcFSProcessStacks::traverse_as_directory(unsigned fsid, Function callback) const +{ + Locker locker(m_lock); + callback({ ".", { fsid, component_index() }, 0 }); + callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); + + auto process = m_process_folder->m_associated_process; + process->for_each_thread([&](const Thread& thread) { + int tid = thread.tid().value(); + InodeIdentifier identifier = { fsid, thread.global_procfs_inode_index() }; + callback({ String::number(tid), identifier, 0 }); + }); + return KSuccess; +} + +RefPtr ProcFSProcessStacks::lookup(StringView name) +{ + Locker locker(m_lock); + auto process = m_process_folder->m_associated_process; + RefPtr procfd_stack; + // FIXME: Try to exit the loop earlier + process->for_each_thread([&](const Thread& thread) { + int tid = thread.tid().value(); + if (name == String::number(tid)) { + procfd_stack = ProcFSThreadStack::create(*m_process_folder, *this, thread); + } + }); + return procfd_stack; +} + +class ProcFSProcessFileDescriptions; +class ProcFSProcessFileDescription final : public ProcFSExposedLink { +public: + // Note: we pass const ProcFSProcessFileDescriptions& just to enforce creation of this in the correct folder. + static NonnullRefPtr create(unsigned fd_number, const FileDescription& fd, InodeIndex preallocated_index, const ProcFSProcessFileDescriptions&) + { + return adopt_ref(*new (nothrow) ProcFSProcessFileDescription(fd_number, fd, preallocated_index)); + } + +private: + explicit ProcFSProcessFileDescription(unsigned fd_number, const FileDescription& fd, InodeIndex preallocated_index) + : ProcFSExposedLink(String::formatted("{}", fd_number), preallocated_index) + , m_associated_file_description(fd) + { + } + virtual bool acquire_link(KBufferBuilder& builder) override + { + builder.append_bytes(m_associated_file_description->absolute_path().bytes()); + return true; + } + + NonnullRefPtr m_associated_file_description; +}; + +class ProcFSProcessFileDescriptions final : public ProcFSExposedFolder { + // Note: This folder is special, because everything that is created here is dynamic! + // This means we don't register anything in the m_components Vector, and every inode + // is created in runtime when called to get it + // Every ProcFSProcessFileDescription (that represents a file descriptor) is created only as a temporary object + // therefore, we don't use m_components so when we are done with the ProcFSProcessFileDescription object, + // It should be deleted (as soon as possible) +public: + virtual KResultOr entries_count() const override; + virtual KResult traverse_as_directory(unsigned, Function) const override; + virtual RefPtr lookup(StringView name) override; + + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessFileDescriptions(parent_folder)); + } + + virtual void prepare_for_deletion() override + { + ProcFSExposedFolder::prepare_for_deletion(); + m_process_folder.clear(); + } + +private: + explicit ProcFSProcessFileDescriptions(const ProcFSProcessFolder& parent_folder) + : ProcFSExposedFolder("fd"sv, parent_folder) + , m_process_folder(parent_folder) + { + } + RefPtr m_process_folder; + mutable Lock m_lock; +}; + +KResultOr ProcFSProcessFileDescriptions::entries_count() const +{ + Locker locker(m_lock); + return m_process_folder->m_associated_process->fds().open_count(); +} +KResult ProcFSProcessFileDescriptions::traverse_as_directory(unsigned fsid, Function callback) const +{ + Locker locker(m_lock); + callback({ ".", { fsid, component_index() }, 0 }); + callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); + + auto process = m_process_folder->m_associated_process; + size_t count = 0; + m_process_folder->m_associated_process->fds().enumerate([&](auto& file_description_metadata) { + if (!file_description_metadata.is_valid()) { + count++; + return; + } + InodeIdentifier identifier = { fsid, file_description_metadata.global_procfs_inode_index() }; + callback({ String::number(count), identifier, 0 }); + count++; + }); + return KSuccess; +} +RefPtr ProcFSProcessFileDescriptions::lookup(StringView name) +{ + Locker locker(m_lock); + auto process = m_process_folder->m_associated_process; + RefPtr procfd_fd; + // FIXME: Try to exit the loop earlier + size_t count = 0; + m_process_folder->m_associated_process->fds().enumerate([&](auto& file_description_metadata) { + if (!file_description_metadata.is_valid()) { + count++; + return; + } + if (name == String::number(count)) { + procfd_fd = ProcFSProcessFileDescription::create(count, *file_description_metadata.description(), file_description_metadata.global_procfs_inode_index(), *this); + } + count++; + }); + return procfd_fd; +} + +class ProcFSProcessUnveil final : public ProcFSProcessInformation { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessUnveil(parent_folder)); + } + +private: + explicit ProcFSProcessUnveil(const ProcFSProcessFolder& parent_folder) + : ProcFSProcessInformation("unveil"sv, parent_folder) + { + } + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + for (auto& unveiled_path : m_parent_folder->m_associated_process->unveiled_paths()) { + if (!unveiled_path.was_explicitly_unveiled()) + continue; + auto obj = array.add_object(); + obj.add("path", unveiled_path.path()); + StringBuilder permissions_builder; + if (unveiled_path.permissions() & UnveilAccess::Read) + permissions_builder.append('r'); + if (unveiled_path.permissions() & UnveilAccess::Write) + permissions_builder.append('w'); + if (unveiled_path.permissions() & UnveilAccess::Execute) + permissions_builder.append('x'); + if (unveiled_path.permissions() & UnveilAccess::CreateOrRemove) + permissions_builder.append('c'); + if (unveiled_path.permissions() & UnveilAccess::Browse) + permissions_builder.append('b'); + obj.add("permissions", permissions_builder.to_string()); + } + array.finish(); + return true; + } +}; + +class ProcFSProcessPerformanceEvents final : public ProcFSProcessInformation { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessPerformanceEvents(parent_folder)); + } + +private: + explicit ProcFSProcessPerformanceEvents(const ProcFSProcessFolder& parent_folder) + : ProcFSProcessInformation("perf_events"sv, parent_folder) + { + } + virtual bool output(KBufferBuilder& builder) override + { + InterruptDisabler disabler; + if (!m_parent_folder->m_associated_process->perf_events()) { + dbgln("ProcFS: No perf events for {}", m_parent_folder->m_associated_process->pid()); + return false; + } + return m_parent_folder->m_associated_process->perf_events()->to_json(builder); + } +}; + +class ProcFSProcessOverallFileDescriptions final : public ProcFSProcessInformation { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessOverallFileDescriptions(parent_folder)); + } + +private: + explicit ProcFSProcessOverallFileDescriptions(const ProcFSProcessFolder& parent_folder) + : ProcFSProcessInformation("fds"sv, parent_folder) + { + } + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + auto process = m_parent_folder->m_associated_process; + if (process->fds().open_count() == 0) { + array.finish(); + return true; + } + + size_t count = 0; + process->fds().enumerate([&](auto& file_description_metadata) { + if (!file_description_metadata.is_valid()) { + count++; + return; + } + bool cloexec = file_description_metadata.flags() & FD_CLOEXEC; + RefPtr description = file_description_metadata.description(); + auto description_object = array.add_object(); + description_object.add("fd", count); + description_object.add("absolute_path", description->absolute_path()); + description_object.add("seekable", description->file().is_seekable()); + description_object.add("class", description->file().class_name()); + description_object.add("offset", description->offset()); + description_object.add("cloexec", cloexec); + description_object.add("blocking", description->is_blocking()); + description_object.add("can_read", description->can_read()); + description_object.add("can_write", description->can_write()); + count++; + }); + + array.finish(); + return true; + } +}; + +class ProcFSProcessRoot final : public ProcFSExposedLink { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessRoot(parent_folder)); + } + +private: + explicit ProcFSProcessRoot(const ProcFSProcessFolder& parent_folder) + : ProcFSExposedLink("root"sv) + , m_parent_process_directory(parent_folder) + { + } + virtual bool acquire_link(KBufferBuilder& builder) override + { + builder.append_bytes(m_parent_process_directory->m_associated_process->root_directory_relative_to_global_root().absolute_path().to_byte_buffer()); + return true; + } + NonnullRefPtr m_parent_process_directory; +}; + +class ProcFSProcessVirtualMemory final : public ProcFSProcessInformation { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessVirtualMemory(parent_folder)); + } + +private: + explicit ProcFSProcessVirtualMemory(const ProcFSProcessFolder& parent_folder) + : ProcFSProcessInformation("vm"sv, parent_folder) + { + } + virtual bool output(KBufferBuilder& builder) override + { + auto process = m_parent_folder->m_associated_process; + JsonArraySerializer array { builder }; + { + ScopedSpinLock lock(process->space().get_lock()); + for (auto& region : process->space().regions()) { + if (!region->is_user() && !Process::current()->is_superuser()) + continue; + auto region_object = array.add_object(); + region_object.add("readable", region->is_readable()); + region_object.add("writable", region->is_writable()); + region_object.add("executable", region->is_executable()); + region_object.add("stack", region->is_stack()); + region_object.add("shared", region->is_shared()); + region_object.add("syscall", region->is_syscall_region()); + region_object.add("purgeable", region->vmobject().is_anonymous()); + if (region->vmobject().is_anonymous()) { + region_object.add("volatile", static_cast(region->vmobject()).is_any_volatile()); + } + region_object.add("cacheable", region->is_cacheable()); + region_object.add("address", region->vaddr().get()); + region_object.add("size", region->size()); + region_object.add("amount_resident", region->amount_resident()); + region_object.add("amount_dirty", region->amount_dirty()); + region_object.add("cow_pages", region->cow_pages()); + region_object.add("name", region->name()); + region_object.add("vmobject", region->vmobject().class_name()); + + StringBuilder pagemap_builder; + for (size_t i = 0; i < region->page_count(); ++i) { + auto* page = region->physical_page(i); + if (!page) + pagemap_builder.append('N'); + else if (page->is_shared_zero_page() || page->is_lazy_committed_page()) + pagemap_builder.append('Z'); + else + pagemap_builder.append('P'); + } + region_object.add("pagemap", pagemap_builder.to_string()); + } + } + array.finish(); + return true; + } +}; + +class ProcFSProcessCurrentWorkDirectory final : public ProcFSExposedLink { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessCurrentWorkDirectory(parent_folder)); + } + +private: + explicit ProcFSProcessCurrentWorkDirectory(const ProcFSProcessFolder& parent_folder) + : ProcFSExposedLink("cwd"sv) + , m_parent_process_directory(parent_folder) + { + } + virtual bool acquire_link(KBufferBuilder& builder) override + { + builder.append_bytes(m_parent_process_directory->m_associated_process->current_directory().absolute_path().bytes()); + return true; + } + + NonnullRefPtr m_parent_process_directory; +}; + +class ProcFSProcessBinary final : public ProcFSExposedLink { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessBinary(parent_folder)); + } + + virtual mode_t required_mode() const override + { + if (!m_parent_process_directory->m_associated_process->executable()) + return 0; + return ProcFSExposedComponent::required_mode(); + } + +private: + explicit ProcFSProcessBinary(const ProcFSProcessFolder& parent_folder) + : ProcFSExposedLink("exe"sv) + , m_parent_process_directory(parent_folder) + { + } + virtual bool acquire_link(KBufferBuilder& builder) override + { + auto* custody = m_parent_process_directory->m_associated_process->executable(); + if (!custody) + return false; + builder.append(custody->absolute_path().bytes()); + return true; + } + + NonnullRefPtr m_parent_process_directory; +}; + +NonnullRefPtr ProcFSProcessFolder::create(const Process& process) +{ + auto folder = adopt_ref_if_nonnull(new (nothrow) ProcFSProcessFolder(process)).release_nonnull(); + folder->m_components.append(ProcFSProcessUnveil::create(folder)); + folder->m_components.append(ProcFSProcessPerformanceEvents::create(folder)); + folder->m_components.append(ProcFSProcessFileDescriptions::create(folder)); + folder->m_components.append(ProcFSProcessOverallFileDescriptions::create(folder)); + folder->m_components.append(ProcFSProcessRoot::create(folder)); + folder->m_components.append(ProcFSProcessVirtualMemory::create(folder)); + folder->m_components.append(ProcFSProcessCurrentWorkDirectory::create(folder)); + folder->m_components.append(ProcFSProcessBinary::create(folder)); + folder->m_components.append(ProcFSProcessStacks::create(folder)); + return folder; +} + +ProcFSProcessFolder::ProcFSProcessFolder(const Process& process) + : ProcFSExposedFolder(String::formatted("{:d}", process.pid().value()), ProcFSComponentsRegistrar::the().root_folder()) + , m_associated_process(process) +{ +} + +}