1use std::path::PathBuf;
11
12use sysinfo::{Pid, Process, ProcessRefreshKind, ProcessesToUpdate, System, UpdateKind};
13
14pub struct ProcSnapshot {
15 sys: System,
16}
17
18impl ProcSnapshot {
19 pub fn new() -> Self {
20 let mut sys = System::new();
21 sys.refresh_processes_specifics(ProcessesToUpdate::All, true, Self::refresh_kind());
22 Self { sys }
23 }
24
25 pub fn refresh(&mut self) {
26 self.sys
27 .refresh_processes_specifics(ProcessesToUpdate::All, true, Self::refresh_kind());
28 }
29
30 fn refresh_kind() -> ProcessRefreshKind {
31 ProcessRefreshKind::nothing()
32 .with_exe(UpdateKind::OnlyIfNotSet)
33 .with_cmd(UpdateKind::OnlyIfNotSet)
34 .with_cwd(UpdateKind::Always)
35 }
36
37 pub fn pid_alive(&self, pid: u32) -> bool {
41 self.sys.process(Pid::from_u32(pid)).is_some()
42 }
43
44 pub fn find_descendant(&self, root_pid: u32, targets: &[&str]) -> Option<ProcInfo> {
47 let mut best: Option<&Process> = None;
48 let mut stack = vec![Pid::from_u32(root_pid)];
49
50 while let Some(pid) = stack.pop() {
54 for (other_pid, proc) in self.sys.processes() {
55 if proc.parent() != Some(pid) {
56 continue;
57 }
58 if let Some(name) = exe_basename(proc)
59 && targets.contains(&name)
60 {
61 best = match best {
62 None => Some(proc),
63 Some(prev) if proc.start_time() > prev.start_time() => Some(proc),
64 Some(_) => best,
65 };
66 }
67 stack.push(*other_pid);
68 }
69 }
70
71 best.map(|p| ProcInfo {
72 pid: p.pid().as_u32(),
73 name: exe_basename(p).unwrap_or_default().to_string(),
74 cwd: p.cwd().map(PathBuf::from),
75 })
76 }
77}
78
79impl Default for ProcSnapshot {
80 fn default() -> Self {
81 Self::new()
82 }
83}
84
85#[derive(Debug, Clone)]
86pub struct ProcInfo {
87 pub pid: u32,
88 pub name: String,
89 pub cwd: Option<PathBuf>,
90}
91
92fn exe_basename(proc: &Process) -> Option<&str> {
93 proc.name().to_str().or_else(|| {
100 proc.exe()
101 .and_then(|p| p.file_name())
102 .and_then(|n| n.to_str())
103 })
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn snapshot_finds_self_as_no_target() {
112 let snap = ProcSnapshot::new();
114 let me = std::process::id();
115 assert!(
116 snap.find_descendant(me, &["this_binary_does_not_exist"])
117 .is_none()
118 );
119 }
120}