cloud_terrastodon_zombies/
lib.rs1use cloud_terrastodon_user_input::Choice;
2use cloud_terrastodon_user_input::FzfArgs;
3use cloud_terrastodon_user_input::pick_many;
4use eyre::Context;
5use eyre::bail;
6use std::collections::HashSet;
7use std::path::Path;
8use tracing::debug;
9use tracing::info;
10
11pub fn prompt_kill_processes_using_dirs(
14 dirs: impl IntoIterator<Item = impl AsRef<Path>>,
15 header: String,
16) -> eyre::Result<()> {
17 let mut system = sysinfo::System::new_all();
18 system.refresh_all();
19
20 let mut parents = HashSet::new();
21 for dir in dirs {
22 let dir = dir.as_ref().to_path_buf();
23 let dir = dir
24 .canonicalize()
25 .wrap_err(format!("Failed to canonicalize given dir {dir:?}"))?;
26 parents.insert(dir);
27 }
28
29 let mut to_kill = Vec::new();
31 for (pid, process) in system.processes() {
32 let Some(exe_path) = process.exe() else {
33 continue;
34 };
35 let Some(exe_dir) = exe_path.parent() else {
36 continue;
37 };
38 let exe_dir = exe_dir
39 .canonicalize()
40 .wrap_err(format!("Failed to canonicalize exe dir {exe_dir:?}"))?;
41 let is_in_dir = exe_dir
42 .ancestors()
43 .any(|ancestor| parents.contains(ancestor));
44 if is_in_dir {
45 to_kill.push(Choice {
46 key: format!(
47 "ID: {}, Name: {}, Path: {:?}",
48 pid,
49 process.name().to_string_lossy(),
50 process.exe()
51 ),
52 value: (pid, process),
53 });
54 }
55 }
56 if to_kill.is_empty() {
57 return Ok(());
58 }
59 let to_kill = pick_many(FzfArgs {
60 choices: to_kill,
61 header: Some(header),
62 ..Default::default()
63 })?;
64 for Choice {
65 key,
66 value: (_pid, process),
67 } in to_kill
68 {
69 debug!("Killing: {key}");
70 let signal_sent_success = process.kill();
71 if !signal_sent_success {
72 bail!("Failed to send kill signal to {key}");
73 }
74 let exit_status = process.wait();
75 info!("Killed {key} with exit status: {exit_status:#?}");
76 }
77
78 Ok(())
79}
80
81#[cfg(test)]
82mod test {
83 use crate::prompt_kill_processes_using_dirs;
84 use cloud_terrastodon_pathing::AppDir;
85
86 #[test]
87 fn main() {
88 let mut system = sysinfo::System::new_all();
90
91 system.refresh_all();
93
94 let pattern = "terraform-provider";
96
97 for (pid, process) in system.processes() {
99 let name = process.name().to_string_lossy();
100 if !name.contains(pattern) {
101 continue;
102 }
103 println!("ID: {}, Name: {}, Path: {:?}", pid, name, process.exe());
104 }
105 }
106 #[test]
107 #[ignore]
108 fn kill() {
109 prompt_kill_processes_using_dirs(
110 [
111 AppDir::Imports.as_path_buf(),
112 AppDir::Processed.as_path_buf(),
113 ],
114 "Found the following processes, select the ones you want to kill".to_string(),
115 )
116 .unwrap();
117 }
118}