#[cfg(windows)]
use super::windows_impl;
#[cfg(all(doc, unix))]
use super::WalpinPidHealth;
#[cfg(target_os = "linux")]
use super::{census_visible_self_pid, fs};
#[cfg(unix)]
use super::{
enumerate_live_bounded, io, unix_impl, Duration, EnumerationPurpose, Path, WalpinReport,
MAX_SIDECAR_ENTRIES,
};
#[cfg(any(unix, test))]
use super::{SystemTime, UNIX_EPOCH};
pub fn is_process_alive(pid: u32) -> bool {
#[cfg(unix)]
{
unix_impl::is_process_alive(pid)
}
#[cfg(windows)]
{
windows_impl::is_process_alive(pid)
}
}
pub fn reporting_pid() -> u32 {
#[cfg(target_os = "linux")]
{
census_visible_self_pid().unwrap_or_else(std::process::id)
}
#[cfg(not(target_os = "linux"))]
{
std::process::id()
}
}
#[cfg(target_os = "macos")]
pub fn start_time_resolution_secs() -> Option<u64> {
Some(1)
}
#[cfg(target_os = "linux")]
pub fn start_time_resolution_secs() -> Option<u64> {
Some(2)
}
#[cfg(windows)]
pub fn start_time_resolution_secs() -> Option<u64> {
Some(1)
}
#[cfg(not(any(target_os = "macos", target_os = "linux", windows)))]
pub fn start_time_resolution_secs() -> Option<u64> {
None
}
#[cfg(target_os = "macos")]
pub fn process_start_time_secs(pid: u32) -> Option<i64> {
use std::os::raw::{c_int, c_void};
const PROC_PIDTBSDINFO: c_int = 3;
const MAXCOMLEN: usize = 16;
#[repr(C)]
struct ProcBsdInfo {
pbi_flags: u32,
pbi_status: u32,
pbi_xstatus: u32,
pbi_pid: u32,
pbi_ppid: u32,
pbi_uid: u32,
pbi_gid: u32,
pbi_ruid: u32,
pbi_rgid: u32,
pbi_svuid: u32,
pbi_svgid: u32,
rfu_1: u32,
pbi_comm: [u8; MAXCOMLEN],
pbi_name: [u8; 2 * MAXCOMLEN],
pbi_nfiles: u32,
pbi_pgid: u32,
pbi_pjobc: u32,
e_tdev: u32,
e_tpgid: u32,
pbi_nice: i32,
pbi_start_tvsec: u64,
pbi_start_tvusec: u64,
}
#[link(name = "proc")]
extern "C" {
fn proc_pidinfo(
pid: c_int,
flavor: c_int,
arg: u64,
buffer: *mut c_void,
buffersize: c_int,
) -> c_int;
}
let pid_i32 = i32::try_from(pid).ok()?;
let mut info: ProcBsdInfo = unsafe { std::mem::zeroed() };
let size = std::mem::size_of::<ProcBsdInfo>() as c_int;
let ret = unsafe {
proc_pidinfo(
pid_i32,
PROC_PIDTBSDINFO,
0,
&mut info as *mut _ as *mut c_void,
size,
)
};
if ret != size {
return None;
}
i64::try_from(info.pbi_start_tvsec).ok()
}
#[cfg(target_os = "linux")]
pub fn process_start_time_secs(pid: u32) -> Option<i64> {
let stat = fs::read_to_string(format!("/proc/{pid}/stat")).ok()?;
let rparen = stat.rfind(')')?;
let rest = stat.get(rparen + 1..)?;
let fields: Vec<&str> = rest.split_whitespace().collect();
let starttime_ticks: u64 = fields.get(19)?.parse().ok()?;
let clk_tck = unsafe { libc::sysconf(libc::_SC_CLK_TCK) };
if clk_tck <= 0 {
return None;
}
let secs_since_boot = starttime_ticks / clk_tck as u64;
let stat_all = fs::read_to_string("/proc/stat").ok()?;
let btime = stat_all.lines().find_map(|line| {
line.strip_prefix("btime ")
.and_then(|v| v.trim().parse::<i64>().ok())
})?;
Some(btime + secs_since_boot as i64)
}
#[cfg(windows)]
pub fn process_start_time_secs(pid: u32) -> Option<i64> {
windows_impl::process_start_time_secs(pid)
}
#[cfg(not(any(target_os = "macos", target_os = "linux", windows)))]
pub fn process_start_time_secs(_pid: u32) -> Option<i64> {
None
}
#[cfg(any(unix, test))]
pub(super) fn now_epoch_secs() -> i64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs() as i64)
.unwrap_or(0)
}
#[cfg(unix)]
pub(super) fn stale_window_from(interval: Duration) -> i64 {
interval
.max(Duration::from_secs(1))
.saturating_mul(3)
.as_secs() as i64
}
#[cfg(unix)]
pub(super) fn stale_window_secs(producer_interval_ms: u64, fallback_secs: i64) -> i64 {
if producer_interval_ms == 0 {
fallback_secs
} else {
stale_window_from(Duration::from_millis(producer_interval_ms))
}
}
#[cfg(unix)]
pub(super) fn epoch_abs_diff(a: i64, b: i64) -> u64 {
a.checked_sub(b)
.map(|d| d.unsigned_abs())
.unwrap_or(u64::MAX)
}
#[cfg(unix)]
pub fn enumerate_live(dir: &Path, sweep_interval: Duration) -> io::Result<WalpinReport> {
enumerate_live_bounded(
dir,
sweep_interval,
MAX_SIDECAR_ENTRIES,
EnumerationPurpose::Attribution,
)
}
#[cfg(unix)]
pub(crate) fn inspect_live(dir: &Path, sweep_interval: Duration) -> io::Result<WalpinReport> {
enumerate_live_bounded(
dir,
sweep_interval,
MAX_SIDECAR_ENTRIES,
EnumerationPurpose::Diagnostics,
)
}
#[cfg(unix)]
pub(crate) fn housekeep_live(
dir: &Path,
legacy_sweep_interval: Duration,
) -> io::Result<WalpinReport> {
enumerate_live_bounded(
dir,
legacy_sweep_interval,
MAX_SIDECAR_ENTRIES,
EnumerationPurpose::Housekeeping,
)
}