/* * Copyright (c) 2021, Liav A. * Copyright (c) 2022, Linus Groh * * 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 namespace Kernel { class ProcFSAdapters final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSAdapters(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto array = TRY(JsonArraySerializer<>::try_create(builder)); TRY(NetworkingManagement::the().try_for_each([&array](auto& adapter) -> ErrorOr { auto obj = TRY(array.add_object()); TRY(obj.add("name"sv, adapter.name())); TRY(obj.add("class_name"sv, adapter.class_name())); auto mac_address = TRY(adapter.mac_address().to_string()); TRY(obj.add("mac_address"sv, mac_address->view())); if (!adapter.ipv4_address().is_zero()) { auto ipv4_address = TRY(adapter.ipv4_address().to_string()); TRY(obj.add("ipv4_address"sv, ipv4_address->view())); auto ipv4_netmask = TRY(adapter.ipv4_netmask().to_string()); TRY(obj.add("ipv4_netmask"sv, ipv4_netmask->view())); } TRY(obj.add("packets_in"sv, adapter.packets_in())); TRY(obj.add("bytes_in"sv, adapter.bytes_in())); TRY(obj.add("packets_out"sv, adapter.packets_out())); TRY(obj.add("bytes_out"sv, adapter.bytes_out())); TRY(obj.add("link_up"sv, adapter.link_up())); TRY(obj.add("link_speed"sv, adapter.link_speed())); TRY(obj.add("link_full_duplex"sv, adapter.link_full_duplex())); TRY(obj.add("mtu"sv, adapter.mtu())); TRY(obj.finish()); return {}; })); TRY(array.finish()); return {}; } }; class ProcFSARP final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSARP(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto array = TRY(JsonArraySerializer<>::try_create(builder)); TRY(arp_table().with([&](auto const& table) -> ErrorOr { for (auto& it : table) { auto obj = TRY(array.add_object()); auto mac_address = TRY(it.value.to_string()); TRY(obj.add("mac_address"sv, mac_address->view())); auto ip_address = TRY(it.key.to_string()); TRY(obj.add("ip_address"sv, ip_address->view())); TRY(obj.finish()); } return {}; })); TRY(array.finish()); return {}; } }; class ProcFSRoute final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSRoute(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto array = TRY(JsonArraySerializer<>::try_create(builder)); TRY(routing_table().with([&](auto const& table) -> ErrorOr { for (auto& it : table) { auto obj = TRY(array.add_object()); auto destination = TRY(it.destination.to_string()); TRY(obj.add("destination"sv, destination->view())); auto gateway = TRY(it.gateway.to_string()); TRY(obj.add("gateway"sv, gateway->view())); auto netmask = TRY(it.netmask.to_string()); TRY(obj.add("genmask"sv, netmask->view())); TRY(obj.add("flags"sv, it.flags)); TRY(obj.add("interface"sv, it.adapter->name())); TRY(obj.finish()); } return {}; })); TRY(array.finish()); return {}; } }; class ProcFSTCP final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSTCP(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto array = TRY(JsonArraySerializer<>::try_create(builder)); TRY(TCPSocket::try_for_each([&array](auto& socket) -> ErrorOr { auto obj = TRY(array.add_object()); auto local_address = TRY(socket.local_address().to_string()); TRY(obj.add("local_address"sv, local_address->view())); TRY(obj.add("local_port"sv, socket.local_port())); auto peer_address = TRY(socket.peer_address().to_string()); TRY(obj.add("peer_address"sv, peer_address->view())); TRY(obj.add("peer_port"sv, socket.peer_port())); TRY(obj.add("state"sv, TCPSocket::to_string(socket.state()))); TRY(obj.add("ack_number"sv, socket.ack_number())); TRY(obj.add("sequence_number"sv, socket.sequence_number())); TRY(obj.add("packets_in"sv, socket.packets_in())); TRY(obj.add("bytes_in"sv, socket.bytes_in())); TRY(obj.add("packets_out"sv, socket.packets_out())); TRY(obj.add("bytes_out"sv, socket.bytes_out())); if (Process::current().is_superuser() || Process::current().uid() == socket.origin_uid()) { TRY(obj.add("origin_pid"sv, socket.origin_pid().value())); TRY(obj.add("origin_uid"sv, socket.origin_uid().value())); TRY(obj.add("origin_gid"sv, socket.origin_gid().value())); } TRY(obj.finish()); return {}; })); TRY(array.finish()); return {}; } }; class ProcFSLocalNet final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSLocalNet(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto array = TRY(JsonArraySerializer<>::try_create(builder)); TRY(LocalSocket::try_for_each([&array](auto& socket) -> ErrorOr { auto obj = TRY(array.add_object()); TRY(obj.add("path"sv, socket.socket_path())); TRY(obj.add("origin_pid"sv, socket.origin_pid().value())); TRY(obj.add("origin_uid"sv, socket.origin_uid().value())); TRY(obj.add("origin_gid"sv, socket.origin_gid().value())); TRY(obj.add("acceptor_pid"sv, socket.acceptor_pid().value())); TRY(obj.add("acceptor_uid"sv, socket.acceptor_uid().value())); TRY(obj.add("acceptor_gid"sv, socket.acceptor_gid().value())); TRY(obj.finish()); return {}; })); TRY(array.finish()); return {}; } }; class ProcFSUDP final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSUDP(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto array = TRY(JsonArraySerializer<>::try_create(builder)); TRY(UDPSocket::try_for_each([&array](auto& socket) -> ErrorOr { auto obj = TRY(array.add_object()); auto local_address = TRY(socket.local_address().to_string()); TRY(obj.add("local_address"sv, local_address->view())); TRY(obj.add("local_port"sv, socket.local_port())); auto peer_address = TRY(socket.peer_address().to_string()); TRY(obj.add("peer_address"sv, peer_address->view())); TRY(obj.add("peer_port"sv, socket.peer_port())); if (Process::current().is_superuser() || Process::current().uid() == socket.origin_uid()) { TRY(obj.add("origin_pid"sv, socket.origin_pid().value())); TRY(obj.add("origin_uid"sv, socket.origin_uid().value())); TRY(obj.add("origin_gid"sv, socket.origin_gid().value())); } TRY(obj.finish()); return {}; })); TRY(array.finish()); return {}; } }; class ProcFSNetworkDirectory : public ProcFSExposedDirectory { public: static NonnullRefPtr must_create(ProcFSRootDirectory const& parent_directory); private: ProcFSNetworkDirectory(ProcFSRootDirectory const& parent_directory); }; class ProcFSSystemDirectory : public ProcFSExposedDirectory { public: static NonnullRefPtr must_create(ProcFSRootDirectory const& parent_directory); private: ProcFSSystemDirectory(ProcFSRootDirectory const& parent_directory); }; 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 ProcFSRoute::must_create() { return adopt_ref_if_nonnull(new (nothrow) ProcFSRoute).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(ProcFSRootDirectory const& parent_directory) { auto directory = adopt_ref(*new (nothrow) ProcFSNetworkDirectory(parent_directory)); directory->m_components.append(ProcFSAdapters::must_create()); directory->m_components.append(ProcFSARP::must_create()); directory->m_components.append(ProcFSRoute::must_create()); directory->m_components.append(ProcFSTCP::must_create()); directory->m_components.append(ProcFSLocalNet::must_create()); directory->m_components.append(ProcFSUDP::must_create()); return directory; } UNMAP_AFTER_INIT ProcFSAdapters::ProcFSAdapters() : ProcFSGlobalInformation("adapters"sv) { } UNMAP_AFTER_INIT ProcFSARP::ProcFSARP() : ProcFSGlobalInformation("arp"sv) { } UNMAP_AFTER_INIT ProcFSRoute::ProcFSRoute() : ProcFSGlobalInformation("route"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(ProcFSRootDirectory const& parent_directory) : ProcFSExposedDirectory("net"sv, parent_directory) { } class ProcFSDumpKmallocStacks : public ProcFSSystemBoolean { public: static NonnullRefPtr must_create(ProcFSSystemDirectory const&); virtual bool value() const override { MutexLocker locker(m_lock); return g_dump_kmalloc_stacks; } virtual void set_value(bool new_value) override { MutexLocker locker(m_lock); g_dump_kmalloc_stacks = new_value; } private: ProcFSDumpKmallocStacks(); mutable Mutex m_lock; }; class ProcFSUBSanDeadly : public ProcFSSystemBoolean { public: static NonnullRefPtr must_create(ProcFSSystemDirectory const&); virtual bool value() const override { return AK::UBSanitizer::g_ubsan_is_deadly; } virtual void set_value(bool new_value) override { AK::UBSanitizer::g_ubsan_is_deadly = new_value; } private: ProcFSUBSanDeadly(); }; class ProcFSCapsLockRemap : public ProcFSSystemBoolean { public: static NonnullRefPtr must_create(ProcFSSystemDirectory const&); virtual bool value() const override { MutexLocker locker(m_lock); return g_caps_lock_remapped_to_ctrl.load(); } virtual void set_value(bool new_value) override { MutexLocker locker(m_lock); g_caps_lock_remapped_to_ctrl.exchange(new_value); } private: ProcFSCapsLockRemap(); mutable Mutex m_lock; }; UNMAP_AFTER_INIT NonnullRefPtr ProcFSDumpKmallocStacks::must_create(ProcFSSystemDirectory const&) { return adopt_ref_if_nonnull(new (nothrow) ProcFSDumpKmallocStacks).release_nonnull(); } UNMAP_AFTER_INIT NonnullRefPtr ProcFSUBSanDeadly::must_create(ProcFSSystemDirectory const&) { return adopt_ref_if_nonnull(new (nothrow) ProcFSUBSanDeadly).release_nonnull(); } UNMAP_AFTER_INIT NonnullRefPtr ProcFSCapsLockRemap::must_create(ProcFSSystemDirectory const&) { 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 ProcFSSelfProcessDirectory final : public ProcFSExposedLink { public: static NonnullRefPtr must_create(); private: ProcFSSelfProcessDirectory(); virtual bool acquire_link(KBufferBuilder& builder) override { return !builder.appendff("{}", Process::current().pid().value()).is_error(); } }; class ProcFSDiskUsage final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSDiskUsage(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto array = TRY(JsonArraySerializer<>::try_create(builder)); TRY(VirtualFileSystem::the().for_each_mount([&array](auto& mount) -> ErrorOr { auto& fs = mount.guest_fs(); auto fs_object = TRY(array.add_object()); TRY(fs_object.add("class_name"sv, fs.class_name())); TRY(fs_object.add("total_block_count"sv, fs.total_block_count())); TRY(fs_object.add("free_block_count"sv, fs.free_block_count())); TRY(fs_object.add("total_inode_count"sv, fs.total_inode_count())); TRY(fs_object.add("free_inode_count"sv, fs.free_inode_count())); auto mount_point = TRY(mount.absolute_path()); TRY(fs_object.add("mount_point"sv, mount_point->view())); TRY(fs_object.add("block_size"sv, static_cast(fs.block_size()))); TRY(fs_object.add("readonly"sv, fs.is_readonly())); TRY(fs_object.add("mount_flags"sv, mount.flags())); if (fs.is_file_backed()) { auto pseudo_path = TRY(static_cast(fs).file_description().pseudo_path()); TRY(fs_object.add("source"sv, pseudo_path->view())); } else { TRY(fs_object.add("source"sv, "none")); } TRY(fs_object.finish()); return {}; })); TRY(array.finish()); return {}; } }; class ProcFSMemoryStatus final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSMemoryStatus(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { InterruptDisabler disabler; kmalloc_stats stats; get_kmalloc_stats(stats); auto system_memory = MM.get_system_memory_info(); auto json = TRY(JsonObjectSerializer<>::try_create(builder)); TRY(json.add("kmalloc_allocated"sv, stats.bytes_allocated)); TRY(json.add("kmalloc_available"sv, stats.bytes_free)); TRY(json.add("physical_allocated"sv, system_memory.physical_pages_used)); TRY(json.add("physical_available"sv, system_memory.physical_pages - system_memory.physical_pages_used)); TRY(json.add("physical_committed"sv, system_memory.physical_pages_committed)); TRY(json.add("physical_uncommitted"sv, system_memory.physical_pages_uncommitted)); TRY(json.add("kmalloc_call_count"sv, stats.kmalloc_call_count)); TRY(json.add("kfree_call_count"sv, stats.kfree_call_count)); TRY(json.finish()); return {}; } }; class ProcFSSystemStatistics final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSSystemStatistics(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto json = TRY(JsonObjectSerializer<>::try_create(builder)); auto total_time_scheduled = Scheduler::get_total_time_scheduled(); TRY(json.add("total_time"sv, total_time_scheduled.total)); TRY(json.add("kernel_time"sv, total_time_scheduled.total_kernel)); TRY(json.add("user_time"sv, total_time_scheduled.total - total_time_scheduled.total_kernel)); u64 idle_time = 0; Processor::for_each([&](Processor& processor) { idle_time += processor.time_spent_idle(); }); TRY(json.add("idle_time"sv, idle_time)); TRY(json.finish()); return {}; } }; class ProcFSOverallProcesses final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSOverallProcesses(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto json = TRY(JsonObjectSerializer<>::try_create(builder)); // Keep this in sync with CProcessStatistics. auto build_process = [&](JsonArraySerializer& array, Process const& process) -> ErrorOr { auto process_object = TRY(array.add_object()); if (process.is_user_process()) { StringBuilder pledge_builder; #define __ENUMERATE_PLEDGE_PROMISE(promise) \ if (process.has_promised(Pledge::promise)) \ TRY(pledge_builder.try_append(#promise " "sv)); ENUMERATE_PLEDGE_PROMISES #undef __ENUMERATE_PLEDGE_PROMISE TRY(process_object.add("pledge"sv, pledge_builder.string_view())); switch (process.veil_state()) { case VeilState::None: TRY(process_object.add("veil"sv, "None")); break; case VeilState::Dropped: TRY(process_object.add("veil"sv, "Dropped")); break; case VeilState::Locked: TRY(process_object.add("veil"sv, "Locked")); break; } } else { TRY(process_object.add("pledge"sv, ""sv)); TRY(process_object.add("veil"sv, ""sv)); } TRY(process_object.add("pid"sv, process.pid().value())); TRY(process_object.add("pgid"sv, process.tty() ? process.tty()->pgid().value() : 0)); TRY(process_object.add("pgp"sv, process.pgid().value())); TRY(process_object.add("sid"sv, process.sid().value())); TRY(process_object.add("uid"sv, process.uid().value())); TRY(process_object.add("gid"sv, process.gid().value())); TRY(process_object.add("ppid"sv, process.ppid().value())); if (process.tty()) { auto tty_pseudo_name = TRY(process.tty()->pseudo_name()); TRY(process_object.add("tty"sv, tty_pseudo_name->view())); } else { TRY(process_object.add("tty"sv, "")); } TRY(process_object.add("nfds"sv, process.fds().with_shared([](auto& fds) { return fds.open_count(); }))); TRY(process_object.add("name"sv, process.name())); TRY(process_object.add("executable"sv, process.executable() ? TRY(process.executable()->try_serialize_absolute_path())->view() : ""sv)); TRY(process_object.add("amount_virtual"sv, process.address_space().amount_virtual())); TRY(process_object.add("amount_resident"sv, process.address_space().amount_resident())); TRY(process_object.add("amount_dirty_private"sv, process.address_space().amount_dirty_private())); TRY(process_object.add("amount_clean_inode"sv, TRY(process.address_space().amount_clean_inode()))); TRY(process_object.add("amount_shared"sv, process.address_space().amount_shared())); TRY(process_object.add("amount_purgeable_volatile"sv, process.address_space().amount_purgeable_volatile())); TRY(process_object.add("amount_purgeable_nonvolatile"sv, process.address_space().amount_purgeable_nonvolatile())); TRY(process_object.add("dumpable"sv, process.is_dumpable())); TRY(process_object.add("kernel"sv, process.is_kernel_process())); auto thread_array = TRY(process_object.add_array("threads"sv)); TRY(process.try_for_each_thread([&](const Thread& thread) -> ErrorOr { SpinlockLocker locker(thread.get_lock()); auto thread_object = TRY(thread_array.add_object()); #if LOCK_DEBUG TRY(thread_object.add("lock_count"sv, thread.lock_count())); #endif TRY(thread_object.add("tid"sv, thread.tid().value())); TRY(thread_object.add("name"sv, thread.name())); TRY(thread_object.add("times_scheduled"sv, thread.times_scheduled())); TRY(thread_object.add("time_user"sv, thread.time_in_user())); TRY(thread_object.add("time_kernel"sv, thread.time_in_kernel())); TRY(thread_object.add("state"sv, thread.state_string())); TRY(thread_object.add("cpu"sv, thread.cpu())); TRY(thread_object.add("priority"sv, thread.priority())); TRY(thread_object.add("syscall_count"sv, thread.syscall_count())); TRY(thread_object.add("inode_faults"sv, thread.inode_faults())); TRY(thread_object.add("zero_faults"sv, thread.zero_faults())); TRY(thread_object.add("cow_faults"sv, thread.cow_faults())); TRY(thread_object.add("file_read_bytes"sv, thread.file_read_bytes())); TRY(thread_object.add("file_write_bytes"sv, thread.file_write_bytes())); TRY(thread_object.add("unix_socket_read_bytes"sv, thread.unix_socket_read_bytes())); TRY(thread_object.add("unix_socket_write_bytes"sv, thread.unix_socket_write_bytes())); TRY(thread_object.add("ipv4_socket_read_bytes"sv, thread.ipv4_socket_read_bytes())); TRY(thread_object.add("ipv4_socket_write_bytes"sv, thread.ipv4_socket_write_bytes())); TRY(thread_object.finish()); return {}; })); TRY(thread_array.finish()); TRY(process_object.finish()); return {}; }; { auto array = TRY(json.add_array("processes"sv)); TRY(build_process(array, *Scheduler::colonel())); TRY(Process::all_instances().with([&](auto& processes) -> ErrorOr { for (auto& process : processes) TRY(build_process(array, process)); return {}; })); TRY(array.finish()); } auto total_time_scheduled = Scheduler::get_total_time_scheduled(); TRY(json.add("total_time"sv, total_time_scheduled.total)); TRY(json.add("total_time_kernel"sv, total_time_scheduled.total_kernel)); TRY(json.finish()); return {}; } }; class ProcFSCPUInformation final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSCPUInformation(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto array = TRY(JsonArraySerializer<>::try_create(builder)); TRY(Processor::try_for_each( [&](Processor& proc) -> ErrorOr { auto& info = proc.info(); auto obj = TRY(array.add_object()); TRY(obj.add("processor"sv, proc.id())); TRY(obj.add("vendor_id"sv, info.vendor_id_string())); TRY(obj.add("family"sv, info.display_family())); if (!info.hypervisor_vendor_id_string().is_null()) TRY(obj.add("hypervisor_vendor_id"sv, info.hypervisor_vendor_id_string())); auto features_array = TRY(obj.add_array("features"sv)); auto keep_empty = false; ErrorOr result; // FIXME: Make this nicer info.features_string().for_each_split_view(' ', keep_empty, [&](StringView feature) { if (result.is_error()) return; result = features_array.add(feature); }); TRY(result); TRY(features_array.finish()); TRY(obj.add("model"sv, info.display_model())); TRY(obj.add("stepping"sv, info.stepping())); TRY(obj.add("type"sv, info.type())); TRY(obj.add("brand"sv, info.brand_string())); auto caches = TRY(obj.add_object("caches"sv)); auto add_cache_info = [&](StringView name, ProcessorInfo::Cache const& cache) -> ErrorOr { auto cache_object = TRY(caches.add_object(name)); TRY(cache_object.add("size"sv, cache.size)); TRY(cache_object.add("line_size"sv, cache.line_size)); TRY(cache_object.finish()); return {}; }; if (info.l1_data_cache().has_value()) TRY(add_cache_info("l1_data"sv, *info.l1_data_cache())); if (info.l1_instruction_cache().has_value()) TRY(add_cache_info("l1_instruction"sv, *info.l1_instruction_cache())); if (info.l2_cache().has_value()) TRY(add_cache_info("l2"sv, *info.l2_cache())); if (info.l3_cache().has_value()) TRY(add_cache_info("l3"sv, *info.l3_cache())); TRY(caches.finish()); TRY(obj.finish()); return {}; })); TRY(array.finish()); return {}; } }; class ProcFSDmesg final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); virtual mode_t required_mode() const override { return 0400; } private: ProcFSDmesg(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { VERIFY(DeviceManagement::the().is_console_device_attached()); InterruptDisabler disabler; for (char ch : DeviceManagement::the().console_device().logbuffer()) { TRY(builder.append(ch)); } return {}; } }; class ProcFSInterrupts final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSInterrupts(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto array = TRY(JsonArraySerializer<>::try_create(builder)); ErrorOr result; // FIXME: Make this nicer InterruptManagement::the().enumerate_interrupt_handlers([&array, &result](GenericInterruptHandler& handler) { if (result.is_error()) return; result = ([&]() -> ErrorOr { auto obj = TRY(array.add_object()); TRY(obj.add("purpose"sv, handler.purpose())); TRY(obj.add("interrupt_line"sv, handler.interrupt_number())); TRY(obj.add("controller"sv, handler.controller())); TRY(obj.add("cpu_handler"sv, 0)); // FIXME: Determine the responsible CPU for each interrupt handler. TRY(obj.add("device_sharing"sv, (unsigned)handler.sharing_devices_count())); TRY(obj.add("call_count"sv, (unsigned)handler.get_invoking_count())); TRY(obj.finish()); return {}; })(); }); TRY(result); TRY(array.finish()); return {}; } }; class ProcFSKeymap final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSKeymap(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto json = TRY(JsonObjectSerializer<>::try_create(builder)); TRY(HIDManagement::the().keymap_data().with([&](auto const& keymap_data) { return json.add("keymap"sv, keymap_data.character_map_name->view()); })); TRY(json.finish()); return {}; } }; // 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 ErrorOr try_generate(KBufferBuilder& builder) override { auto array = TRY(JsonArraySerializer<>::try_create(builder)); ErrorOr result; // FIXME: Make this nicer TRY(PCI::enumerate([&array, &result](PCI::DeviceIdentifier const& device_identifier) { if (result.is_error()) return; result = ([&]() -> ErrorOr { auto obj = TRY(array.add_object()); TRY(obj.add("domain"sv, device_identifier.address().domain())); TRY(obj.add("bus"sv, device_identifier.address().bus())); TRY(obj.add("device"sv, device_identifier.address().device())); TRY(obj.add("function"sv, device_identifier.address().function())); TRY(obj.add("vendor_id"sv, device_identifier.hardware_id().vendor_id)); TRY(obj.add("device_id"sv, device_identifier.hardware_id().device_id)); TRY(obj.add("revision_id"sv, device_identifier.revision_id().value())); TRY(obj.add("subclass"sv, device_identifier.subclass_code().value())); TRY(obj.add("class"sv, device_identifier.class_code().value())); TRY(obj.add("subsystem_id"sv, device_identifier.subsystem_id().value())); TRY(obj.add("subsystem_vendor_id"sv, device_identifier.subsystem_vendor_id().value())); TRY(obj.finish()); return {}; })(); })); TRY(result); TRY(array.finish()); return {}; } }; class ProcFSDevices final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSDevices(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { auto array = TRY(JsonArraySerializer<>::try_create(builder)); TRY(DeviceManagement::the().try_for_each([&array](auto& device) -> ErrorOr { auto obj = TRY(array.add_object()); TRY(obj.add("major"sv, device.major().value())); TRY(obj.add("minor"sv, device.minor().value())); TRY(obj.add("class_name"sv, device.class_name())); if (device.is_block_device()) TRY(obj.add("type"sv, "block")); else if (device.is_character_device()) TRY(obj.add("type"sv, "character")); else VERIFY_NOT_REACHED(); TRY(obj.finish()); return {}; })); TRY(array.finish()); return {}; } }; class ProcFSUptime final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSUptime(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { return builder.appendff("{}\n", TimeManagement::the().uptime_ms() / 1000); } }; class ProcFSCommandLine final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSCommandLine(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { TRY(builder.append(kernel_command_line().string())); TRY(builder.append('\n')); return {}; } }; class ProcFSSystemMode final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSSystemMode(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { TRY(builder.append(kernel_command_line().system_mode())); TRY(builder.append('\n')); return {}; } }; class ProcFSProfile final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); virtual mode_t required_mode() const override { return 0400; } private: ProcFSProfile(); virtual ErrorOr try_generate(KBufferBuilder& builder) override { if (!g_global_perf_events) return ENOENT; TRY(g_global_perf_events->to_json(builder)); return {}; } }; class ProcFSKernelBase final : public ProcFSGlobalInformation { public: static NonnullRefPtr must_create(); private: ProcFSKernelBase(); virtual mode_t required_mode() const override { return 0400; } virtual ErrorOr try_generate(KBufferBuilder& builder) override { if (!Process::current().is_superuser()) return EPERM; return builder.appendff("{}", kernel_load_base); } }; UNMAP_AFTER_INIT NonnullRefPtr ProcFSSelfProcessDirectory::must_create() { return adopt_ref_if_nonnull(new (nothrow) ProcFSSelfProcessDirectory()).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 ProcFSSystemStatistics::must_create() { return adopt_ref_if_nonnull(new (nothrow) ProcFSSystemStatistics).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 ProcFSSystemMode::must_create() { return adopt_ref_if_nonnull(new (nothrow) ProcFSSystemMode).release_nonnull(); } UNMAP_AFTER_INIT NonnullRefPtr ProcFSProfile::must_create() { return adopt_ref_if_nonnull(new (nothrow) ProcFSProfile).release_nonnull(); } UNMAP_AFTER_INIT NonnullRefPtr ProcFSKernelBase::must_create() { return adopt_ref_if_nonnull(new (nothrow) ProcFSKernelBase).release_nonnull(); } UNMAP_AFTER_INIT ProcFSSelfProcessDirectory::ProcFSSelfProcessDirectory() : ProcFSExposedLink("self"sv) { } UNMAP_AFTER_INIT ProcFSDiskUsage::ProcFSDiskUsage() : ProcFSGlobalInformation("df"sv) { } UNMAP_AFTER_INIT ProcFSMemoryStatus::ProcFSMemoryStatus() : ProcFSGlobalInformation("memstat"sv) { } UNMAP_AFTER_INIT ProcFSSystemStatistics::ProcFSSystemStatistics() : ProcFSGlobalInformation("stat"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 ProcFSSystemMode::ProcFSSystemMode() : ProcFSGlobalInformation("system_mode"sv) { } UNMAP_AFTER_INIT ProcFSProfile::ProcFSProfile() : ProcFSGlobalInformation("profile"sv) { } UNMAP_AFTER_INIT ProcFSKernelBase::ProcFSKernelBase() : ProcFSGlobalInformation("kernel_base"sv) { } UNMAP_AFTER_INIT NonnullRefPtr ProcFSSystemDirectory::must_create(ProcFSRootDirectory const& parent_directory) { auto directory = adopt_ref(*new (nothrow) ProcFSSystemDirectory(parent_directory)); directory->m_components.append(ProcFSDumpKmallocStacks::must_create(directory)); directory->m_components.append(ProcFSUBSanDeadly::must_create(directory)); directory->m_components.append(ProcFSCapsLockRemap::must_create(directory)); return directory; } UNMAP_AFTER_INIT ProcFSSystemDirectory::ProcFSSystemDirectory(ProcFSRootDirectory const& parent_directory) : ProcFSExposedDirectory("sys"sv, parent_directory) { } UNMAP_AFTER_INIT void ProcFSRootDirectory::add_pci_node(Badge) { m_components.append(ProcFSPCI::must_create()); } UNMAP_AFTER_INIT NonnullRefPtr ProcFSRootDirectory::must_create() { auto directory = adopt_ref(*new (nothrow) ProcFSRootDirectory); directory->m_components.append(ProcFSSelfProcessDirectory::must_create()); directory->m_components.append(ProcFSDiskUsage::must_create()); directory->m_components.append(ProcFSMemoryStatus::must_create()); directory->m_components.append(ProcFSSystemStatistics::must_create()); directory->m_components.append(ProcFSOverallProcesses::must_create()); directory->m_components.append(ProcFSCPUInformation::must_create()); directory->m_components.append(ProcFSDmesg::must_create()); directory->m_components.append(ProcFSInterrupts::must_create()); directory->m_components.append(ProcFSKeymap::must_create()); directory->m_components.append(ProcFSDevices::must_create()); directory->m_components.append(ProcFSUptime::must_create()); directory->m_components.append(ProcFSCommandLine::must_create()); directory->m_components.append(ProcFSSystemMode::must_create()); directory->m_components.append(ProcFSProfile::must_create()); directory->m_components.append(ProcFSKernelBase::must_create()); directory->m_components.append(ProcFSNetworkDirectory::must_create(*directory)); directory->m_components.append(ProcFSSystemDirectory::must_create(*directory)); return directory; } ErrorOr ProcFSRootDirectory::traverse_as_directory(FileSystemID fsid, Function(FileSystem::DirectoryEntryView const&)> callback) const { MutexLocker locker(ProcFSComponentRegistry::the().get_lock()); TRY(callback({ "."sv, { fsid, component_index() }, 0 })); TRY(callback({ ".."sv, { fsid, 0 }, 0 })); for (auto const& component : m_components) { InodeIdentifier identifier = { fsid, component.component_index() }; TRY(callback({ component.name(), identifier, 0 })); } return Process::all_instances().with([&](auto& list) -> ErrorOr { for (auto& process : list) { VERIFY(!(process.pid() < 0)); u64 process_id = (u64)process.pid().value(); InodeIdentifier identifier = { fsid, static_cast(process_id << 36) }; auto process_id_string = TRY(KString::formatted("{:d}", process_id)); TRY(callback({ process_id_string->view(), identifier, 0 })); } return {}; }); } ErrorOr> ProcFSRootDirectory::lookup(StringView name) { auto maybe_candidate = ProcFSExposedDirectory::lookup(name); if (maybe_candidate.is_error()) { if (maybe_candidate.error().code() != ENOENT) { return maybe_candidate.release_error(); } } else { return maybe_candidate.release_value(); } auto pid = name.to_uint(); if (!pid.has_value()) return ESRCH; auto actual_pid = pid.value(); if (auto maybe_process = Process::from_pid(actual_pid)) return maybe_process->procfs_traits(); return ENOENT; } UNMAP_AFTER_INIT ProcFSRootDirectory::ProcFSRootDirectory() : ProcFSExposedDirectory("."sv) { } UNMAP_AFTER_INIT ProcFSRootDirectory::~ProcFSRootDirectory() = default; }