2019-12-21 13:16:18 +02:00
|
|
|
#![unstable(feature = "process_internals", issue = "none")]
|
2017-12-17 15:21:47 +00:00
|
|
|
|
2020-08-27 13:45:01 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
2021-05-19 23:34:15 +01:00
|
|
|
use core::ffi::c_void;
|
2024-07-29 08:13:50 +10:00
|
|
|
|
2024-06-09 14:29:28 +00:00
|
|
|
use super::api::{self, WinError};
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::collections::BTreeMap;
|
2021-10-30 12:20:50 +01:00
|
|
|
use crate::env::consts::{EXE_EXTENSION, EXE_SUFFIX};
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::ffi::{OsStr, OsString};
|
|
|
|
use crate::io::{self, Error, ErrorKind};
|
2024-01-29 23:59:09 +01:00
|
|
|
use crate::num::NonZero;
|
2021-10-30 12:20:50 +01:00
|
|
|
use crate::os::windows::ffi::{OsStrExt, OsStringExt};
|
2022-05-10 02:41:19 -03:00
|
|
|
use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle};
|
2024-04-07 21:08:37 +02:00
|
|
|
use crate::os::windows::process::ProcThreadAttributeList;
|
2021-10-30 12:20:50 +01:00
|
|
|
use crate::path::{Path, PathBuf};
|
2022-09-03 14:21:38 +02:00
|
|
|
use crate::sync::Mutex;
|
2022-03-23 04:18:47 +00:00
|
|
|
use crate::sys::args::{self, Arg};
|
2024-07-14 06:28:27 +00:00
|
|
|
use crate::sys::c::{self, EXIT_FAILURE, EXIT_SUCCESS};
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::sys::fs::{File, OpenOptions};
|
|
|
|
use crate::sys::handle::Handle;
|
2022-05-17 18:46:11 -04:00
|
|
|
use crate::sys::pipe::{self, AnonPipe};
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::sys::{cvt, path, stdio};
|
2022-03-23 04:18:47 +00:00
|
|
|
use crate::sys_common::IntoInner;
|
2020-09-21 11:32:06 -07:00
|
|
|
use crate::sys_common::process::{CommandEnv, CommandEnvs};
|
2024-06-09 14:29:28 +00:00
|
|
|
use crate::{cmp, env, fmt, mem, ptr};
|
|
|
|
|
2015-02-06 09:42:57 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Command
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2021-05-19 23:34:15 +01:00
|
|
|
#[derive(Clone, Debug, Eq)]
|
2017-12-17 15:21:47 +00:00
|
|
|
#[doc(hidden)]
|
2021-05-19 23:34:15 +01:00
|
|
|
pub struct EnvKey {
|
|
|
|
os_string: OsString,
|
|
|
|
// This stores a UTF-16 encoded string to workaround the mismatch between
|
|
|
|
// Rust's OsString (WTF-8) and the Windows API string type (UTF-16).
|
|
|
|
// Normally converting on every API call is acceptable but here
|
|
|
|
// `c::CompareStringOrdinal` will be called for every use of `==`.
|
|
|
|
utf16: Vec<u16>,
|
|
|
|
}
|
|
|
|
|
2021-08-08 14:23:08 +01:00
|
|
|
impl EnvKey {
|
|
|
|
fn new<T: Into<OsString>>(key: T) -> Self {
|
|
|
|
EnvKey::from(key.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-25 12:01:08 +02:00
|
|
|
// Comparing Windows environment variable keys[1] are behaviorally the
|
2021-06-17 07:15:33 +01:00
|
|
|
// composition of two operations[2]:
|
|
|
|
//
|
|
|
|
// 1. Case-fold both strings. This is done using a language-independent
|
|
|
|
// uppercase mapping that's unique to Windows (albeit based on data from an
|
|
|
|
// older Unicode spec). It only operates on individual UTF-16 code units so
|
|
|
|
// surrogates are left unchanged. This uppercase mapping can potentially change
|
|
|
|
// between Windows versions.
|
|
|
|
//
|
|
|
|
// 2. Perform an ordinal comparison of the strings. A comparison using ordinal
|
|
|
|
// is just a comparison based on the numerical value of each UTF-16 code unit[3].
|
|
|
|
//
|
|
|
|
// Because the case-folding mapping is unique to Windows and not guaranteed to
|
|
|
|
// be stable, we ask the OS to compare the strings for us. This is done by
|
|
|
|
// calling `CompareStringOrdinal`[4] with `bIgnoreCase` set to `TRUE`.
|
|
|
|
//
|
|
|
|
// [1] https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#choosing-a-stringcomparison-member-for-your-method-call
|
|
|
|
// [2] https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#stringtoupper-and-stringtolower
|
|
|
|
// [3] https://docs.microsoft.com/en-us/dotnet/api/system.stringcomparison?view=net-5.0#System_StringComparison_Ordinal
|
|
|
|
// [4] https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringordinal
|
2021-05-19 23:34:15 +01:00
|
|
|
impl Ord for EnvKey {
|
|
|
|
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
|
|
|
unsafe {
|
|
|
|
let result = c::CompareStringOrdinal(
|
|
|
|
self.utf16.as_ptr(),
|
|
|
|
self.utf16.len() as _,
|
|
|
|
other.utf16.as_ptr(),
|
|
|
|
other.utf16.len() as _,
|
|
|
|
c::TRUE,
|
|
|
|
);
|
|
|
|
match result {
|
|
|
|
c::CSTR_LESS_THAN => cmp::Ordering::Less,
|
|
|
|
c::CSTR_EQUAL => cmp::Ordering::Equal,
|
|
|
|
c::CSTR_GREATER_THAN => cmp::Ordering::Greater,
|
|
|
|
// `CompareStringOrdinal` should never fail so long as the parameters are correct.
|
|
|
|
_ => panic!("comparing environment keys failed: {}", Error::last_os_error()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl PartialOrd for EnvKey {
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
|
|
|
Some(self.cmp(other))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl PartialEq for EnvKey {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
if self.utf16.len() != other.utf16.len() {
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
self.cmp(other) == cmp::Ordering::Equal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-08 14:23:08 +01:00
|
|
|
impl PartialOrd<str> for EnvKey {
|
|
|
|
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
|
|
|
|
Some(self.cmp(&EnvKey::new(other)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl PartialEq<str> for EnvKey {
|
|
|
|
fn eq(&self, other: &str) -> bool {
|
|
|
|
if self.os_string.len() != other.len() {
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
self.cmp(&EnvKey::new(other)) == cmp::Ordering::Equal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-12-17 15:21:47 +00:00
|
|
|
|
2021-06-17 07:15:33 +01:00
|
|
|
// Environment variable keys should preserve their original case even though
|
|
|
|
// they are compared using a caseless string mapping.
|
2019-09-03 19:32:44 -07:00
|
|
|
impl From<OsString> for EnvKey {
|
2021-05-19 23:34:15 +01:00
|
|
|
fn from(k: OsString) -> Self {
|
|
|
|
EnvKey { utf16: k.encode_wide().collect(), os_string: k }
|
2017-12-17 15:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-03 19:32:44 -07:00
|
|
|
impl From<EnvKey> for OsString {
|
|
|
|
fn from(k: EnvKey) -> Self {
|
2021-05-19 23:34:15 +01:00
|
|
|
k.os_string
|
2019-09-03 19:32:44 -07:00
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
2021-08-08 14:23:08 +01:00
|
|
|
impl From<&OsStr> for EnvKey {
|
|
|
|
fn from(k: &OsStr) -> Self {
|
|
|
|
Self::from(k.to_os_string())
|
2017-12-17 15:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-03 19:32:44 -07:00
|
|
|
impl AsRef<OsStr> for EnvKey {
|
2017-12-17 15:21:47 +00:00
|
|
|
fn as_ref(&self) -> &OsStr {
|
2021-05-19 23:34:15 +01:00
|
|
|
&self.os_string
|
2017-12-17 15:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-07 14:12:07 +02:00
|
|
|
pub(crate) fn ensure_no_nuls<T: AsRef<OsStr>>(s: T) -> io::Result<T> {
|
|
|
|
if s.as_ref().encode_wide().any(|b| b == 0) {
|
2024-11-25 13:49:25 +01:00
|
|
|
Err(io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data"))
|
2016-01-15 15:29:45 -05:00
|
|
|
} else {
|
2025-01-07 14:12:07 +02:00
|
|
|
Ok(s)
|
2016-01-15 15:29:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-06 09:42:57 -08:00
|
|
|
pub struct Command {
|
2016-01-15 15:29:45 -05:00
|
|
|
program: OsString,
|
2021-05-30 17:01:02 +01:00
|
|
|
args: Vec<Arg>,
|
2019-09-03 19:32:44 -07:00
|
|
|
env: CommandEnv,
|
2016-01-15 15:29:45 -05:00
|
|
|
cwd: Option<OsString>,
|
2016-11-30 19:44:07 -05:00
|
|
|
flags: u32,
|
2024-06-19 10:15:50 -04:00
|
|
|
show_window: Option<u16>,
|
2016-01-15 15:29:45 -05:00
|
|
|
detach: bool, // not currently exposed in std::process
|
2016-02-04 11:10:37 -08:00
|
|
|
stdin: Option<Stdio>,
|
|
|
|
stdout: Option<Stdio>,
|
|
|
|
stderr: Option<Stdio>,
|
2020-10-08 22:26:31 +00:00
|
|
|
force_quotes_enabled: bool,
|
2016-02-04 11:10:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum Stdio {
|
|
|
|
Inherit,
|
2024-07-14 07:41:14 +00:00
|
|
|
InheritSpecific { from_stdio_id: u32 },
|
2016-02-04 11:10:37 -08:00
|
|
|
Null,
|
|
|
|
MakePipe,
|
2022-05-17 18:46:11 -04:00
|
|
|
Pipe(AnonPipe),
|
2016-02-04 11:10:37 -08:00
|
|
|
Handle(Handle),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct StdioPipes {
|
|
|
|
pub stdin: Option<AnonPipe>,
|
|
|
|
pub stdout: Option<AnonPipe>,
|
|
|
|
pub stderr: Option<AnonPipe>,
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Command {
|
|
|
|
pub fn new(program: &OsStr) -> Command {
|
|
|
|
Command {
|
|
|
|
program: program.to_os_string(),
|
|
|
|
args: Vec::new(),
|
2017-12-17 15:21:47 +00:00
|
|
|
env: Default::default(),
|
2015-02-06 09:42:57 -08:00
|
|
|
cwd: None,
|
2016-11-30 19:44:07 -05:00
|
|
|
flags: 0,
|
2024-06-19 10:15:50 -04:00
|
|
|
show_window: None,
|
2015-02-06 09:42:57 -08:00
|
|
|
detach: false,
|
2016-02-04 11:10:37 -08:00
|
|
|
stdin: None,
|
|
|
|
stdout: None,
|
|
|
|
stderr: None,
|
2020-10-08 22:26:31 +00:00
|
|
|
force_quotes_enabled: false,
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn arg(&mut self, arg: &OsStr) {
|
2021-05-30 17:01:02 +01:00
|
|
|
self.args.push(Arg::Regular(arg.to_os_string()))
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2019-09-03 19:32:44 -07:00
|
|
|
pub fn env_mut(&mut self) -> &mut CommandEnv {
|
2017-12-17 15:21:47 +00:00
|
|
|
&mut self.env
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
pub fn cwd(&mut self, dir: &OsStr) {
|
|
|
|
self.cwd = Some(dir.to_os_string())
|
|
|
|
}
|
2016-02-04 11:10:37 -08:00
|
|
|
pub fn stdin(&mut self, stdin: Stdio) {
|
|
|
|
self.stdin = Some(stdin);
|
|
|
|
}
|
|
|
|
pub fn stdout(&mut self, stdout: Stdio) {
|
|
|
|
self.stdout = Some(stdout);
|
|
|
|
}
|
|
|
|
pub fn stderr(&mut self, stderr: Stdio) {
|
|
|
|
self.stderr = Some(stderr);
|
2016-01-15 15:29:45 -05:00
|
|
|
}
|
2016-11-30 21:31:47 -05:00
|
|
|
pub fn creation_flags(&mut self, flags: u32) {
|
2016-11-30 19:44:07 -05:00
|
|
|
self.flags = flags;
|
|
|
|
}
|
2024-06-19 10:15:50 -04:00
|
|
|
pub fn show_window(&mut self, cmd_show: Option<u16>) {
|
|
|
|
self.show_window = cmd_show;
|
|
|
|
}
|
2015-06-09 16:41:14 -07:00
|
|
|
|
2020-10-08 22:26:31 +00:00
|
|
|
pub fn force_quotes(&mut self, enabled: bool) {
|
|
|
|
self.force_quotes_enabled = enabled;
|
|
|
|
}
|
|
|
|
|
2021-05-30 17:01:02 +01:00
|
|
|
pub fn raw_arg(&mut self, command_str_to_append: &OsStr) {
|
|
|
|
self.args.push(Arg::Raw(command_str_to_append.to_os_string()))
|
|
|
|
}
|
|
|
|
|
2020-09-21 11:32:06 -07:00
|
|
|
pub fn get_program(&self) -> &OsStr {
|
|
|
|
&self.program
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_args(&self) -> CommandArgs<'_> {
|
|
|
|
let iter = self.args.iter();
|
|
|
|
CommandArgs { iter }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_envs(&self) -> CommandEnvs<'_> {
|
|
|
|
self.env.iter()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_current_dir(&self) -> Option<&Path> {
|
2023-11-21 23:45:52 +00:00
|
|
|
self.cwd.as_ref().map(Path::new)
|
2020-09-21 11:32:06 -07:00
|
|
|
}
|
|
|
|
|
2024-04-07 21:08:37 +02:00
|
|
|
pub fn spawn(
|
2023-08-25 16:19:16 +02:00
|
|
|
&mut self,
|
2024-04-07 21:08:37 +02:00
|
|
|
default: Stdio,
|
|
|
|
needs_stdin: bool,
|
|
|
|
) -> io::Result<(Process, StdioPipes)> {
|
|
|
|
self.spawn_with_attributes(default, needs_stdin, None)
|
2023-08-25 16:19:16 +02:00
|
|
|
}
|
|
|
|
|
2024-04-07 21:08:37 +02:00
|
|
|
pub fn spawn_with_attributes(
|
2016-02-12 10:28:03 -08:00
|
|
|
&mut self,
|
|
|
|
default: Stdio,
|
|
|
|
needs_stdin: bool,
|
2024-04-07 21:08:37 +02:00
|
|
|
proc_thread_attribute_list: Option<&ProcThreadAttributeList<'_>>,
|
2016-02-04 11:10:37 -08:00
|
|
|
) -> io::Result<(Process, StdioPipes)> {
|
2017-12-17 15:21:47 +00:00
|
|
|
let maybe_env = self.env.capture_if_changed();
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2021-10-30 12:20:50 +01:00
|
|
|
let child_paths = if let Some(env) = maybe_env.as_ref() {
|
|
|
|
env.get(&EnvKey::new("PATH")).map(|s| s.as_os_str())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2022-01-03 12:55:42 +00:00
|
|
|
let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?;
|
2024-07-18 14:35:01 +00:00
|
|
|
let has_bat_extension = |program: &[u16]| {
|
|
|
|
matches!(
|
|
|
|
// Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd"
|
|
|
|
program.len().checked_sub(4).and_then(|i| program.get(i..)),
|
|
|
|
Some([46, 98 | 66, 97 | 65, 116 | 84] | [46, 99 | 67, 109 | 77, 100 | 68])
|
|
|
|
)
|
|
|
|
};
|
|
|
|
let is_batch_file = if path::is_verbatim(&program) {
|
|
|
|
has_bat_extension(&program[..program.len() - 1])
|
|
|
|
} else {
|
|
|
|
super::fill_utf16_buf(
|
|
|
|
|buffer, size| unsafe {
|
|
|
|
// resolve the path so we can test the final file name.
|
|
|
|
c::GetFullPathNameW(program.as_ptr(), size, buffer, ptr::null_mut())
|
|
|
|
},
|
|
|
|
|program| has_bat_extension(program),
|
|
|
|
)?
|
|
|
|
};
|
2022-03-23 04:28:04 +00:00
|
|
|
let (program, mut cmd_str) = if is_batch_file {
|
|
|
|
(
|
|
|
|
command_prompt()?,
|
2022-12-02 14:07:58 +00:00
|
|
|
args::make_bat_command_line(&program, &self.args, self.force_quotes_enabled)?,
|
2022-03-23 04:28:04 +00:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
let cmd_str = make_command_line(&self.program, &self.args, self.force_quotes_enabled)?;
|
|
|
|
(program, cmd_str)
|
|
|
|
};
|
2015-04-27 13:44:20 -07:00
|
|
|
cmd_str.push(0); // add null terminator
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
// stolen from the libuv code.
|
2016-11-30 19:44:07 -05:00
|
|
|
let mut flags = self.flags | c::CREATE_UNICODE_ENVIRONMENT;
|
2016-02-04 11:10:37 -08:00
|
|
|
if self.detach {
|
2015-11-02 16:23:22 -08:00
|
|
|
flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
|
2015-04-27 13:44:20 -07:00
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2017-12-17 15:21:47 +00:00
|
|
|
let (envp, _data) = make_envp(maybe_env)?;
|
2016-03-22 22:01:37 -05:00
|
|
|
let (dirp, _data) = make_dirp(self.cwd.as_ref())?;
|
2015-04-27 13:44:20 -07:00
|
|
|
let mut pi = zeroed_process_information();
|
|
|
|
|
2016-02-04 09:59:47 -08:00
|
|
|
// Prepare all stdio handles to be inherited by the child. This
|
|
|
|
// currently involves duplicating any existing ones with the ability to
|
|
|
|
// be inherited by child processes. Note, however, that once an
|
|
|
|
// inheritable handle is created, *any* spawned child will inherit that
|
|
|
|
// handle. We only want our own child to inherit this handle, so we wrap
|
|
|
|
// the remaining portion of this spawn in a mutex.
|
|
|
|
//
|
|
|
|
// For more information, msdn also has an article about this race:
|
2021-06-23 16:26:46 -04:00
|
|
|
// https://support.microsoft.com/kb/315939
|
2022-09-03 14:21:38 +02:00
|
|
|
static CREATE_PROCESS_LOCK: Mutex<()> = Mutex::new(());
|
2021-04-28 19:11:57 +02:00
|
|
|
|
2022-09-03 14:21:38 +02:00
|
|
|
let _guard = CREATE_PROCESS_LOCK.lock();
|
2016-02-04 09:59:47 -08:00
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
let mut pipes = StdioPipes { stdin: None, stdout: None, stderr: None };
|
2016-02-12 10:28:03 -08:00
|
|
|
let null = Stdio::Null;
|
|
|
|
let default_stdin = if needs_stdin { &default } else { &null };
|
|
|
|
let stdin = self.stdin.as_ref().unwrap_or(default_stdin);
|
2016-02-04 11:10:37 -08:00
|
|
|
let stdout = self.stdout.as_ref().unwrap_or(&default);
|
|
|
|
let stderr = self.stderr.as_ref().unwrap_or(&default);
|
2016-03-22 22:01:37 -05:00
|
|
|
let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?;
|
|
|
|
let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?;
|
|
|
|
let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?;
|
2022-12-06 16:54:32 +00:00
|
|
|
|
|
|
|
let mut si = zeroed_startupinfo();
|
|
|
|
|
|
|
|
// If at least one of stdin, stdout or stderr are set (i.e. are non null)
|
|
|
|
// then set the `hStd` fields in `STARTUPINFO`.
|
2024-10-25 12:01:08 +02:00
|
|
|
// Otherwise skip this and allow the OS to apply its default behavior.
|
|
|
|
// This provides more consistent behavior between Win7 and Win8+.
|
2022-12-06 16:54:32 +00:00
|
|
|
let is_set = |stdio: &Handle| !stdio.as_raw_handle().is_null();
|
|
|
|
if is_set(&stderr) || is_set(&stdout) || is_set(&stdin) {
|
|
|
|
si.dwFlags |= c::STARTF_USESTDHANDLES;
|
|
|
|
si.hStdInput = stdin.as_raw_handle();
|
|
|
|
si.hStdOutput = stdout.as_raw_handle();
|
|
|
|
si.hStdError = stderr.as_raw_handle();
|
|
|
|
}
|
2016-02-04 09:59:47 -08:00
|
|
|
|
2024-06-19 10:15:50 -04:00
|
|
|
if let Some(cmd_show) = self.show_window {
|
|
|
|
si.dwFlags |= c::STARTF_USESHOWWINDOW;
|
|
|
|
si.wShowWindow = cmd_show;
|
|
|
|
}
|
|
|
|
|
2023-08-25 16:19:16 +02:00
|
|
|
let si_ptr: *mut c::STARTUPINFOW;
|
|
|
|
|
|
|
|
let mut si_ex;
|
|
|
|
|
2024-04-07 21:08:37 +02:00
|
|
|
if let Some(proc_thread_attribute_list) = proc_thread_attribute_list {
|
2023-08-25 16:19:16 +02:00
|
|
|
si.cb = mem::size_of::<c::STARTUPINFOEXW>() as u32;
|
|
|
|
flags |= c::EXTENDED_STARTUPINFO_PRESENT;
|
|
|
|
|
|
|
|
si_ex = c::STARTUPINFOEXW {
|
|
|
|
StartupInfo: si,
|
2024-04-07 21:08:37 +02:00
|
|
|
// SAFETY: Casting this `*const` pointer to a `*mut` pointer is "safe"
|
|
|
|
// here because windows does not internally mutate the attribute list.
|
|
|
|
// Ideally this should be reflected in the interface of the `windows-sys` crate.
|
|
|
|
lpAttributeList: proc_thread_attribute_list.as_ptr().cast::<c_void>().cast_mut(),
|
2023-08-25 16:19:16 +02:00
|
|
|
};
|
2024-09-25 17:03:20 -07:00
|
|
|
si_ptr = (&raw mut si_ex) as _;
|
2023-08-25 16:19:16 +02:00
|
|
|
} else {
|
2024-07-14 07:41:14 +00:00
|
|
|
si.cb = mem::size_of::<c::STARTUPINFOW>() as u32;
|
2024-09-25 17:03:20 -07:00
|
|
|
si_ptr = (&raw mut si) as _;
|
2023-08-25 16:19:16 +02:00
|
|
|
}
|
|
|
|
|
2016-03-22 22:01:37 -05:00
|
|
|
unsafe {
|
2015-11-02 16:23:22 -08:00
|
|
|
cvt(c::CreateProcessW(
|
2021-10-30 12:20:50 +01:00
|
|
|
program.as_ptr(),
|
2015-11-02 16:23:22 -08:00
|
|
|
cmd_str.as_mut_ptr(),
|
|
|
|
ptr::null_mut(),
|
|
|
|
ptr::null_mut(),
|
|
|
|
c::TRUE,
|
|
|
|
flags,
|
|
|
|
envp,
|
|
|
|
dirp,
|
2023-08-25 16:19:16 +02:00
|
|
|
si_ptr,
|
2015-11-02 16:23:22 -08:00
|
|
|
&mut pi,
|
|
|
|
))
|
2016-03-22 22:01:37 -05:00
|
|
|
}?;
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2021-06-30 21:44:30 -07:00
|
|
|
unsafe {
|
2022-05-10 02:41:19 -03:00
|
|
|
Ok((
|
|
|
|
Process {
|
|
|
|
handle: Handle::from_raw_handle(pi.hProcess),
|
|
|
|
main_thread_handle: Handle::from_raw_handle(pi.hThread),
|
|
|
|
},
|
|
|
|
pipes,
|
|
|
|
))
|
2021-06-30 21:44:30 -07:00
|
|
|
}
|
2016-02-04 11:10:37 -08:00
|
|
|
}
|
2022-12-08 18:22:33 +05:30
|
|
|
|
|
|
|
pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
|
|
|
|
let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
|
|
|
|
crate::sys_common::process::wait_with_output(proc, pipes)
|
|
|
|
}
|
2016-02-04 11:10:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Debug for Command {
|
2019-03-01 09:34:11 +01:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2021-07-09 14:20:01 +01:00
|
|
|
self.program.fmt(f)?;
|
2016-02-04 11:10:37 -08:00
|
|
|
for arg in &self.args {
|
2021-07-09 14:20:01 +01:00
|
|
|
f.write_str(" ")?;
|
|
|
|
match arg {
|
|
|
|
Arg::Regular(s) => s.fmt(f),
|
|
|
|
Arg::Raw(s) => f.write_str(&s.to_string_lossy()),
|
|
|
|
}?;
|
2016-02-04 11:10:37 -08:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-30 12:20:50 +01:00
|
|
|
// Resolve `exe_path` to the executable name.
|
|
|
|
//
|
|
|
|
// * If the path is simply a file name then use the paths given by `search_paths` to find the executable.
|
|
|
|
// * Otherwise use the `exe_path` as given.
|
|
|
|
//
|
|
|
|
// This function may also append `.exe` to the name. The rationale for doing so is as follows:
|
|
|
|
//
|
|
|
|
// It is a very strong convention that Windows executables have the `exe` extension.
|
|
|
|
// In Rust, it is common to omit this extension.
|
|
|
|
// Therefore this functions first assumes `.exe` was intended.
|
|
|
|
// It falls back to the plain file name if a full path is given and the extension is omitted
|
|
|
|
// or if only a file name is given and it already contains an extension.
|
2022-01-03 12:55:42 +00:00
|
|
|
fn resolve_exe<'a>(
|
|
|
|
exe_path: &'a OsStr,
|
|
|
|
parent_paths: impl FnOnce() -> Option<OsString>,
|
|
|
|
child_paths: Option<&OsStr>,
|
2022-02-17 13:17:19 +00:00
|
|
|
) -> io::Result<Vec<u16>> {
|
2021-10-30 12:20:50 +01:00
|
|
|
// Early return if there is no filename.
|
|
|
|
if exe_path.is_empty() || path::has_trailing_slash(exe_path) {
|
2025-02-10 16:34:13 -08:00
|
|
|
return Err(io::const_error!(io::ErrorKind::InvalidInput, "program path has no file name"));
|
2021-10-30 12:20:50 +01:00
|
|
|
}
|
|
|
|
// Test if the file name has the `exe` extension.
|
|
|
|
// This does a case-insensitive `ends_with`.
|
|
|
|
let has_exe_suffix = if exe_path.len() >= EXE_SUFFIX.len() {
|
2023-09-01 19:33:16 -05:00
|
|
|
exe_path.as_encoded_bytes()[exe_path.len() - EXE_SUFFIX.len()..]
|
2021-10-30 12:20:50 +01:00
|
|
|
.eq_ignore_ascii_case(EXE_SUFFIX.as_bytes())
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
};
|
|
|
|
|
|
|
|
// If `exe_path` is an absolute path or a sub-path then don't search `PATH` for it.
|
|
|
|
if !path::is_file_name(exe_path) {
|
|
|
|
if has_exe_suffix {
|
|
|
|
// The application name is a path to a `.exe` file.
|
|
|
|
// Let `CreateProcessW` figure out if it exists or not.
|
2022-12-02 14:07:58 +00:00
|
|
|
return args::to_user_path(Path::new(exe_path));
|
2021-10-30 12:20:50 +01:00
|
|
|
}
|
|
|
|
let mut path = PathBuf::from(exe_path);
|
|
|
|
|
|
|
|
// Append `.exe` if not already there.
|
|
|
|
path = path::append_suffix(path, EXE_SUFFIX.as_ref());
|
2022-02-17 13:17:19 +00:00
|
|
|
if let Some(path) = program_exists(&path) {
|
2021-10-30 12:20:50 +01:00
|
|
|
return Ok(path);
|
|
|
|
} else {
|
|
|
|
// It's ok to use `set_extension` here because the intent is to
|
|
|
|
// remove the extension that was just added.
|
|
|
|
path.set_extension("");
|
2022-12-02 14:07:58 +00:00
|
|
|
return args::to_user_path(&path);
|
2021-10-30 12:20:50 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ensure_no_nuls(exe_path)?;
|
|
|
|
// From the `CreateProcessW` docs:
|
|
|
|
// > If the file name does not contain an extension, .exe is appended.
|
|
|
|
// Note that this rule only applies when searching paths.
|
2023-09-01 19:33:16 -05:00
|
|
|
let has_extension = exe_path.as_encoded_bytes().contains(&b'.');
|
2021-10-30 12:20:50 +01:00
|
|
|
|
|
|
|
// Search the directories given by `search_paths`.
|
2022-01-03 12:55:42 +00:00
|
|
|
let result = search_paths(parent_paths, child_paths, |mut path| {
|
2023-11-21 23:21:56 +00:00
|
|
|
path.push(exe_path);
|
2021-10-30 12:20:50 +01:00
|
|
|
if !has_extension {
|
|
|
|
path.set_extension(EXE_EXTENSION);
|
|
|
|
}
|
2022-02-17 13:17:19 +00:00
|
|
|
program_exists(&path)
|
2021-10-30 12:20:50 +01:00
|
|
|
});
|
|
|
|
if let Some(path) = result {
|
|
|
|
return Ok(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If we get here then the executable cannot be found.
|
2024-11-25 13:49:25 +01:00
|
|
|
Err(io::const_error!(io::ErrorKind::NotFound, "program not found"))
|
2021-10-30 12:20:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Calls `f` for every path that should be used to find an executable.
|
|
|
|
// Returns once `f` returns the path to an executable or all paths have been searched.
|
2022-01-03 12:55:42 +00:00
|
|
|
fn search_paths<Paths, Exists>(
|
|
|
|
parent_paths: Paths,
|
|
|
|
child_paths: Option<&OsStr>,
|
|
|
|
mut exists: Exists,
|
2022-02-17 13:17:19 +00:00
|
|
|
) -> Option<Vec<u16>>
|
2021-10-30 12:20:50 +01:00
|
|
|
where
|
2022-01-03 12:55:42 +00:00
|
|
|
Paths: FnOnce() -> Option<OsString>,
|
2022-02-17 13:17:19 +00:00
|
|
|
Exists: FnMut(PathBuf) -> Option<Vec<u16>>,
|
2021-10-30 12:20:50 +01:00
|
|
|
{
|
|
|
|
// 1. Child paths
|
2024-10-25 12:01:08 +02:00
|
|
|
// This is for consistency with Rust's historic behavior.
|
2021-10-30 12:20:50 +01:00
|
|
|
if let Some(paths) = child_paths {
|
|
|
|
for path in env::split_paths(paths).filter(|p| !p.as_os_str().is_empty()) {
|
2022-01-03 12:55:42 +00:00
|
|
|
if let Some(path) = exists(path) {
|
2021-10-30 12:20:50 +01:00
|
|
|
return Some(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. Application path
|
|
|
|
if let Ok(mut app_path) = env::current_exe() {
|
|
|
|
app_path.pop();
|
2022-01-03 12:55:42 +00:00
|
|
|
if let Some(path) = exists(app_path) {
|
2021-10-30 12:20:50 +01:00
|
|
|
return Some(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3 & 4. System paths
|
|
|
|
// SAFETY: This uses `fill_utf16_buf` to safely call the OS functions.
|
|
|
|
unsafe {
|
|
|
|
if let Ok(Some(path)) = super::fill_utf16_buf(
|
|
|
|
|buf, size| c::GetSystemDirectoryW(buf, size),
|
2022-01-03 12:55:42 +00:00
|
|
|
|buf| exists(PathBuf::from(OsString::from_wide(buf))),
|
2021-10-30 12:20:50 +01:00
|
|
|
) {
|
|
|
|
return Some(path);
|
|
|
|
}
|
|
|
|
#[cfg(not(target_vendor = "uwp"))]
|
|
|
|
{
|
|
|
|
if let Ok(Some(path)) = super::fill_utf16_buf(
|
|
|
|
|buf, size| c::GetWindowsDirectoryW(buf, size),
|
2022-01-03 12:55:42 +00:00
|
|
|
|buf| exists(PathBuf::from(OsString::from_wide(buf))),
|
2021-10-30 12:20:50 +01:00
|
|
|
) {
|
|
|
|
return Some(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 5. Parent paths
|
2022-01-03 12:55:42 +00:00
|
|
|
if let Some(parent_paths) = parent_paths() {
|
2021-10-30 12:20:50 +01:00
|
|
|
for path in env::split_paths(&parent_paths).filter(|p| !p.as_os_str().is_empty()) {
|
2022-01-03 12:55:42 +00:00
|
|
|
if let Some(path) = exists(path) {
|
2021-10-30 12:20:50 +01:00
|
|
|
return Some(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2024-07-15 12:26:30 +01:00
|
|
|
/// Checks if a file exists without following symlinks.
|
2022-02-17 13:17:19 +00:00
|
|
|
fn program_exists(path: &Path) -> Option<Vec<u16>> {
|
2022-02-14 12:20:40 +00:00
|
|
|
unsafe {
|
2022-12-02 14:07:58 +00:00
|
|
|
let path = args::to_user_path(path).ok()?;
|
2022-02-17 13:17:19 +00:00
|
|
|
// Getting attributes using `GetFileAttributesW` does not follow symlinks
|
|
|
|
// and it will almost always be successful if the link exists.
|
|
|
|
// There are some exceptions for special system files (e.g. the pagefile)
|
|
|
|
// but these are not executable.
|
|
|
|
if c::GetFileAttributesW(path.as_ptr()) == c::INVALID_FILE_ATTRIBUTES {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(path)
|
|
|
|
}
|
2022-02-14 12:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
impl Stdio {
|
2024-07-14 07:41:14 +00:00
|
|
|
fn to_handle(&self, stdio_id: u32, pipe: &mut Option<AnonPipe>) -> io::Result<Handle> {
|
2023-01-03 15:01:19 +00:00
|
|
|
let use_stdio_id = |stdio_id| match stdio::get_handle(stdio_id) {
|
|
|
|
Ok(io) => unsafe {
|
|
|
|
let io = Handle::from_raw_handle(io);
|
|
|
|
let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS);
|
2024-07-10 21:03:25 -05:00
|
|
|
let _ = io.into_raw_handle(); // Don't close the handle
|
2023-01-03 15:01:19 +00:00
|
|
|
ret
|
2016-02-04 11:10:37 -08:00
|
|
|
},
|
2023-01-03 15:01:19 +00:00
|
|
|
// If no stdio handle is available, then propagate the null value.
|
|
|
|
Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) },
|
|
|
|
};
|
|
|
|
match *self {
|
|
|
|
Stdio::Inherit => use_stdio_id(stdio_id),
|
2021-11-08 18:38:04 +00:00
|
|
|
Stdio::InheritSpecific { from_stdio_id } => use_stdio_id(from_stdio_id),
|
2016-02-04 11:10:37 -08:00
|
|
|
|
|
|
|
Stdio::MakePipe => {
|
2022-05-17 18:46:11 -04:00
|
|
|
let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
|
|
|
|
let pipes = pipe::anon_pipe(ours_readable, true)?;
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
*pipe = Some(pipes.ours);
|
|
|
|
Ok(pipes.theirs.into_handle())
|
2016-02-04 11:10:37 -08:00
|
|
|
}
|
|
|
|
|
2022-05-17 18:46:11 -04:00
|
|
|
Stdio::Pipe(ref source) => {
|
2022-04-08 11:35:29 +01:00
|
|
|
let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
|
|
|
|
pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::into_handle)
|
|
|
|
}
|
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
Stdio::Handle(ref handle) => handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS),
|
|
|
|
|
|
|
|
// Open up a reference to NUL with appropriate read/write
|
|
|
|
// permissions as well as the ability to be inherited to child
|
|
|
|
// processes (as this is about to be inherited).
|
|
|
|
Stdio::Null => {
|
|
|
|
let size = mem::size_of::<c::SECURITY_ATTRIBUTES>();
|
|
|
|
let mut sa = c::SECURITY_ATTRIBUTES {
|
2024-07-14 07:41:14 +00:00
|
|
|
nLength: size as u32,
|
2016-02-04 11:10:37 -08:00
|
|
|
lpSecurityDescriptor: ptr::null_mut(),
|
|
|
|
bInheritHandle: 1,
|
|
|
|
};
|
|
|
|
let mut opts = OpenOptions::new();
|
|
|
|
opts.read(stdio_id == c::STD_INPUT_HANDLE);
|
|
|
|
opts.write(stdio_id != c::STD_INPUT_HANDLE);
|
|
|
|
opts.security_attributes(&mut sa);
|
2023-11-19 16:24:39 +00:00
|
|
|
File::open(Path::new(r"\\.\NUL"), &opts).map(|file| file.into_inner())
|
2016-02-04 11:10:37 -08:00
|
|
|
}
|
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2016-02-04 11:10:37 -08:00
|
|
|
}
|
|
|
|
|
2017-06-06 15:42:55 -07:00
|
|
|
impl From<AnonPipe> for Stdio {
|
|
|
|
fn from(pipe: AnonPipe) -> Stdio {
|
2022-05-17 18:46:11 -04:00
|
|
|
Stdio::Pipe(pipe)
|
2017-06-06 15:42:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<File> for Stdio {
|
|
|
|
fn from(file: File) -> Stdio {
|
2021-06-30 21:44:30 -07:00
|
|
|
Stdio::Handle(file.into_inner())
|
2017-06-06 15:42:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-08 18:38:04 +00:00
|
|
|
impl From<io::Stdout> for Stdio {
|
|
|
|
fn from(_: io::Stdout) -> Stdio {
|
|
|
|
Stdio::InheritSpecific { from_stdio_id: c::STD_OUTPUT_HANDLE }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<io::Stderr> for Stdio {
|
|
|
|
fn from(_: io::Stderr) -> Stdio {
|
|
|
|
Stdio::InheritSpecific { from_stdio_id: c::STD_ERROR_HANDLE }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Processes
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/// A value representing a child process.
|
|
|
|
///
|
|
|
|
/// The lifetime of this value is linked to the lifetime of the actual
|
|
|
|
/// process - the Process destructor calls self.finish() which waits
|
|
|
|
/// for the process to terminate.
|
|
|
|
pub struct Process {
|
|
|
|
handle: Handle,
|
2022-05-10 02:41:19 -03:00
|
|
|
main_thread_handle: Handle,
|
2016-02-04 11:10:37 -08:00
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
impl Process {
|
2016-02-03 18:09:35 -08:00
|
|
|
pub fn kill(&mut self) -> io::Result<()> {
|
2023-06-13 20:32:31 +01:00
|
|
|
let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) };
|
|
|
|
if result == c::FALSE {
|
2024-06-09 14:29:28 +00:00
|
|
|
let error = api::get_last_error();
|
2023-06-13 20:32:31 +01:00
|
|
|
// TerminateProcess returns ERROR_ACCESS_DENIED if the process has already been
|
|
|
|
// terminated (by us, or for any other reason). So check if the process was actually
|
|
|
|
// terminated, and if so, do not return an error.
|
2024-06-09 14:29:28 +00:00
|
|
|
if error != WinError::ACCESS_DENIED || self.try_wait().is_err() {
|
|
|
|
return Err(crate::io::Error::from_raw_os_error(error.code as i32));
|
2023-06-13 20:32:31 +01:00
|
|
|
}
|
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2015-04-16 09:44:05 -07:00
|
|
|
pub fn id(&self) -> u32 {
|
2023-11-21 23:40:46 +00:00
|
|
|
unsafe { c::GetProcessId(self.handle.as_raw_handle()) }
|
2015-04-16 09:44:05 -07:00
|
|
|
}
|
|
|
|
|
2022-05-10 02:41:19 -03:00
|
|
|
pub fn main_thread_handle(&self) -> BorrowedHandle<'_> {
|
|
|
|
self.main_thread_handle.as_handle()
|
|
|
|
}
|
|
|
|
|
2016-02-03 18:09:35 -08:00
|
|
|
pub fn wait(&mut self) -> io::Result<ExitStatus> {
|
2015-02-06 09:42:57 -08:00
|
|
|
unsafe {
|
2021-06-30 21:44:30 -07:00
|
|
|
let res = c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE);
|
2015-11-02 16:23:22 -08:00
|
|
|
if res != c::WAIT_OBJECT_0 {
|
2015-10-29 13:45:56 -07:00
|
|
|
return Err(Error::last_os_error());
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2015-10-29 13:45:56 -07:00
|
|
|
let mut status = 0;
|
2021-06-30 21:44:30 -07:00
|
|
|
cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?;
|
2015-10-29 13:45:56 -07:00
|
|
|
Ok(ExitStatus(status))
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
2015-05-12 11:03:49 -07:00
|
|
|
|
2017-02-03 17:39:41 -05:00
|
|
|
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
|
2017-01-05 22:47:09 -08:00
|
|
|
unsafe {
|
2021-06-30 21:44:30 -07:00
|
|
|
match c::WaitForSingleObject(self.handle.as_raw_handle(), 0) {
|
2017-01-05 22:47:09 -08:00
|
|
|
c::WAIT_OBJECT_0 => {}
|
|
|
|
c::WAIT_TIMEOUT => {
|
2017-02-03 17:39:41 -05:00
|
|
|
return Ok(None);
|
2017-01-05 22:47:09 -08:00
|
|
|
}
|
|
|
|
_ => return Err(io::Error::last_os_error()),
|
|
|
|
}
|
|
|
|
let mut status = 0;
|
2021-06-30 21:44:30 -07:00
|
|
|
cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?;
|
2017-02-03 17:39:41 -05:00
|
|
|
Ok(Some(ExitStatus(status)))
|
2017-01-05 22:47:09 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-12 11:03:49 -07:00
|
|
|
pub fn handle(&self) -> &Handle {
|
|
|
|
&self.handle
|
|
|
|
}
|
2015-07-15 23:31:24 -07:00
|
|
|
|
|
|
|
pub fn into_handle(self) -> Handle {
|
|
|
|
self.handle
|
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
2023-01-03 20:47:07 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
|
2024-07-14 07:41:14 +00:00
|
|
|
pub struct ExitStatus(u32);
|
2015-02-06 09:42:57 -08:00
|
|
|
|
|
|
|
impl ExitStatus {
|
2021-03-03 12:17:16 +00:00
|
|
|
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
|
2024-07-14 06:28:27 +00:00
|
|
|
match NonZero::<u32>::try_from(self.0) {
|
2021-03-03 12:17:16 +00:00
|
|
|
/* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
|
|
|
|
/* was zero, couldn't convert */ Err(_) => Ok(()),
|
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
pub fn code(&self) -> Option<i32> {
|
2015-10-29 13:45:56 -07:00
|
|
|
Some(self.0 as i32)
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-14 07:41:14 +00:00
|
|
|
/// Converts a raw `u32` to a type-safe `ExitStatus` by wrapping it without copying.
|
|
|
|
impl From<u32> for ExitStatus {
|
|
|
|
fn from(u: u32) -> ExitStatus {
|
2016-04-26 15:23:46 -07:00
|
|
|
ExitStatus(u)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-06 09:42:57 -08:00
|
|
|
impl fmt::Display for ExitStatus {
|
2019-03-01 09:34:11 +01:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2019-01-09 09:35:09 -08:00
|
|
|
// Windows exit codes with the high bit set typically mean some form of
|
|
|
|
// unhandled exception or warning. In this scenario printing the exit
|
|
|
|
// code in decimal doesn't always make sense because it's a very large
|
|
|
|
// and somewhat gibberish number. The hex code is a bit more
|
|
|
|
// recognizable and easier to search for, so print that.
|
|
|
|
if self.0 & 0x80000000 != 0 {
|
|
|
|
write!(f, "exit code: {:#x}", self.0)
|
|
|
|
} else {
|
|
|
|
write!(f, "exit code: {}", self.0)
|
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-03 12:17:16 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
2024-07-14 06:28:27 +00:00
|
|
|
pub struct ExitStatusError(NonZero<u32>);
|
2021-03-03 12:17:16 +00:00
|
|
|
|
|
|
|
impl Into<ExitStatus> for ExitStatusError {
|
|
|
|
fn into(self) -> ExitStatus {
|
|
|
|
ExitStatus(self.0.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExitStatusError {
|
2024-01-29 23:59:09 +01:00
|
|
|
pub fn code(self) -> Option<NonZero<i32>> {
|
2021-03-03 12:17:16 +00:00
|
|
|
Some((u32::from(self.0) as i32).try_into().unwrap())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-03 18:29:30 -08:00
|
|
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
2024-07-14 07:41:14 +00:00
|
|
|
pub struct ExitCode(u32);
|
2018-03-03 18:29:30 -08:00
|
|
|
|
|
|
|
impl ExitCode {
|
|
|
|
pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _);
|
|
|
|
pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _);
|
|
|
|
|
2018-04-05 11:07:19 -07:00
|
|
|
#[inline]
|
2018-03-03 18:29:30 -08:00
|
|
|
pub fn as_i32(&self) -> i32 {
|
|
|
|
self.0 as i32
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 14:07:27 -08:00
|
|
|
impl From<u8> for ExitCode {
|
|
|
|
fn from(code: u8) -> Self {
|
2024-07-14 07:41:14 +00:00
|
|
|
ExitCode(u32::from(code))
|
2022-01-28 14:07:27 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-09 15:29:58 +02:00
|
|
|
impl From<u32> for ExitCode {
|
|
|
|
fn from(code: u32) -> Self {
|
2024-07-14 07:41:14 +00:00
|
|
|
ExitCode(u32::from(code))
|
2022-06-09 15:29:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-06 17:27:47 +01:00
|
|
|
fn zeroed_startupinfo() -> c::STARTUPINFOW {
|
|
|
|
c::STARTUPINFOW {
|
2015-02-06 09:42:57 -08:00
|
|
|
cb: 0,
|
|
|
|
lpReserved: ptr::null_mut(),
|
|
|
|
lpDesktop: ptr::null_mut(),
|
|
|
|
lpTitle: ptr::null_mut(),
|
|
|
|
dwX: 0,
|
|
|
|
dwY: 0,
|
|
|
|
dwXSize: 0,
|
|
|
|
dwYSize: 0,
|
|
|
|
dwXCountChars: 0,
|
2023-04-06 17:27:47 +01:00
|
|
|
dwYCountChars: 0,
|
2015-02-06 09:42:57 -08:00
|
|
|
dwFillAttribute: 0,
|
|
|
|
dwFlags: 0,
|
|
|
|
wShowWindow: 0,
|
|
|
|
cbReserved2: 0,
|
|
|
|
lpReserved2: ptr::null_mut(),
|
2022-10-24 02:34:48 +01:00
|
|
|
hStdInput: ptr::null_mut(),
|
|
|
|
hStdOutput: ptr::null_mut(),
|
|
|
|
hStdError: ptr::null_mut(),
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-02 16:23:22 -08:00
|
|
|
fn zeroed_process_information() -> c::PROCESS_INFORMATION {
|
|
|
|
c::PROCESS_INFORMATION {
|
2015-02-06 09:42:57 -08:00
|
|
|
hProcess: ptr::null_mut(),
|
|
|
|
hThread: ptr::null_mut(),
|
|
|
|
dwProcessId: 0,
|
|
|
|
dwThreadId: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-15 15:29:45 -05:00
|
|
|
// Produces a wide string *without terminating null*; returns an error if
|
|
|
|
// `prog` or any of the `args` contain a nul.
|
2022-03-23 04:28:04 +00:00
|
|
|
fn make_command_line(argv0: &OsStr, args: &[Arg], force_quotes: bool) -> io::Result<Vec<u16>> {
|
2015-04-25 15:35:22 -04:00
|
|
|
// Encode the command and arguments in a command line string such
|
|
|
|
// that the spawned process may recover them using CommandLineToArgvW.
|
2015-02-06 09:42:57 -08:00
|
|
|
let mut cmd: Vec<u16> = Vec::new();
|
2021-12-16 17:21:34 +00:00
|
|
|
|
2022-01-05 17:05:58 +00:00
|
|
|
// Always quote the program name so CreateProcess to avoid ambiguity when
|
|
|
|
// the child process parses its arguments.
|
|
|
|
// Note that quotes aren't escaped here because they can't be used in arg0.
|
|
|
|
// But that's ok because file paths can't contain quotes.
|
|
|
|
cmd.push(b'"' as u16);
|
2022-03-23 04:28:04 +00:00
|
|
|
cmd.extend(argv0.encode_wide());
|
2022-01-05 17:05:58 +00:00
|
|
|
cmd.push(b'"' as u16);
|
|
|
|
|
2015-02-06 09:42:57 -08:00
|
|
|
for arg in args {
|
|
|
|
cmd.push(' ' as u16);
|
2022-03-23 04:18:47 +00:00
|
|
|
args::append_arg(&mut cmd, arg, force_quotes)?;
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2022-03-23 04:18:47 +00:00
|
|
|
Ok(cmd)
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
2022-03-23 04:28:04 +00:00
|
|
|
// Get `cmd.exe` for use with bat scripts, encoded as a UTF-16 string.
|
|
|
|
fn command_prompt() -> io::Result<Vec<u16>> {
|
|
|
|
let mut system: Vec<u16> = super::fill_utf16_buf(
|
|
|
|
|buf, size| unsafe { c::GetSystemDirectoryW(buf, size) },
|
|
|
|
|buf| buf.into(),
|
|
|
|
)?;
|
|
|
|
system.extend("\\cmd.exe".encode_utf16().chain([0]));
|
|
|
|
Ok(system)
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
2019-09-03 19:32:44 -07:00
|
|
|
fn make_envp(maybe_env: Option<BTreeMap<EnvKey, OsString>>) -> io::Result<(*mut c_void, Vec<u16>)> {
|
2015-02-06 09:42:57 -08:00
|
|
|
// On Windows we pass an "environment block" which is not a char**, but
|
|
|
|
// rather a concatenation of null-terminated k=v\0 sequences, with a final
|
|
|
|
// \0 to terminate.
|
2017-12-17 15:21:47 +00:00
|
|
|
if let Some(env) = maybe_env {
|
|
|
|
let mut blk = Vec::new();
|
|
|
|
|
2021-06-19 09:46:34 +01:00
|
|
|
// If there are no environment variables to set then signal this by
|
|
|
|
// pushing a null.
|
|
|
|
if env.is_empty() {
|
|
|
|
blk.push(0);
|
|
|
|
}
|
|
|
|
|
2017-12-17 15:21:47 +00:00
|
|
|
for (k, v) in env {
|
2021-05-19 23:34:15 +01:00
|
|
|
ensure_no_nuls(k.os_string)?;
|
|
|
|
blk.extend(k.utf16);
|
2017-12-17 15:21:47 +00:00
|
|
|
blk.push('=' as u16);
|
|
|
|
blk.extend(ensure_no_nuls(v)?.encode_wide());
|
2015-02-06 09:42:57 -08:00
|
|
|
blk.push(0);
|
|
|
|
}
|
2017-12-17 15:21:47 +00:00
|
|
|
blk.push(0);
|
|
|
|
Ok((blk.as_mut_ptr() as *mut c_void, blk))
|
|
|
|
} else {
|
|
|
|
Ok((ptr::null_mut(), Vec::new()))
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-15 15:29:45 -05:00
|
|
|
fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
|
2015-02-06 09:42:57 -08:00
|
|
|
match d {
|
2015-04-27 13:44:20 -07:00
|
|
|
Some(dir) => {
|
2016-03-22 22:01:37 -05:00
|
|
|
let mut dir_str: Vec<u16> = ensure_no_nuls(dir)?.encode_wide().collect();
|
2015-04-27 13:44:20 -07:00
|
|
|
dir_str.push(0);
|
2016-01-15 15:29:45 -05:00
|
|
|
Ok((dir_str.as_ptr(), dir_str))
|
2015-04-27 13:44:20 -07:00
|
|
|
}
|
2016-01-15 15:29:45 -05:00
|
|
|
None => Ok((ptr::null(), Vec::new())),
|
2015-04-27 13:44:20 -07:00
|
|
|
}
|
|
|
|
}
|
2020-09-21 11:32:06 -07:00
|
|
|
|
|
|
|
pub struct CommandArgs<'a> {
|
2021-05-30 17:01:02 +01:00
|
|
|
iter: crate::slice::Iter<'a, Arg>,
|
2020-09-21 11:32:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for CommandArgs<'a> {
|
|
|
|
type Item = &'a OsStr;
|
|
|
|
fn next(&mut self) -> Option<&'a OsStr> {
|
2021-05-30 17:01:02 +01:00
|
|
|
self.iter.next().map(|arg| match arg {
|
|
|
|
Arg::Regular(s) | Arg::Raw(s) => s.as_ref(),
|
|
|
|
})
|
2020-09-21 11:32:06 -07:00
|
|
|
}
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
self.iter.size_hint()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> ExactSizeIterator for CommandArgs<'a> {
|
|
|
|
fn len(&self) -> usize {
|
|
|
|
self.iter.len()
|
|
|
|
}
|
|
|
|
fn is_empty(&self) -> bool {
|
|
|
|
self.iter.is_empty()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> fmt::Debug for CommandArgs<'a> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
f.debug_list().entries(self.iter.clone()).finish()
|
|
|
|
}
|
|
|
|
}
|