use std::collections::{HashMap, HashSet};
use super::processes::Process;
use super::types::{ProcessId, ProcessInfo};
use crate::error::Result;
impl Process {
pub fn kill_tree(&self) -> Result<()> {
Self::kill_tree_by_id(self.id())
}
pub fn kill_tree_by_id(pid: ProcessId) -> Result<()> {
let mut buffer = Vec::with_capacity(8192);
Self::kill_tree_by_id_with_buffer(pid, &mut buffer)
}
pub fn kill_tree_by_id_with_buffer(
pid: ProcessId,
out_processes: &mut Vec<ProcessInfo>,
) -> Result<()> {
Self::list_with_buffer(out_processes)?;
let tree = build_process_tree(out_processes);
let mut to_kill = HashSet::new();
collect_descendants(pid, &tree, &mut to_kill);
to_kill.insert(pid);
let mut kill_order: Vec<_> = to_kill.into_iter().collect();
kill_order.sort_by_key(|&pid| std::cmp::Reverse(tree_depth(pid, &tree)));
for kill_pid in kill_order {
let _ = Self::kill_by_id(kill_pid);
}
Ok(())
}
pub fn kill_tree_from_root(pid: ProcessId) -> Result<()> {
let mut buffer = Vec::with_capacity(8192);
Self::kill_tree_from_root_with_buffer(pid, &mut buffer)
}
pub fn kill_tree_from_root_with_buffer(
pid: ProcessId,
out_processes: &mut Vec<ProcessInfo>,
) -> Result<()> {
Self::list_with_buffer(out_processes)?;
let tree = build_process_tree(out_processes);
let root = find_root_ancestor(pid, &tree);
Self::kill_tree_by_id_with_buffer(root, out_processes)
}
}
fn build_process_tree(processes: &[ProcessInfo]) -> HashMap<ProcessId, Vec<ProcessId>> {
let mut tree: HashMap<ProcessId, Vec<ProcessId>> = HashMap::new();
for proc in processes {
if let Some(parent_pid) = proc.parent_pid {
tree.entry(parent_pid).or_default().push(proc.pid);
}
}
tree
}
fn collect_descendants(
pid: ProcessId,
tree: &HashMap<ProcessId, Vec<ProcessId>>,
result: &mut HashSet<ProcessId>,
) {
if let Some(children) = tree.get(&pid) {
for &child in children {
if result.insert(child) {
collect_descendants(child, tree, result);
}
}
}
}
fn tree_depth(pid: ProcessId, tree: &HashMap<ProcessId, Vec<ProcessId>>) -> usize {
if let Some(children) = tree.get(&pid) {
1 + children
.iter()
.map(|&child| tree_depth(child, tree))
.max()
.unwrap_or(0)
} else {
0
}
}
fn find_root_ancestor(
mut pid: ProcessId,
parent_map: &HashMap<ProcessId, Vec<ProcessId>>,
) -> ProcessId {
let mut child_to_parent: HashMap<ProcessId, ProcessId> = HashMap::new();
for (&parent, children) in parent_map {
for &child in children {
child_to_parent.insert(child, parent);
}
}
let mut visited = HashSet::new();
while let Some(&parent) = child_to_parent.get(&pid) {
if !visited.insert(pid) {
break;
}
pid = parent;
}
pid
}