use sysinfo::{
Pid,
ProcessRefreshKind,
ProcessesToUpdate,
System,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ProcessInfo {
pub pid: u32,
pub start_time: u64,
}
impl ProcessInfo {
pub fn current() -> Self {
let pid = std::process::id();
let sysinfo_pid = Pid::from_u32(pid);
let mut system = System::new();
system.refresh_processes_specifics(
ProcessesToUpdate::Some(&[sysinfo_pid]),
false,
ProcessRefreshKind::nothing(),
);
let start_time = system
.process(sysinfo_pid)
.map(|p| p.start_time())
.unwrap_or(0);
Self { pid, start_time }
}
pub fn is_alive(pid: u32, expected_start_time: Option<u64>) -> bool {
let sysinfo_pid = Pid::from_u32(pid);
let mut system = System::new();
system.refresh_processes_specifics(
ProcessesToUpdate::Some(&[sysinfo_pid]),
false,
ProcessRefreshKind::nothing(),
);
match system.process(sysinfo_pid) {
| Some(process) => {
if let Some(expected) = expected_start_time {
process.start_time() == expected
} else {
true
}
},
| None => false,
}
}
pub fn is_self_alive(&self) -> bool {
Self::is_alive(self.pid, Some(self.start_time))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn current_process_has_valid_pid() {
let info = ProcessInfo::current();
assert_eq!(info.pid, std::process::id());
}
#[test]
fn current_process_has_nonzero_start_time() {
let info = ProcessInfo::current();
assert!(
info.start_time > 1_577_836_800,
"start_time {} should be after 2020",
info.start_time
);
}
#[test]
fn current_process_is_alive() {
let pid = std::process::id();
assert!(
ProcessInfo::is_alive(pid, None),
"current process should be alive"
);
}
#[test]
fn current_process_is_alive_with_correct_start_time() {
let info = ProcessInfo::current();
assert!(
ProcessInfo::is_alive(info.pid, Some(info.start_time)),
"current process should be alive with matching start time"
);
}
#[test]
fn current_process_not_alive_with_wrong_start_time() {
let info = ProcessInfo::current();
let wrong_start_time = info.start_time.saturating_sub(1000);
assert!(
!ProcessInfo::is_alive(info.pid, Some(wrong_start_time)),
"process should not match with wrong start time (PID reuse detection)"
);
}
#[test]
fn nonexistent_process_is_not_alive() {
let unlikely_pid = u32::MAX - 1;
assert!(
!ProcessInfo::is_alive(unlikely_pid, None),
"nonexistent PID {} should not be alive",
unlikely_pid
);
}
#[test]
fn is_self_alive_returns_true() {
let info = ProcessInfo::current();
assert!(info.is_self_alive(), "is_self_alive should return true");
}
#[test]
fn process_info_is_copy() {
let info = ProcessInfo::current();
let copy = info;
assert_eq!(info.pid, copy.pid);
assert_eq!(info.start_time, copy.start_time);
}
#[test]
fn init_process_is_alive() {
let _ = ProcessInfo::is_alive(1, None);
}
#[test]
fn multiple_calls_return_consistent_results() {
let info1 = ProcessInfo::current();
let info2 = ProcessInfo::current();
assert_eq!(info1.pid, info2.pid, "PID should be consistent");
assert_eq!(
info1.start_time, info2.start_time,
"start_time should be consistent"
);
}
}