Serenity Operating System
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}