kernel: expose proc process info

This commit is contained in:
2026-07-08 15:04:42 +03:00
parent 9360e493db
commit eba85456c1
+240 -3
View File
@@ -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<usize> {
let avail_src = usize::try_from(offset)
@@ -148,6 +149,23 @@ enum ContextHandle {
SchedAffinity,
MmapMinAddr(Arc<AddrSpaceWrapper>),
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<Arc<AddrSpaceWrapper>>, 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<Arc<ContextLock>> {
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<ContextLock>),
Auth,
}
impl ProcScheme {
fn proc_open(path: &str) -> Option<ContextHandle> {
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::<usize>().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<usize> {
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());