From eba85456c15622825c8eee52869926102b0aadc2 Mon Sep 17 00:00:00 2001 From: vasilito Date: Wed, 8 Jul 2026 15:04:42 +0300 Subject: [PATCH] kernel: expose proc process info --- src/scheme/proc.rs | 243 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 240 insertions(+), 3 deletions(-) diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index dc9949850a..e133da4960 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -23,7 +23,7 @@ use super::{CallerCtx, KernelSchemes, OpenResult}; use ::syscall::{ProcSchemeAttrs, SigProcControl, Sigcontrol}; use alloc::{ boxed::Box, - string::String, + string::{String, ToString}, sync::{Arc, Weak}, vec::Vec, }; @@ -38,6 +38,7 @@ use hashbrown::{ HashMap, }; use syscall::data::GlobalSchemes; +use syscall::dirent::{DirEntry, DirentBuf, DirentKind}; fn read_from(dst: UserSliceWo, src: &[u8], offset: u64) -> Result { let avail_src = usize::try_from(offset) @@ -148,6 +149,23 @@ enum ContextHandle { SchedAffinity, MmapMinAddr(Arc), + + ProcRoot, + ProcDir { + pid: usize, + }, + ProcStat { + pid: usize, + }, + ProcComm { + pid: usize, + }, + ProcCmdline { + pid: usize, + }, + ProcStatus { + pid: usize, + }, } #[derive(Clone)] struct Handle { @@ -192,12 +210,121 @@ fn new_handle( Ok((id, fl)) } +fn proc_status_char(status: &Status) -> char { + match status { + Status::Runnable => 'R', + Status::Blocked => 'S', + Status::HardBlocked { reason } => match reason { + HardBlockedReason::AwaitingMmap { .. } => 'D', + HardBlockedReason::Stopped => 'T', + HardBlockedReason::NotYetStarted => 'S', + }, + Status::Dead { .. } => 'Z', + } +} + +fn proc_vsize(addr_space: &Option>, token: &mut CleanLockToken) -> usize { + addr_space + .as_ref() + .map(|addrspace| { + let guard = addrspace.acquire_read(token.downgrade()); + guard + .grants + .iter() + .map(|(_, info)| info.page_count() * PAGE_SIZE) + .sum() + }) + .unwrap_or(0) +} + +fn proc_context(pid: usize, token: &mut CleanLockToken) -> Option> { + let mut contexts = context::contexts(token.downgrade()); + let (contexts, mut token2) = contexts.token_split(); + contexts.iter().find_map(|ctx| { + let ctx = Arc::clone(&ctx.0); + let matches = ctx.read(token2.token()).pid == pid; + matches.then_some(ctx) + }) +} + +fn proc_stat_line( + pid: usize, + comm: &str, + state: char, + ppid: usize, + priority: usize, + utime: u128, + vsize: usize, +) -> String { + format!( + "{pid} ({comm}) {state} {ppid} {pgrp} {session} {tty_nr} {tpgid} {flags} {minflt} {cminflt} {majflt} {cmajflt} {utime} {stime} {cutime} {cstime} {priority} {nice} {num_threads} {itrealvalue} {starttime} {vsize} {rss} {rsslim}\n", + pid = pid, + comm = comm, + state = state, + ppid = ppid, + pgrp = ppid, + session = ppid, + tty_nr = 0, + tpgid = 0, + flags = 0, + minflt = 0, + cminflt = 0, + majflt = 0, + cmajflt = 0, + utime = utime, + stime = 0, + cutime = 0, + cstime = 0, + priority = priority, + nice = 0, + num_threads = 1, + itrealvalue = 0, + starttime = 0, + vsize = vsize, + rss = 0, + rsslim = 0, + ) +} + +fn proc_status_text(context: &Context) -> String { + let comm = context.name.as_str(); + let state = proc_status_char(&context.status); + let ppid = context.owner_proc_id.map_or(0, NonZeroUsize::get); + format!( + "Name:\t{}\nState:\t{}\nPid:\t{}\nPPid:\t{}\nUid:\t{}\nGid:\t{}\nThreads:\t1\nCpus_allowed_list:\t{}\n", + comm, + state, + context.pid, + ppid, + context.euid, + context.egid, + context.sched_affinity + ) +} + enum OpenTy { Ctxt(Arc), Auth, } impl ProcScheme { + fn proc_open(path: &str) -> Option { + let path = path.trim_start_matches('/'); + if path.is_empty() || path == "proc" { + return Some(ContextHandle::ProcRoot); + } + let mut parts = path.split('/'); + let pid = parts.next()?.parse::().ok()?; + match (parts.next(), parts.next()) { + (None, None) => Some(ContextHandle::ProcDir { pid }), + (Some("stat"), None) => Some(ContextHandle::ProcStat { pid }), + (Some("comm"), None) => Some(ContextHandle::ProcComm { pid }), + (Some("cmdline"), None) => Some(ContextHandle::ProcCmdline { pid }), + (Some("status"), None) => Some(ContextHandle::ProcStatus { pid }), + _ => None, + } + } + fn openat_context( &self, path: &str, @@ -251,6 +378,7 @@ impl ProcScheme { ), "sched-affinity" => (ContextHandle::SchedAffinity, true), "status" => (ContextHandle::Status { privileged: false }, false), + "proc" => (ContextHandle::ProcRoot, true), _ if path.starts_with("auth-") => { let nonprefix = &path["auth-".len()..]; let next_dash = nonprefix.find('-').ok_or(Error::new(ENOENT))?; @@ -292,6 +420,13 @@ impl ProcScheme { token: &mut CleanLockToken, ) -> Result<(usize, InternalFlags)> { let operation_name = operation_str.ok_or(Error::new(EINVAL))?; + if let Some(kind) = Self::proc_open(operation_name) { + let handle = Handle { + context: context::current(), + kind, + }; + return new_handle((handle, InternalFlags::POSITIONED), token); + } let (mut handle, positioned) = match ty { OpenTy::Ctxt(context) => { match self.openat_context(operation_name, Arc::clone(&context), token)? { @@ -690,12 +825,72 @@ impl KernelScheme for ProcScheme { buf.copy_common_bytes_from_slice("/scheme/kernel.proc/".as_bytes()) } + fn getdents( + &self, + id: usize, + buf: UserSliceWo, + header_size: u16, + opaque_id_start: u64, + token: &mut CleanLockToken, + ) -> Result { + let Ok(opaque) = usize::try_from(opaque_id_start) else { + return Ok(0); + }; + + let handle = { + let handles = HANDLES.read(token.token()); + handles.get(&id).ok_or(Error::new(EBADF))?.clone() + }; + + let mut buf = DirentBuf::new(buf, header_size).ok_or(Error::new(EIO))?; + match handle.kind { + ContextHandle::ProcRoot => { + let mut pids = Vec::new(); + let mut contexts = context::contexts(token.downgrade()); + let (contexts, mut token) = contexts.token_split(); + for context_ref in contexts.iter() { + pids.push(context_ref.read(token.token()).pid); + } + pids.sort_unstable(); + for (idx, pid) in pids.into_iter().enumerate().skip(opaque) { + let name = format!("{}", pid); + buf.entry(DirEntry { + inode: pid as u64, + next_opaque_id: (idx + 1) as u64, + kind: DirentKind::Directory, + name: &name, + })?; + } + Ok(buf.finalize()) + } + ContextHandle::ProcDir { pid } => { + for (idx, name) in ["stat", "comm", "cmdline", "status"] + .iter() + .enumerate() + .skip(opaque) + { + buf.entry(DirEntry { + inode: pid as u64, + next_opaque_id: (idx + 1) as u64, + kind: DirentKind::Regular, + name, + })?; + } + Ok(buf.finalize()) + } + _ => Err(Error::new(ENOTDIR)), + } + } + fn kfstat(&self, id: usize, buffer: UserSliceWo, token: &mut CleanLockToken) -> Result<()> { let handles = HANDLES.read(token.token()); let handle = handles.get(&id).ok_or(Error::new(EBADF))?; buffer.copy_exactly(&Stat { - st_mode: MODE_FILE | 0o666, + st_mode: match handle.kind { + ContextHandle::ProcRoot | ContextHandle::ProcDir { .. } => MODE_DIR | 0o755, + _ => MODE_FILE | 0o666, + }, st_size: handle.fsize()?, ..Stat::default() @@ -708,7 +903,10 @@ impl KernelScheme for ProcScheme { let mut handles = HANDLES.write(token.token()); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - handle.fsize() + match handle.kind { + ContextHandle::ProcRoot | ContextHandle::ProcDir { .. } => Ok(0), + _ => handle.fsize(), + } } /// Dup is currently used to implement clone() and execve(). @@ -1458,6 +1656,45 @@ impl ContextHandle { } ContextHandle::Filetable { data, .. } => read_from(buf, data, offset), + ContextHandle::ProcStat { pid } => { + let context = proc_context(*pid, token).ok_or(Error::new(ESRCH))?; + let (pid, comm, state, ppid, priority, utime, addr_space) = { + let context = context.read(token.token()); + ( + context.pid, + context.name.to_string(), + proc_status_char(&context.status), + context.owner_proc_id.map_or(0, NonZeroUsize::get), + context.prio, + context.cpu_time / crate::time::NANOS_PER_SEC, + context.addr_space.clone(), + ) + }; + let vsize = proc_vsize(&addr_space, token); + read_from( + buf, + proc_stat_line(pid, &comm, state, ppid, priority, utime, vsize).as_bytes(), + offset, + ) + } + ContextHandle::ProcComm { pid } => { + let context = proc_context(*pid, token).ok_or(Error::new(ESRCH))?; + let context = context.read(token.token()); + read_from(buf, context.name.as_bytes(), offset) + } + ContextHandle::ProcCmdline { pid } => { + let context = proc_context(*pid, token).ok_or(Error::new(ESRCH))?; + let context = context.read(token.token()); + let mut cmd = context.name.as_bytes().to_vec(); + cmd.push(0); + read_from(buf, &cmd, offset) + } + ContextHandle::ProcStatus { pid } => { + let context = proc_context(*pid, token).ok_or(Error::new(ESRCH))?; + let context = context.read(token.token()); + read_from(buf, proc_status_text(&context).as_bytes(), offset) + } + ContextHandle::ProcRoot | ContextHandle::ProcDir { .. } => Err(Error::new(EBADF)), ContextHandle::MmapMinAddr(addrspace) => { let mut token = token.token(); let addr = addrspace.acquire_read(token.downgrade());