Serenity Operating System
at master 372 lines 17 kB view raw
1/* 2 * Copyright (c) 2021-2023, Liav A. <liavalb@hotmail.co.il> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/JsonArraySerializer.h> 8#include <AK/JsonObjectSerializer.h> 9#include <AK/JsonValue.h> 10#include <Kernel/FileSystem/Custody.h> 11#include <Kernel/FileSystem/ProcFS/Inode.h> 12#include <Kernel/InterruptDisabler.h> 13#include <Kernel/KBufferBuilder.h> 14#include <Kernel/Memory/AnonymousVMObject.h> 15#include <Kernel/Memory/MemoryManager.h> 16#include <Kernel/Process.h> 17#include <Kernel/TTY/TTY.h> 18 19namespace Kernel { 20 21ErrorOr<void> Process::traverse_as_directory(FileSystemID fsid, Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const 22{ 23 TRY(callback({ main_process_directory_root_entry.name, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), main_process_directory_root_entry) }, main_process_directory_root_entry.file_type })); 24 TRY(callback({ ".."sv, { fsid, ProcFSInode::create_index_from_global_directory_entry(global_inode_ids[0]) }, global_inode_ids[0].file_type })); 25 26 for (auto& entry : main_process_directory_entries) { 27 TRY(callback({ entry.name, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }, entry.file_type })); 28 } 29 return {}; 30} 31 32ErrorOr<NonnullRefPtr<Inode>> Process::lookup_as_directory(ProcFS& procfs, StringView name) const 33{ 34 for (auto& entry : main_process_directory_entries) { 35 if (entry.name == name) 36 return procfs.get_inode({ procfs.fsid(), ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }); 37 } 38 return ENOENT; 39} 40 41ErrorOr<void> Process::procfs_get_thread_stack(ThreadID thread_id, KBufferBuilder& builder) const 42{ 43 auto array = TRY(JsonArraySerializer<>::try_create(builder)); 44 auto thread = Thread::from_tid(thread_id); 45 if (!thread) 46 return ESRCH; 47 auto current_process_credentials = Process::current().credentials(); 48 bool show_kernel_addresses = current_process_credentials->is_superuser(); 49 bool kernel_address_added = false; 50 for (auto address : TRY(Processor::capture_stack_trace(*thread, 1024))) { 51 if (!show_kernel_addresses && !Memory::is_user_address(VirtualAddress { address })) { 52 if (kernel_address_added) 53 continue; 54 address = 0xdeadc0de; 55 kernel_address_added = true; 56 } 57 TRY(array.add(address)); 58 } 59 60 TRY(array.finish()); 61 return {}; 62} 63 64ErrorOr<void> Process::traverse_stacks_directory(FileSystemID fsid, Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const 65{ 66 TRY(callback({ "."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), process_stacks_subdirectory_root_entry) }, DT_DIR })); 67 TRY(callback({ ".."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), main_process_directory_root_entry) }, main_process_directory_root_entry.file_type })); 68 69 return thread_list().with([&](auto& list) -> ErrorOr<void> { 70 for (auto const& thread : list) { 71 // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. 72 auto entry = segmented_process_directory_entry { {}, DT_REG, process_stacks_subdirectory_root_entry.subdirectory, static_cast<u32>(thread.tid().value() + 1) }; 73 InodeIdentifier identifier = { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }; 74 auto name = TRY(KString::number(thread.tid().value())); 75 TRY(callback({ name->view(), identifier, 0 })); 76 } 77 return {}; 78 }); 79} 80 81ErrorOr<NonnullRefPtr<Inode>> Process::lookup_stacks_directory(ProcFS& procfs, StringView name) const 82{ 83 auto maybe_needle = name.to_uint(); 84 if (!maybe_needle.has_value()) 85 return ENOENT; 86 auto needle = maybe_needle.release_value(); 87 88 ErrorOr<NonnullRefPtr<Inode>> thread_stack_inode { ENOENT }; 89 for_each_thread([&](Thread const& thread) { 90 int tid = thread.tid().value(); 91 VERIFY(!(tid < 0)); 92 if (needle == (unsigned)tid) { 93 // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. 94 auto entry = segmented_process_directory_entry { {}, DT_REG, process_stacks_subdirectory_root_entry.subdirectory, static_cast<u32>(thread.tid().value() + 1) }; 95 thread_stack_inode = procfs.get_inode({ procfs.fsid(), ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }); 96 return IterationDecision::Break; 97 } 98 return IterationDecision::Continue; 99 }); 100 101 if (thread_stack_inode.is_error()) 102 return thread_stack_inode.release_error(); 103 return thread_stack_inode.release_value(); 104} 105 106ErrorOr<void> Process::traverse_children_directory(FileSystemID fsid, Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const 107{ 108 TRY(callback({ "."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), process_children_subdirectory_root_entry) }, DT_DIR })); 109 TRY(callback({ ".."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), main_process_directory_root_entry) }, main_process_directory_root_entry.file_type })); 110 return Process::for_each_in_same_jail([&](Process& process) -> ErrorOr<void> { 111 if (process.ppid() == pid()) { 112 auto name = TRY(KString::number(process.pid().value())); 113 // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. 114 auto entry = segmented_process_directory_entry { {}, DT_LNK, process_children_subdirectory_root_entry.subdirectory, static_cast<u32>(process.pid().value() + 1) }; 115 InodeIdentifier identifier = { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }; 116 TRY(callback({ name->view(), identifier, DT_LNK })); 117 } 118 return {}; 119 }); 120} 121 122ErrorOr<NonnullRefPtr<Inode>> Process::lookup_children_directory(ProcFS& procfs, StringView name) const 123{ 124 auto maybe_pid = name.to_uint(); 125 if (!maybe_pid.has_value()) 126 return ENOENT; 127 128 auto child_process = Process::from_pid_in_same_jail(*maybe_pid); 129 if (!child_process || child_process->ppid() != pid()) 130 return ENOENT; 131 132 // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. 133 auto entry = segmented_process_directory_entry { {}, DT_LNK, process_children_subdirectory_root_entry.subdirectory, (maybe_pid.value() + 1) }; 134 return procfs.get_inode({ procfs.fsid(), ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }); 135} 136 137ErrorOr<size_t> Process::procfs_get_child_process_link(ProcessID child_pid, KBufferBuilder& builder) const 138{ 139 TRY(builder.appendff("../../{}", child_pid.value())); 140 return builder.length(); 141} 142 143ErrorOr<size_t> Process::procfs_get_file_description_link(unsigned fd, KBufferBuilder& builder) const 144{ 145 auto file_description = TRY(open_file_description(fd)); 146 // Note: These links are not guaranteed to point to actual VFS paths, just like in other kernels. 147 auto data = TRY(file_description->pseudo_path()); 148 TRY(builder.append(data->view())); 149 return data->length(); 150} 151 152ErrorOr<void> Process::traverse_file_descriptions_directory(FileSystemID fsid, Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const 153{ 154 TRY(callback({ "."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), process_fd_subdirectory_root_entry) }, DT_DIR })); 155 TRY(callback({ ".."sv, { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), main_process_directory_root_entry) }, main_process_directory_root_entry.file_type })); 156 u32 count = 0; 157 TRY(fds().with_shared([&](auto& fds) -> ErrorOr<void> { 158 return fds.try_enumerate([&](auto& file_description_metadata) -> ErrorOr<void> { 159 if (!file_description_metadata.is_valid()) { 160 count++; 161 return {}; 162 } 163 auto name = TRY(KString::number(count)); 164 // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. 165 auto entry = segmented_process_directory_entry { {}, DT_LNK, process_fd_subdirectory_root_entry.subdirectory, count + 1 }; 166 InodeIdentifier identifier = { fsid, ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }; 167 TRY(callback({ name->view(), identifier, DT_LNK })); 168 count++; 169 return {}; 170 }); 171 })); 172 return {}; 173} 174 175ErrorOr<NonnullRefPtr<Inode>> Process::lookup_file_descriptions_directory(ProcFS& procfs, StringView name) const 176{ 177 auto maybe_index = name.to_uint(); 178 if (!maybe_index.has_value()) 179 return ENOENT; 180 181 if (!m_fds.with_shared([&](auto& fds) { return fds.get_if_valid(*maybe_index); })) 182 return ENOENT; 183 184 // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. 185 auto entry = segmented_process_directory_entry { {}, DT_LNK, process_fd_subdirectory_root_entry.subdirectory, (maybe_index.value() + 1) }; 186 return procfs.get_inode({ procfs.fsid(), ProcFSInode::create_index_from_process_directory_entry(pid(), entry) }); 187} 188 189ErrorOr<void> Process::procfs_get_pledge_stats(KBufferBuilder& builder) const 190{ 191 auto obj = TRY(JsonObjectSerializer<>::try_create(builder)); 192#define __ENUMERATE_PLEDGE_PROMISE(x) \ 193 if (has_promised(Pledge::x)) { \ 194 if (!promises_builder.is_empty()) \ 195 TRY(promises_builder.try_append(' ')); \ 196 TRY(promises_builder.try_append(#x##sv)); \ 197 } 198 if (has_promises()) { 199 StringBuilder promises_builder; 200 ENUMERATE_PLEDGE_PROMISES 201 TRY(obj.add("promises"sv, promises_builder.string_view())); 202 } 203#undef __ENUMERATE_PLEDGE_PROMISE 204 TRY(obj.finish()); 205 return {}; 206} 207 208ErrorOr<void> Process::procfs_get_unveil_stats(KBufferBuilder& builder) const 209{ 210 auto array = TRY(JsonArraySerializer<>::try_create(builder)); 211 TRY(m_unveil_data.with([&](auto& unveil_data) -> ErrorOr<void> { 212 TRY(unveil_data.paths.for_each_node_in_tree_order([&](auto const& unveiled_path) -> ErrorOr<IterationDecision> { 213 if (!unveiled_path.was_explicitly_unveiled()) 214 return IterationDecision::Continue; 215 auto obj = TRY(array.add_object()); 216 TRY(obj.add("path"sv, unveiled_path.path())); 217 StringBuilder permissions_builder; 218 if (unveiled_path.permissions() & UnveilAccess::Read) 219 permissions_builder.append('r'); 220 if (unveiled_path.permissions() & UnveilAccess::Write) 221 permissions_builder.append('w'); 222 if (unveiled_path.permissions() & UnveilAccess::Execute) 223 permissions_builder.append('x'); 224 if (unveiled_path.permissions() & UnveilAccess::CreateOrRemove) 225 permissions_builder.append('c'); 226 if (unveiled_path.permissions() & UnveilAccess::Browse) 227 permissions_builder.append('b'); 228 TRY(obj.add("permissions"sv, permissions_builder.string_view())); 229 TRY(obj.finish()); 230 return IterationDecision::Continue; 231 })); 232 return {}; 233 })); 234 TRY(array.finish()); 235 return {}; 236} 237 238ErrorOr<void> Process::procfs_get_perf_events(KBufferBuilder& builder) const 239{ 240 InterruptDisabler disabler; 241 if (!perf_events()) { 242 dbgln("ProcFS: No perf events for {}", pid()); 243 return Error::from_errno(ENOBUFS); 244 } 245 return perf_events()->to_json(builder); 246} 247 248ErrorOr<void> Process::procfs_get_fds_stats(KBufferBuilder& builder) const 249{ 250 auto array = TRY(JsonArraySerializer<>::try_create(builder)); 251 252 return fds().with_shared([&](auto& fds) -> ErrorOr<void> { 253 if (fds.open_count() == 0) { 254 TRY(array.finish()); 255 return {}; 256 } 257 258 size_t count = 0; 259 TRY(fds.try_enumerate([&](auto& file_description_metadata) -> ErrorOr<void> { 260 if (!file_description_metadata.is_valid()) { 261 count++; 262 return {}; 263 } 264 bool cloexec = file_description_metadata.flags() & FD_CLOEXEC; 265 auto const* description = file_description_metadata.description(); 266 auto description_object = TRY(array.add_object()); 267 TRY(description_object.add("fd"sv, count)); 268 // TODO: Better OOM handling. 269 auto pseudo_path_or_error = description->pseudo_path(); 270 TRY(description_object.add("absolute_path"sv, pseudo_path_or_error.is_error() ? "???"sv : pseudo_path_or_error.value()->view())); 271 TRY(description_object.add("seekable"sv, description->file().is_seekable())); 272 TRY(description_object.add("class"sv, description->file().class_name())); 273 TRY(description_object.add("offset"sv, description->offset())); 274 TRY(description_object.add("cloexec"sv, cloexec)); 275 TRY(description_object.add("blocking"sv, description->is_blocking())); 276 TRY(description_object.add("can_read"sv, description->can_read())); 277 TRY(description_object.add("can_write"sv, description->can_write())); 278 Inode const* inode = description->inode(); 279 if (inode != nullptr) { 280 auto inode_object = TRY(description_object.add_object("inode"sv)); 281 TRY(inode_object.add("fsid"sv, inode->fsid().value())); 282 TRY(inode_object.add("index"sv, inode->index().value())); 283 TRY(inode_object.finish()); 284 } 285 TRY(description_object.finish()); 286 count++; 287 return {}; 288 })); 289 290 TRY(array.finish()); 291 return {}; 292 }); 293} 294 295ErrorOr<void> Process::procfs_get_virtual_memory_stats(KBufferBuilder& builder) const 296{ 297 auto array = TRY(JsonArraySerializer<>::try_create(builder)); 298 TRY(address_space().with([&](auto& space) -> ErrorOr<void> { 299 for (auto const& region : space->region_tree().regions()) { 300 auto current_process_credentials = Process::current().credentials(); 301 if (!region.is_user() && !current_process_credentials->is_superuser()) 302 continue; 303 auto region_object = TRY(array.add_object()); 304 TRY(region_object.add("readable"sv, region.is_readable())); 305 TRY(region_object.add("writable"sv, region.is_writable())); 306 TRY(region_object.add("executable"sv, region.is_executable())); 307 TRY(region_object.add("stack"sv, region.is_stack())); 308 TRY(region_object.add("shared"sv, region.is_shared())); 309 TRY(region_object.add("syscall"sv, region.is_syscall_region())); 310 TRY(region_object.add("purgeable"sv, region.vmobject().is_anonymous())); 311 if (region.vmobject().is_anonymous()) { 312 TRY(region_object.add("volatile"sv, static_cast<Memory::AnonymousVMObject const&>(region.vmobject()).is_volatile())); 313 } 314 TRY(region_object.add("cacheable"sv, region.is_cacheable())); 315 TRY(region_object.add("address"sv, region.vaddr().get())); 316 TRY(region_object.add("size"sv, region.size())); 317 TRY(region_object.add("amount_resident"sv, region.amount_resident())); 318 TRY(region_object.add("amount_dirty"sv, region.amount_dirty())); 319 TRY(region_object.add("cow_pages"sv, region.cow_pages())); 320 TRY(region_object.add("name"sv, region.name())); 321 TRY(region_object.add("vmobject"sv, region.vmobject().class_name())); 322 323 StringBuilder pagemap_builder; 324 for (size_t i = 0; i < region.page_count(); ++i) { 325 auto page = region.physical_page(i); 326 if (!page) 327 pagemap_builder.append('N'); 328 else if (page->is_shared_zero_page() || page->is_lazy_committed_page()) 329 pagemap_builder.append('Z'); 330 else 331 pagemap_builder.append('P'); 332 } 333 TRY(region_object.add("pagemap"sv, pagemap_builder.string_view())); 334 TRY(region_object.finish()); 335 } 336 return {}; 337 })); 338 TRY(array.finish()); 339 return {}; 340} 341 342ErrorOr<void> Process::procfs_get_current_work_directory_link(KBufferBuilder& builder) const 343{ 344 return builder.append(TRY(const_cast<Process&>(*this).current_directory()->try_serialize_absolute_path())->view()); 345} 346 347ErrorOr<void> Process::procfs_get_command_line(KBufferBuilder& builder) const 348{ 349 auto array = TRY(JsonArraySerializer<>::try_create(builder)); 350 for (auto const& arg : arguments()) { 351 TRY(array.add(arg->view())); 352 } 353 TRY(array.finish()); 354 return {}; 355} 356 357mode_t Process::binary_link_required_mode() const 358{ 359 if (!executable()) 360 return 0; 361 return 0555; 362} 363 364ErrorOr<void> Process::procfs_get_binary_link(KBufferBuilder& builder) const 365{ 366 auto custody = executable(); 367 if (!custody) 368 return Error::from_errno(ENOEXEC); 369 return builder.append(TRY(custody->try_serialize_absolute_path())->view()); 370} 371 372}