1use std::path::PathBuf;
11
12pub fn get_mock_bin() -> PathBuf {
27 if let Ok(path) = std::env::var("CARGO_BIN_EXE_term-session-mock") {
28 return PathBuf::from(path);
29 }
30
31 let resolved = resolve_mock_bin();
32 if let Some(path) = resolved {
33 return path;
34 }
35
36 build_mock_bin();
37
38 match resolve_mock_bin() {
39 Some(path) => path,
40 None => panic!(
41 "term-session-mock binary still missing after `cargo build`; \
42 searched {:?} and {:?}",
43 mock_bin_candidates().0,
44 mock_bin_candidates().1,
45 ),
46 }
47}
48
49fn mock_bin_candidates() -> (PathBuf, PathBuf) {
51 let mut path = std::env::current_exe().expect("test exe path");
52 path.pop();
53 if path.ends_with("deps") {
54 path.pop();
55 }
56 let plain = path.join(format!("term-session-mock{}", std::env::consts::EXE_SUFFIX));
57 let deps_dir = path.join("deps");
58 (plain, deps_dir)
59}
60
61fn resolve_mock_bin() -> Option<PathBuf> {
62 let (plain, deps_dir) = mock_bin_candidates();
63 if plain.exists() {
64 return Some(plain);
65 }
66
67 let suffix = std::env::consts::EXE_SUFFIX;
68 if let Ok(entries) = std::fs::read_dir(&deps_dir) {
69 for entry in entries.flatten() {
70 let name = entry.file_name();
71 let name = name.to_string_lossy();
72 if name.starts_with("term-session-mock-") && name.ends_with(suffix) {
73 return Some(entry.path());
74 }
75 }
76 }
77
78 None
79}
80
81fn build_mock_bin() {
85 let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml");
86 let status = std::process::Command::new(env!("CARGO"))
87 .arg("build")
88 .arg("--manifest-path")
89 .arg(&manifest)
90 .status()
91 .expect("failed to spawn `cargo build` for term-session-mock");
92 if !status.success() {
93 panic!(
94 "`cargo build --manifest-path {}` failed with {status}",
95 manifest.display()
96 );
97 }
98}
99
100pub const CHECK_PID_ALIVE: i32 = 0;
105pub const CHECK_PID_DEAD: i32 = 1;
107
108#[cfg(windows)]
110pub fn process_is_alive(pid: u32) -> bool {
111 use windows_sys::Win32::Foundation::{CloseHandle, STILL_ACTIVE};
112 use windows_sys::Win32::System::Threading::{
113 GetExitCodeProcess, OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION,
114 };
115
116 unsafe {
117 let handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid);
118 if handle.is_null() {
119 return false;
120 }
121 let mut code: u32 = 0;
122 let ok = GetExitCodeProcess(handle, &mut code);
123 let _ = CloseHandle(handle);
124 ok != 0 && code == STILL_ACTIVE as u32
125 }
126}
127
128#[cfg(not(windows))]
130pub fn process_is_alive(pid: u32) -> bool {
131 unsafe { libc::kill(pid as i32, 0) == 0 }
133}