running_process/observer/
cmdline.rs1pub fn read_process_argv(pid: u32) -> std::io::Result<Vec<std::ffi::OsString>> {
14 running_process_platform_internal::platform::process::read_process_argv(pid)
15}
16
17pub fn read_process_cmdline(pid: u32) -> std::io::Result<String> {
22 running_process_platform_internal::platform::process::read_process_cmdline(pid)
23}
24
25#[cfg(test)]
26mod tests {
27 use super::*;
28 use crate::observer::ObserverConfig;
29 use crate::{CommandSpec, NativeProcess, ProcessConfig, StderrMode, StdinMode};
30 use std::time::Duration;
31
32 fn fixture_program() -> String {
33 let exe = std::env::current_exe().expect("test executable path");
34 let dir = exe
35 .parent()
36 .and_then(std::path::Path::parent)
37 .expect("test binary should live in <profile>/deps/");
38 dir.join(format!(
39 "testbin-stdio-scripted{}",
40 std::env::consts::EXE_SUFFIX
41 ))
42 .to_string_lossy()
43 .into_owned()
44 }
45
46 fn config(args: Vec<String>) -> ProcessConfig {
47 ProcessConfig {
48 command: CommandSpec::Argv(args),
49 cwd: None,
50 env: None,
51 capture: false,
52 stderr_mode: StderrMode::Stdout,
53 creationflags: None,
54 create_process_group: false,
55 stdin_mode: StdinMode::Inherit,
56 nice: None,
57 address_space_limit_bytes: None,
58 }
59 }
60
61 #[test]
62 fn read_cmdline_for_pid_zero_returns_invalid_input() {
63 let err = read_process_cmdline(0).expect_err("pid 0 should be rejected");
64 assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
65 }
66
67 #[test]
68 fn read_cmdline_round_trips_known_args_from_spawned_child() {
69 let marker = "running-process-cmdline-marker";
70 let (process, _sub) = NativeProcess::with_observer(
71 config(vec![
72 fixture_program(),
73 "sleep-ms:30000".into(),
74 format!("out:{marker}"),
75 ]),
76 ObserverConfig::lifecycle(),
77 );
78 process.start().expect("spawn fixture");
79 let pid = process.pid().expect("pid");
80 std::thread::sleep(Duration::from_millis(150));
81
82 let argv = read_process_argv(pid).expect("read argv");
83 let cmdline = read_process_cmdline(pid).expect("read display string");
84 process.kill().ok();
85 process.close().ok();
86 assert!(
87 argv[0].to_string_lossy().contains("testbin-stdio-scripted"),
88 "expected fixture name in argv, got: {argv:?}"
89 );
90 assert!(
91 argv.iter()
92 .any(|argument| argument.to_string_lossy().contains(marker)),
93 "expected marker in argv, got: {argv:?}"
94 );
95 assert!(
96 cmdline.contains("testbin-stdio-scripted"),
97 "expected fixture name in display string, got: {cmdline:?}"
98 );
99 assert!(
100 cmdline.contains(marker),
101 "expected marker in display string, got: {cmdline:?}"
102 );
103 }
104
105 #[test]
106 fn argv_preserves_ambiguous_argument_boundaries() {
107 let args = vec![
108 fixture_program(),
109 "sleep-ms:30000".into(),
110 "has space".into(),
111 "quote\"".into(),
112 String::new(),
113 r"back\slash".into(),
114 ];
115 let (process, _sub) =
116 NativeProcess::with_observer(config(args.clone()), ObserverConfig::lifecycle());
117 process.start().expect("spawn fixture");
118 let pid = process.pid().expect("pid");
119 std::thread::sleep(Duration::from_millis(150));
120
121 let argv = read_process_argv(pid).expect("read argv");
122 let cmdline = read_process_cmdline(pid).expect("read display string");
123 process.kill().ok();
124 process.close().ok();
125 assert_eq!(
126 argv.iter()
127 .map(|argument| argument.to_string_lossy().into_owned())
128 .collect::<Vec<_>>(),
129 args
130 );
131 assert!(
132 cmdline.contains("has space") && cmdline.contains(r"back\slash"),
133 "native display string unexpectedly changed: {cmdline:?}"
134 );
135 }
136
137 #[test]
138 fn read_cmdline_for_unknown_pid_returns_an_os_error() {
139 let err = read_process_cmdline(0x7FFF_FFFE).expect_err("nonexistent pid");
140 assert!(
141 err.raw_os_error().is_some() || err.kind() == std::io::ErrorKind::NotFound,
142 "expected an OS-level missing-process error, got: {err}"
143 );
144 }
145}