1#[cfg(unix)]
9fn parent_pid() -> u32 {
10 #[allow(clippy::cast_sign_loss)]
11 let pid = unsafe { libc::getppid() } as u32;
12 pid
13}
14
15#[cfg(windows)]
16fn parent_pid() -> u32 {
17 use windows::Win32::Foundation::CloseHandle;
18 use windows::Win32::System::Diagnostics::ToolHelp::{
19 CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W,
20 TH32CS_SNAPPROCESS,
21 };
22 use windows::Win32::System::Threading::GetCurrentProcessId;
23 unsafe {
24 let Ok(snap) = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) else {
25 return 0;
26 };
27 let me = GetCurrentProcessId();
28 let mut entry = PROCESSENTRY32W {
29 dwSize: u32::try_from(std::mem::size_of::<PROCESSENTRY32W>()).unwrap_or(0),
30 ..PROCESSENTRY32W::default()
31 };
32 let mut found = 0;
33 if Process32FirstW(snap, &raw mut entry).is_ok() {
34 loop {
35 if entry.th32ProcessID == me {
36 found = entry.th32ParentProcessID;
37 break;
38 }
39 if Process32NextW(snap, &raw mut entry).is_err() {
40 break;
41 }
42 }
43 }
44 let _ = CloseHandle(snap);
45 found
46 }
47}
48
49#[cfg(target_os = "macos")]
50fn parent_start_time(ppid: u32) -> Option<u64> {
51 let mut info: libc::proc_bsdinfo = unsafe { std::mem::zeroed() };
52 #[allow(clippy::cast_possible_wrap)]
53 let ret = unsafe {
54 libc::proc_pidinfo(
55 ppid as i32,
56 libc::PROC_PIDTBSDINFO,
57 0,
58 std::ptr::from_mut(&mut info).cast::<libc::c_void>(),
59 i32::try_from(std::mem::size_of::<libc::proc_bsdinfo>()).unwrap_or(0),
60 )
61 };
62 if ret > 0 {
63 Some(info.pbi_start_tvsec)
64 } else {
65 None
66 }
67}
68
69#[cfg(target_os = "linux")]
70fn parent_start_time(ppid: u32) -> Option<u64> {
71 let stat = std::fs::read_to_string(format!("/proc/{ppid}/stat")).ok()?;
72 let after_paren = stat.rsplit_once(')')?.1;
77 let fields: Vec<&str> = after_paren.split_whitespace().collect();
78 fields.get(19)?.parse().ok()
79}
80
81#[cfg(target_os = "windows")]
82fn parent_start_time(ppid: u32) -> Option<u64> {
83 use windows::Win32::Foundation::{CloseHandle, FILETIME};
84 use windows::Win32::System::Threading::{
85 GetProcessTimes, OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION,
86 };
87 unsafe {
88 let h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, ppid).ok()?;
89 let mut creation = FILETIME::default();
90 let mut exit = FILETIME::default();
91 let mut kernel = FILETIME::default();
92 let mut user = FILETIME::default();
93 let r = GetProcessTimes(
94 h,
95 &raw mut creation,
96 &raw mut exit,
97 &raw mut kernel,
98 &raw mut user,
99 );
100 let _ = CloseHandle(h);
101 r.ok()?;
102 let high = u64::from(creation.dwHighDateTime);
103 let low = u64::from(creation.dwLowDateTime);
104 Some((high << 32) | low)
105 }
106}
107
108#[must_use]
113pub fn caller_key() -> Option<String> {
114 let ppid = parent_pid();
115 if ppid == 0 {
116 return None;
117 }
118 let start = parent_start_time(ppid).unwrap_or(0);
119 Some(format!("{ppid}-{start}"))
120}