]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | #![unstable(feature = "process_internals", issue = "none")] |
ff7c6d11 | 2 | |
1b1a35ee XL |
3 | #[cfg(test)] |
4 | mod tests; | |
5 | ||
136023e0 | 6 | use crate::cmp; |
532ac7d7 | 7 | use crate::collections::BTreeMap; |
532ac7d7 | 8 | use crate::env; |
3c0e092e | 9 | use crate::env::consts::{EXE_EXTENSION, EXE_SUFFIX}; |
dfeec247 | 10 | use crate::ffi::{OsStr, OsString}; |
532ac7d7 | 11 | use crate::fmt; |
532ac7d7 XL |
12 | use crate::io::{self, Error, ErrorKind}; |
13 | use crate::mem; | |
17df50a5 | 14 | use crate::num::NonZeroI32; |
3c0e092e | 15 | use crate::os::windows::ffi::{OsStrExt, OsStringExt}; |
04454e1e | 16 | use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle}; |
3c0e092e | 17 | use crate::path::{Path, PathBuf}; |
532ac7d7 | 18 | use crate::ptr; |
04454e1e | 19 | use crate::sys::args::{self, Arg}; |
532ac7d7 | 20 | use crate::sys::c; |
17df50a5 | 21 | use crate::sys::c::NonZeroDWORD; |
04454e1e | 22 | use crate::sys::cvt; |
dfeec247 | 23 | use crate::sys::fs::{File, OpenOptions}; |
532ac7d7 | 24 | use crate::sys::handle::Handle; |
3c0e092e | 25 | use crate::sys::path; |
532ac7d7 XL |
26 | use crate::sys::pipe::{self, AnonPipe}; |
27 | use crate::sys::stdio; | |
cdc7bbd5 | 28 | use crate::sys_common::mutex::StaticMutex; |
1b1a35ee | 29 | use crate::sys_common::process::{CommandEnv, CommandEnvs}; |
04454e1e | 30 | use crate::sys_common::IntoInner; |
532ac7d7 | 31 | |
dfeec247 | 32 | use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS}; |
85aaf69f SL |
33 | |
34 | //////////////////////////////////////////////////////////////////////////////// | |
35 | // Command | |
36 | //////////////////////////////////////////////////////////////////////////////// | |
37 | ||
136023e0 | 38 | #[derive(Clone, Debug, Eq)] |
ff7c6d11 | 39 | #[doc(hidden)] |
136023e0 XL |
40 | pub struct EnvKey { |
41 | os_string: OsString, | |
42 | // This stores a UTF-16 encoded string to workaround the mismatch between | |
43 | // Rust's OsString (WTF-8) and the Windows API string type (UTF-16). | |
44 | // Normally converting on every API call is acceptable but here | |
45 | // `c::CompareStringOrdinal` will be called for every use of `==`. | |
46 | utf16: Vec<u16>, | |
47 | } | |
48 | ||
49 | impl EnvKey { | |
50 | fn new<T: Into<OsString>>(key: T) -> Self { | |
51 | EnvKey::from(key.into()) | |
52 | } | |
53 | } | |
54 | ||
55 | // Comparing Windows environment variable keys[1] are behaviourally the | |
56 | // composition of two operations[2]: | |
57 | // | |
58 | // 1. Case-fold both strings. This is done using a language-independent | |
59 | // uppercase mapping that's unique to Windows (albeit based on data from an | |
60 | // older Unicode spec). It only operates on individual UTF-16 code units so | |
61 | // surrogates are left unchanged. This uppercase mapping can potentially change | |
62 | // between Windows versions. | |
63 | // | |
64 | // 2. Perform an ordinal comparison of the strings. A comparison using ordinal | |
65 | // is just a comparison based on the numerical value of each UTF-16 code unit[3]. | |
66 | // | |
67 | // Because the case-folding mapping is unique to Windows and not guaranteed to | |
68 | // be stable, we ask the OS to compare the strings for us. This is done by | |
69 | // calling `CompareStringOrdinal`[4] with `bIgnoreCase` set to `TRUE`. | |
70 | // | |
71 | // [1] https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#choosing-a-stringcomparison-member-for-your-method-call | |
72 | // [2] https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#stringtoupper-and-stringtolower | |
73 | // [3] https://docs.microsoft.com/en-us/dotnet/api/system.stringcomparison?view=net-5.0#System_StringComparison_Ordinal | |
74 | // [4] https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringordinal | |
75 | impl Ord for EnvKey { | |
76 | fn cmp(&self, other: &Self) -> cmp::Ordering { | |
77 | unsafe { | |
78 | let result = c::CompareStringOrdinal( | |
79 | self.utf16.as_ptr(), | |
80 | self.utf16.len() as _, | |
81 | other.utf16.as_ptr(), | |
82 | other.utf16.len() as _, | |
83 | c::TRUE, | |
84 | ); | |
85 | match result { | |
86 | c::CSTR_LESS_THAN => cmp::Ordering::Less, | |
87 | c::CSTR_EQUAL => cmp::Ordering::Equal, | |
88 | c::CSTR_GREATER_THAN => cmp::Ordering::Greater, | |
89 | // `CompareStringOrdinal` should never fail so long as the parameters are correct. | |
90 | _ => panic!("comparing environment keys failed: {}", Error::last_os_error()), | |
91 | } | |
92 | } | |
93 | } | |
94 | } | |
95 | impl PartialOrd for EnvKey { | |
96 | fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { | |
97 | Some(self.cmp(other)) | |
98 | } | |
99 | } | |
100 | impl PartialEq for EnvKey { | |
101 | fn eq(&self, other: &Self) -> bool { | |
102 | if self.utf16.len() != other.utf16.len() { | |
103 | false | |
104 | } else { | |
105 | self.cmp(other) == cmp::Ordering::Equal | |
106 | } | |
107 | } | |
108 | } | |
109 | impl PartialOrd<str> for EnvKey { | |
110 | fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> { | |
111 | Some(self.cmp(&EnvKey::new(other))) | |
112 | } | |
113 | } | |
114 | impl PartialEq<str> for EnvKey { | |
115 | fn eq(&self, other: &str) -> bool { | |
116 | if self.os_string.len() != other.len() { | |
117 | false | |
118 | } else { | |
119 | self.cmp(&EnvKey::new(other)) == cmp::Ordering::Equal | |
120 | } | |
121 | } | |
122 | } | |
ff7c6d11 | 123 | |
136023e0 XL |
124 | // Environment variable keys should preserve their original case even though |
125 | // they are compared using a caseless string mapping. | |
e1599b0c | 126 | impl From<OsString> for EnvKey { |
136023e0 XL |
127 | fn from(k: OsString) -> Self { |
128 | EnvKey { utf16: k.encode_wide().collect(), os_string: k } | |
ff7c6d11 XL |
129 | } |
130 | } | |
131 | ||
e1599b0c | 132 | impl From<EnvKey> for OsString { |
dfeec247 | 133 | fn from(k: EnvKey) -> Self { |
136023e0 | 134 | k.os_string |
dfeec247 | 135 | } |
85aaf69f SL |
136 | } |
137 | ||
136023e0 XL |
138 | impl From<&OsStr> for EnvKey { |
139 | fn from(k: &OsStr) -> Self { | |
140 | Self::from(k.to_os_string()) | |
dfeec247 | 141 | } |
ff7c6d11 XL |
142 | } |
143 | ||
e1599b0c | 144 | impl AsRef<OsStr> for EnvKey { |
dfeec247 | 145 | fn as_ref(&self) -> &OsStr { |
136023e0 | 146 | &self.os_string |
dfeec247 | 147 | } |
ff7c6d11 XL |
148 | } |
149 | ||
04454e1e | 150 | pub(crate) fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> { |
7453a54e | 151 | if str.as_ref().encode_wide().any(|b| b == 0) { |
5099ac24 | 152 | Err(io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data")) |
7453a54e SL |
153 | } else { |
154 | Ok(str) | |
155 | } | |
156 | } | |
157 | ||
85aaf69f | 158 | pub struct Command { |
7453a54e | 159 | program: OsString, |
136023e0 | 160 | args: Vec<Arg>, |
e1599b0c | 161 | env: CommandEnv, |
7453a54e | 162 | cwd: Option<OsString>, |
476ff2be | 163 | flags: u32, |
7453a54e SL |
164 | detach: bool, // not currently exposed in std::process |
165 | stdin: Option<Stdio>, | |
166 | stdout: Option<Stdio>, | |
167 | stderr: Option<Stdio>, | |
6a06907d | 168 | force_quotes_enabled: bool, |
7453a54e SL |
169 | } |
170 | ||
171 | pub enum Stdio { | |
172 | Inherit, | |
173 | Null, | |
174 | MakePipe, | |
04454e1e | 175 | Pipe(AnonPipe), |
7453a54e SL |
176 | Handle(Handle), |
177 | } | |
178 | ||
179 | pub struct StdioPipes { | |
180 | pub stdin: Option<AnonPipe>, | |
181 | pub stdout: Option<AnonPipe>, | |
182 | pub stderr: Option<AnonPipe>, | |
85aaf69f SL |
183 | } |
184 | ||
185 | impl Command { | |
186 | pub fn new(program: &OsStr) -> Command { | |
187 | Command { | |
188 | program: program.to_os_string(), | |
189 | args: Vec::new(), | |
ff7c6d11 | 190 | env: Default::default(), |
85aaf69f | 191 | cwd: None, |
476ff2be | 192 | flags: 0, |
85aaf69f | 193 | detach: false, |
7453a54e SL |
194 | stdin: None, |
195 | stdout: None, | |
196 | stderr: None, | |
6a06907d | 197 | force_quotes_enabled: false, |
85aaf69f SL |
198 | } |
199 | } | |
200 | ||
201 | pub fn arg(&mut self, arg: &OsStr) { | |
136023e0 | 202 | self.args.push(Arg::Regular(arg.to_os_string())) |
85aaf69f | 203 | } |
e1599b0c | 204 | pub fn env_mut(&mut self) -> &mut CommandEnv { |
ff7c6d11 | 205 | &mut self.env |
85aaf69f SL |
206 | } |
207 | pub fn cwd(&mut self, dir: &OsStr) { | |
208 | self.cwd = Some(dir.to_os_string()) | |
209 | } | |
7453a54e SL |
210 | pub fn stdin(&mut self, stdin: Stdio) { |
211 | self.stdin = Some(stdin); | |
212 | } | |
213 | pub fn stdout(&mut self, stdout: Stdio) { | |
214 | self.stdout = Some(stdout); | |
215 | } | |
216 | pub fn stderr(&mut self, stderr: Stdio) { | |
217 | self.stderr = Some(stderr); | |
218 | } | |
476ff2be SL |
219 | pub fn creation_flags(&mut self, flags: u32) { |
220 | self.flags = flags; | |
221 | } | |
62682a34 | 222 | |
6a06907d XL |
223 | pub fn force_quotes(&mut self, enabled: bool) { |
224 | self.force_quotes_enabled = enabled; | |
225 | } | |
226 | ||
136023e0 XL |
227 | pub fn raw_arg(&mut self, command_str_to_append: &OsStr) { |
228 | self.args.push(Arg::Raw(command_str_to_append.to_os_string())) | |
229 | } | |
230 | ||
1b1a35ee XL |
231 | pub fn get_program(&self) -> &OsStr { |
232 | &self.program | |
233 | } | |
234 | ||
235 | pub fn get_args(&self) -> CommandArgs<'_> { | |
236 | let iter = self.args.iter(); | |
237 | CommandArgs { iter } | |
238 | } | |
239 | ||
240 | pub fn get_envs(&self) -> CommandEnvs<'_> { | |
241 | self.env.iter() | |
242 | } | |
243 | ||
244 | pub fn get_current_dir(&self) -> Option<&Path> { | |
245 | self.cwd.as_ref().map(|cwd| Path::new(cwd)) | |
246 | } | |
247 | ||
dfeec247 XL |
248 | pub fn spawn( |
249 | &mut self, | |
250 | default: Stdio, | |
251 | needs_stdin: bool, | |
252 | ) -> io::Result<(Process, StdioPipes)> { | |
ff7c6d11 | 253 | let maybe_env = self.env.capture_if_changed(); |
85aaf69f | 254 | |
bd371182 | 255 | let mut si = zeroed_startupinfo(); |
92a42be0 SL |
256 | si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD; |
257 | si.dwFlags = c::STARTF_USESTDHANDLES; | |
85aaf69f | 258 | |
3c0e092e XL |
259 | let child_paths = if let Some(env) = maybe_env.as_ref() { |
260 | env.get(&EnvKey::new("PATH")).map(|s| s.as_os_str()) | |
261 | } else { | |
262 | None | |
263 | }; | |
a2a8927a | 264 | let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?; |
04454e1e FG |
265 | // Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd" |
266 | let is_batch_file = matches!( | |
267 | program.len().checked_sub(5).and_then(|i| program.get(i..)), | |
268 | Some([46, 98 | 66, 97 | 65, 116 | 84, 0] | [46, 99 | 67, 109 | 77, 100 | 68, 0]) | |
269 | ); | |
270 | let (program, mut cmd_str) = if is_batch_file { | |
271 | ( | |
272 | command_prompt()?, | |
273 | args::make_bat_command_line( | |
274 | &args::to_user_path(program)?, | |
275 | &self.args, | |
276 | self.force_quotes_enabled, | |
277 | )?, | |
278 | ) | |
279 | } else { | |
280 | let cmd_str = make_command_line(&self.program, &self.args, self.force_quotes_enabled)?; | |
281 | (program, cmd_str) | |
282 | }; | |
bd371182 | 283 | cmd_str.push(0); // add null terminator |
85aaf69f | 284 | |
bd371182 | 285 | // stolen from the libuv code. |
476ff2be | 286 | let mut flags = self.flags | c::CREATE_UNICODE_ENVIRONMENT; |
7453a54e | 287 | if self.detach { |
92a42be0 | 288 | flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP; |
bd371182 | 289 | } |
85aaf69f | 290 | |
ff7c6d11 | 291 | let (envp, _data) = make_envp(maybe_env)?; |
54a0048b | 292 | let (dirp, _data) = make_dirp(self.cwd.as_ref())?; |
bd371182 | 293 | let mut pi = zeroed_process_information(); |
bd371182 | 294 | |
7453a54e SL |
295 | // Prepare all stdio handles to be inherited by the child. This |
296 | // currently involves duplicating any existing ones with the ability to | |
297 | // be inherited by child processes. Note, however, that once an | |
298 | // inheritable handle is created, *any* spawned child will inherit that | |
299 | // handle. We only want our own child to inherit this handle, so we wrap | |
300 | // the remaining portion of this spawn in a mutex. | |
301 | // | |
302 | // For more information, msdn also has an article about this race: | |
136023e0 | 303 | // https://support.microsoft.com/kb/315939 |
cdc7bbd5 XL |
304 | static CREATE_PROCESS_LOCK: StaticMutex = StaticMutex::new(); |
305 | ||
306 | let _guard = unsafe { CREATE_PROCESS_LOCK.lock() }; | |
7453a54e | 307 | |
dfeec247 | 308 | let mut pipes = StdioPipes { stdin: None, stdout: None, stderr: None }; |
54a0048b | 309 | let null = Stdio::Null; |
dfeec247 | 310 | let default_stdin = if needs_stdin { &default } else { &null }; |
54a0048b | 311 | let stdin = self.stdin.as_ref().unwrap_or(default_stdin); |
7453a54e SL |
312 | let stdout = self.stdout.as_ref().unwrap_or(&default); |
313 | let stderr = self.stderr.as_ref().unwrap_or(&default); | |
54a0048b | 314 | let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?; |
dfeec247 XL |
315 | let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?; |
316 | let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?; | |
94222f64 XL |
317 | si.hStdInput = stdin.as_raw_handle(); |
318 | si.hStdOutput = stdout.as_raw_handle(); | |
319 | si.hStdError = stderr.as_raw_handle(); | |
7453a54e | 320 | |
54a0048b | 321 | unsafe { |
dfeec247 | 322 | cvt(c::CreateProcessW( |
3c0e092e | 323 | program.as_ptr(), |
dfeec247 XL |
324 | cmd_str.as_mut_ptr(), |
325 | ptr::null_mut(), | |
326 | ptr::null_mut(), | |
327 | c::TRUE, | |
328 | flags, | |
329 | envp, | |
330 | dirp, | |
331 | &mut si, | |
332 | &mut pi, | |
333 | )) | |
54a0048b | 334 | }?; |
85aaf69f | 335 | |
94222f64 | 336 | unsafe { |
04454e1e FG |
337 | Ok(( |
338 | Process { | |
339 | handle: Handle::from_raw_handle(pi.hProcess), | |
340 | main_thread_handle: Handle::from_raw_handle(pi.hThread), | |
341 | }, | |
342 | pipes, | |
343 | )) | |
94222f64 | 344 | } |
85aaf69f | 345 | } |
7453a54e SL |
346 | } |
347 | ||
348 | impl fmt::Debug for Command { | |
532ac7d7 | 349 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
136023e0 | 350 | self.program.fmt(f)?; |
7453a54e | 351 | for arg in &self.args { |
136023e0 XL |
352 | f.write_str(" ")?; |
353 | match arg { | |
354 | Arg::Regular(s) => s.fmt(f), | |
355 | Arg::Raw(s) => f.write_str(&s.to_string_lossy()), | |
356 | }?; | |
7453a54e SL |
357 | } |
358 | Ok(()) | |
359 | } | |
360 | } | |
361 | ||
3c0e092e XL |
362 | // Resolve `exe_path` to the executable name. |
363 | // | |
364 | // * If the path is simply a file name then use the paths given by `search_paths` to find the executable. | |
365 | // * Otherwise use the `exe_path` as given. | |
366 | // | |
367 | // This function may also append `.exe` to the name. The rationale for doing so is as follows: | |
368 | // | |
369 | // It is a very strong convention that Windows executables have the `exe` extension. | |
370 | // In Rust, it is common to omit this extension. | |
371 | // Therefore this functions first assumes `.exe` was intended. | |
372 | // It falls back to the plain file name if a full path is given and the extension is omitted | |
373 | // or if only a file name is given and it already contains an extension. | |
a2a8927a XL |
374 | fn resolve_exe<'a>( |
375 | exe_path: &'a OsStr, | |
376 | parent_paths: impl FnOnce() -> Option<OsString>, | |
377 | child_paths: Option<&OsStr>, | |
04454e1e | 378 | ) -> io::Result<Vec<u16>> { |
3c0e092e XL |
379 | // Early return if there is no filename. |
380 | if exe_path.is_empty() || path::has_trailing_slash(exe_path) { | |
5099ac24 | 381 | return Err(io::const_io_error!( |
3c0e092e | 382 | io::ErrorKind::InvalidInput, |
5099ac24 | 383 | "program path has no file name", |
3c0e092e XL |
384 | )); |
385 | } | |
386 | // Test if the file name has the `exe` extension. | |
387 | // This does a case-insensitive `ends_with`. | |
388 | let has_exe_suffix = if exe_path.len() >= EXE_SUFFIX.len() { | |
389 | exe_path.bytes()[exe_path.len() - EXE_SUFFIX.len()..] | |
390 | .eq_ignore_ascii_case(EXE_SUFFIX.as_bytes()) | |
391 | } else { | |
392 | false | |
393 | }; | |
394 | ||
395 | // If `exe_path` is an absolute path or a sub-path then don't search `PATH` for it. | |
396 | if !path::is_file_name(exe_path) { | |
397 | if has_exe_suffix { | |
398 | // The application name is a path to a `.exe` file. | |
399 | // Let `CreateProcessW` figure out if it exists or not. | |
04454e1e | 400 | return path::maybe_verbatim(Path::new(exe_path)); |
3c0e092e XL |
401 | } |
402 | let mut path = PathBuf::from(exe_path); | |
403 | ||
404 | // Append `.exe` if not already there. | |
405 | path = path::append_suffix(path, EXE_SUFFIX.as_ref()); | |
04454e1e | 406 | if let Some(path) = program_exists(&path) { |
3c0e092e XL |
407 | return Ok(path); |
408 | } else { | |
409 | // It's ok to use `set_extension` here because the intent is to | |
410 | // remove the extension that was just added. | |
411 | path.set_extension(""); | |
04454e1e | 412 | return path::maybe_verbatim(&path); |
3c0e092e XL |
413 | } |
414 | } else { | |
415 | ensure_no_nuls(exe_path)?; | |
416 | // From the `CreateProcessW` docs: | |
417 | // > If the file name does not contain an extension, .exe is appended. | |
418 | // Note that this rule only applies when searching paths. | |
419 | let has_extension = exe_path.bytes().contains(&b'.'); | |
420 | ||
421 | // Search the directories given by `search_paths`. | |
a2a8927a | 422 | let result = search_paths(parent_paths, child_paths, |mut path| { |
3c0e092e XL |
423 | path.push(&exe_path); |
424 | if !has_extension { | |
425 | path.set_extension(EXE_EXTENSION); | |
426 | } | |
04454e1e | 427 | program_exists(&path) |
3c0e092e XL |
428 | }); |
429 | if let Some(path) = result { | |
430 | return Ok(path); | |
431 | } | |
432 | } | |
433 | // If we get here then the executable cannot be found. | |
5099ac24 | 434 | Err(io::const_io_error!(io::ErrorKind::NotFound, "program not found")) |
3c0e092e XL |
435 | } |
436 | ||
437 | // Calls `f` for every path that should be used to find an executable. | |
438 | // Returns once `f` returns the path to an executable or all paths have been searched. | |
a2a8927a XL |
439 | fn search_paths<Paths, Exists>( |
440 | parent_paths: Paths, | |
441 | child_paths: Option<&OsStr>, | |
442 | mut exists: Exists, | |
04454e1e | 443 | ) -> Option<Vec<u16>> |
3c0e092e | 444 | where |
a2a8927a | 445 | Paths: FnOnce() -> Option<OsString>, |
04454e1e | 446 | Exists: FnMut(PathBuf) -> Option<Vec<u16>>, |
3c0e092e XL |
447 | { |
448 | // 1. Child paths | |
449 | // This is for consistency with Rust's historic behaviour. | |
450 | if let Some(paths) = child_paths { | |
451 | for path in env::split_paths(paths).filter(|p| !p.as_os_str().is_empty()) { | |
a2a8927a | 452 | if let Some(path) = exists(path) { |
3c0e092e XL |
453 | return Some(path); |
454 | } | |
455 | } | |
456 | } | |
457 | ||
458 | // 2. Application path | |
459 | if let Ok(mut app_path) = env::current_exe() { | |
460 | app_path.pop(); | |
a2a8927a | 461 | if let Some(path) = exists(app_path) { |
3c0e092e XL |
462 | return Some(path); |
463 | } | |
464 | } | |
465 | ||
466 | // 3 & 4. System paths | |
467 | // SAFETY: This uses `fill_utf16_buf` to safely call the OS functions. | |
468 | unsafe { | |
469 | if let Ok(Some(path)) = super::fill_utf16_buf( | |
470 | |buf, size| c::GetSystemDirectoryW(buf, size), | |
a2a8927a | 471 | |buf| exists(PathBuf::from(OsString::from_wide(buf))), |
3c0e092e XL |
472 | ) { |
473 | return Some(path); | |
474 | } | |
475 | #[cfg(not(target_vendor = "uwp"))] | |
476 | { | |
477 | if let Ok(Some(path)) = super::fill_utf16_buf( | |
478 | |buf, size| c::GetWindowsDirectoryW(buf, size), | |
a2a8927a | 479 | |buf| exists(PathBuf::from(OsString::from_wide(buf))), |
3c0e092e XL |
480 | ) { |
481 | return Some(path); | |
482 | } | |
483 | } | |
484 | } | |
485 | ||
486 | // 5. Parent paths | |
a2a8927a | 487 | if let Some(parent_paths) = parent_paths() { |
3c0e092e | 488 | for path in env::split_paths(&parent_paths).filter(|p| !p.as_os_str().is_empty()) { |
a2a8927a | 489 | if let Some(path) = exists(path) { |
3c0e092e XL |
490 | return Some(path); |
491 | } | |
492 | } | |
493 | } | |
494 | None | |
495 | } | |
496 | ||
5099ac24 | 497 | /// Check if a file exists without following symlinks. |
04454e1e | 498 | fn program_exists(path: &Path) -> Option<Vec<u16>> { |
5099ac24 | 499 | unsafe { |
04454e1e FG |
500 | let path = path::maybe_verbatim(path).ok()?; |
501 | // Getting attributes using `GetFileAttributesW` does not follow symlinks | |
502 | // and it will almost always be successful if the link exists. | |
503 | // There are some exceptions for special system files (e.g. the pagefile) | |
504 | // but these are not executable. | |
505 | if c::GetFileAttributesW(path.as_ptr()) == c::INVALID_FILE_ATTRIBUTES { | |
506 | None | |
507 | } else { | |
508 | Some(path) | |
509 | } | |
5099ac24 FG |
510 | } |
511 | } | |
512 | ||
7453a54e | 513 | impl Stdio { |
dfeec247 | 514 | fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option<AnonPipe>) -> io::Result<Handle> { |
7453a54e SL |
515 | match *self { |
516 | // If no stdio handle is available, then inherit means that it | |
517 | // should still be unavailable so propagate the | |
518 | // INVALID_HANDLE_VALUE. | |
dfeec247 | 519 | Stdio::Inherit => match stdio::get_handle(stdio_id) { |
94222f64 XL |
520 | Ok(io) => unsafe { |
521 | let io = Handle::from_raw_handle(io); | |
dfeec247 | 522 | let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); |
94222f64 | 523 | io.into_raw_handle(); |
dfeec247 | 524 | ret |
94222f64 XL |
525 | }, |
526 | Err(..) => unsafe { Ok(Handle::from_raw_handle(c::INVALID_HANDLE_VALUE)) }, | |
dfeec247 | 527 | }, |
7453a54e SL |
528 | |
529 | Stdio::MakePipe => { | |
476ff2be | 530 | let ours_readable = stdio_id != c::STD_INPUT_HANDLE; |
416331ca | 531 | let pipes = pipe::anon_pipe(ours_readable, true)?; |
476ff2be | 532 | *pipe = Some(pipes.ours); |
476ff2be | 533 | Ok(pipes.theirs.into_handle()) |
7453a54e SL |
534 | } |
535 | ||
04454e1e FG |
536 | Stdio::Pipe(ref source) => { |
537 | let ours_readable = stdio_id != c::STD_INPUT_HANDLE; | |
538 | pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::into_handle) | |
539 | } | |
540 | ||
dfeec247 | 541 | Stdio::Handle(ref handle) => handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS), |
7453a54e SL |
542 | |
543 | // Open up a reference to NUL with appropriate read/write | |
544 | // permissions as well as the ability to be inherited to child | |
545 | // processes (as this is about to be inherited). | |
546 | Stdio::Null => { | |
547 | let size = mem::size_of::<c::SECURITY_ATTRIBUTES>(); | |
548 | let mut sa = c::SECURITY_ATTRIBUTES { | |
549 | nLength: size as c::DWORD, | |
550 | lpSecurityDescriptor: ptr::null_mut(), | |
551 | bInheritHandle: 1, | |
552 | }; | |
553 | let mut opts = OpenOptions::new(); | |
554 | opts.read(stdio_id == c::STD_INPUT_HANDLE); | |
555 | opts.write(stdio_id != c::STD_INPUT_HANDLE); | |
556 | opts.security_attributes(&mut sa); | |
94222f64 | 557 | File::open(Path::new("NUL"), &opts).map(|file| file.into_inner()) |
7453a54e SL |
558 | } |
559 | } | |
560 | } | |
561 | } | |
562 | ||
041b39d2 XL |
563 | impl From<AnonPipe> for Stdio { |
564 | fn from(pipe: AnonPipe) -> Stdio { | |
04454e1e | 565 | Stdio::Pipe(pipe) |
041b39d2 XL |
566 | } |
567 | } | |
568 | ||
569 | impl From<File> for Stdio { | |
570 | fn from(file: File) -> Stdio { | |
94222f64 | 571 | Stdio::Handle(file.into_inner()) |
041b39d2 XL |
572 | } |
573 | } | |
574 | ||
7453a54e SL |
575 | //////////////////////////////////////////////////////////////////////////////// |
576 | // Processes | |
577 | //////////////////////////////////////////////////////////////////////////////// | |
578 | ||
579 | /// A value representing a child process. | |
580 | /// | |
581 | /// The lifetime of this value is linked to the lifetime of the actual | |
582 | /// process - the Process destructor calls self.finish() which waits | |
583 | /// for the process to terminate. | |
584 | pub struct Process { | |
585 | handle: Handle, | |
04454e1e | 586 | main_thread_handle: Handle, |
7453a54e SL |
587 | } |
588 | ||
589 | impl Process { | |
590 | pub fn kill(&mut self) -> io::Result<()> { | |
94222f64 | 591 | cvt(unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) })?; |
85aaf69f SL |
592 | Ok(()) |
593 | } | |
594 | ||
62682a34 | 595 | pub fn id(&self) -> u32 { |
94222f64 | 596 | unsafe { c::GetProcessId(self.handle.as_raw_handle()) as u32 } |
62682a34 SL |
597 | } |
598 | ||
04454e1e FG |
599 | pub fn main_thread_handle(&self) -> BorrowedHandle<'_> { |
600 | self.main_thread_handle.as_handle() | |
601 | } | |
602 | ||
7453a54e | 603 | pub fn wait(&mut self) -> io::Result<ExitStatus> { |
85aaf69f | 604 | unsafe { |
94222f64 | 605 | let res = c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE); |
92a42be0 | 606 | if res != c::WAIT_OBJECT_0 { |
dfeec247 | 607 | return Err(Error::last_os_error()); |
85aaf69f | 608 | } |
92a42be0 | 609 | let mut status = 0; |
94222f64 | 610 | cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?; |
92a42be0 | 611 | Ok(ExitStatus(status)) |
85aaf69f SL |
612 | } |
613 | } | |
62682a34 | 614 | |
8bb4bdeb | 615 | pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { |
32a655c1 | 616 | unsafe { |
94222f64 | 617 | match c::WaitForSingleObject(self.handle.as_raw_handle(), 0) { |
32a655c1 SL |
618 | c::WAIT_OBJECT_0 => {} |
619 | c::WAIT_TIMEOUT => { | |
8bb4bdeb | 620 | return Ok(None); |
32a655c1 SL |
621 | } |
622 | _ => return Err(io::Error::last_os_error()), | |
623 | } | |
624 | let mut status = 0; | |
94222f64 | 625 | cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?; |
8bb4bdeb | 626 | Ok(Some(ExitStatus(status))) |
32a655c1 SL |
627 | } |
628 | } | |
629 | ||
dfeec247 XL |
630 | pub fn handle(&self) -> &Handle { |
631 | &self.handle | |
632 | } | |
c1a9b12d | 633 | |
dfeec247 XL |
634 | pub fn into_handle(self) -> Handle { |
635 | self.handle | |
636 | } | |
85aaf69f SL |
637 | } |
638 | ||
639 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
92a42be0 | 640 | pub struct ExitStatus(c::DWORD); |
85aaf69f SL |
641 | |
642 | impl ExitStatus { | |
17df50a5 XL |
643 | pub fn exit_ok(&self) -> Result<(), ExitStatusError> { |
644 | match NonZeroDWORD::try_from(self.0) { | |
645 | /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), | |
646 | /* was zero, couldn't convert */ Err(_) => Ok(()), | |
647 | } | |
85aaf69f SL |
648 | } |
649 | pub fn code(&self) -> Option<i32> { | |
92a42be0 | 650 | Some(self.0 as i32) |
85aaf69f SL |
651 | } |
652 | } | |
653 | ||
3dfed10e | 654 | /// Converts a raw `c::DWORD` to a type-safe `ExitStatus` by wrapping it without copying. |
a7813a04 XL |
655 | impl From<c::DWORD> for ExitStatus { |
656 | fn from(u: c::DWORD) -> ExitStatus { | |
657 | ExitStatus(u) | |
658 | } | |
659 | } | |
660 | ||
85aaf69f | 661 | impl fmt::Display for ExitStatus { |
532ac7d7 | 662 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
0731742a XL |
663 | // Windows exit codes with the high bit set typically mean some form of |
664 | // unhandled exception or warning. In this scenario printing the exit | |
665 | // code in decimal doesn't always make sense because it's a very large | |
666 | // and somewhat gibberish number. The hex code is a bit more | |
667 | // recognizable and easier to search for, so print that. | |
668 | if self.0 & 0x80000000 != 0 { | |
669 | write!(f, "exit code: {:#x}", self.0) | |
670 | } else { | |
671 | write!(f, "exit code: {}", self.0) | |
672 | } | |
85aaf69f SL |
673 | } |
674 | } | |
675 | ||
17df50a5 XL |
676 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
677 | pub struct ExitStatusError(c::NonZeroDWORD); | |
678 | ||
679 | impl Into<ExitStatus> for ExitStatusError { | |
680 | fn into(self) -> ExitStatus { | |
681 | ExitStatus(self.0.into()) | |
682 | } | |
683 | } | |
684 | ||
685 | impl ExitStatusError { | |
686 | pub fn code(self) -> Option<NonZeroI32> { | |
687 | Some((u32::from(self.0) as i32).try_into().unwrap()) | |
688 | } | |
689 | } | |
690 | ||
0531ce1d XL |
691 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
692 | pub struct ExitCode(c::DWORD); | |
693 | ||
694 | impl ExitCode { | |
695 | pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); | |
696 | pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); | |
697 | ||
83c7162d | 698 | #[inline] |
0531ce1d XL |
699 | pub fn as_i32(&self) -> i32 { |
700 | self.0 as i32 | |
701 | } | |
702 | } | |
703 | ||
5099ac24 FG |
704 | impl From<u8> for ExitCode { |
705 | fn from(code: u8) -> Self { | |
706 | ExitCode(c::DWORD::from(code)) | |
707 | } | |
708 | } | |
709 | ||
92a42be0 SL |
710 | fn zeroed_startupinfo() -> c::STARTUPINFO { |
711 | c::STARTUPINFO { | |
85aaf69f SL |
712 | cb: 0, |
713 | lpReserved: ptr::null_mut(), | |
714 | lpDesktop: ptr::null_mut(), | |
715 | lpTitle: ptr::null_mut(), | |
716 | dwX: 0, | |
717 | dwY: 0, | |
718 | dwXSize: 0, | |
719 | dwYSize: 0, | |
720 | dwXCountChars: 0, | |
721 | dwYCountCharts: 0, | |
722 | dwFillAttribute: 0, | |
723 | dwFlags: 0, | |
724 | wShowWindow: 0, | |
725 | cbReserved2: 0, | |
726 | lpReserved2: ptr::null_mut(), | |
92a42be0 SL |
727 | hStdInput: c::INVALID_HANDLE_VALUE, |
728 | hStdOutput: c::INVALID_HANDLE_VALUE, | |
729 | hStdError: c::INVALID_HANDLE_VALUE, | |
85aaf69f SL |
730 | } |
731 | } | |
732 | ||
92a42be0 SL |
733 | fn zeroed_process_information() -> c::PROCESS_INFORMATION { |
734 | c::PROCESS_INFORMATION { | |
85aaf69f SL |
735 | hProcess: ptr::null_mut(), |
736 | hThread: ptr::null_mut(), | |
737 | dwProcessId: 0, | |
dfeec247 | 738 | dwThreadId: 0, |
85aaf69f SL |
739 | } |
740 | } | |
741 | ||
7453a54e SL |
742 | // Produces a wide string *without terminating null*; returns an error if |
743 | // `prog` or any of the `args` contain a nul. | |
04454e1e | 744 | fn make_command_line(argv0: &OsStr, args: &[Arg], force_quotes: bool) -> io::Result<Vec<u16>> { |
d9579d0f AL |
745 | // Encode the command and arguments in a command line string such |
746 | // that the spawned process may recover them using CommandLineToArgvW. | |
85aaf69f | 747 | let mut cmd: Vec<u16> = Vec::new(); |
3c0e092e | 748 | |
04454e1e FG |
749 | // Always quote the program name so CreateProcess to avoid ambiguity when |
750 | // the child process parses its arguments. | |
751 | // Note that quotes aren't escaped here because they can't be used in arg0. | |
752 | // But that's ok because file paths can't contain quotes. | |
753 | cmd.push(b'"' as u16); | |
754 | cmd.extend(argv0.encode_wide()); | |
755 | cmd.push(b'"' as u16); | |
756 | ||
85aaf69f SL |
757 | for arg in args { |
758 | cmd.push(' ' as u16); | |
04454e1e | 759 | args::append_arg(&mut cmd, arg, force_quotes)?; |
85aaf69f | 760 | } |
04454e1e FG |
761 | Ok(cmd) |
762 | } | |
763 | ||
764 | // Get `cmd.exe` for use with bat scripts, encoded as a UTF-16 string. | |
765 | fn command_prompt() -> io::Result<Vec<u16>> { | |
766 | let mut system: Vec<u16> = super::fill_utf16_buf( | |
767 | |buf, size| unsafe { c::GetSystemDirectoryW(buf, size) }, | |
768 | |buf| buf.into(), | |
769 | )?; | |
770 | system.extend("\\cmd.exe".encode_utf16().chain([0])); | |
771 | Ok(system) | |
85aaf69f SL |
772 | } |
773 | ||
dfeec247 | 774 | fn make_envp(maybe_env: Option<BTreeMap<EnvKey, OsString>>) -> io::Result<(*mut c_void, Vec<u16>)> { |
85aaf69f SL |
775 | // On Windows we pass an "environment block" which is not a char**, but |
776 | // rather a concatenation of null-terminated k=v\0 sequences, with a final | |
777 | // \0 to terminate. | |
ff7c6d11 XL |
778 | if let Some(env) = maybe_env { |
779 | let mut blk = Vec::new(); | |
780 | ||
136023e0 XL |
781 | // If there are no environment variables to set then signal this by |
782 | // pushing a null. | |
783 | if env.is_empty() { | |
784 | blk.push(0); | |
785 | } | |
786 | ||
ff7c6d11 | 787 | for (k, v) in env { |
136023e0 XL |
788 | ensure_no_nuls(k.os_string)?; |
789 | blk.extend(k.utf16); | |
ff7c6d11 XL |
790 | blk.push('=' as u16); |
791 | blk.extend(ensure_no_nuls(v)?.encode_wide()); | |
85aaf69f | 792 | blk.push(0); |
85aaf69f | 793 | } |
ff7c6d11 XL |
794 | blk.push(0); |
795 | Ok((blk.as_mut_ptr() as *mut c_void, blk)) | |
796 | } else { | |
797 | Ok((ptr::null_mut(), Vec::new())) | |
85aaf69f SL |
798 | } |
799 | } | |
800 | ||
7453a54e | 801 | fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> { |
85aaf69f | 802 | match d { |
bd371182 | 803 | Some(dir) => { |
54a0048b | 804 | let mut dir_str: Vec<u16> = ensure_no_nuls(dir)?.encode_wide().collect(); |
bd371182 | 805 | dir_str.push(0); |
7453a54e | 806 | Ok((dir_str.as_ptr(), dir_str)) |
dfeec247 XL |
807 | } |
808 | None => Ok((ptr::null(), Vec::new())), | |
85aaf69f SL |
809 | } |
810 | } | |
811 | ||
1b1a35ee | 812 | pub struct CommandArgs<'a> { |
136023e0 | 813 | iter: crate::slice::Iter<'a, Arg>, |
1b1a35ee XL |
814 | } |
815 | ||
816 | impl<'a> Iterator for CommandArgs<'a> { | |
817 | type Item = &'a OsStr; | |
818 | fn next(&mut self) -> Option<&'a OsStr> { | |
136023e0 XL |
819 | self.iter.next().map(|arg| match arg { |
820 | Arg::Regular(s) | Arg::Raw(s) => s.as_ref(), | |
821 | }) | |
1b1a35ee XL |
822 | } |
823 | fn size_hint(&self) -> (usize, Option<usize>) { | |
824 | self.iter.size_hint() | |
825 | } | |
826 | } | |
85aaf69f | 827 | |
1b1a35ee XL |
828 | impl<'a> ExactSizeIterator for CommandArgs<'a> { |
829 | fn len(&self) -> usize { | |
830 | self.iter.len() | |
831 | } | |
832 | fn is_empty(&self) -> bool { | |
833 | self.iter.is_empty() | |
834 | } | |
835 | } | |
836 | ||
837 | impl<'a> fmt::Debug for CommandArgs<'a> { | |
838 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
839 | f.debug_list().entries(self.iter.clone()).finish() | |
85aaf69f SL |
840 | } |
841 | } |