Skip to main content

running_process_platform_internal/
platform_linux_cmdline.rs

1
2    //! Linux `/proc/<pid>/cmdline` implementation. The kernel writes argv as
3    //! NUL-separated opaque bytes, with a trailing NUL.
4
5    use std::os::unix::ffi::OsStringExt;
6
7    /// Return the process argv without flattening its argument boundaries.
8    pub fn read_process_argv(pid: u32) -> std::io::Result<Vec<std::ffi::OsString>> {
9        if pid == 0 {
10            return Err(std::io::Error::new(
11                std::io::ErrorKind::InvalidInput,
12                "pid 0 is the kernel scheduler — not queryable",
13            ));
14        }
15        let path = format!("/proc/{pid}/cmdline");
16        let bytes = std::fs::read(&path)?;
17        // `/proc/<pid>/cmdline` is empty for kernel threads.
18        if bytes.is_empty() {
19            return Ok(Vec::new());
20        }
21        let bytes = bytes.strip_suffix(&[0]).unwrap_or(&bytes);
22        Ok(bytes
23            .split(|byte| *byte == 0)
24            .map(|argument| std::ffi::OsString::from_vec(argument.to_vec()))
25            .collect())
26    }
27
28    /// Return a stable human-readable rendering of [`read_process_argv`].
29    ///
30    /// This intentionally is not shell syntax and must not be parsed back
31    /// into argv. It preserves the historical `read_process_cmdline` output.
32    pub fn read_process_cmdline(pid: u32) -> std::io::Result<String> {
33        Ok(render_display(&read_process_argv(pid)?))
34    }
35
36    fn render_display(argv: &[std::ffi::OsString]) -> String {
37        argv.iter()
38            .map(|argument| argument.to_string_lossy())
39            .collect::<Vec<_>>()
40            .join(" ")
41    }
42
43    #[cfg(test)]
44    mod tests {
45        use super::render_display;
46        use std::os::unix::ffi::OsStringExt;
47
48        #[test]
49        fn display_is_separate_from_structured_argv() {
50            let argv = vec![
51                std::ffi::OsString::from("tool"),
52                std::ffi::OsString::from("has space"),
53                std::ffi::OsString::from("quote\""),
54                std::ffi::OsString::new(),
55                std::ffi::OsString::from_vec(b"back\\slash".to_vec()),
56            ];
57            assert_eq!(render_display(&argv), "tool has space quote\"  back\\slash");
58            assert_eq!(argv[3], std::ffi::OsString::new());
59        }
60    }