use aurora_core::{AuroraResult, Pipeline, Value};
use std::fs;
use std::process::Command;
pub fn sys_info() -> AuroraResult<Pipeline> {
let mut rows: Vec<Vec<Value>> = Vec::new();
if let Ok(cpuinfo) = fs::read_to_string("/proc/cpuinfo") {
for line in cpuinfo.lines() {
if let Some(key) = line.split(':').next() {
let key = key.trim();
if key == "model name" {
if let Some(val) = line.split(':').nth(1) {
rows.push(vec![
Value::String("CPU".into()),
Value::String(val.trim().into()),
]);
}
break;
}
}
}
}
if let Ok(meminfo) = fs::read_to_string("/proc/meminfo") {
let mut total = String::new();
for line in meminfo.lines() {
if line.starts_with("MemTotal:") {
if let Some(val) = line.split(':').nth(1) {
total = val.trim().to_string();
}
break;
}
}
rows.push(vec![
Value::String("Memory".into()),
Value::String(total),
]);
}
if let Ok(uptime) = fs::read_to_string("/proc/uptime") {
if let Some(secs) = uptime.split(' ').next() {
rows.push(vec![
Value::String("Uptime".into()),
Value::String(format!("{}s", secs.trim())),
]);
}
}
if let Ok(output) = Command::new("uname").arg("-r").output() {
let kernel = String::from_utf8_lossy(&output.stdout).trim().to_string();
rows.push(vec![
Value::String("Kernel".into()),
Value::String(kernel),
]);
}
if let Ok(host) = fs::read_to_string("/proc/sys/kernel/hostname") {
rows.push(vec![
Value::String("Hostname".into()),
Value::String(host.trim().into()),
]);
}
Ok(Pipeline::table(
vec!["key".into(), "value".into()],
rows,
))
}
pub fn sys_process() -> AuroraResult<Pipeline> {
let mut rows: Vec<Vec<Value>> = Vec::new();
let dir = fs::read_dir("/proc").map_err(|e| aurora_core::AuroraError::Io(e))?;
for entry in dir.flatten() {
let name = entry.file_name();
let pid_str = name.to_string_lossy();
let pid: i64 = match pid_str.parse() {
Ok(n) => n,
Err(_) => continue,
};
let status_path = entry.path().join("status");
let status_content = match fs::read_to_string(&status_path) {
Ok(c) => c,
Err(_) => continue,
};
let mut proc_name = String::new();
let mut state = String::new();
for line in status_content.lines() {
if line.starts_with("Name:") {
if let Some(val) = line.split(':').nth(1) {
proc_name = val.trim().to_string();
}
}
if line.starts_with("State:") {
if let Some(val) = line.split(':').nth(1) {
state = val.trim().to_string();
}
}
}
rows.push(vec![
Value::Int(pid),
Value::String(proc_name),
Value::String(state),
Value::String("--".into()),
Value::String("--".into()),
]);
}
Ok(Pipeline::table(
vec!["pid".into(), "name".into(), "state".into(), "cpu%".into(), "mem%".into()],
rows,
))
}
pub fn sys_disk() -> AuroraResult<Pipeline> {
let output = Command::new("df")
.arg("-h")
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("df failed: {}", e)
))?;
let stdout = String::from_utf8_lossy(&output.stdout);
let mut rows: Vec<Vec<Value>> = Vec::new();
for (i, line) in stdout.lines().enumerate() {
if i == 0 || line.trim().is_empty() { continue; }
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 6 {
rows.push(vec![
Value::String(parts[5].into()),
Value::String(parts[1].into()),
Value::String(parts[2].into()),
Value::String(parts[3].into()),
Value::String(parts[4].into()),
]);
}
}
Ok(Pipeline::table(
vec!["mount".into(), "total".into(), "used".into(), "avail".into(), "use%".into()],
rows,
))
}
pub fn sys_memory() -> AuroraResult<Pipeline> {
let content = fs::read_to_string("/proc/meminfo")
.map_err(|e| aurora_core::AuroraError::Io(e))?;
let mut total = String::new();
let mut free = String::new();
let mut buffers = String::new();
let mut cached = String::new();
for line in content.lines() {
if line.starts_with("MemTotal:") {
if let Some(v) = line.split(':').nth(1) {
total = v.trim().to_string();
}
} else if line.starts_with("MemFree:") {
if let Some(v) = line.split(':').nth(1) {
free = v.trim().to_string();
}
} else if line.starts_with("Buffers:") {
if let Some(v) = line.split(':').nth(1) {
buffers = v.trim().to_string();
}
} else if line.starts_with("Cached:") {
if let Some(v) = line.split(':').nth(1) {
cached = v.trim().to_string();
}
}
}
let used_kb = parse_kb(&total).saturating_sub(parse_kb(&free));
let used = format!("{} kB", used_kb);
Ok(Pipeline::table(
vec!["total".into(), "used".into(), "free".into(), "buffers".into(), "cached".into()],
vec![vec![
Value::String(total),
Value::String(used),
Value::String(free),
Value::String(buffers),
Value::String(cached),
]],
))
}
fn parse_kb(s: &str) -> u64 {
let num: String = s.chars().filter(|c| c.is_ascii_digit()).collect();
num.parse().unwrap_or(0)
}
pub fn sys_service(name: &str) -> AuroraResult<Pipeline> {
let output = Command::new("systemctl")
.args(["status", name, "--no-pager", "-l"])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("systemctl failed: {}", e)
))?;
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined = format!("{}{}", stdout, stderr);
Ok(Pipeline::table(
vec!["service".into(), "info".into()],
vec![vec![
Value::String(name.into()),
Value::String(combined),
]],
))
}