planarian_backend_netwatch/
host.rs1use planarian_schema::UnitId;
5use std::collections::{BTreeMap, BTreeSet};
6use std::path::Path;
7
8pub fn network_tools(export_json: &str) -> Option<Vec<String>> {
12 let v: serde_json::Value = serde_json::from_str(export_json).ok()?;
13 v.get("networkTools")?.as_array()?.iter().map(|t| t.as_str().map(str::to_string)).collect()
14}
15
16pub fn unit_of_cgroup(rel: &str) -> Option<UnitId> {
21 let mut parts = rel.trim_start_matches('/').split('/');
22 if parts.next()? != "system.slice" {
23 return None;
24 }
25 let svc = parts.find(|p| !p.ends_with(".slice"))?.strip_suffix(".service")?;
29 (!svc.is_empty()).then(|| UnitId::service(svc))
30}
31
32pub fn expand_tools(tools: &[String], running: &BTreeSet<String>) -> Vec<UnitId> {
35 let mut out = BTreeSet::new();
36 for t in tools {
37 if t.ends_with('@') {
38 out.extend(running.iter().filter(|r| r.starts_with(t.as_str()) && r.len() > t.len()).cloned());
39 } else {
40 out.insert(t.trim_end_matches(".service").to_string());
41 }
42 }
43 out.into_iter().map(|n| UnitId::service(&n)).collect()
44}
45
46pub fn boottime_secs() -> Option<u64> {
48 let s = std::fs::read_to_string("/proc/uptime").ok()?;
49 s.split(['.', ' ']).next()?.parse().ok()
50}
51
52pub fn read_cgroups(root: &Path) -> (BTreeMap<u64, UnitId>, BTreeSet<String>) {
55 use std::os::unix::fs::MetadataExt;
56 let mut ids = BTreeMap::new();
57 let mut running = BTreeSet::new();
58 let mut stack = vec![root.join("system.slice")];
59 while let Some(dir) = stack.pop() {
60 let Ok(entries) = std::fs::read_dir(&dir) else { continue };
61 if let (Ok(meta), Ok(rel)) = (std::fs::metadata(&dir), dir.strip_prefix(root))
62 && let Some(u) = unit_of_cgroup(&rel.to_string_lossy())
63 {
64 running.insert(u.name.clone());
65 ids.insert(meta.ino(), u);
66 }
67 stack.extend(entries.flatten().filter(|e| e.file_type().is_ok_and(|t| t.is_dir())).map(|e| e.path()));
68 }
69 (ids, running)
70}
71
72pub fn read_virtual_ifaces(class_net: &Path) -> BTreeSet<String> {
76 std::fs::read_dir(class_net)
77 .into_iter()
78 .flatten()
79 .flatten()
80 .filter(|e| std::fs::read_link(e.path()).is_ok_and(|t| t.to_string_lossy().contains("/devices/virtual/")))
81 .map(|e| e.file_name().to_string_lossy().into_owned())
82 .collect()
83}
84
85pub fn read_ifnames() -> BTreeMap<u32, String> {
87 let mut out = BTreeMap::new();
88 for e in std::fs::read_dir("/sys/class/net").into_iter().flatten().flatten() {
89 let name = e.file_name().to_string_lossy().into_owned();
90 if let Some(i) = std::fs::read_to_string(e.path().join("ifindex")).ok().and_then(|s| s.trim().parse().ok()) {
91 out.insert(i, name);
92 }
93 }
94 out
95}