diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 9652fbace8..d073b6c8de 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -148,6 +148,7 @@ set(KERNEL_SOURCES Panic.cpp PerformanceEventBuffer.cpp Process.cpp + ProcessExposed.cpp ProcessGroup.cpp RTC.cpp Random.cpp diff --git a/Kernel/Devices/USB/UHCIController.cpp b/Kernel/Devices/USB/UHCIController.cpp index 153c825b58..9c68018c92 100644 --- a/Kernel/Devices/USB/UHCIController.cpp +++ b/Kernel/Devices/USB/UHCIController.cpp @@ -5,12 +5,17 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -65,6 +70,144 @@ static constexpr u16 UHCI_PORTSC_SUSPEND = 0x1000; static constexpr u8 UHCI_NUMBER_OF_ISOCHRONOUS_TDS = 128; static constexpr u16 UHCI_NUMBER_OF_FRAMES = 1024; +class ProcFSUSBBusFolder; +static ProcFSUSBBusFolder* s_procfs_usb_bus_folder; + +class ProcFSUSBDeviceInformation : public ProcFSGlobalInformation { + friend class ProcFSUSBBusFolder; + +public: + virtual ~ProcFSUSBDeviceInformation() override {}; + + static NonnullRefPtr create(USB::Device&); + + RefPtr device() const { return m_device; } + +protected: + explicit ProcFSUSBDeviceInformation(USB::Device& device) + : ProcFSGlobalInformation(String::formatted("{}", device.address())) + , m_device(device) + { + } + virtual bool output(KBufferBuilder& builder) override + { + VERIFY(m_device); // Something has gone very wrong if this isn't true + + JsonArraySerializer array { builder }; + + auto obj = array.add_object(); + obj.add("usb_spec_compliance_bcd", m_device->device_descriptor().usb_spec_compliance_bcd); + obj.add("device_class", m_device->device_descriptor().device_class); + obj.add("device_sub_class", m_device->device_descriptor().device_sub_class); + obj.add("device_protocol", m_device->device_descriptor().device_protocol); + obj.add("max_packet_size", m_device->device_descriptor().max_packet_size); + obj.add("vendor_id", m_device->device_descriptor().vendor_id); + obj.add("product_id", m_device->device_descriptor().product_id); + obj.add("device_release_bcd", m_device->device_descriptor().device_release_bcd); + obj.add("manufacturer_id_descriptor_index", m_device->device_descriptor().manufacturer_id_descriptor_index); + obj.add("product_string_descriptor_index", m_device->device_descriptor().product_string_descriptor_index); + obj.add("serial_number_descriptor_index", m_device->device_descriptor().serial_number_descriptor_index); + obj.add("num_configurations", m_device->device_descriptor().num_configurations); + obj.finish(); + array.finish(); + return true; + } + IntrusiveListNode> m_list_node; + RefPtr m_device; +}; + +class ProcFSUSBBusFolder final : public ProcFSExposedFolder { + friend class ProcFSComponentsRegistrar; + +public: + static void initialize(); + void plug(USB::Device&); + void unplug(USB::Device&); + + virtual KResultOr entries_count() const override; + virtual KResult traverse_as_directory(unsigned, Function) const override; + virtual RefPtr lookup(StringView name) override; + +private: + ProcFSUSBBusFolder(const ProcFSBusDirectory&); + + RefPtr device_node_for(USB::Device& device); + + IntrusiveList, &ProcFSUSBDeviceInformation::m_list_node> m_device_nodes; + mutable SpinLock m_lock; +}; + +KResultOr ProcFSUSBBusFolder::entries_count() const +{ + ScopedSpinLock lock(m_lock); + return m_device_nodes.size_slow(); +} +KResult ProcFSUSBBusFolder::traverse_as_directory(unsigned fsid, Function callback) const +{ + ScopedSpinLock lock(m_lock); + VERIFY(m_parent_folder); + callback({ ".", { fsid, component_index() }, 0 }); + callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); + + for (auto& device_node : m_device_nodes) { + InodeIdentifier identifier = { fsid, device_node.component_index() }; + callback({ device_node.name(), identifier, 0 }); + } + return KSuccess; +} +RefPtr ProcFSUSBBusFolder::lookup(StringView name) +{ + ScopedSpinLock lock(m_lock); + for (auto& device_node : m_device_nodes) { + if (device_node.name() == name) { + return device_node; + } + } + return {}; +} + +RefPtr ProcFSUSBBusFolder::device_node_for(USB::Device& device) +{ + RefPtr checked_device = device; + for (auto& device_node : m_device_nodes) { + if (device_node.device().ptr() == checked_device.ptr()) + return device_node; + } + return {}; +} + +void ProcFSUSBBusFolder::plug(USB::Device& new_device) +{ + ScopedSpinLock lock(m_lock); + auto device_node = device_node_for(new_device); + VERIFY(!device_node); + m_device_nodes.append(ProcFSUSBDeviceInformation::create(new_device)); +} +void ProcFSUSBBusFolder::unplug(USB::Device& deleted_device) +{ + ScopedSpinLock lock(m_lock); + auto device_node = device_node_for(deleted_device); + VERIFY(device_node); + device_node->m_list_node.remove(); +} + +UNMAP_AFTER_INIT ProcFSUSBBusFolder::ProcFSUSBBusFolder(const ProcFSBusDirectory& buses_folder) + : ProcFSExposedFolder("usb"sv, buses_folder) +{ +} + +UNMAP_AFTER_INIT void ProcFSUSBBusFolder::initialize() +{ + auto folder = adopt_ref(*new ProcFSUSBBusFolder(ProcFSComponentsRegistrar::the().buses_folder())); + ProcFSComponentsRegistrar::the().register_new_bus_folder(folder); + s_procfs_usb_bus_folder = folder; +} + +NonnullRefPtr ProcFSUSBDeviceInformation::create(USB::Device& device) +{ + return adopt_ref(*new ProcFSUSBDeviceInformation(device)); +} + UHCIController& UHCIController::the() { return *s_the; @@ -75,6 +218,10 @@ UNMAP_AFTER_INIT void UHCIController::detect() if (kernel_command_line().disable_uhci_controller()) return; + // FIXME: We create the /proc/bus/usb representation here, but it should really be handled + // in a more broad singleton than this once we refactor things in USB subsystem. + ProcFSUSBBusFolder::initialize(); + PCI::enumerate([&](const PCI::Address& address, PCI::ID id) { if (address.is_null()) return; @@ -569,8 +716,14 @@ void UHCIController::spawn_port_proc() dmesgln("UHCI: Device creation failed on port 1 ({})", device.error()); m_devices.at(0) = device.value(); + VERIFY(s_procfs_usb_bus_folder); + s_procfs_usb_bus_folder->plug(device.value()); } else { + // FIXME: Clean up (and properly) the RefPtr to the device in m_devices + VERIFY(s_procfs_usb_bus_folder); + VERIFY(m_devices.at(0)); dmesgln("UHCI: Device detach detected on Root Port 1"); + s_procfs_usb_bus_folder->unplug(*m_devices.at(0)); } } } else { @@ -601,8 +754,14 @@ void UHCIController::spawn_port_proc() dmesgln("UHCI: Device creation failed on port 2 ({})", device.error()); m_devices.at(1) = device.value(); + VERIFY(s_procfs_usb_bus_folder); + s_procfs_usb_bus_folder->plug(device.value()); } else { + // FIXME: Clean up (and properly) the RefPtr to the device in m_devices + VERIFY(s_procfs_usb_bus_folder); + VERIFY(m_devices.at(1)); dmesgln("UHCI: Device detach detected on Root Port 2"); + s_procfs_usb_bus_folder->unplug(*m_devices.at(1)); } } } diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 3bf3d79d7d..aae7bbfbe4 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -1,264 +1,66 @@ /* * Copyright (c) 2018-2021, Andreas Kling * Copyright (c) 2021, Spencer Dixon + * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include namespace Kernel { -enum ProcParentDirectory { - PDI_AbstractRoot = 0, - PDI_Root, - PDI_Root_bus, - PDI_Root_bus_usb, - PDI_Root_sys, - PDI_Root_net, - PDI_PID, - PDI_PID_fd, - PDI_PID_stacks, -}; -static_assert(PDI_PID_stacks < 16, "Too many directories for identifier scheme"); +static AK::Singleton s_the; -enum ProcFileType { - FI_Invalid = 0, - - FI_Root = 1, // directory - - __FI_Root_Start, - FI_Root_df, - FI_Root_all, - FI_Root_memstat, - FI_Root_cpuinfo, - FI_Root_dmesg, - FI_Root_interrupts, - FI_Root_keymap, - FI_Root_pci, - FI_Root_devices, - FI_Root_uptime, - FI_Root_cmdline, - FI_Root_modules, - FI_Root_profile, - FI_Root_self, // symlink - FI_Root_sys, // directory - FI_Root_net, // directory - FI_Root_bus, // directory - __FI_Root_End, - - FI_Root_sys_variable, - - FI_Root_net_adapters, - FI_Root_net_arp, - FI_Root_net_tcp, - FI_Root_net_udp, - FI_Root_net_local, - - FI_Root_bus_usb, - FI_Root_bus_usb_device, - - FI_PID, - - __FI_PID_Start, - FI_PID_perf_events, - FI_PID_vm, - FI_PID_stacks, // directory - FI_PID_fds, - FI_PID_unveil, - FI_PID_exe, // symlink - FI_PID_cwd, // symlink - FI_PID_root, // symlink - FI_PID_fd, // directory - __FI_PID_End, - - FI_MaxStaticFileIndex, -}; - -static inline ProcessID to_pid(const InodeIdentifier& identifier) +ProcFSComponentsRegistrar& ProcFSComponentsRegistrar::the() { - return identifier.index().value() >> 16u; + return *s_the; } -static inline ThreadID to_tid(const InodeIdentifier& identifier) +UNMAP_AFTER_INIT void ProcFSComponentsRegistrar::initialize() { - // Sneakily, use the exact same mechanism. - return to_pid(identifier).value(); + VERIFY(!s_the.is_initialized()); + s_the.ensure_instance(); } -static inline ProcParentDirectory to_proc_parent_directory(const InodeIdentifier& identifier) +UNMAP_AFTER_INIT ProcFSComponentsRegistrar::ProcFSComponentsRegistrar() + : m_root_folder(ProcFSRootFolder::must_create()) { - return (ProcParentDirectory)((identifier.index().value() >> 12) & 0xf); } -static inline ProcFileType to_proc_file_type(const InodeIdentifier& identifier) +const ProcFSBusDirectory& ProcFSComponentsRegistrar::buses_folder() const { - return (ProcFileType)(identifier.index().value() & 0xfff); + return *m_root_folder->m_buses_folder; } -static inline int to_fd(const InodeIdentifier& identifier) +void ProcFSComponentsRegistrar::register_new_bus_folder(ProcFSExposedFolder& new_bus_folder) { - VERIFY(to_proc_parent_directory(identifier) == PDI_PID_fd); - return (identifier.index().value() & 0xfff) - FI_MaxStaticFileIndex; + VERIFY(!m_root_folder->m_buses_folder.is_null()); + m_root_folder->m_buses_folder->m_components.append(new_bus_folder); } -static inline size_t to_sys_index(const InodeIdentifier& identifier) +void ProcFSComponentsRegistrar::register_new_process(Process& new_process) { - VERIFY(to_proc_parent_directory(identifier) == PDI_Root_sys); - VERIFY(to_proc_file_type(identifier) == FI_Root_sys_variable); - return identifier.index().value() >> 16u; + Locker locker(m_lock); + m_root_folder->m_process_folders.append(ProcFSProcessFolder::create(new_process)); } -static inline u8 to_usb_device_address(const InodeIdentifier& identifier) +void ProcFSComponentsRegistrar::unregister_process(Process& deleted_process) { - VERIFY(to_proc_parent_directory(identifier) == PDI_Root_bus_usb); - VERIFY(to_proc_file_type(identifier) == FI_Root_bus_usb_device); - return (identifier.index().value() >> 16u) & 0xff; + auto process_folder = m_root_folder->process_folder_for(deleted_process); + VERIFY(process_folder); + process_folder->m_list_node.remove(); } -static inline InodeIdentifier to_identifier(unsigned fsid, ProcParentDirectory parent, ProcessID pid, ProcFileType proc_file_type) -{ - return { fsid, ((unsigned)parent << 12u) | ((unsigned)pid.value() << 16u) | (unsigned)proc_file_type }; -} - -static inline InodeIdentifier to_identifier_with_fd(unsigned fsid, ProcessID pid, int fd) -{ - return { fsid, (PDI_PID_fd << 12u) | ((unsigned)pid.value() << 16u) | (FI_MaxStaticFileIndex + fd) }; -} - -static inline InodeIdentifier to_identifier_with_stack(unsigned fsid, ThreadID tid) -{ - return { fsid, (PDI_PID_stacks << 12u) | ((unsigned)tid.value() << 16u) | FI_MaxStaticFileIndex }; -} - -static inline InodeIdentifier sys_var_to_identifier(unsigned fsid, unsigned index) -{ - VERIFY(index < 256); - return { fsid, (PDI_Root_sys << 12u) | (index << 16u) | FI_Root_sys_variable }; -} - -static inline InodeIdentifier usb_device_address_to_identifier(unsigned fsid, unsigned device_address) -{ - VERIFY(device_address < 127); - return { fsid, (PDI_Root_bus_usb << 12u) | (device_address << 16u) | FI_Root_bus_usb_device }; -} - -static inline InodeIdentifier to_parent_id(const InodeIdentifier& identifier) -{ - switch (to_proc_parent_directory(identifier)) { - case PDI_AbstractRoot: - case PDI_Root: - return { identifier.fsid(), FI_Root }; - case PDI_Root_sys: - return { identifier.fsid(), FI_Root_sys }; - case PDI_Root_net: - return { identifier.fsid(), FI_Root_net }; - case PDI_Root_bus: - return { identifier.fsid(), FI_Root_bus }; - case PDI_Root_bus_usb: - return to_identifier(identifier.fsid(), PDI_Root_bus, to_pid(identifier), FI_Root_bus_usb); - case PDI_PID: - return to_identifier(identifier.fsid(), PDI_Root, to_pid(identifier), FI_PID); - case PDI_PID_fd: - return to_identifier(identifier.fsid(), PDI_PID, to_pid(identifier), FI_PID_fd); - case PDI_PID_stacks: - return to_identifier(identifier.fsid(), PDI_PID, to_pid(identifier), FI_PID_stacks); - } - VERIFY_NOT_REACHED(); -} - -#if 0 -static inline u8 to_unused_metadata(const InodeIdentifier& identifier) -{ - return (identifier.index() >> 8) & 0xf; -} -#endif - -static inline bool is_process_related_file(const InodeIdentifier& identifier) -{ - if (to_proc_file_type(identifier) == FI_PID) - return true; - auto proc_parent_directory = to_proc_parent_directory(identifier); - switch (proc_parent_directory) { - case PDI_PID: - case PDI_PID_fd: - return true; - default: - return false; - } -} - -static inline bool is_thread_related_file(const InodeIdentifier& identifier) -{ - auto proc_parent_directory = to_proc_parent_directory(identifier); - return proc_parent_directory == PDI_PID_stacks; -} - -static inline bool is_directory(const InodeIdentifier& identifier) -{ - auto proc_file_type = to_proc_file_type(identifier); - switch (proc_file_type) { - case FI_Root: - case FI_Root_sys: - case FI_Root_net: - case FI_Root_bus: - case FI_Root_bus_usb: - case FI_PID: - case FI_PID_fd: - case FI_PID_stacks: - return true; - default: - return false; - } -} - -static inline bool is_persistent_inode(const InodeIdentifier& identifier) -{ - return to_proc_parent_directory(identifier) == PDI_Root_sys; -} - -struct ProcFSInodeData : public FileDescriptionData { - RefPtr buffer; -}; - RefPtr ProcFS::create() { return adopt_ref_if_nonnull(new (nothrow) ProcFS); @@ -268,1478 +70,112 @@ ProcFS::~ProcFS() { } -static bool procfs$pid_fds(InodeIdentifier identifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - - auto process = Process::from_pid(to_pid(identifier)); - if (!process) { - array.finish(); - return true; - } - if (process->number_of_open_file_descriptors() == 0) { - array.finish(); - return true; - } - - for (int i = 0; i < process->max_open_file_descriptors(); ++i) { - auto description = process->file_description(i); - if (!description) - continue; - bool cloexec = process->fd_flags(i) & FD_CLOEXEC; - - auto description_object = array.add_object(); - description_object.add("fd", i); - 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()); - } - array.finish(); - return true; -} - -static bool procfs$pid_fd_entry(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - int fd = to_fd(identifier); - auto description = process->file_description(fd); - if (!description) - return false; - builder.append_bytes(description->absolute_path().bytes()); - return true; -} - -static bool procfs$pid_vm(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - 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; -} - -static bool procfs$pci(InodeIdentifier, KBufferBuilder& builder) -{ - 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; -} - -static bool procfs$interrupts(InodeIdentifier, KBufferBuilder& builder) -{ - 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; -} - -static bool procfs$keymap(InodeIdentifier, KBufferBuilder& builder) -{ - JsonObjectSerializer json { builder }; - json.add("keymap", HIDManagement::the().keymap_name()); - json.finish(); - return true; -} - -static bool procfs$devices(InodeIdentifier, KBufferBuilder& builder) -{ - 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; -} - -static bool procfs$uptime(InodeIdentifier, KBufferBuilder& builder) -{ - builder.appendff("{}\n", TimeManagement::the().uptime_ms() / 1000); - return true; -} - -static bool procfs$cmdline(InodeIdentifier, KBufferBuilder& builder) -{ - builder.append(kernel_command_line().string()); - builder.append('\n'); - return true; -} - -static bool procfs$modules(InodeIdentifier, KBufferBuilder& builder) -{ - 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; -} - -static bool procfs$profile(InodeIdentifier, KBufferBuilder& builder) -{ - extern PerformanceEventBuffer* g_global_perf_events; - if (!g_global_perf_events) - return false; - - return g_global_perf_events->to_json(builder); -} - -static bool procfs$pid_perf_events(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - InterruptDisabler disabler; - if (!process->perf_events()) - return false; - return process->perf_events()->to_json(builder); -} - -static bool procfs$net_adapters(InodeIdentifier, KBufferBuilder& builder) -{ - 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; -} - -static bool procfs$net_arp(InodeIdentifier, KBufferBuilder& builder) -{ - 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; -} - -static bool procfs$net_tcp(InodeIdentifier, KBufferBuilder& builder) -{ - 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; -} - -static bool procfs$usb_entry(InodeIdentifier identifier, KBufferBuilder& builder) -{ - u8 dev_id = to_usb_device_address(identifier); - auto const& device = USB::UHCIController::the().get_device_from_address(dev_id); - VERIFY(device); // Something has gone very wrong if this isn't true - - JsonArraySerializer array { builder }; - - auto obj = array.add_object(); - obj.add("usb_spec_compliance_bcd", device->device_descriptor().usb_spec_compliance_bcd); - obj.add("device_class", device->device_descriptor().device_class); - obj.add("device_sub_class", device->device_descriptor().device_sub_class); - obj.add("device_protocol", device->device_descriptor().device_protocol); - obj.add("max_packet_size", device->device_descriptor().max_packet_size); - obj.add("vendor_id", device->device_descriptor().vendor_id); - obj.add("product_id", device->device_descriptor().product_id); - obj.add("device_release_bcd", device->device_descriptor().device_release_bcd); - obj.add("manufacturer_id_descriptor_index", device->device_descriptor().manufacturer_id_descriptor_index); - obj.add("product_string_descriptor_index", device->device_descriptor().product_string_descriptor_index); - obj.add("serial_number_descriptor_index", device->device_descriptor().serial_number_descriptor_index); - obj.add("num_configurations", device->device_descriptor().num_configurations); - obj.finish(); - array.finish(); - - return true; -} - -static bool procfs$net_udp(InodeIdentifier, KBufferBuilder& builder) -{ - 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; -} - -static bool procfs$net_local(InodeIdentifier, KBufferBuilder& builder) -{ - 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; -} - -static bool procfs$pid_unveil(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - JsonArraySerializer array { builder }; - for (auto& unveiled_path : 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; -} - -static bool procfs$tid_stack(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto thread = Thread::from_tid(to_tid(identifier)); - if (!thread) - return false; - - JsonArraySerializer array { builder }; - bool show_kernel_addresses = Process::current()->is_superuser(); - bool kernel_address_added = false; - for (auto address : Processor::capture_stack_trace(*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; -} - -static bool procfs$pid_exe(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - auto* custody = process->executable(); - VERIFY(custody); - builder.append(custody->absolute_path().bytes()); - return true; -} - -static bool procfs$pid_cwd(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - builder.append_bytes(process->current_directory().absolute_path().bytes()); - return true; -} - -static bool procfs$pid_root(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - builder.append_bytes(process->root_directory_relative_to_global_root().absolute_path().to_byte_buffer()); - return true; -} - -static bool procfs$self(InodeIdentifier, KBufferBuilder& builder) -{ - builder.appendff("{}", Process::current()->pid().value()); - return true; -} - -static bool procfs$dmesg(InodeIdentifier, KBufferBuilder& builder) -{ - InterruptDisabler disabler; - for (char ch : ConsoleDevice::the().logbuffer()) - builder.append(ch); - return true; -} - -static bool procfs$df(InodeIdentifier, KBufferBuilder& builder) -{ - // FIXME: This is obviously racy against the VFS mounts changing. - 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; -} - -static bool procfs$cpuinfo(InodeIdentifier, KBufferBuilder& builder) -{ - 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; -} - -static bool procfs$memstat(InodeIdentifier, KBufferBuilder& builder) -{ - 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; -} - -static bool procfs$all(InodeIdentifier, KBufferBuilder& builder) -{ - 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.number_of_open_file_descriptors()); - 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; -} - -struct SysVariable { - String name; - enum class Type : u8 { - Invalid, - Boolean, - String, - }; - Type type { Type::Invalid }; - Function notify_callback; - void* address { nullptr }; - - static SysVariable& for_inode(InodeIdentifier); - - void notify() - { - if (notify_callback) - notify_callback(); - } -}; - -static Vector* s_sys_variables; - -static inline Vector& sys_variables() -{ - if (s_sys_variables == nullptr) { - s_sys_variables = new Vector; - s_sys_variables->append({ "", SysVariable::Type::Invalid, nullptr, nullptr }); - } - return *s_sys_variables; -} - -SysVariable& SysVariable::for_inode(InodeIdentifier id) -{ - auto index = to_sys_index(id); - if (index >= sys_variables().size()) - return sys_variables()[0]; - auto& variable = sys_variables()[index]; - VERIFY(variable.address); - return variable; -} - -static bool read_sys_bool(InodeIdentifier inode_id, KBufferBuilder& builder) -{ - auto& variable = SysVariable::for_inode(inode_id); - VERIFY(variable.type == SysVariable::Type::Boolean); - - u8 buffer[2]; - auto* lockable_bool = reinterpret_cast*>(variable.address); - { - Locker locker(lockable_bool->lock(), Lock::Mode::Shared); - buffer[0] = lockable_bool->resource() ? '1' : '0'; - } - buffer[1] = '\n'; - builder.append_bytes(ReadonlyBytes { buffer, sizeof(buffer) }); - return true; -} - -static KResultOr write_sys_bool(InodeIdentifier inode_id, const UserOrKernelBuffer& buffer, size_t size) -{ - auto& variable = SysVariable::for_inode(inode_id); - VERIFY(variable.type == SysVariable::Type::Boolean); - - char value = 0; - bool did_read = false; - auto result = buffer.read_buffered<1>(1, [&](u8 const* data, size_t) { - if (did_read) - return 0; - value = (char)data[0]; - did_read = true; - return 1; - }); - if (result.is_error()) - return result.error(); - VERIFY(result.value() == 0 || (result.value() == 1 && did_read)); - if (result.value() == 0 || !(value == '0' || value == '1')) - return size; - - auto* lockable_bool = reinterpret_cast*>(variable.address); - { - Locker locker(lockable_bool->lock()); - lockable_bool->resource() = value == '1'; - } - variable.notify(); - return size; -} - -static bool read_sys_string(InodeIdentifier inode_id, KBufferBuilder& builder) -{ - auto& variable = SysVariable::for_inode(inode_id); - VERIFY(variable.type == SysVariable::Type::String); - - auto* lockable_string = reinterpret_cast*>(variable.address); - Locker locker(lockable_string->lock(), Lock::Mode::Shared); - builder.append_bytes(lockable_string->resource().bytes()); - return true; -} - -static KResultOr write_sys_string(InodeIdentifier inode_id, const UserOrKernelBuffer& buffer, size_t size) -{ - auto& variable = SysVariable::for_inode(inode_id); - VERIFY(variable.type == SysVariable::Type::String); - - auto string_copy = buffer.copy_into_string(size); - if (string_copy.is_null()) - return EFAULT; - - { - auto* lockable_string = reinterpret_cast*>(variable.address); - Locker locker(lockable_string->lock()); - lockable_string->resource() = move(string_copy); - } - variable.notify(); - return size; -} - -void ProcFS::add_sys_bool(String&& name, Lockable& var, Function&& notify_callback) -{ - InterruptDisabler disabler; - - SysVariable variable; - variable.name = move(name); - variable.type = SysVariable::Type::Boolean; - variable.notify_callback = move(notify_callback); - variable.address = &var; - - sys_variables().append(move(variable)); -} - bool ProcFS::initialize() { - static Lockable* kmalloc_stack_helper; - static Lockable* ubsan_deadly_helper; - static Lockable* caps_lock_to_ctrl_helper; - - if (kmalloc_stack_helper == nullptr) { - kmalloc_stack_helper = new Lockable(); - kmalloc_stack_helper->resource() = g_dump_kmalloc_stacks; - ProcFS::add_sys_bool("kmalloc_stacks", *kmalloc_stack_helper, [] { - g_dump_kmalloc_stacks = kmalloc_stack_helper->resource(); - }); - ubsan_deadly_helper = new Lockable(); - ubsan_deadly_helper->resource() = AK::UBSanitizer::g_ubsan_is_deadly; - ProcFS::add_sys_bool("ubsan_is_deadly", *ubsan_deadly_helper, [] { - AK::UBSanitizer::g_ubsan_is_deadly = ubsan_deadly_helper->resource(); - }); - caps_lock_to_ctrl_helper = new Lockable(); - ProcFS::add_sys_bool("caps_lock_to_ctrl", *caps_lock_to_ctrl_helper, [] { - Kernel::g_caps_lock_remapped_to_ctrl.exchange(caps_lock_to_ctrl_helper->resource()); - }); - } return true; } -const char* ProcFS::class_name() const -{ - return "ProcFS"; -} - NonnullRefPtr ProcFS::root_inode() const { return *m_root_inode; } -RefPtr ProcFS::get_inode(InodeIdentifier inode_id) const +NonnullRefPtr ProcFSInode::create(const ProcFS& fs, const ProcFSExposedComponent& component) { - dbgln_if(PROCFS_DEBUG, "ProcFS::get_inode({})", inode_id.index()); - if (inode_id == root_inode()->identifier()) - return m_root_inode; - - Locker locker(m_inodes_lock); - auto it = m_inodes.find(inode_id.index().value()); - if (it != m_inodes.end()) { - // It's possible that the ProcFSInode ref count was dropped to 0 or - // the ~ProcFSInode destructor is even running already, but blocked - // from removing it from this map. So we need to *try* to ref it, - // and if that fails we cannot return this instance anymore and just - // create a new one. - if (it->value->try_ref()) - return adopt_ref_if_nonnull(it->value); - // We couldn't ref it, so just create a new one and replace the entry - } - auto inode = adopt_ref_if_nonnull(new (nothrow) ProcFSInode(const_cast(*this), inode_id.index())); - if (!inode) - return {}; - auto result = m_inodes.set(inode_id.index().value(), inode.ptr()); - VERIFY(result == ((it == m_inodes.end()) ? AK::HashSetResult::InsertedNewEntry : AK::HashSetResult::ReplacedExistingEntry)); - return inode; + return adopt_ref(*new (nothrow) ProcFSInode(fs, component)); } -ProcFSInode::ProcFSInode(ProcFS& fs, InodeIndex index) - : Inode(fs, index) +ProcFSInode::ProcFSInode(const ProcFS& fs, const ProcFSExposedComponent& component) + : Inode(const_cast(fs), component.component_index()) + , m_associated_component(component) { } -ProcFSInode::~ProcFSInode() -{ - Locker locker(fs().m_inodes_lock); - auto it = fs().m_inodes.find(index().value()); - if (it != fs().m_inodes.end() && it->value == this) - fs().m_inodes.remove(it); -} - -RefPtr ProcFSInode::process() const -{ - auto parent = to_proc_parent_directory(identifier()); - if (parent == PDI_PID || parent == PDI_PID_fd || parent == PDI_PID_stacks) - return Process::from_pid(to_pid(identifier())); - - return nullptr; -} - -KResult ProcFSInode::refresh_data(FileDescription& description) const -{ - if (Kernel::is_directory(identifier())) - return KSuccess; - - // For process-specific inodes, hold the process's ptrace lock across refresh - // and refuse to load data if the process is not dumpable. - // Without this, files opened before a process went non-dumpable could still be used for dumping. - auto process = this->process(); - if (process) { - process->ptrace_lock().lock(); - if (!process->is_dumpable()) { - process->ptrace_lock().unlock(); - return EPERM; - } - } - ScopeGuard guard = [&] { - if (process) - process->ptrace_lock().unlock(); - }; - - auto& cached_data = description.data(); - auto* directory_entry = fs().get_directory_entry(identifier()); - - bool (*read_callback)(InodeIdentifier, KBufferBuilder&) = nullptr; - - if (directory_entry) { - read_callback = directory_entry->read_callback; - VERIFY(read_callback); - } else { - switch (to_proc_parent_directory(identifier())) { - case PDI_PID_fd: - read_callback = procfs$pid_fd_entry; - break; - case PDI_PID_stacks: - read_callback = procfs$tid_stack; - break; - case PDI_Root_sys: - switch (SysVariable::for_inode(identifier()).type) { - case SysVariable::Type::Invalid: - VERIFY_NOT_REACHED(); - case SysVariable::Type::Boolean: - read_callback = read_sys_bool; - break; - case SysVariable::Type::String: - read_callback = read_sys_string; - break; - } - break; - case PDI_Root_bus_usb: - read_callback = procfs$usb_entry; - break; - default: - VERIFY_NOT_REACHED(); - } - - VERIFY(read_callback); - } - - if (!cached_data) - cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData); - auto& buffer = static_cast(*cached_data).buffer; - if (buffer) { - // If we're reusing the buffer, reset the size to 0 first. This - // ensures we don't accidentally leak previously written data. - buffer->set_size(0); - } - KBufferBuilder builder(buffer, true); - if (!read_callback(identifier(), builder)) - return ENOENT; - // We don't use builder.build() here, which would steal our buffer - // and turn it into an OwnPtr. Instead, just flush to the buffer so - // that we can read all the data that was written. - if (!builder.flush()) - return ENOMEM; - if (!buffer) - return ENOMEM; - return KSuccess; -} - KResult ProcFSInode::attach(FileDescription& description) { - return refresh_data(description); + return m_associated_component->refresh_data(description); } void ProcFSInode::did_seek(FileDescription& description, off_t new_offset) { if (new_offset != 0) return; - auto result = refresh_data(description); + auto result = m_associated_component->refresh_data(description); if (result.is_error()) { // Subsequent calls to read will return EIO! dbgln("ProcFS: Could not refresh contents: {}", result.error()); } } +ProcFSInode::~ProcFSInode() +{ +} + +ProcFS::ProcFS() + : m_root_inode(ProcFSComponentsRegistrar::the().m_root_folder->to_inode(*this)) +{ +} + +KResultOr ProcFSInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* fd) const +{ + return m_associated_component->read_bytes(offset, count, buffer, fd); +} + +StringView ProcFSInode::name() const +{ + return m_associated_component->name(); +} + +KResult ProcFSInode::traverse_as_directory(Function) const +{ + VERIFY_NOT_REACHED(); +} + +RefPtr ProcFSInode::lookup(StringView) +{ + VERIFY_NOT_REACHED(); +} + InodeMetadata ProcFSInode::metadata() const { - dbgln_if(PROCFS_DEBUG, "ProcFSInode::metadata({})", index()); + Locker locker(m_lock); InodeMetadata metadata; - metadata.inode = identifier(); - metadata.ctime = mepoch; - metadata.atime = mepoch; + metadata.inode = { fsid(), m_associated_component->component_index() }; + metadata.mode = m_associated_component->required_mode(); + metadata.uid = 0; + metadata.gid = 0; + metadata.size = m_associated_component->size(); metadata.mtime = mepoch; - auto proc_parent_directory = to_proc_parent_directory(identifier()); - auto proc_file_type = to_proc_file_type(identifier()); - - dbgln_if(PROCFS_DEBUG, " -> pid={}, fi={}, pdi={}", to_pid(identifier()).value(), (int)proc_file_type, (int)proc_parent_directory); - - if (is_process_related_file(identifier())) { - ProcessID pid = to_pid(identifier()); - auto process = Process::from_pid(pid); - if (process && process->is_dumpable()) { - metadata.uid = process->euid(); - metadata.gid = process->egid(); - } else { - metadata.uid = 0; - metadata.gid = 0; - } - } else if (is_thread_related_file(identifier())) { - ThreadID tid = to_tid(identifier()); - auto thread = Thread::from_tid(tid); - if (thread && thread->process().is_dumpable()) { - metadata.uid = thread->process().euid(); - metadata.gid = thread->process().egid(); - } else { - metadata.uid = 0; - metadata.gid = 0; - } - } - - if (proc_parent_directory == PDI_PID_fd) { - metadata.mode = S_IFLNK | S_IRUSR | S_IWUSR | S_IXUSR; - return metadata; - } - - if (proc_parent_directory == PDI_Root_bus_usb) { - metadata.mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - return metadata; - } - - switch (proc_file_type) { - case FI_Root_self: - metadata.mode = S_IFLNK | S_IRUSR | S_IRGRP | S_IROTH; - break; - case FI_PID_cwd: - case FI_PID_exe: - case FI_PID_root: - metadata.mode = S_IFLNK | S_IRUSR; - break; - case FI_Root: - case FI_Root_sys: - case FI_Root_net: - case FI_Root_bus: - case FI_Root_bus_usb: - metadata.mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; - break; - case FI_PID: - case FI_PID_fd: - case FI_PID_stacks: - metadata.mode = S_IFDIR | S_IRUSR | S_IXUSR; - break; - default: - metadata.mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - break; - } - - if (proc_file_type > FI_Invalid && proc_file_type < FI_MaxStaticFileIndex) { - if (fs().m_entries[proc_file_type].supervisor_only) { - metadata.uid = 0; - metadata.gid = 0; - metadata.mode &= ~077; - } - } return metadata; } -KResultOr ProcFSInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const -{ - dbgln_if(PROCFS_DEBUG, "ProcFS: read_bytes offset: {} count: {}", offset, count); - VERIFY(offset >= 0); - VERIFY(buffer.user_or_kernel_ptr()); - - if (!description) - return EIO; - if (!description->data()) { - dbgln_if(PROCFS_DEBUG, "ProcFS: Do not have cached data!"); - return EIO; - } - - // Be sure to keep a reference to data_buffer while we use it! - RefPtr data_buffer = static_cast(*description->data()).buffer; - - if (!data_buffer || (size_t)offset >= data_buffer->size()) - return 0; - - size_t nread = min(static_cast(data_buffer->size() - offset), static_cast(count)); - if (!buffer.write(data_buffer->data() + offset, nread)) - return EFAULT; - - return nread; -} - -InodeIdentifier ProcFS::ProcFSDirectoryEntry::identifier(unsigned fsid) const -{ - return to_identifier(fsid, PDI_Root, 0, (ProcFileType)proc_file_type); -} - -KResult ProcFSInode::traverse_as_directory(Function callback) const -{ - dbgln_if(PROCFS_DEBUG, "ProcFS: traverse_as_directory {}", index()); - - if (!Kernel::is_directory(identifier())) - return ENOTDIR; - - auto proc_file_type = to_proc_file_type(identifier()); - auto parent_id = to_parent_id(identifier()); - - callback({ ".", identifier(), 2 }); - callback({ "..", parent_id, 2 }); - - switch (proc_file_type) { - case FI_Root: - for (auto& entry : fs().m_entries) { - // FIXME: strlen() here is sad. - if (!entry.name) - continue; - if (entry.proc_file_type > __FI_Root_Start && entry.proc_file_type < __FI_Root_End) - callback({ { entry.name, strlen(entry.name) }, to_identifier(fsid(), PDI_Root, 0, (ProcFileType)entry.proc_file_type), 0 }); - } - for (auto pid_child : Process::all_pids()) { - callback({ String::number(pid_child.value()), to_identifier(fsid(), PDI_Root, pid_child, FI_PID), 0 }); - } - break; - - case FI_Root_sys: - for (size_t i = 1; i < sys_variables().size(); ++i) { - auto& variable = sys_variables()[i]; - callback({ variable.name, sys_var_to_identifier(fsid(), i), 0 }); - } - break; - - case FI_Root_net: - callback({ "adapters", to_identifier(fsid(), PDI_Root_net, 0, FI_Root_net_adapters), 0 }); - callback({ "arp", to_identifier(fsid(), PDI_Root_net, 0, FI_Root_net_arp), 0 }); - callback({ "tcp", to_identifier(fsid(), PDI_Root_net, 0, FI_Root_net_tcp), 0 }); - callback({ "udp", to_identifier(fsid(), PDI_Root_net, 0, FI_Root_net_udp), 0 }); - callback({ "local", to_identifier(fsid(), PDI_Root_net, 0, FI_Root_net_local), 0 }); - break; - - case FI_PID: { - auto pid = to_pid(identifier()); - auto process = Process::from_pid(pid); - if (!process) - return ENOENT; - for (auto& entry : fs().m_entries) { - if (entry.proc_file_type > __FI_PID_Start && entry.proc_file_type < __FI_PID_End) { - if (entry.proc_file_type == FI_PID_exe && !process->executable()) - continue; - // FIXME: strlen() here is sad. - callback({ { entry.name, strlen(entry.name) }, to_identifier(fsid(), PDI_PID, pid, (ProcFileType)entry.proc_file_type), 0 }); - } - } - } break; - - case FI_PID_fd: { - auto pid = to_pid(identifier()); - auto process = Process::from_pid(pid); - if (!process) - return ENOENT; - for (int i = 0; i < process->max_open_file_descriptors(); ++i) { - auto description = process->file_description(i); - if (!description) - continue; - callback({ String::number(i), to_identifier_with_fd(fsid(), pid, i), 0 }); - } - } break; - - case FI_PID_stacks: { - auto pid = to_pid(identifier()); - auto process = Process::from_pid(pid); - if (!process) - return ENOENT; - process->for_each_thread([&](const Thread& thread) { - int tid = thread.tid().value(); - callback({ String::number(tid), to_identifier_with_stack(fsid(), tid), 0 }); - }); - } break; - - case FI_Root_bus: { - callback({ "usb", to_identifier(fsid(), PDI_Root_bus, 0, FI_Root_bus_usb), 0 }); - break; - } - - case FI_Root_bus_usb: { - // FIXME: We currently only support 2 USB devices due to UHCI only... The controller stack should be modified - // to support any number of devices (this is a gross hack....) - for (auto port = 0; port < 2; port++) { - auto const& device = USB::UHCIController::the().get_device_at_port(static_cast(port)); - if (device == nullptr) - continue; - - callback({ String::number(device->address()), usb_device_address_to_identifier(fsid(), device->address()), 0 }); - } - break; - } - - default: - return KSuccess; - } - - return KSuccess; -} - -RefPtr ProcFSInode::lookup(StringView name) -{ - VERIFY(is_directory()); - if (name == ".") - return this; - if (name == "..") - return fs().get_inode(to_parent_id(identifier())); - - auto proc_file_type = to_proc_file_type(identifier()); - - if (proc_file_type == FI_Root) { - for (auto& entry : fs().m_entries) { - if (entry.name == nullptr) - continue; - if (entry.proc_file_type > __FI_Root_Start && entry.proc_file_type < __FI_Root_End) { - if (name == entry.name) { - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, (ProcFileType)entry.proc_file_type)); - } - } - } - auto name_as_number = name.to_uint(); - if (!name_as_number.has_value()) - return {}; - bool process_exists = false; - { - InterruptDisabler disabler; - process_exists = Process::from_pid(name_as_number.value()); - } - if (process_exists) - return fs().get_inode(to_identifier(fsid(), PDI_Root, name_as_number.value(), FI_PID)); - return {}; - } - - if (proc_file_type == FI_Root_sys) { - for (size_t i = 1; i < sys_variables().size(); ++i) { - auto& variable = sys_variables()[i]; - if (name == variable.name) - return fs().get_inode(sys_var_to_identifier(fsid(), i)); - } - return {}; - } - - if (proc_file_type == FI_Root_net) { - if (name == "adapters") - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, FI_Root_net_adapters)); - if (name == "arp") - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, FI_Root_net_arp)); - if (name == "tcp") - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, FI_Root_net_tcp)); - if (name == "udp") - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, FI_Root_net_udp)); - if (name == "local") - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, FI_Root_net_local)); - return {}; - } - - if (proc_file_type == FI_PID) { - auto process = Process::from_pid(to_pid(identifier())); - if (!process) - return {}; - for (auto& entry : fs().m_entries) { - if (entry.proc_file_type > __FI_PID_Start && entry.proc_file_type < __FI_PID_End) { - if (entry.proc_file_type == FI_PID_exe && !process->executable()) - continue; - if (entry.name == nullptr) - continue; - if (name == entry.name) { - return fs().get_inode(to_identifier(fsid(), PDI_PID, to_pid(identifier()), (ProcFileType)entry.proc_file_type)); - } - } - } - return {}; - } - - if (proc_file_type == FI_Root_bus) { - if (name == "usb") - return fs().get_inode(to_identifier(fsid(), PDI_Root_bus, 0, FI_Root_bus_usb)); - } - - if (proc_file_type == FI_Root_bus_usb) { - u8 device_address = name.to_uint().value(); - return fs().get_inode(usb_device_address_to_identifier(fsid(), device_address)); - } - - if (proc_file_type == FI_PID_fd) { - auto name_as_number = name.to_uint(); - if (!name_as_number.has_value()) - return {}; - bool fd_exists = false; - { - if (auto process = Process::from_pid(to_pid(identifier()))) - fd_exists = process->file_description(name_as_number.value()); - } - if (fd_exists) - return fs().get_inode(to_identifier_with_fd(fsid(), to_pid(identifier()), name_as_number.value())); - } - - if (proc_file_type == FI_PID_stacks) { - auto name_as_number = name.to_int(); - if (!name_as_number.has_value()) - return {}; - int tid = name_as_number.value(); - if (tid <= 0) { - return {}; - } - bool thread_exists = false; - { - auto process = Process::from_pid(to_pid(identifier())); - auto thread = Thread::from_tid(tid); - thread_exists = process && thread && process->pid() == thread->pid(); - } - if (thread_exists) - return fs().get_inode(to_identifier_with_stack(fsid(), tid)); - } - - return {}; -} - void ProcFSInode::flush_metadata() { } -KResultOr ProcFSInode::write_bytes(off_t offset, size_t size, const UserOrKernelBuffer& buffer, FileDescription*) +KResultOr ProcFSInode::write_bytes(off_t offset, size_t count, const UserOrKernelBuffer& buffer, FileDescription* fd) { - // For process-specific inodes, hold the process's ptrace lock across the write - // and refuse to write at all data if the process is not dumpable. - // Without this, files opened before a process went non-dumpable could still be used for dumping. - auto process = this->process(); - if (process) { - process->ptrace_lock().lock(); - if (!process->is_dumpable()) { - process->ptrace_lock().unlock(); - return EPERM; - } - } - ScopeGuard guard = [&] { - if (process) - process->ptrace_lock().unlock(); - }; - - auto result = prepare_to_write_data(); - if (result.is_error()) - return result; - - auto* directory_entry = fs().get_directory_entry(identifier()); - - KResultOr (*write_callback)(InodeIdentifier, const UserOrKernelBuffer&, size_t) = nullptr; - - if (directory_entry == nullptr) { - if (to_proc_parent_directory(identifier()) == PDI_Root_sys) { - switch (SysVariable::for_inode(identifier()).type) { - case SysVariable::Type::Invalid: - VERIFY_NOT_REACHED(); - case SysVariable::Type::Boolean: - write_callback = write_sys_bool; - break; - case SysVariable::Type::String: - write_callback = write_sys_string; - break; - } - } else - return EPERM; - } else { - if (!directory_entry->write_callback) - return EPERM; - write_callback = directory_entry->write_callback; - } - - VERIFY(is_persistent_inode(identifier())); - // FIXME: Being able to write into ProcFS at a non-zero offset seems like something we should maybe support.. - VERIFY(offset == 0); - auto nwritten_or_error = write_callback(identifier(), buffer, size); - if (nwritten_or_error.is_error()) - dbgln("ProcFS: Writing {} bytes failed: {}", size, nwritten_or_error.error()); - return nwritten_or_error; -} - -KResultOr> ProcFSInode::resolve_as_link(Custody& base, RefPtr* out_parent, int options, int symlink_recursion_level) const -{ - if (FI_Root_self == to_proc_file_type(identifier())) { - return VFS::the().resolve_path(String::number(Process::current()->pid().value()), base, out_parent, options, symlink_recursion_level); - } - - // The only other links are in pid directories, so it's safe to ignore - // unrelated files and the thread-specific stacks/ directory. - if (!is_process_related_file(identifier())) - return Inode::resolve_as_link(base, out_parent, options, symlink_recursion_level); - - // FIXME: We should return a custody for FI_PID or FI_PID_fd here - // for correctness. It's impossible to create files in ProcFS, - // so returning null shouldn't break much. - if (out_parent) - *out_parent = nullptr; - - auto pid = to_pid(identifier()); - auto proc_file_type = to_proc_file_type(identifier()); - auto process = Process::from_pid(pid); - if (!process) - return ENOENT; - - if (to_proc_parent_directory(identifier()) == PDI_PID_fd) { - if (out_parent) - *out_parent = base; - int fd = to_fd(identifier()); - auto description = process->file_description(fd); - if (!description) - return ENOENT; - auto proxy_inode = ProcFSProxyInode::create(const_cast(fs()), *description); - return Custody::try_create(&base, "", proxy_inode, base.mount_flags()); - } - - Custody* res = nullptr; - - switch (proc_file_type) { - case FI_PID_cwd: - res = &process->current_directory(); - break; - case FI_PID_exe: - res = process->executable(); - break; - case FI_PID_root: - // Note: we open root_directory() here, not - // root_directory_relative_to_global_root(). - // This seems more useful. - res = &process->root_directory(); - break; - default: - VERIFY_NOT_REACHED(); - } - - if (!res) - return ENOENT; - - return *res; -} - -KResult ProcFSInode::set_mtime(time_t) -{ - return KSuccess; -} - -ProcFSProxyInode::ProcFSProxyInode(ProcFS& fs, FileDescription& fd) - : Inode(fs, 0) - , m_fd(fd) -{ -} - -ProcFSProxyInode::~ProcFSProxyInode() -{ -} - -KResult ProcFSProxyInode::attach(FileDescription& fd) -{ - return m_fd->inode()->attach(fd); -} - -void ProcFSProxyInode::did_seek(FileDescription& fd, off_t new_offset) -{ - return m_fd->inode()->did_seek(fd, new_offset); -} - -InodeMetadata ProcFSProxyInode::metadata() const -{ - InodeMetadata metadata = m_fd->metadata(); - - if (m_fd->is_readable()) - metadata.mode |= 0444; - else - metadata.mode &= ~0444; - - if (m_fd->is_writable()) - metadata.mode |= 0222; - else - metadata.mode &= ~0222; - - if (!metadata.is_directory()) - metadata.mode &= ~0111; - - return metadata; -} - -KResultOr> ProcFSProxyInode::create_child(const String& name, mode_t mode, dev_t dev, uid_t uid, gid_t gid) -{ - if (!m_fd->inode()) - return EINVAL; - return m_fd->inode()->create_child(name, mode, dev, uid, gid); -} - -KResult ProcFSProxyInode::add_child(Inode& child, const StringView& name, mode_t mode) -{ - if (!m_fd->inode()) - return EINVAL; - return m_fd->inode()->add_child(child, name, mode); -} - -KResult ProcFSProxyInode::remove_child(const StringView& name) -{ - if (!m_fd->inode()) - return EINVAL; - return m_fd->inode()->remove_child(name); -} - -RefPtr ProcFSProxyInode::lookup(StringView name) -{ - if (!m_fd->inode()) - return {}; - return m_fd->inode()->lookup(name); -} - -KResultOr ProcFSProxyInode::directory_entry_count() const -{ - if (!m_fd->inode()) - return EINVAL; - return m_fd->inode()->directory_entry_count(); + return m_associated_component->write_bytes(offset, count, buffer, fd); } KResultOr> ProcFSInode::create_child(const String&, mode_t, dev_t, uid_t, gid_t) { - return EPERM; + return EROFS; } KResult ProcFSInode::add_child(Inode&, const StringView&, mode_t) { - return EPERM; + return EROFS; } -KResult ProcFSInode::remove_child([[maybe_unused]] const StringView& name) +KResult ProcFSInode::remove_child(const StringView&) { - return EPERM; + return EROFS; } KResultOr ProcFSInode::directory_entry_count() const { - VERIFY(is_directory()); - size_t count = 0; - KResult result = traverse_as_directory([&count](auto&) { - ++count; - return true; - }); - - if (result.is_error()) - return result; - - return count; + VERIFY_NOT_REACHED(); } KResult ProcFSInode::chmod(mode_t) @@ -1747,55 +183,83 @@ KResult ProcFSInode::chmod(mode_t) return EPERM; } -ProcFS::ProcFS() -{ - m_root_inode = adopt_ref(*new ProcFSInode(*this, 1)); - m_entries.resize(FI_MaxStaticFileIndex); - m_entries[FI_Root_df] = { "df", FI_Root_df, false, procfs$df }; - m_entries[FI_Root_all] = { "all", FI_Root_all, false, procfs$all }; - m_entries[FI_Root_memstat] = { "memstat", FI_Root_memstat, false, procfs$memstat }; - m_entries[FI_Root_cpuinfo] = { "cpuinfo", FI_Root_cpuinfo, false, procfs$cpuinfo }; - m_entries[FI_Root_dmesg] = { "dmesg", FI_Root_dmesg, true, procfs$dmesg }; - m_entries[FI_Root_self] = { "self", FI_Root_self, false, procfs$self }; - m_entries[FI_Root_pci] = { "pci", FI_Root_pci, false, procfs$pci }; - m_entries[FI_Root_interrupts] = { "interrupts", FI_Root_interrupts, false, procfs$interrupts }; - m_entries[FI_Root_keymap] = { "keymap", FI_Root_keymap, false, procfs$keymap }; - m_entries[FI_Root_devices] = { "devices", FI_Root_devices, false, procfs$devices }; - m_entries[FI_Root_uptime] = { "uptime", FI_Root_uptime, false, procfs$uptime }; - m_entries[FI_Root_cmdline] = { "cmdline", FI_Root_cmdline, true, procfs$cmdline }; - m_entries[FI_Root_modules] = { "modules", FI_Root_modules, true, procfs$modules }; - m_entries[FI_Root_profile] = { "profile", FI_Root_profile, true, procfs$profile }; - m_entries[FI_Root_sys] = { "sys", FI_Root_sys, true }; - m_entries[FI_Root_net] = { "net", FI_Root_net, false }; - m_entries[FI_Root_bus] = { "bus", FI_Root_bus, false }; - - m_entries[FI_Root_net_adapters] = { "adapters", FI_Root_net_adapters, false, procfs$net_adapters }; - m_entries[FI_Root_net_arp] = { "arp", FI_Root_net_arp, true, procfs$net_arp }; - m_entries[FI_Root_net_tcp] = { "tcp", FI_Root_net_tcp, false, procfs$net_tcp }; - m_entries[FI_Root_net_udp] = { "udp", FI_Root_net_udp, false, procfs$net_udp }; - m_entries[FI_Root_net_local] = { "local", FI_Root_net_local, false, procfs$net_local }; - - m_entries[FI_PID_vm] = { "vm", FI_PID_vm, false, procfs$pid_vm }; - m_entries[FI_PID_stacks] = { "stacks", FI_PID_stacks, false }; - m_entries[FI_PID_fds] = { "fds", FI_PID_fds, false, procfs$pid_fds }; - m_entries[FI_PID_exe] = { "exe", FI_PID_exe, false, procfs$pid_exe }; - m_entries[FI_PID_cwd] = { "cwd", FI_PID_cwd, false, procfs$pid_cwd }; - m_entries[FI_PID_unveil] = { "unveil", FI_PID_unveil, false, procfs$pid_unveil }; - m_entries[FI_PID_root] = { "root", FI_PID_root, false, procfs$pid_root }; - m_entries[FI_PID_perf_events] = { "perf_events", FI_PID_perf_events, false, procfs$pid_perf_events }; - m_entries[FI_PID_fd] = { "fd", FI_PID_fd, false }; -} - -ProcFS::ProcFSDirectoryEntry* ProcFS::get_directory_entry(InodeIdentifier identifier) const -{ - auto proc_file_type = to_proc_file_type(identifier); - if (proc_file_type != FI_Invalid && proc_file_type != FI_Root_sys_variable && proc_file_type != FI_Root_bus_usb_device && proc_file_type < FI_MaxStaticFileIndex) - return const_cast(&m_entries[proc_file_type]); - return nullptr; -} - KResult ProcFSInode::chown(uid_t, gid_t) { return EPERM; } + +KResult ProcFSInode::truncate(u64) +{ + return EPERM; +} + +NonnullRefPtr ProcFSDirectoryInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component) +{ + return adopt_ref(*new (nothrow) ProcFSDirectoryInode(procfs, component)); +} + +ProcFSDirectoryInode::ProcFSDirectoryInode(const ProcFS& fs, const ProcFSExposedComponent& component) + : ProcFSInode(fs, component) + , m_parent_fs(const_cast(fs)) +{ +} + +ProcFSDirectoryInode::~ProcFSDirectoryInode() +{ +} +InodeMetadata ProcFSDirectoryInode::metadata() const +{ + Locker locker(m_lock); + InodeMetadata metadata; + metadata.inode = { fsid(), m_associated_component->component_index() }; + metadata.mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH | S_IXOTH; + metadata.uid = 0; + metadata.gid = 0; + metadata.size = 0; + metadata.mtime = mepoch; + return metadata; +} +KResult ProcFSDirectoryInode::traverse_as_directory(Function callback) const +{ + Locker locker(m_parent_fs.m_lock); + return m_associated_component->traverse_as_directory(m_parent_fs.fsid(), move(callback)); +} + +RefPtr ProcFSDirectoryInode::lookup(StringView name) +{ + Locker locker(m_parent_fs.m_lock); + auto component = m_associated_component->lookup(name); + if (!component) + return {}; + return component->to_inode(m_parent_fs); +} + +KResultOr ProcFSDirectoryInode::directory_entry_count() const +{ + Locker locker(m_lock); + return m_associated_component->entries_count(); +} + +NonnullRefPtr ProcFSLinkInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component) +{ + return adopt_ref(*new (nothrow) ProcFSLinkInode(procfs, component)); +} + +ProcFSLinkInode::ProcFSLinkInode(const ProcFS& fs, const ProcFSExposedComponent& component) + : ProcFSInode(fs, component) +{ +} +InodeMetadata ProcFSLinkInode::metadata() const +{ + Locker locker(m_lock); + InodeMetadata metadata; + metadata.inode = { fsid(), m_associated_component->component_index() }; + metadata.mode = S_IFLNK | m_associated_component->required_mode(); + metadata.uid = 0; + metadata.gid = 0; + metadata.size = 0; + metadata.mtime = mepoch; + return metadata; +} + } diff --git a/Kernel/FileSystem/ProcFS.h b/Kernel/FileSystem/ProcFS.h index a93b8c5414..71d32a5384 100644 --- a/Kernel/FileSystem/ProcFS.h +++ b/Kernel/FileSystem/ProcFS.h @@ -12,130 +12,91 @@ #include #include #include +#include namespace Kernel { class Process; class ProcFSInode; +class ProcFSDirectoryInode; class ProcFS final : public FS { friend class ProcFSInode; + friend class ProcFSDirectoryInode; public: virtual ~ProcFS() override; static RefPtr create(); virtual bool initialize() override; - virtual const char* class_name() const override; + virtual const char* class_name() const override { return "ProcFS"; } virtual NonnullRefPtr root_inode() const override; - static void add_sys_bool(String&&, Lockable&, Function&& notify_callback = nullptr); - private: ProcFS(); - struct ProcFSDirectoryEntry { - ProcFSDirectoryEntry() = default; - ProcFSDirectoryEntry(const char* a_name, unsigned a_proc_file_type, bool a_supervisor_only, bool (*read_callback)(InodeIdentifier, KBufferBuilder&) = nullptr, KResultOr (*write_callback)(InodeIdentifier, const UserOrKernelBuffer&, size_t) = nullptr, RefPtr&& a_inode = nullptr) - : name(a_name) - , proc_file_type(a_proc_file_type) - , supervisor_only(a_supervisor_only) - , read_callback(read_callback) - , write_callback(write_callback) - , inode(move(a_inode)) - { - } - - const char* name { nullptr }; - unsigned proc_file_type { 0 }; - bool supervisor_only { false }; - bool (*read_callback)(InodeIdentifier, KBufferBuilder&); - KResultOr (*write_callback)(InodeIdentifier, const UserOrKernelBuffer&, size_t); - RefPtr inode; - InodeIdentifier identifier(unsigned fsid) const; - }; - - RefPtr get_inode(InodeIdentifier) const; - ProcFSDirectoryEntry* get_directory_entry(InodeIdentifier) const; - - Vector m_entries; - - mutable Lock m_inodes_lock; - mutable HashMap m_inodes; - RefPtr m_root_inode; + NonnullRefPtr m_root_inode; }; -class ProcFSInode final : public Inode { +class ProcFSInode : public Inode { friend class ProcFS; public: + static NonnullRefPtr create(const ProcFS&, const ProcFSExposedComponent&); virtual ~ProcFSInode() override; + StringView name() const; + +protected: + ProcFSInode(const ProcFS&, const ProcFSExposedComponent&); -private: // ^Inode - virtual KResult attach(FileDescription&) override; - virtual void did_seek(FileDescription&, off_t) override; + virtual KResult attach(FileDescription& description) override; virtual KResultOr read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override; - virtual InodeMetadata metadata() const override; virtual KResult traverse_as_directory(Function) const override; virtual RefPtr lookup(StringView name) override; virtual void flush_metadata() override; + virtual InodeMetadata metadata() const override; virtual KResultOr write_bytes(off_t, size_t, const UserOrKernelBuffer& buffer, FileDescription*) override; virtual KResultOr> create_child(const String& name, mode_t, dev_t, uid_t, gid_t) override; virtual KResult add_child(Inode&, const StringView& name, mode_t) override; virtual KResult remove_child(const StringView& name) override; + virtual void did_seek(FileDescription&, off_t) override; virtual KResultOr directory_entry_count() const override; virtual KResult chmod(mode_t) override; virtual KResult chown(uid_t, gid_t) override; - virtual KResultOr> resolve_as_link(Custody& base, RefPtr* out_parent = nullptr, int options = 0, int symlink_recursion_level = 0) const override; - virtual KResult set_mtime(time_t) override; + virtual KResult truncate(u64) override; - KResult refresh_data(FileDescription&) const; - - RefPtr process() const; - - ProcFS& fs() { return static_cast(Inode::fs()); } - const ProcFS& fs() const { return static_cast(Inode::fs()); } - ProcFSInode(ProcFS&, InodeIndex); + NonnullRefPtr m_associated_component; }; -class ProcFSProxyInode final : public Inode { - friend class ProcFSInode; +class ProcFSLinkInode : public ProcFSInode { + friend class ProcFS; public: - virtual ~ProcFSProxyInode() override; + static NonnullRefPtr create(const ProcFS&, const ProcFSExposedComponent&); -private: - // ^Inode - virtual KResult attach(FileDescription&) override; - virtual void did_seek(FileDescription&, off_t) override; - virtual KResultOr read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const override { VERIFY_NOT_REACHED(); } +protected: + ProcFSLinkInode(const ProcFS&, const ProcFSExposedComponent&); virtual InodeMetadata metadata() const override; - virtual KResult traverse_as_directory(Function) const override { VERIFY_NOT_REACHED(); } - virtual RefPtr lookup(StringView name) override; - virtual void flush_metadata() override {}; - virtual KResultOr write_bytes(off_t, size_t, const UserOrKernelBuffer&, FileDescription*) override { VERIFY_NOT_REACHED(); } - virtual KResultOr> create_child(const String& name, mode_t, dev_t, uid_t, gid_t) override; - virtual KResult add_child(Inode&, const StringView& name, mode_t) override; - virtual KResult remove_child(const StringView& name) override; - virtual KResultOr directory_entry_count() const override; - virtual KResult chmod(mode_t) override { return EINVAL; } - virtual KResult chown(uid_t, gid_t) override { return EINVAL; } - virtual KResultOr> resolve_as_link(Custody&, RefPtr*, int, int) const override { VERIFY_NOT_REACHED(); } - virtual FileDescription* preopen_fd() override { return m_fd; } - - ProcFS& fs() { return static_cast(Inode::fs()); } - const ProcFS& fs() const { return static_cast(Inode::fs()); } - - ProcFSProxyInode(ProcFS&, FileDescription&); - static NonnullRefPtr create(ProcFS& fs, FileDescription& fd) - { - return adopt_ref(*new ProcFSProxyInode(fs, fd)); - } - - NonnullRefPtr m_fd; }; +class ProcFSDirectoryInode : public ProcFSInode { + friend class ProcFS; + +public: + static NonnullRefPtr create(const ProcFS&, const ProcFSExposedComponent&); + virtual ~ProcFSDirectoryInode() override; + +protected: + ProcFSDirectoryInode(const ProcFS&, const ProcFSExposedComponent&); + // ^Inode + virtual InodeMetadata metadata() const override; + virtual KResult traverse_as_directory(Function) const override; + virtual RefPtr lookup(StringView name) override; + virtual KResultOr directory_entry_count() const override; + + ProcFS& m_parent_fs; +}; } diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index a96383d4c6..1ea0850b53 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +130,15 @@ void Process::kill_all_threads() }); } +void Process::register_new(Process& process) +{ + // Note: this is essentially the same like process->ref() + RefPtr new_process = process; + ScopedSpinLock lock(g_processes_lock); + g_processes->prepend(process); + ProcFSComponentsRegistrar::the().register_new_process(process); +} + RefPtr Process::create_user_process(RefPtr& first_thread, const String& path, uid_t uid, gid_t gid, ProcessID parent_pid, int& error, Vector&& arguments, Vector&& environment, TTY* tty) { auto parts = path.split('/'); @@ -166,11 +176,7 @@ RefPtr Process::create_user_process(RefPtr& first_thread, const return {}; } - { - process->ref(); - ScopedSpinLock lock(g_processes_lock); - g_processes->prepend(*process); - } + register_new(*process); error = 0; return process; } @@ -189,9 +195,7 @@ RefPtr Process::create_kernel_process(RefPtr& first_thread, Str #endif if (process->pid() != 0) { - process->ref(); - ScopedSpinLock lock(g_processes_lock); - g_processes->prepend(*process); + register_new(*process); } ScopedSpinLock lock(g_scheduler_lock); @@ -391,6 +395,7 @@ RefPtr Process::from_pid(ProcessID pid) RefPtr Process::file_description(int fd) const { + ScopedSpinLock lock(m_fds_lock); if (fd < 0) return nullptr; if (static_cast(fd) < m_fds.size()) @@ -400,6 +405,7 @@ RefPtr Process::file_description(int fd) const int Process::fd_flags(int fd) const { + ScopedSpinLock lock(m_fds_lock); if (fd < 0) return -1; if (static_cast(fd) < m_fds.size()) @@ -409,9 +415,10 @@ int Process::fd_flags(int fd) const int Process::number_of_open_file_descriptors() const { + ScopedSpinLock lock(m_fds_lock); int count = 0; - for (auto& description : m_fds) { - if (description) + for (size_t index = 0; index < m_fds.size(); index++) { + if (m_fds[index].is_valid()) ++count; } return count; @@ -419,6 +426,7 @@ int Process::number_of_open_file_descriptors() const int Process::alloc_fd(int first_candidate_fd) { + ScopedSpinLock lock(m_fds_lock); for (int i = first_candidate_fd; i < (int)m_max_open_file_descriptors; ++i) { if (!m_fds[i]) return i; @@ -535,6 +543,12 @@ void Process::finalize() m_arguments.clear(); m_environment.clear(); + // Note: We need to remove the references from the ProcFS registrar + // If we don't do it here, we can't drop the object later, and we can't + // do this from the destructor because the state of the object doesn't + // allow us to take references anymore. + ProcFSComponentsRegistrar::the().unregister_process(*this); + m_dead = true; { @@ -676,14 +690,24 @@ RefPtr Process::create_kernel_thread(void (*entry)(void*), void* entry_d void Process::FileDescriptionAndFlags::clear() { + // FIXME: Verify Process::m_fds_lock is locked! m_description = nullptr; m_flags = 0; + m_global_procfs_inode_index = 0; +} + +void Process::FileDescriptionAndFlags::refresh_inode_index() +{ + // FIXME: Verify Process::m_fds_lock is locked! + m_global_procfs_inode_index = ProcFSComponentsRegistrar::the().allocate_inode_index(); } void Process::FileDescriptionAndFlags::set(NonnullRefPtr&& description, u32 flags) { + // FIXME: Verify Process::m_fds_lock is locked! m_description = move(description); m_flags = flags; + m_global_procfs_inode_index = ProcFSComponentsRegistrar::the().allocate_inode_index(); } Custody& Process::root_directory() diff --git a/Kernel/Process.h b/Kernel/Process.h index 8359ed0171..5b1bc7bd65 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +130,7 @@ class Process friend class Thread; friend class CoreDump; + friend class ProcFSProcessFileDescriptions; // Helper class to temporarily unprotect a process's protected data so you can write to it. class ProtectedDataMutationScope { @@ -169,6 +171,7 @@ public: static RefPtr create_kernel_process(RefPtr& first_thread, String&& name, void (*entry)(void*), void* entry_data = nullptr, u32 affinity = THREAD_AFFINITY_DEFAULT); static RefPtr create_user_process(RefPtr& first_thread, const String& path, uid_t, gid_t, ProcessID ppid, int& error, Vector&& arguments = Vector(), Vector&& environment = Vector(), TTY* = nullptr); + static void register_new(Process&); ~Process(); static Vector all_pids(); @@ -585,23 +588,41 @@ private: static constexpr int m_max_open_file_descriptors { FD_SETSIZE }; +public: class FileDescriptionAndFlags { + friend class FileDescriptionRegistrar; + public: operator bool() const { return !!m_description; } + bool is_valid() const { return !m_description.is_null(); } + FileDescription* description() { return m_description; } const FileDescription* description() const { return m_description; } - + InodeIndex global_procfs_inode_index() const { return m_global_procfs_inode_index; } u32 flags() const { return m_flags; } void set_flags(u32 flags) { m_flags = flags; } void clear(); void set(NonnullRefPtr&&, u32 flags = 0); + void refresh_inode_index(); private: RefPtr m_description; u32 m_flags { 0 }; + + // Note: This is needed so when we generate inodes for ProcFS, we know that + // we assigned a global Inode index to it so we can use it later + InodeIndex m_global_procfs_inode_index; }; + + // FIXME: We create a copy when trying to iterate the Vector because + // we don't want to put lots of locking when accessing this Vector. + // Maybe try to encapsulate the Vector inside a protective class with locking + // mechanism. + Vector fds() const { return m_fds; } + +private: Vector m_fds; mutable RecursiveSpinLock m_thread_list_lock; @@ -638,6 +659,7 @@ private: FutexQueues m_futex_queues; SpinLock m_futex_lock; + mutable SpinLock m_fds_lock; // This member is used in the implementation of ptrace's PT_TRACEME flag. // If it is set to true, the process will stop at the next execve syscall diff --git a/Kernel/ProcessExposed.cpp b/Kernel/ProcessExposed.cpp new file mode 100644 index 0000000000..e9f81aa91e --- /dev/null +++ b/Kernel/ProcessExposed.cpp @@ -0,0 +1,1626 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +static SpinLock s_index_lock; +static InodeIndex s_next_inode_index = 0; + +static size_t s_allocate_inode_index() +{ + ScopedSpinLock lock(s_index_lock); + s_next_inode_index = s_next_inode_index.value() + 1; + VERIFY(s_next_inode_index > 0); + return s_next_inode_index.value(); +} + +InodeIndex ProcFSComponentsRegistrar::allocate_inode_index() const +{ + return s_allocate_inode_index(); +} + +ProcFSExposedComponent::ProcFSExposedComponent(StringView name) + : m_component_index(s_allocate_inode_index()) +{ + m_name = KString::try_create(name); +} + +// Note: This constructor is intended to be used in /proc/pid/fd/* symlinks +// so we preallocated inode index for them so we just need to set it here. +ProcFSExposedComponent::ProcFSExposedComponent(StringView name, InodeIndex preallocated_index) + : m_component_index(preallocated_index.value()) +{ + VERIFY(preallocated_index.value() != 0); + VERIFY(preallocated_index <= s_next_inode_index); + m_name = KString::try_create(name); +} + +ProcFSExposedFolder::ProcFSExposedFolder(StringView name) + : ProcFSExposedComponent(name) +{ +} + +ProcFSExposedFolder::ProcFSExposedFolder(StringView name, const ProcFSExposedFolder& parent_folder) + : ProcFSExposedComponent(name) + , m_parent_folder(parent_folder) +{ +} + +ProcFSExposedLink::ProcFSExposedLink(StringView name) + : ProcFSExposedComponent(name) +{ +} + +ProcFSExposedLink::ProcFSExposedLink(StringView name, InodeIndex preallocated_index) + : ProcFSExposedComponent(name, preallocated_index) +{ +} + +struct ProcFSInodeData : public FileDescriptionData { + RefPtr buffer; +}; + +KResultOr ProcFSGlobalInformation::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const +{ + dbgln_if(PROCFS_DEBUG, "ProcFSGlobalInformation @ {}: read_bytes offset: {} count: {}", name(), offset, count); + + VERIFY(offset >= 0); + VERIFY(buffer.user_or_kernel_ptr()); + + if (!description) + return KResult(EIO); + if (!description->data()) { + dbgln("ProcFSGlobalInformation: Do not have cached data!"); + return KResult(EIO); + } + + // Be sure to keep a reference to data_buffer while we use it! + RefPtr data_buffer = static_cast(*description->data()).buffer; + + if (!data_buffer || (size_t)offset >= data_buffer->size()) + return 0; + + ssize_t nread = min(static_cast(data_buffer->size() - offset), static_cast(count)); + if (!buffer.write(data_buffer->data() + offset, nread)) + return KResult(EFAULT); + + return nread; +} + +KResult ProcFSGlobalInformation::refresh_data(FileDescription& description) const +{ + ScopedSpinLock lock(m_refresh_lock); + auto& cached_data = description.data(); + if (!cached_data) + cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData); + VERIFY(description.data()); + auto& buffer = static_cast(*cached_data).buffer; + if (buffer) { + // If we're reusing the buffer, reset the size to 0 first. This + // ensures we don't accidentally leak previously written data. + buffer->set_size(0); + } + KBufferBuilder builder(buffer, true); + if (!const_cast(*this).output(builder)) + return ENOENT; + // We don't use builder.build() here, which would steal our buffer + // and turn it into an OwnPtr. Instead, just flush to the buffer so + // that we can read all the data that was written. + if (!builder.flush()) + return ENOMEM; + if (!buffer) + return ENOMEM; + 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); + + VERIFY(offset >= 0); + VERIFY(buffer.user_or_kernel_ptr()); + + if (!description) + return KResult(EIO); + if (!description->data()) { + dbgln("ProcFSGlobalInformation: Do not have cached data!"); + return KResult(EIO); + } + + // Be sure to keep a reference to data_buffer while we use it! + RefPtr data_buffer = static_cast(*description->data()).buffer; + + if (!data_buffer || (size_t)offset >= data_buffer->size()) + return 0; + + ssize_t nread = min(static_cast(data_buffer->size() - offset), static_cast(count)); + if (!buffer.write(data_buffer->data() + offset, nread)) + return KResult(EFAULT); + + return nread; +} + +KResult ProcFSProcessInformation::refresh_data(FileDescription& description) const +{ + // For process-specific inodes, hold the process's ptrace lock across refresh + // and refuse to load data if the process is not dumpable. + // Without this, files opened before a process went non-dumpable could still be used for dumping. + auto process = const_cast(*this).m_parent_folder->m_associated_process; + process->ptrace_lock().lock(); + if (!process->is_dumpable()) { + process->ptrace_lock().unlock(); + return EPERM; + } + ScopeGuard guard = [&] { + process->ptrace_lock().unlock(); + }; + ScopedSpinLock lock(m_refresh_lock); + auto& cached_data = description.data(); + if (!cached_data) + cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData); + VERIFY(description.data()); + auto& buffer = static_cast(*cached_data).buffer; + if (buffer) { + // If we're reusing the buffer, reset the size to 0 first. This + // ensures we don't accidentally leak previously written data. + buffer->set_size(0); + } + KBufferBuilder builder(buffer, true); + if (!const_cast(*this).output(builder)) + return ENOENT; + // We don't use builder.build() here, which would steal our buffer + // and turn it into an OwnPtr. Instead, just flush to the buffer so + // that we can read all the data that was written. + if (!builder.flush()) + return ENOMEM; + if (!buffer) + return ENOMEM; + 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.number_of_open_file_descriptors()); + 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->number_of_open_file_descriptors(); +} +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; + for (int i = 0; i < process->max_open_file_descriptors(); ++i) { + auto description_metadata = process->fds()[i]; + if (!description_metadata.is_valid()) + continue; + InodeIdentifier identifier = { fsid, description_metadata.global_procfs_inode_index() }; + callback({ String::number(i), identifier, 0 }); + } + return KSuccess; +} +RefPtr ProcFSProcessFileDescriptions::lookup(StringView name) +{ + Locker locker(m_lock); + auto process = m_process_folder->m_associated_process; + ScopedSpinLock lock(process->m_fds_lock); + for (int i = 0; i < process->max_open_file_descriptors(); ++i) { + auto description_metadata = process->fds()[i]; + if (!description_metadata.is_valid()) + continue; + if (name == String::number(i)) { + return ProcFSProcessFileDescription::create(i, *description_metadata.description(), description_metadata.global_procfs_inode_index(), *this); + } + } + return nullptr; +} + +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->number_of_open_file_descriptors() == 0) { + array.finish(); + return true; + } + + for (int i = 0; i < process->max_open_file_descriptors(); ++i) { + auto description = process->file_description(i); + if (!description) + continue; + bool cloexec = process->fd_flags(i) & FD_CLOEXEC; + + auto description_object = array.add_object(); + description_object.add("fd", i); + 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()); + } + 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); + Locker locker(m_lock); + KBufferBuilder builder; + if (!const_cast(*this).acquire_link(builder)) + return KResult(EFAULT); + auto blob = builder.build(); + if (!blob) + return KResult(EFAULT); + + ssize_t nread = min(static_cast(blob->size() - offset), static_cast(count)); + if (!buffer.write(blob->data() + offset, nread)) + return KResult(EFAULT); + return nread; +} + +NonnullRefPtr ProcFSExposedLink::to_inode(const ProcFS& procfs_instance) const +{ + return ProcFSLinkInode::create(procfs_instance, *this); +} + +NonnullRefPtr ProcFSExposedComponent::to_inode(const ProcFS& procfs_instance) const +{ + return ProcFSInode::create(procfs_instance, *this); +} + +NonnullRefPtr ProcFSExposedFolder::to_inode(const ProcFS& procfs_instance) const +{ + return ProcFSDirectoryInode::create(procfs_instance, *this); +} + +void ProcFSExposedFolder::add_component(const ProcFSExposedComponent&) +{ + TODO(); +} + +RefPtr ProcFSExposedFolder::lookup(StringView name) +{ + for (auto& component : m_components) { + if (component.name() == name) { + return component; + } + } + return {}; +} + +KResult ProcFSExposedFolder::traverse_as_directory(unsigned fsid, Function callback) const +{ + Locker locker(ProcFSComponentsRegistrar::the().m_lock); + VERIFY(m_parent_folder); + callback({ ".", { fsid, component_index() }, 0 }); + callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); + + for (auto& component : m_components) { + 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 {}; +} + +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/ProcessExposed.h b/Kernel/ProcessExposed.h new file mode 100644 index 0000000000..105af7c24d --- /dev/null +++ b/Kernel/ProcessExposed.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class ProcFS; +class ProcFSExposedComponent; +class ProcFSExposedFolder; +class ProcFSRootFolder; +class ProcFSBusDirectory; +class ProcFSSystemBoolean; + +class ProcFSComponentsRegistrar { + friend class ProcFS; + friend class ProcFSExposedComponent; + friend class ProcFSExposedFolder; + friend class ProcFSRootFolder; + +public: + static ProcFSComponentsRegistrar& the(); + + static void initialize(); + + InodeIndex allocate_inode_index() const; + + ProcFSComponentsRegistrar(); + void register_new_bus_folder(ProcFSExposedFolder&); + + const ProcFSBusDirectory& buses_folder() const; + + void register_new_process(Process&); + void unregister_process(Process&); + + ProcFSRootFolder& root_folder() { return *m_root_folder; } + +private: + Lock m_lock; + NonnullRefPtr m_root_folder; +}; + +class ProcFSExposedComponent : public RefCounted { +public: + virtual KResultOr entries_count() const { VERIFY_NOT_REACHED(); }; + StringView name() const { return m_name->view(); } + virtual KResultOr read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const { VERIFY_NOT_REACHED(); } + virtual KResult traverse_as_directory(unsigned, Function) const { VERIFY_NOT_REACHED(); } + virtual RefPtr lookup(StringView) { VERIFY_NOT_REACHED(); }; + virtual KResultOr write_bytes(off_t, size_t, const UserOrKernelBuffer&, FileDescription*) { return KResult(EROFS); } + virtual size_t size() const { return 0; } + + virtual mode_t required_mode() const { return 0444; } + virtual uid_t owner_user() const { return 0; } + virtual gid_t owner_group() const { return 0; } + + virtual void prepare_for_deletion() { } + virtual KResult refresh_data(FileDescription&) const + { + return KSuccess; + } + + virtual NonnullRefPtr to_inode(const ProcFS& procfs_instance) const; + + size_t component_index() const { return m_component_index; }; + + virtual ~ProcFSExposedComponent() = default; + +protected: + explicit ProcFSExposedComponent(StringView name); + ProcFSExposedComponent(StringView name, InodeIndex preallocated_index); + +private: + OwnPtr m_name; + size_t m_component_index; +}; + +class ProcFSExposedFolder : public ProcFSExposedComponent { + friend class ProcFSProcessFolder; + friend class ProcFSComponentsRegistrar; + +public: + virtual KResultOr entries_count() const override { return m_components.size(); }; + virtual KResult traverse_as_directory(unsigned, Function) const override; + virtual RefPtr lookup(StringView name) override; + void add_component(const ProcFSExposedComponent&); + + virtual void prepare_for_deletion() override + { + m_components.clear(); + m_parent_folder.clear(); + } + virtual mode_t required_mode() const override { return 0555; } + + virtual NonnullRefPtr to_inode(const ProcFS& procfs_instance) const override final; + +protected: + explicit ProcFSExposedFolder(StringView name); + ProcFSExposedFolder(StringView name, const ProcFSExposedFolder& parent_folder); + NonnullRefPtrVector m_components; + RefPtr m_parent_folder; +}; + +class ProcFSExposedLink : public ProcFSExposedComponent { +public: + virtual NonnullRefPtr to_inode(const ProcFS& procfs_instance) const override final; + + virtual KResultOr read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const override; + +protected: + virtual bool acquire_link(KBufferBuilder& builder) = 0; + explicit ProcFSExposedLink(StringView name); + ProcFSExposedLink(StringView name, InodeIndex preallocated_index); + mutable Lock m_lock { "ProcFSLink" }; +}; + +class ProcFSRootFolder; +class ProcFSProcessInformation; + +class ProcFSProcessFolder final : public ProcFSExposedFolder { + friend class ProcFSComponentsRegistrar; + friend class ProcFSRootFolder; + friend class ProcFSProcessInformation; + friend class ProcFSProcessUnveil; + friend class ProcFSProcessPerformanceEvents; + friend class ProcFSProcessFileDescription; + friend class ProcFSProcessFileDescriptions; + friend class ProcFSProcessOverallFileDescriptions; + friend class ProcFSProcessRoot; + friend class ProcFSProcessVirtualMemory; + friend class ProcFSProcessCurrentWorkDirectory; + friend class ProcFSProcessBinary; + friend class ProcFSProcessStacks; + +public: + static NonnullRefPtr create(const Process&); + NonnullRefPtr associated_process() { return m_associated_process; } + + virtual uid_t owner_user() const override { return m_associated_process->uid(); } + virtual gid_t owner_group() const override { return m_associated_process->gid(); } + +private: + IntrusiveListNode> m_list_node; + + explicit ProcFSProcessFolder(const Process&); + NonnullRefPtr m_associated_process; +}; + +class ProcFSRootFolder; + +class ProcFSBusDirectory : public ProcFSExposedFolder { + friend class ProcFSComponentsRegistrar; + +public: + static NonnullRefPtr must_create(const ProcFSRootFolder& parent_folder); + +private: + ProcFSBusDirectory(const ProcFSRootFolder& parent_folder); +}; + +class ProcFSRootFolder final : public ProcFSExposedFolder { + friend class ProcFSComponentsRegistrar; + +public: + virtual RefPtr lookup(StringView name) override; + + RefPtr process_folder_for(Process&); + static NonnullRefPtr must_create(); + virtual ~ProcFSRootFolder(); + +private: + virtual KResult traverse_as_directory(unsigned, Function) const override; + ProcFSRootFolder(); + + RefPtr m_buses_folder; + IntrusiveList, &ProcFSProcessFolder::m_list_node> m_process_folders; +}; + +class ProcFSGlobalInformation : public ProcFSExposedComponent { +public: + virtual ~ProcFSGlobalInformation() override {}; + + virtual KResultOr read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const override; + + virtual mode_t required_mode() const override { return 0444; } + +protected: + explicit ProcFSGlobalInformation(StringView name) + : ProcFSExposedComponent(name) + { + } + virtual KResult refresh_data(FileDescription&) const override; + virtual bool output(KBufferBuilder& builder) = 0; + + mutable SpinLock m_refresh_lock; +}; + +class ProcFSSystemBoolean : public ProcFSGlobalInformation { +public: + virtual bool value() const = 0; + virtual void set_value(bool new_value) = 0; + +protected: + explicit ProcFSSystemBoolean(StringView name) + : ProcFSGlobalInformation(name) + { + } + virtual bool output(KBufferBuilder& builder) override + { + builder.appendff("{}\n", value()); + return true; + } +}; + +class ProcFSProcessInformation : public ProcFSExposedComponent { +public: + virtual ~ProcFSProcessInformation() override {}; + + virtual KResultOr read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const override; + + virtual uid_t owner_user() const override { return m_parent_folder->m_associated_process->uid(); } + virtual gid_t owner_group() const override { return m_parent_folder->m_associated_process->gid(); } + +protected: + ProcFSProcessInformation(StringView name, const ProcFSProcessFolder& process_folder) + : ProcFSExposedComponent(name) + , m_parent_folder(process_folder) + { + } + + virtual KResult refresh_data(FileDescription&) const override; + virtual bool output(KBufferBuilder& builder) = 0; + + NonnullRefPtr m_parent_folder; + mutable SpinLock m_refresh_lock; +}; + +} diff --git a/Kernel/Syscalls/fork.cpp b/Kernel/Syscalls/fork.cpp index 1021233f03..4619424fc6 100644 --- a/Kernel/Syscalls/fork.cpp +++ b/Kernel/Syscalls/fork.cpp @@ -109,8 +109,7 @@ KResultOr Process::sys$fork(RegisterState& regs) child->m_master_tls_region = child_region; } - ScopedSpinLock processes_lock(g_processes_lock); - g_processes->prepend(*child); + Process::register_new(*child); } PerformanceManager::add_process_created_event(*child); diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index e18dd2c782..3eca547e4b 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ Thread::Thread(NonnullRefPtr process, NonnullOwnPtr kernel_stac , m_kernel_stack_region(move(kernel_stack_region)) , m_name(m_process->name()) , m_block_timer(block_timer) + , m_global_procfs_inode_index(ProcFSComponentsRegistrar::the().allocate_inode_index()) { bool is_first_thread = m_process->add_thread(*this); if (is_first_thread) { diff --git a/Kernel/Thread.h b/Kernel/Thread.h index f71d51d22f..ad758aa5e7 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1184,6 +1185,7 @@ public: bool may_die_immediately() const { return m_may_die_immediately; } void set_may_die_immediately(bool flag) { m_may_die_immediately = flag; } + InodeIndex global_procfs_inode_index() const { return m_global_procfs_inode_index; } private: Thread(NonnullRefPtr, NonnullOwnPtr, NonnullRefPtr); @@ -1328,6 +1330,10 @@ private: RefPtr m_block_timer; + // Note: This is needed so when we generate thread stack inodes for ProcFS, we know that + // we assigned a global Inode index to it so we can use it later + InodeIndex m_global_procfs_inode_index; + bool m_is_profiling_suppressed { false }; void yield_without_holding_big_lock(); diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 10e4e8b1eb..908ef0f248 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -8,8 +8,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -148,6 +149,7 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT void init() // Initialize the PCI Bus as early as possible, for early boot (PCI based) serial logging SystemRegistrar::initialize(); + ProcFSComponentsRegistrar::initialize(); PCI::initialize(); PCISerialDevice::detect();