pscand/pscand-core/src/helpers/process.rs
NotAShelf 4f9c7057ff
various: cleanup
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I26df4d852b4b22d0df6b6871fe9cbde96a6a6964
2026-02-19 00:13:13 +03:00

136 lines
4 KiB
Rust

use std::collections::HashMap;
use std::fs;
pub struct ProcessHelper;
#[derive(Debug)]
pub struct ProcessInfo {
pub pid: u32,
pub name: String,
pub state: String,
pub ppid: u32,
pub memory_kb: u64,
pub cpu_percent: f32,
}
impl ProcessHelper {
pub fn list_processes() -> std::io::Result<Vec<ProcessInfo>> {
let mut processes = Vec::new();
let proc_path = fs::read_dir("/proc")?;
for entry in proc_path.flatten() {
let path = entry.path();
if !path.is_dir() {
continue;
}
let pid: u32 = match path.file_name() {
Some(name) => match name.to_str() {
Some(s) => s.parse().ok(),
None => None,
}
.ok_or(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"invalid pid",
))?,
None => continue,
};
if let Ok(info) = Self::process_info(pid) {
processes.push(info);
}
}
Ok(processes)
}
pub fn process_info(pid: u32) -> std::io::Result<ProcessInfo> {
let status_path = format!("/proc/{}/status", pid);
let content = fs::read_to_string(status_path)?;
let mut name = String::new();
let mut state = String::new();
let mut ppid: u32 = 0;
let mut memory_kb: u64 = 0;
for line in content.lines() {
if line.starts_with("Name:") {
name = line
.split_whitespace()
.skip(1)
.collect::<Vec<_>>()
.join(" ");
} else if line.starts_with("State:") {
state = line.split_whitespace().nth(1).unwrap_or("").to_string();
} else if line.starts_with("PPid:") {
ppid = line
.split_whitespace()
.nth(1)
.and_then(|s| s.parse().ok())
.unwrap_or(0);
} else if line.starts_with("VmRSS:") {
memory_kb = line
.split_whitespace()
.nth(1)
.and_then(|s| s.parse().ok())
.unwrap_or(0);
}
}
Ok(ProcessInfo {
pid,
name,
state,
ppid,
memory_kb,
cpu_percent: 0.0,
})
}
pub fn zombie_processes() -> std::io::Result<Vec<ProcessInfo>> {
Ok(Self::list_processes()?
.into_iter()
.filter(|p| p.state.starts_with('Z'))
.collect())
}
pub fn process_count() -> std::io::Result<HashMap<String, usize>> {
let mut counts = HashMap::new();
counts.insert("total".to_string(), 0);
counts.insert("running".to_string(), 0);
counts.insert("sleeping".to_string(), 0);
counts.insert("zombie".to_string(), 0);
for proc in Self::list_processes()? {
*counts.get_mut("total").unwrap() += 1;
let first_char = proc.state.chars().next().unwrap_or(' ');
match first_char {
'R' => *counts.get_mut("running").unwrap() += 1,
'S' | 'D' => *counts.get_mut("sleeping").unwrap() += 1,
'Z' => *counts.get_mut("zombie").unwrap() += 1,
_ => {}
}
}
Ok(counts)
}
pub fn top_memory_processes(count: usize) -> std::io::Result<Vec<ProcessInfo>> {
let mut processes = Self::list_processes()?;
processes.sort_by(|a, b| b.memory_kb.cmp(&a.memory_kb));
processes.truncate(count);
Ok(processes)
}
pub fn top_cpu_processes(count: usize) -> std::io::Result<Vec<ProcessInfo>> {
let mut processes = Self::list_processes()?;
processes.sort_by(|a, b| {
b.cpu_percent
.partial_cmp(&a.cpu_percent)
.unwrap_or(std::cmp::Ordering::Equal)
});
processes.truncate(count);
Ok(processes)
}
}