Skip to main content

running_process/observer/
file_handles.rs

1//! Portable facade for native file-handle snapshots.
2
3pub fn read_process_file_handles(pid: u32) -> std::io::Result<Vec<String>> {
4    running_process_platform_internal::platform::process::read_process_file_handles(pid)
5}
6
7#[cfg(test)]
8mod tests {
9    use super::*;
10
11    #[test]
12    fn read_handles_for_pid_zero_returns_invalid_input() {
13        let err = read_process_file_handles(0).expect_err("pid 0 should be rejected");
14        assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
15    }
16
17    #[test]
18    fn self_snapshot_includes_a_temp_file_we_just_opened() {
19        // Open a temp file, snapshot our own fds, assert the temp
20        // file's path is in the result.
21        //
22        // Unix backends return POSIX paths that match `path` /
23        // `canonical` directly. The Windows backend returns NT
24        // object names like `\Device\HarddiskVolume3\Users\...\tmpXXXX`
25        // which won't match a DOS-style path equal-for-equal — so we
26        // also match on filename suffix, which is reliable across
27        // the NT/DOS path translation gap.
28        let tmp = tempfile::NamedTempFile::new().expect("tempfile");
29        let path = tmp.path().to_path_buf();
30        let canonical = std::fs::canonicalize(&path).unwrap_or(path.clone());
31        let filename = path
32            .file_name()
33            .and_then(|s| s.to_str())
34            .map(str::to_owned)
35            .unwrap_or_default();
36
37        let handles = read_process_file_handles(std::process::id()).expect("read self handles");
38        let canonical_str = canonical.to_string_lossy();
39        let raw_str = path.to_string_lossy();
40        let found = handles.iter().any(|h| {
41            h == canonical_str.as_ref()
42                || h == raw_str.as_ref()
43                || (!filename.is_empty() && h.ends_with(&filename))
44        });
45        assert!(
46            found,
47            "expected temp file (filename={filename}, canonical={canonical_str}, raw={raw_str}) in handles, got {handles:?}",
48        );
49        // Drop tmp explicitly so it stays alive until after the snapshot.
50        drop(tmp);
51    }
52}