use std::process::Command;
pub fn process_alive(pid: u32) -> bool {
#[cfg(target_os = "linux")]
{
std::path::Path::new(&format!("/proc/{pid}")).exists()
}
#[cfg(all(unix, not(target_os = "linux")))]
{
Command::new("kill")
.args(["-0", &pid.to_string()])
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
#[cfg(windows)]
{
let out = Command::new("tasklist.exe")
.args(["/FI", &format!("PID eq {pid}"), "/FO", "CSV", "/NH"])
.output();
match out {
Ok(o) if o.status.success() => {
let s = String::from_utf8_lossy(&o.stdout);
let trimmed = s.trim();
!trimmed.is_empty() && !trimmed.starts_with("INFO:")
}
_ => false,
}
}
}
pub fn find_processes_by_cmdline(pattern: &str) -> Vec<u32> {
#[cfg(unix)]
{
Command::new("pgrep")
.args(["-f", pattern])
.output()
.ok()
.filter(|o| o.status.success())
.map(|o| {
String::from_utf8_lossy(&o.stdout)
.split_whitespace()
.filter_map(|s| s.parse::<u32>().ok())
.collect()
})
.unwrap_or_default()
}
#[cfg(windows)]
{
let escaped = pattern.replace('\'', "''");
let ps = format!(
"Get-CimInstance Win32_Process | \
Where-Object {{ $_.CommandLine -like '*{escaped}*' }} | \
Select-Object -ExpandProperty ProcessId"
);
Command::new("powershell.exe")
.args(["-NoProfile", "-NonInteractive", "-Command", &ps])
.output()
.ok()
.filter(|o| o.status.success())
.map(|o| {
String::from_utf8_lossy(&o.stdout)
.split_whitespace()
.filter_map(|s| s.parse::<u32>().ok())
.collect()
})
.unwrap_or_default()
}
#[cfg(not(any(unix, windows)))]
{
let _ = pattern;
Vec::new()
}
}
pub fn kill_process(pid: u32, force: bool) -> bool {
#[cfg(unix)]
{
let sig = if force { "-9" } else { "-15" };
Command::new("kill")
.args([sig, &pid.to_string()])
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
#[cfg(windows)]
{
let pid_str = pid.to_string();
let mut args: Vec<&str> = vec!["/PID", &pid_str, "/T"];
if force {
args.push("/F");
}
Command::new("taskkill.exe")
.args(&args)
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
#[cfg(not(any(unix, windows)))]
{
let _ = (pid, force);
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn process_alive_returns_true_for_self() {
let me = std::process::id();
assert!(
process_alive(me),
"process_alive should return true for self pid {me}"
);
}
#[test]
fn process_alive_returns_false_for_clearly_dead_pid() {
let dead = 4_000_000_001;
assert!(
!process_alive(dead),
"process_alive should return false for synthetic dead pid {dead}"
);
}
#[test]
fn kill_process_on_nonexistent_pid_returns_false_or_noop() {
let dead = 4_000_000_002;
let _ = kill_process(dead, false);
}
}