Skip to main content

aurora_modules/
sys.rs

1use aurora_core::{AuroraResult, Pipeline, Value};
2use std::fs;
3use std::process::Command;
4
5pub fn sys_info() -> AuroraResult<Pipeline> {
6    let mut rows: Vec<Vec<Value>> = Vec::new();
7
8    if let Ok(cpuinfo) = fs::read_to_string("/proc/cpuinfo") {
9        for line in cpuinfo.lines() {
10            if let Some(key) = line.split(':').next() {
11                let key = key.trim();
12                if key == "model name" {
13                    if let Some(val) = line.split(':').nth(1) {
14                        rows.push(vec![
15                            Value::String("CPU".into()),
16                            Value::String(val.trim().into()),
17                        ]);
18                    }
19                    break;
20                }
21            }
22        }
23    }
24
25    if let Ok(meminfo) = fs::read_to_string("/proc/meminfo") {
26        let mut total = String::new();
27        for line in meminfo.lines() {
28            if line.starts_with("MemTotal:") {
29                if let Some(val) = line.split(':').nth(1) {
30                    total = val.trim().to_string();
31                }
32                break;
33            }
34        }
35        rows.push(vec![
36            Value::String("Memory".into()),
37            Value::String(total),
38        ]);
39    }
40
41    if let Ok(uptime) = fs::read_to_string("/proc/uptime") {
42        if let Some(secs) = uptime.split(' ').next() {
43            rows.push(vec![
44                Value::String("Uptime".into()),
45                Value::String(format!("{}s", secs.trim())),
46            ]);
47        }
48    }
49
50    if let Ok(output) = Command::new("uname").arg("-r").output() {
51        let kernel = String::from_utf8_lossy(&output.stdout).trim().to_string();
52        rows.push(vec![
53            Value::String("Kernel".into()),
54            Value::String(kernel),
55        ]);
56    }
57
58    if let Ok(host) = fs::read_to_string("/proc/sys/kernel/hostname") {
59        rows.push(vec![
60            Value::String("Hostname".into()),
61            Value::String(host.trim().into()),
62        ]);
63    }
64
65    Ok(Pipeline::table(
66        vec!["key".into(), "value".into()],
67        rows,
68    ))
69}
70
71pub fn sys_process() -> AuroraResult<Pipeline> {
72    let mut rows: Vec<Vec<Value>> = Vec::new();
73
74    let dir = fs::read_dir("/proc").map_err(|e| aurora_core::AuroraError::Io(e))?;
75    for entry in dir.flatten() {
76        let name = entry.file_name();
77        let pid_str = name.to_string_lossy();
78        let pid: i64 = match pid_str.parse() {
79            Ok(n) => n,
80            Err(_) => continue,
81        };
82
83        let status_path = entry.path().join("status");
84        let status_content = match fs::read_to_string(&status_path) {
85            Ok(c) => c,
86            Err(_) => continue,
87        };
88
89        let mut proc_name = String::new();
90        let mut state = String::new();
91        for line in status_content.lines() {
92            if line.starts_with("Name:") {
93                if let Some(val) = line.split(':').nth(1) {
94                    proc_name = val.trim().to_string();
95                }
96            }
97            if line.starts_with("State:") {
98                if let Some(val) = line.split(':').nth(1) {
99                    state = val.trim().to_string();
100                }
101            }
102        }
103
104        rows.push(vec![
105            Value::Int(pid),
106            Value::String(proc_name),
107            Value::String(state),
108            Value::String("--".into()),
109            Value::String("--".into()),
110        ]);
111    }
112
113    Ok(Pipeline::table(
114        vec!["pid".into(), "name".into(), "state".into(), "cpu%".into(), "mem%".into()],
115        rows,
116    ))
117}
118
119pub fn sys_disk() -> AuroraResult<Pipeline> {
120    let output = Command::new("df")
121        .arg("-h")
122        .output()
123        .map_err(|e| aurora_core::AuroraError::ModuleError(
124            format!("df failed: {}", e)
125        ))?;
126
127    let stdout = String::from_utf8_lossy(&output.stdout);
128    let mut rows: Vec<Vec<Value>> = Vec::new();
129
130    for (i, line) in stdout.lines().enumerate() {
131        if i == 0 || line.trim().is_empty() { continue; }
132        let parts: Vec<&str> = line.split_whitespace().collect();
133        if parts.len() >= 6 {
134            rows.push(vec![
135                Value::String(parts[5].into()),
136                Value::String(parts[1].into()),
137                Value::String(parts[2].into()),
138                Value::String(parts[3].into()),
139                Value::String(parts[4].into()),
140            ]);
141        }
142    }
143
144    Ok(Pipeline::table(
145        vec!["mount".into(), "total".into(), "used".into(), "avail".into(), "use%".into()],
146        rows,
147    ))
148}
149
150pub fn sys_memory() -> AuroraResult<Pipeline> {
151    let content = fs::read_to_string("/proc/meminfo")
152        .map_err(|e| aurora_core::AuroraError::Io(e))?;
153
154    let mut total = String::new();
155    let mut free = String::new();
156    let mut buffers = String::new();
157    let mut cached = String::new();
158
159    for line in content.lines() {
160        if line.starts_with("MemTotal:") {
161            if let Some(v) = line.split(':').nth(1) {
162                total = v.trim().to_string();
163            }
164        } else if line.starts_with("MemFree:") {
165            if let Some(v) = line.split(':').nth(1) {
166                free = v.trim().to_string();
167            }
168        } else if line.starts_with("Buffers:") {
169            if let Some(v) = line.split(':').nth(1) {
170                buffers = v.trim().to_string();
171            }
172        } else if line.starts_with("Cached:") {
173            if let Some(v) = line.split(':').nth(1) {
174                cached = v.trim().to_string();
175            }
176        }
177    }
178
179    let used_kb = parse_kb(&total).saturating_sub(parse_kb(&free));
180    let used = format!("{} kB", used_kb);
181
182    Ok(Pipeline::table(
183        vec!["total".into(), "used".into(), "free".into(), "buffers".into(), "cached".into()],
184        vec![vec![
185            Value::String(total),
186            Value::String(used),
187            Value::String(free),
188            Value::String(buffers),
189            Value::String(cached),
190        ]],
191    ))
192}
193
194fn parse_kb(s: &str) -> u64 {
195    let num: String = s.chars().filter(|c| c.is_ascii_digit()).collect();
196    num.parse().unwrap_or(0)
197}
198
199pub fn sys_service(name: &str) -> AuroraResult<Pipeline> {
200    let output = Command::new("systemctl")
201        .args(["status", name, "--no-pager", "-l"])
202        .output()
203        .map_err(|e| aurora_core::AuroraError::ModuleError(
204            format!("systemctl failed: {}", e)
205        ))?;
206
207    let stdout = String::from_utf8_lossy(&output.stdout);
208    let stderr = String::from_utf8_lossy(&output.stderr);
209    let combined = format!("{}{}", stdout, stderr);
210
211    Ok(Pipeline::table(
212        vec!["service".into(), "info".into()],
213        vec![vec![
214            Value::String(name.into()),
215            Value::String(combined),
216        ]],
217    ))
218}