use std::fs;
use std::process::{Command, ExitStatus};
use humansize::{BINARY, format_size};
use nix::sys::signal::{self, Signal};
use nix::unistd::Pid;
pub fn run_program(program: &str) -> Result<ExitStatus, std::io::Error> {
Command::new(program).spawn()?.wait()
}
pub fn list_processes() -> Vec<(String, String)> {
let mut results = Vec::new();
if let Ok(entries) = fs::read_dir("/proc") {
for entry in entries.flatten() {
let name = entry.file_name().into_string().unwrap_or_default();
if name.chars().all(|c| c.is_numeric()) {
let pid = name.clone();
let cmd_path = format!("/proc/{}/cmdline", pid);
let cmdline = fs::read_to_string(&cmd_path).unwrap_or_default();
let display = if cmdline.is_empty() {
"[kernel/system process]".to_string()
} else {
cmdline.replace('\0', " ")
};
results.push((pid, display));
}
}
}
results
}
pub fn stop_process(pid: u32) -> Result<(), String> {
let pid = Pid::from_raw(pid as i32);
match signal::kill(pid, Signal::SIGTERM) {
Ok(_) => Ok(()),
Err(e) => Err(format!("Failed to stop process {}: {}", pid, e)),
}
}
pub fn process_info(pid: u32) -> String {
let status_path = format!("/proc/{}/status", pid);
let content = std::fs::read_to_string(status_path).unwrap_or_default();
let mut name = "unknown".to_string();
let mut uid = "unknown".to_string();
let mut ppid = "unknown".to_string();
for line in content.lines() {
if line.starts_with("Name:") {
name = line.replace("Name:\t", "").to_string();
} else if line.starts_with("Uid:") {
uid = line
.replace("Uid:\t", "")
.split_whitespace()
.next()
.unwrap_or("unknown")
.to_string();
} else if line.starts_with("PPid:") {
ppid = line.replace("PPid:\t", "").to_string();
}
}
let uid_num: u32 = uid.parse().unwrap_or(99999);
let risk = if pid == 1 {
"❌ Critical system process (NEVER stop)"
} else if uid_num == 0 {
"⚠️ System/root process (dangerous)"
} else {
"✔ Safe user-level process"
};
format!("PID: {pid}\nName: {name}\nUserID: {uid}\nParentPID: {ppid}\nStatus: {risk}")
}
pub fn process_stats(pid: u32) -> Result<String, String> {
let stat_path = format!("/proc/{}/stat", pid);
let status_path = format!("/proc/{}/status", pid);
let status =
std::fs::read_to_string(&status_path).map_err(|_| "Process not found".to_string())?;
let mut name = "unknown".to_string();
let mut memory = 0;
for line in status.lines() {
if line.starts_with("Name:") {
name = line.replace("Name:\t", "");
}
if line.starts_with("VmRSS:") {
let parts: Vec<&str> = line.split_whitespace().collect();
memory = parts[1].parse::<usize>().unwrap_or(0) * 1024;
}
}
let stat1 = std::fs::read_to_string(&stat_path).unwrap_or_default();
std::thread::sleep(std::time::Duration::from_millis(100));
let stat2 = std::fs::read_to_string(&stat_path).unwrap_or_default();
let cpu1: Vec<&str> = stat1.split_whitespace().collect();
let cpu2: Vec<&str> = stat2.split_whitespace().collect();
let utime1: f64 = cpu1[13].parse().unwrap_or(0.0);
let stime1: f64 = cpu1[14].parse().unwrap_or(0.0);
let utime2: f64 = cpu2[13].parse().unwrap_or(0.0);
let stime2: f64 = cpu2[14].parse().unwrap_or(0.0);
let cpu_usage = ((utime2 + stime2) - (utime1 + stime1)) * 10.0;
let memory_fmt = format_size(memory, BINARY);
Ok(format!(
"PID: {pid}\nName: {name}\nCPU: {:.2}%\nMemory: {}\n",
cpu_usage, memory_fmt
))
}