running_process/cleanup/
verify_basic.rs1use std::path::Path;
2
3use crate::broker::{host_identity, manifest};
4use crate::cleanup::json_escape;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct VerifyFinding {
9 pub path: std::path::PathBuf,
11 pub severity: &'static str,
13 pub message: String,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct VerifyReport {
20 pub scanned: usize,
22 pub findings: Vec<VerifyFinding>,
24}
25
26pub fn run(registry_dir: &Path) -> VerifyReport {
28 let current = host_identity::current();
29 let entries = manifest::scan_central(registry_dir);
30 let mut findings = Vec::new();
31 let scanned = entries.len();
32
33 for entry in entries {
34 match entry.result {
35 Ok(manifest) => {
36 if let Some(host) = manifest.host.as_ref() {
37 if !host.machine_id.is_empty() && host.machine_id != current.machine_id {
38 findings.push(VerifyFinding {
39 path: entry.path.clone(),
40 severity: "stale",
41 message: "manifest belongs to another machine".to_string(),
42 });
43 }
44 if !host.boot_id.is_empty() && host.boot_id != current.boot_id {
45 findings.push(VerifyFinding {
46 path: entry.path.clone(),
47 severity: "stale",
48 message: "manifest belongs to a prior boot".to_string(),
49 });
50 }
51 }
52 if let Some(daemon) = manifest.current_daemon.as_ref() {
53 if !process_is_alive(daemon.pid) {
54 findings.push(VerifyFinding {
55 path: entry.path,
56 severity: "stale",
57 message: format!("daemon pid {} is not alive", daemon.pid),
58 });
59 }
60 }
61 }
62 Err(err) => findings.push(VerifyFinding {
63 path: entry.path,
64 severity: "error",
65 message: err.to_string(),
66 }),
67 }
68 }
69
70 VerifyReport { scanned, findings }
71}
72
73pub fn render_json(report: &VerifyReport) -> String {
75 let findings = report
76 .findings
77 .iter()
78 .map(|finding| {
79 format!(
80 "{{\"path\":\"{}\",\"severity\":\"{}\",\"message\":\"{}\"}}",
81 json_escape(&finding.path.to_string_lossy()),
82 finding.severity,
83 json_escape(&finding.message)
84 )
85 })
86 .collect::<Vec<_>>()
87 .join(",");
88 format!(
89 "{{\"schema_version\":1,\"scanned\":{},\"findings\":[{}]}}",
90 report.scanned, findings
91 )
92}
93
94#[cfg(unix)]
95fn process_is_alive(pid: u32) -> bool {
96 if pid == 0 {
97 return false;
98 }
99 let result = unsafe { libc::kill(pid as i32, 0) };
100 result == 0 || std::io::Error::last_os_error().raw_os_error() != Some(libc::ESRCH)
101}
102
103#[cfg(windows)]
104fn process_is_alive(pid: u32) -> bool {
105 if pid == 0 {
106 return false;
107 }
108 let handle = unsafe {
109 winapi::um::processthreadsapi::OpenProcess(
110 winapi::um::winnt::PROCESS_QUERY_LIMITED_INFORMATION,
111 0,
112 pid,
113 )
114 };
115 if handle.is_null() {
116 return false;
117 }
118 unsafe {
119 winapi::um::handleapi::CloseHandle(handle);
120 }
121 true
122}