kernel: expose proc process info
This commit is contained in:
+240
-3
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user