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 crate::cmp;
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::collections::BTreeMap;
|
2021-03-03 12:17:16 +00:00
|
|
|
use crate::convert::{TryFrom, TryInto};
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::env;
|
2021-10-30 12:20:50 +01:00
|
|
|
use crate::env::consts::{EXE_EXTENSION, EXE_SUFFIX};
|
2019-12-22 17:42:04 -05:00
|
|
|
use crate::ffi::{OsStr, OsString};
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::fmt;
|
|
|
|
use crate::io::{self, Error, ErrorKind};
|
|
|
|
use crate::mem;
|
2021-03-03 12:17:16 +00:00
|
|
|
use crate::num::NonZeroI32;
|
2021-10-30 12:20:50 +01:00
|
|
|
use crate::os::windows::ffi::{OsStrExt, OsStringExt};
|
2021-08-12 22:00:06 -07:00
|
|
|
use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle};
|
2021-10-30 12:20:50 +01:00
|
|
|
use crate::path::{Path, PathBuf};
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::ptr;
|
|
|
|
use crate::sys::c;
|
2021-03-03 12:17:16 +00:00
|
|
|
use crate::sys::c::NonZeroDWORD;
|
2019-12-22 17:42:04 -05:00
|
|
|
use crate::sys::fs::{File, OpenOptions};
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::sys::handle::Handle;
|
2021-10-30 12:20:50 +01:00
|
|
|
use crate::sys::path;
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::sys::pipe::{self, AnonPipe};
|
|
|
|
use crate::sys::stdio;
|
2021-10-30 12:20:50 +01:00
|
|
|
use crate::sys::{cvt, to_u16s};
|
2021-04-28 19:11:57 +02:00
|
|
|
use crate::sys_common::mutex::StaticMutex;
|
2020-09-21 11:32:06 -07:00
|
|
|
use crate::sys_common::process::{CommandEnv, CommandEnvs};
|
2021-06-30 21:44:30 -07:00
|
|
|
use crate::sys_common::{AsInner, IntoInner};
|
2019-02-11 04:23:21 +09:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
|
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())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 07:15:33 +01:00
|
|
|
// Comparing Windows environment variable keys[1] are behaviourally the
|
|
|
|
// 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 {
|
2019-12-22 17:42:04 -05:00
|
|
|
fn from(k: EnvKey) -> Self {
|
2021-05-19 23:34:15 +01:00
|
|
|
k.os_string
|
2019-12-22 17:42:04 -05: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())
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2017-12-17 15:21:47 +00:00
|
|
|
}
|
|
|
|
|
2019-09-03 19:32:44 -07:00
|
|
|
impl AsRef<OsStr> for EnvKey {
|
2019-12-22 17:42:04 -05:00
|
|
|
fn as_ref(&self) -> &OsStr {
|
2021-05-19 23:34:15 +01:00
|
|
|
&self.os_string
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2017-12-17 15:21:47 +00:00
|
|
|
}
|
|
|
|
|
2016-01-15 15:29:45 -05:00
|
|
|
fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
|
|
|
|
if str.as_ref().encode_wide().any(|b| b == 0) {
|
2021-03-21 20:22:38 +01:00
|
|
|
Err(io::Error::new_const(ErrorKind::InvalidInput, &"nul byte found in provided data"))
|
2016-01-15 15:29:45 -05:00
|
|
|
} else {
|
|
|
|
Ok(str)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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,
|
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,
|
|
|
|
Null,
|
|
|
|
MakePipe,
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-05-30 17:01:02 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
enum Arg {
|
|
|
|
/// Add quotes (if needed)
|
|
|
|
Regular(OsString),
|
|
|
|
/// Append raw string without quoting
|
|
|
|
Raw(OsString),
|
|
|
|
}
|
|
|
|
|
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,
|
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;
|
|
|
|
}
|
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> {
|
|
|
|
self.cwd.as_ref().map(|cwd| Path::new(cwd))
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
pub fn spawn(
|
|
|
|
&mut self,
|
|
|
|
default: Stdio,
|
|
|
|
needs_stdin: bool,
|
|
|
|
) -> 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
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
let mut si = zeroed_startupinfo();
|
2015-11-02 16:23:22 -08:00
|
|
|
si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
|
|
|
|
si.dwFlags = c::STARTF_USESTDHANDLES;
|
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)?;
|
2021-10-30 12:20:50 +01:00
|
|
|
let mut cmd_str =
|
|
|
|
make_command_line(program.as_os_str(), &self.args, self.force_quotes_enabled)?;
|
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
|
2021-04-28 19:11:57 +02:00
|
|
|
static CREATE_PROCESS_LOCK: StaticMutex = StaticMutex::new();
|
|
|
|
|
|
|
|
let _guard = unsafe { CREATE_PROCESS_LOCK.lock() };
|
2016-02-04 09:59:47 -08:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
let mut pipes = StdioPipes { stdin: None, stdout: None, stderr: None };
|
2016-02-12 10:28:03 -08:00
|
|
|
let null = Stdio::Null;
|
2019-12-22 17:42:04 -05:00
|
|
|
let default_stdin = if needs_stdin { &default } else { &null };
|
2016-02-12 10:28:03 -08:00
|
|
|
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)?;
|
2019-12-22 17:42:04 -05:00
|
|
|
let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?;
|
|
|
|
let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?;
|
2021-06-30 21:44:30 -07:00
|
|
|
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
|
|
|
|
2021-10-30 12:20:50 +01:00
|
|
|
let program = to_u16s(&program)?;
|
2016-03-22 22:01:37 -05:00
|
|
|
unsafe {
|
2019-12-22 17:42:04 -05:00
|
|
|
cvt(c::CreateProcessW(
|
2021-10-30 12:20:50 +01:00
|
|
|
program.as_ptr(),
|
2019-12-22 17:42:04 -05:00
|
|
|
cmd_str.as_mut_ptr(),
|
|
|
|
ptr::null_mut(),
|
|
|
|
ptr::null_mut(),
|
|
|
|
c::TRUE,
|
|
|
|
flags,
|
|
|
|
envp,
|
|
|
|
dirp,
|
|
|
|
&mut si,
|
|
|
|
&mut pi,
|
|
|
|
))
|
2016-03-22 22:01:37 -05:00
|
|
|
}?;
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
// We close the thread handle because we don't care about keeping
|
|
|
|
// the thread id valid, and we aren't keeping the thread handle
|
|
|
|
// around to be able to close it later.
|
2021-06-30 21:44:30 -07:00
|
|
|
unsafe {
|
|
|
|
drop(Handle::from_raw_handle(pi.hThread));
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2021-06-30 21:44:30 -07:00
|
|
|
Ok((Process { handle: Handle::from_raw_handle(pi.hProcess) }, 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>,
|
|
|
|
) -> io::Result<PathBuf> {
|
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) {
|
|
|
|
return Err(io::Error::new_const(
|
|
|
|
io::ErrorKind::InvalidInput,
|
|
|
|
&"program path has no file name",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
// 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() {
|
|
|
|
exe_path.bytes()[exe_path.len() - EXE_SUFFIX.len()..]
|
|
|
|
.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.
|
|
|
|
return Ok(exe_path.into());
|
|
|
|
}
|
|
|
|
let mut path = PathBuf::from(exe_path);
|
|
|
|
|
|
|
|
// Append `.exe` if not already there.
|
|
|
|
path = path::append_suffix(path, EXE_SUFFIX.as_ref());
|
|
|
|
if path.try_exists().unwrap_or(false) {
|
|
|
|
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("");
|
|
|
|
return Ok(path);
|
|
|
|
}
|
|
|
|
} 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.
|
|
|
|
let has_extension = exe_path.bytes().contains(&b'.');
|
|
|
|
|
|
|
|
// 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| {
|
2021-10-30 12:20:50 +01:00
|
|
|
path.push(&exe_path);
|
|
|
|
if !has_extension {
|
|
|
|
path.set_extension(EXE_EXTENSION);
|
|
|
|
}
|
|
|
|
if let Ok(true) = path.try_exists() { Some(path) } else { None }
|
|
|
|
});
|
|
|
|
if let Some(path) = result {
|
|
|
|
return Ok(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If we get here then the executable cannot be found.
|
|
|
|
Err(io::Error::new_const(io::ErrorKind::NotFound, &"program not found"))
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
) -> Option<PathBuf>
|
2021-10-30 12:20:50 +01:00
|
|
|
where
|
2022-01-03 12:55:42 +00:00
|
|
|
Paths: FnOnce() -> Option<OsString>,
|
|
|
|
Exists: FnMut(PathBuf) -> Option<PathBuf>,
|
2021-10-30 12:20:50 +01:00
|
|
|
{
|
|
|
|
// 1. Child paths
|
|
|
|
// This is for consistency with Rust's historic behaviour.
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
impl Stdio {
|
2019-12-22 17:42:04 -05:00
|
|
|
fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option<AnonPipe>) -> io::Result<Handle> {
|
2016-02-04 11:10:37 -08:00
|
|
|
match *self {
|
|
|
|
// If no stdio handle is available, then inherit means that it
|
|
|
|
// should still be unavailable so propagate the
|
|
|
|
// INVALID_HANDLE_VALUE.
|
2019-12-22 17:42:04 -05:00
|
|
|
Stdio::Inherit => match stdio::get_handle(stdio_id) {
|
2021-06-30 21:44:30 -07:00
|
|
|
Ok(io) => unsafe {
|
|
|
|
let io = Handle::from_raw_handle(io);
|
2019-12-22 17:42:04 -05:00
|
|
|
let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS);
|
2021-08-12 22:00:06 -07:00
|
|
|
io.into_raw_handle();
|
2019-12-22 17:42:04 -05:00
|
|
|
ret
|
2021-06-30 21:44:30 -07:00
|
|
|
},
|
|
|
|
Err(..) => unsafe { Ok(Handle::from_raw_handle(c::INVALID_HANDLE_VALUE)) },
|
2019-12-22 17:42:04 -05:00
|
|
|
},
|
2016-02-04 11:10:37 -08:00
|
|
|
|
|
|
|
Stdio::MakePipe => {
|
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
|
|
|
let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
|
2019-05-27 16:51:29 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
Stdio::Handle(ref handle) => handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS),
|
2016-02-04 11:10:37 -08:00
|
|
|
|
|
|
|
// 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 {
|
|
|
|
nLength: size as c::DWORD,
|
|
|
|
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);
|
2021-06-30 21:44:30 -07:00
|
|
|
File::open(Path::new("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 {
|
|
|
|
Stdio::Handle(pipe.into_handle())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
}
|
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<()> {
|
2021-06-30 21:44:30 -07:00
|
|
|
cvt(unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) })?;
|
2015-02-06 09:42:57 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2015-04-16 09:44:05 -07:00
|
|
|
pub fn id(&self) -> u32 {
|
2021-06-30 21:44:30 -07:00
|
|
|
unsafe { c::GetProcessId(self.handle.as_raw_handle()) as u32 }
|
2015-04-16 09:44:05 -07:00
|
|
|
}
|
|
|
|
|
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 {
|
2019-12-22 17:42:04 -05: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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
pub fn handle(&self) -> &Handle {
|
|
|
|
&self.handle
|
|
|
|
}
|
2015-07-15 23:31:24 -07:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
pub fn into_handle(self) -> Handle {
|
|
|
|
self.handle
|
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
2015-11-02 16:23:22 -08:00
|
|
|
pub struct ExitStatus(c::DWORD);
|
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> {
|
|
|
|
match NonZeroDWORD::try_from(self.0) {
|
|
|
|
/* 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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-13 13:47:18 +02:00
|
|
|
/// Converts a raw `c::DWORD` to a type-safe `ExitStatus` by wrapping it without copying.
|
2016-04-26 15:23:46 -07:00
|
|
|
impl From<c::DWORD> for ExitStatus {
|
|
|
|
fn from(u: c::DWORD) -> ExitStatus {
|
|
|
|
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)]
|
|
|
|
pub struct ExitStatusError(c::NonZeroDWORD);
|
|
|
|
|
|
|
|
impl Into<ExitStatus> for ExitStatusError {
|
|
|
|
fn into(self) -> ExitStatus {
|
|
|
|
ExitStatus(self.0.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExitStatusError {
|
|
|
|
pub fn code(self) -> Option<NonZeroI32> {
|
|
|
|
Some((u32::from(self.0) as i32).try_into().unwrap())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-03 18:29:30 -08:00
|
|
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
|
|
|
pub struct ExitCode(c::DWORD);
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-02 16:23:22 -08:00
|
|
|
fn zeroed_startupinfo() -> c::STARTUPINFO {
|
|
|
|
c::STARTUPINFO {
|
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,
|
|
|
|
dwYCountCharts: 0,
|
|
|
|
dwFillAttribute: 0,
|
|
|
|
dwFlags: 0,
|
|
|
|
wShowWindow: 0,
|
|
|
|
cbReserved2: 0,
|
|
|
|
lpReserved2: ptr::null_mut(),
|
2015-11-02 16:23:22 -08:00
|
|
|
hStdInput: c::INVALID_HANDLE_VALUE,
|
|
|
|
hStdOutput: c::INVALID_HANDLE_VALUE,
|
|
|
|
hStdError: c::INVALID_HANDLE_VALUE,
|
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,
|
2019-12-22 17:42:04 -05:00
|
|
|
dwThreadId: 0,
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-30 17:01:02 +01:00
|
|
|
enum Quote {
|
|
|
|
// Every arg is quoted
|
|
|
|
Always,
|
|
|
|
// Whitespace and empty args are quoted
|
|
|
|
Auto,
|
|
|
|
// Arg appended without any changes (#29494)
|
|
|
|
Never,
|
|
|
|
}
|
|
|
|
|
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.
|
2021-05-30 17:01:02 +01:00
|
|
|
fn make_command_line(prog: &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
|
|
|
|
|
|
|
// CreateFileW has special handling for .bat and .cmd files, which means we
|
|
|
|
// need to add an extra pair of quotes surrounding the whole command line
|
|
|
|
// so they are properly passed on to the script.
|
|
|
|
// See issue #91991.
|
|
|
|
let is_batch_file = Path::new(prog)
|
|
|
|
.extension()
|
|
|
|
.map(|ext| ext.eq_ignore_ascii_case("cmd") || ext.eq_ignore_ascii_case("bat"))
|
|
|
|
.unwrap_or(false);
|
|
|
|
if is_batch_file {
|
|
|
|
cmd.push(b'"' as u16);
|
|
|
|
}
|
|
|
|
|
2017-06-04 21:47:24 +01:00
|
|
|
// Always quote the program name so CreateProcess doesn't interpret args as
|
|
|
|
// part of the name if the binary wasn't found first time.
|
2021-05-30 17:01:02 +01:00
|
|
|
append_arg(&mut cmd, prog, Quote::Always)?;
|
2015-02-06 09:42:57 -08:00
|
|
|
for arg in args {
|
|
|
|
cmd.push(' ' as u16);
|
2021-05-30 17:01:02 +01:00
|
|
|
let (arg, quote) = match arg {
|
|
|
|
Arg::Regular(arg) => (arg, if force_quotes { Quote::Always } else { Quote::Auto }),
|
|
|
|
Arg::Raw(arg) => (arg, Quote::Never),
|
|
|
|
};
|
|
|
|
append_arg(&mut cmd, arg, quote)?;
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2021-12-16 17:21:34 +00:00
|
|
|
if is_batch_file {
|
|
|
|
cmd.push(b'"' as u16);
|
|
|
|
}
|
2016-01-15 15:29:45 -05:00
|
|
|
return Ok(cmd);
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2021-05-30 17:01:02 +01:00
|
|
|
fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, quote: Quote) -> io::Result<()> {
|
2015-02-06 09:42:57 -08:00
|
|
|
// If an argument has 0 characters then we need to quote it to ensure
|
|
|
|
// that it actually gets passed through on the command line or otherwise
|
|
|
|
// it will be dropped entirely when parsed on the other end.
|
2016-03-22 22:01:37 -05:00
|
|
|
ensure_no_nuls(arg)?;
|
2015-02-06 09:42:57 -08:00
|
|
|
let arg_bytes = &arg.as_inner().inner.as_inner();
|
2021-05-30 17:01:02 +01:00
|
|
|
let (quote, escape) = match quote {
|
|
|
|
Quote::Always => (true, true),
|
|
|
|
Quote::Auto => {
|
|
|
|
(arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') || arg_bytes.is_empty(), true)
|
|
|
|
}
|
|
|
|
Quote::Never => (false, false),
|
|
|
|
};
|
2015-02-06 09:42:57 -08:00
|
|
|
if quote {
|
|
|
|
cmd.push('"' as u16);
|
|
|
|
}
|
|
|
|
|
2015-04-25 15:35:22 -04:00
|
|
|
let mut backslashes: usize = 0;
|
2019-09-05 13:47:59 +02:00
|
|
|
for x in arg.encode_wide() {
|
2021-05-30 17:01:02 +01:00
|
|
|
if escape {
|
|
|
|
if x == '\\' as u16 {
|
|
|
|
backslashes += 1;
|
|
|
|
} else {
|
|
|
|
if x == '"' as u16 {
|
|
|
|
// Add n+1 backslashes to total 2n+1 before internal '"'.
|
|
|
|
cmd.extend((0..=backslashes).map(|_| '\\' as u16));
|
|
|
|
}
|
|
|
|
backslashes = 0;
|
2015-04-25 15:35:22 -04:00
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2015-04-25 15:35:22 -04:00
|
|
|
cmd.push(x);
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if quote {
|
2015-04-25 15:35:22 -04:00
|
|
|
// Add n backslashes to total 2n before ending '"'.
|
2018-07-26 17:11:10 +02:00
|
|
|
cmd.extend((0..backslashes).map(|_| '\\' as u16));
|
2015-02-06 09:42:57 -08:00
|
|
|
cmd.push('"' as u16);
|
|
|
|
}
|
2016-01-15 15:29:45 -05:00
|
|
|
Ok(())
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05: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))
|
2019-12-22 17:42:04 -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()
|
|
|
|
}
|
|
|
|
}
|