running_process_platform_internal/platform_linux_cmdline.rs
1
2 //! Linux `/proc/<pid>/cmdline` implementation. The kernel writes
3 //! argv as NUL-separated UTF-8 (typically  argv is opaque bytes,
4 //! we lossy-decode), with a trailing NUL.
5
6 pub fn read_process_cmdline(pid: u32) -> std::io::Result<String> {
7 if pid == 0 {
8 return Err(std::io::Error::new(
9 std::io::ErrorKind::InvalidInput,
10 "pid 0 is the kernel scheduler  not queryable",
11 ));
12 }
13 let path = format!("/proc/{pid}/cmdline");
14 let bytes = std::fs::read(&path)?;
15 // `/proc/<pid>/cmdline` is empty for kernel threads  return
16 // empty string rather than synthesizing fake separators.
17 if bytes.is_empty() {
18 return Ok(String::new());
19 }
20 // Drop the trailing NUL terminator if present, then turn
21 // remaining NUL separators into spaces so the result reads as
22 // a single shell-style command line (same convention as
23 // Windows NtQueryInformationProcess and macOS KERN_PROCARGS2,
24 // both of which return one logical command line per PID).
25 let mut trimmed = bytes.as_slice();
26 if trimmed.last() == Some(&0) {
27 trimmed = &trimmed[..trimmed.len() - 1];
28 }
29 let joined: Vec<u8> = trimmed
30 .iter()
31 .map(|b| if *b == 0 { b' ' } else { *b })
32 .collect();
33 Ok(String::from_utf8_lossy(&joined).into_owned())
34 }