ghostscope_process/pid/
procfs.rs1use super::types::PidNamespaceId;
2use anyhow::Result;
3use std::fs;
4use std::os::unix::fs::MetadataExt;
5use std::path::Path;
6
7pub const INITIAL_PID_NAMESPACE_INO: u64 = 4026531836;
9
10pub fn process_exists(pid: u32) -> bool {
11 let proc_path = format!("/proc/{pid}");
12 Path::new(&proc_path).is_dir()
13}
14
15pub fn read_status(pid: u32) -> Result<String> {
16 let path = format!("/proc/{pid}/status");
17 fs::read_to_string(&path)
18 .map_err(|e| anyhow::anyhow!("Failed to read {} while resolving PID mapping: {}", path, e))
19}
20
21pub fn read_nspid_chain(pid: u32) -> Option<Vec<u32>> {
22 let status = fs::read_to_string(format!("/proc/{pid}/status")).ok()?;
23 read_nspid_chain_from_status(&status)
24}
25
26pub fn read_pid_ns_id(pid: u32) -> Option<PidNamespaceId> {
27 if let Ok(metadata) = fs::metadata(format!("/proc/{pid}/ns/pid")) {
28 return Some(PidNamespaceId {
29 dev: Some(metadata.dev()),
30 inode: metadata.ino(),
31 });
32 }
33
34 read_pid_ns_inode_from_link(pid).map(|inode| PidNamespaceId { dev: None, inode })
35}
36
37pub fn read_pid_ns_inode(pid: u32) -> Option<u64> {
38 read_pid_ns_id(pid).map(|pid_ns| pid_ns.inode)
39}
40
41fn parse_status_chain(status: &str, key: &str) -> Option<Vec<u32>> {
42 let line = status.lines().find(|line| line.starts_with(key))?;
43 let payload = line.strip_prefix(key)?.trim();
44 if payload.is_empty() {
45 return None;
46 }
47 let chain: Vec<u32> = payload
48 .split_whitespace()
49 .filter_map(|token| token.parse::<u32>().ok())
50 .collect();
51 if chain.is_empty() {
52 None
53 } else {
54 Some(chain)
55 }
56}
57
58pub(crate) fn read_nspid_chain_from_status(status: &str) -> Option<Vec<u32>> {
59 parse_status_chain(status, "NSpid:")
60}
61
62fn read_pid_ns_inode_from_link(pid: u32) -> Option<u64> {
63 let link = fs::read_link(format!("/proc/{pid}/ns/pid")).ok()?;
64 parse_ns_inode(&link.to_string_lossy())
65}
66
67fn parse_ns_inode(link_target: &str) -> Option<u64> {
68 let lb = link_target.find('[')?;
70 let rb = link_target[lb + 1..].find(']')? + lb + 1;
71 link_target[lb + 1..rb].parse::<u64>().ok()
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn parse_ns_inode_works() {
80 assert_eq!(
81 parse_ns_inode("pid:[4026531836]"),
82 Some(INITIAL_PID_NAMESPACE_INO)
83 );
84 assert_eq!(parse_ns_inode("pid:[abc]"), None);
85 assert_eq!(parse_ns_inode("unknown"), None);
86 }
87
88 #[test]
89 fn parse_status_chain_works() {
90 let status = "Name:\tcat\nNSpid:\t1234 1\n";
91 assert_eq!(parse_status_chain(status, "NSpid:"), Some(vec![1234, 1]));
92 }
93}