diff --git a/src/libstd/sys/windows/fs2.rs b/src/libstd/sys/windows/fs2.rs index 5ac9a0ace5801..1c47176173ade 100644 --- a/src/libstd/sys/windows/fs2.rs +++ b/src/libstd/sys/windows/fs2.rs @@ -56,6 +56,7 @@ pub struct OpenOptions { share_mode: Option, creation_disposition: Option, flags_and_attributes: Option, + security_attributes: usize, // *mut T doesn't have a Default impl } #[derive(Clone, PartialEq, Eq, Debug)] @@ -135,6 +136,9 @@ impl OpenOptions { pub fn share_mode(&mut self, val: i32) { self.share_mode = Some(val as libc::DWORD); } + pub fn security_attributes(&mut self, attrs: libc::LPSECURITY_ATTRIBUTES) { + self.security_attributes = attrs as usize; + } fn get_desired_access(&self) -> libc::DWORD { self.desired_access.unwrap_or({ @@ -186,7 +190,7 @@ impl File { libc::CreateFileW(path.as_ptr(), opts.get_desired_access(), opts.get_share_mode(), - ptr::null_mut(), + opts.security_attributes as *mut _, opts.get_creation_disposition(), opts.get_flags_and_attributes(), ptr::null_mut()) @@ -263,6 +267,8 @@ impl File { } pub fn handle(&self) -> &Handle { &self.handle } + + pub fn into_handle(self) -> Handle { self.handle } } impl FromInner for File { diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs index c3a30aae9e0e8..9481e180ce578 100644 --- a/src/libstd/sys/windows/handle.rs +++ b/src/libstd/sys/windows/handle.rs @@ -12,6 +12,7 @@ use prelude::v1::*; use io::ErrorKind; use io; +use libc::funcs::extra::kernel32::{GetCurrentProcess, DuplicateHandle}; use libc::{self, HANDLE}; use mem; use ptr; @@ -65,6 +66,18 @@ impl Handle { })); Ok(amt as usize) } + + pub fn duplicate(&self, access: libc::DWORD, inherit: bool, + options: libc::DWORD) -> io::Result { + let mut ret = 0 as libc::HANDLE; + try!(cvt(unsafe { + let cur_proc = GetCurrentProcess(); + DuplicateHandle(cur_proc, self.0, cur_proc, &mut ret, + access, inherit as libc::BOOL, + options) + })); + Ok(Handle::new(ret)) + } } impl Drop for Handle { diff --git a/src/libstd/sys/windows/process2.rs b/src/libstd/sys/windows/process2.rs index 2e5585d2f4389..5aad5f668dd41 100644 --- a/src/libstd/sys/windows/process2.rs +++ b/src/libstd/sys/windows/process2.rs @@ -13,17 +13,23 @@ use prelude::v1::*; use ascii::*; use collections::HashMap; use collections; +use env::split_paths; use env; use ffi::{OsString, OsStr}; use fmt; use fs; use io::{self, Error}; use libc::{self, c_void}; +use mem; use os::windows::ffi::OsStrExt; +use path::Path; use ptr; use sync::{StaticMutex, MUTEX_INIT}; +use sys::c; +use sys::fs2::{OpenOptions, File}; use sys::handle::Handle; use sys::pipe2::AnonPipe; +use sys::stdio; use sys::{self, cvt}; use sys_common::{AsInner, FromInner}; @@ -90,18 +96,12 @@ impl Command { // Processes //////////////////////////////////////////////////////////////////////////////// -// `CreateProcess` is racy! -// http://support.microsoft.com/kb/315939 -static CREATE_PROCESS_LOCK: StaticMutex = MUTEX_INIT; - /// 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 { - /// A HANDLE to the process, which will prevent the pid being - /// re-used until the handle is closed. handle: Handle, } @@ -112,32 +112,17 @@ pub enum Stdio { } impl Process { - #[allow(deprecated)] pub fn spawn(cfg: &Command, - in_fd: Stdio, - out_fd: Stdio, - err_fd: Stdio) -> io::Result + in_handle: Stdio, + out_handle: Stdio, + err_handle: Stdio) -> io::Result { - use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO}; - use libc::consts::os::extra::{ - TRUE, FALSE, - STARTF_USESTDHANDLES, - INVALID_HANDLE_VALUE, - DUPLICATE_SAME_ACCESS - }; - use libc::funcs::extra::kernel32::{ - GetCurrentProcess, - DuplicateHandle, - CloseHandle, - CreateProcessW - }; - - use env::split_paths; - use mem; - use iter::Iterator; - - // To have the spawning semantics of unix/windows stay the same, we need to - // read the *child's* PATH if one is provided. See #15149 for more details. + use libc::{TRUE, STARTF_USESTDHANDLES}; + use libc::{DWORD, STARTUPINFO, CreateProcessW}; + + // To have the spawning semantics of unix/windows stay the same, we need + // to read the *child's* PATH if one is provided. See #15149 for more + // details. let program = cfg.env.as_ref().and_then(|env| { for (key, v) in env { if OsStr::new("PATH") != &**key { continue } @@ -156,118 +141,51 @@ impl Process { None }); - unsafe { - let mut si = zeroed_startupinfo(); - si.cb = mem::size_of::() as DWORD; - si.dwFlags = STARTF_USESTDHANDLES; - - let cur_proc = GetCurrentProcess(); - - let set_fd = |fd: &Stdio, slot: &mut HANDLE, - is_stdin: bool| { - match *fd { - Stdio::Inherit => {} - - // Similarly to unix, we don't actually leave holes for the - // stdio file descriptors, but rather open up /dev/null - // equivalents. These equivalents are drawn from libuv's - // windows process spawning. - Stdio::None => { - let access = if is_stdin { - libc::FILE_GENERIC_READ - } else { - libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES - }; - let size = mem::size_of::(); - let mut sa = libc::SECURITY_ATTRIBUTES { - nLength: size as libc::DWORD, - lpSecurityDescriptor: ptr::null_mut(), - bInheritHandle: 1, - }; - let mut filename: Vec = "NUL".utf16_units().collect(); - filename.push(0); - *slot = libc::CreateFileW(filename.as_ptr(), - access, - libc::FILE_SHARE_READ | - libc::FILE_SHARE_WRITE, - &mut sa, - libc::OPEN_EXISTING, - 0, - ptr::null_mut()); - if *slot == INVALID_HANDLE_VALUE { - return Err(Error::last_os_error()) - } - } - Stdio::Piped(ref pipe) => { - let orig = pipe.handle().raw(); - if DuplicateHandle(cur_proc, orig, cur_proc, slot, - 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { - return Err(Error::last_os_error()) - } - } - } - Ok(()) - }; - - try!(set_fd(&in_fd, &mut si.hStdInput, true)); - try!(set_fd(&out_fd, &mut si.hStdOutput, false)); - try!(set_fd(&err_fd, &mut si.hStdError, false)); + let mut si = zeroed_startupinfo(); + si.cb = mem::size_of::() as DWORD; + si.dwFlags = STARTF_USESTDHANDLES; - let mut cmd_str = make_command_line(program.as_ref().unwrap_or(&cfg.program), - &cfg.args); - cmd_str.push(0); // add null terminator + let stdin = try!(in_handle.to_handle(c::STD_INPUT_HANDLE)); + let stdout = try!(out_handle.to_handle(c::STD_OUTPUT_HANDLE)); + let stderr = try!(err_handle.to_handle(c::STD_ERROR_HANDLE)); - let mut pi = zeroed_process_information(); - let mut create_err = None; - - // stolen from the libuv code. - let mut flags = libc::CREATE_UNICODE_ENVIRONMENT; - if cfg.detach { - flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP; - } + si.hStdInput = stdin.raw(); + si.hStdOutput = stdout.raw(); + si.hStdError = stderr.raw(); - with_envp(cfg.env.as_ref(), |envp| { - with_dirp(cfg.cwd.as_ref(), |dirp| { - let _lock = CREATE_PROCESS_LOCK.lock().unwrap(); - let created = CreateProcessW(ptr::null(), - cmd_str.as_mut_ptr(), - ptr::null_mut(), - ptr::null_mut(), - TRUE, - flags, envp, dirp, - &mut si, &mut pi); - if created == FALSE { - create_err = Some(Error::last_os_error()); - } - }) - }); + let program = program.as_ref().unwrap_or(&cfg.program); + let mut cmd_str = make_command_line(program, &cfg.args); + cmd_str.push(0); // add null terminator - if !in_fd.inherited() { - assert!(CloseHandle(si.hStdInput) != 0); - } - if !out_fd.inherited() { - assert!(CloseHandle(si.hStdOutput) != 0); - } - if !err_fd.inherited() { - assert!(CloseHandle(si.hStdError) != 0); - } + // stolen from the libuv code. + let mut flags = libc::CREATE_UNICODE_ENVIRONMENT; + if cfg.detach { + flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP; + } - match create_err { - Some(err) => return Err(err), - None => {} - } + let (envp, _data) = make_envp(cfg.env.as_ref()); + let (dirp, _data) = make_dirp(cfg.cwd.as_ref()); + let mut pi = zeroed_process_information(); + try!(unsafe { + // `CreateProcess` is racy! + // http://support.microsoft.com/kb/315939 + static CREATE_PROCESS_LOCK: StaticMutex = MUTEX_INIT; + let _lock = CREATE_PROCESS_LOCK.lock(); + + cvt(CreateProcessW(ptr::null(), + cmd_str.as_mut_ptr(), + ptr::null_mut(), + ptr::null_mut(), + TRUE, flags, envp, dirp, + &mut si, &mut pi)) + }); - // 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. We don't close the process handle however - // because std::we want the process id to stay valid at least until the - // calling code closes the process handle. - assert!(CloseHandle(pi.hThread) != 0); + // 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. + drop(Handle::new(pi.hThread)); - Ok(Process { - handle: Handle::new(pi.hProcess) - }) - } + Ok(Process { handle: Handle::new(pi.hProcess) }) } pub unsafe fn kill(&self) -> io::Result<()> { @@ -276,45 +194,25 @@ impl Process { } pub fn wait(&self) -> io::Result { - use libc::consts::os::extra::{ - FALSE, - STILL_ACTIVE, - INFINITE, - WAIT_OBJECT_0, - }; - use libc::funcs::extra::kernel32::{ - GetExitCodeProcess, - WaitForSingleObject, - }; + use libc::{STILL_ACTIVE, INFINITE, WAIT_OBJECT_0}; + use libc::{GetExitCodeProcess, WaitForSingleObject}; unsafe { loop { let mut status = 0; - if GetExitCodeProcess(self.handle.raw(), &mut status) == FALSE { - let err = Err(Error::last_os_error()); - return err; - } + try!(cvt(GetExitCodeProcess(self.handle.raw(), &mut status))); if status != STILL_ACTIVE { return Ok(ExitStatus(status as i32)); } match WaitForSingleObject(self.handle.raw(), INFINITE) { WAIT_OBJECT_0 => {} - _ => { - let err = Err(Error::last_os_error()); - return err - } + _ => return Err(Error::last_os_error()), } } } } } -impl Stdio { - fn inherited(&self) -> bool { - match *self { Stdio::Inherit => true, _ => false } - } -} - #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct ExitStatus(i32); @@ -415,9 +313,8 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> Vec { } } -fn with_envp(env: Option<&collections::HashMap>, cb: F) -> T - where F: FnOnce(*mut c_void) -> T, -{ +fn make_envp(env: Option<&collections::HashMap>) + -> (*mut c_void, Vec) { // 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. @@ -432,22 +329,57 @@ fn with_envp(env: Option<&collections::HashMap>, cb: F blk.push(0); } blk.push(0); - cb(blk.as_mut_ptr() as *mut c_void) + (blk.as_mut_ptr() as *mut c_void, blk) } - _ => cb(ptr::null_mut()) + _ => (ptr::null_mut(), Vec::new()) } } -fn with_dirp(d: Option<&OsString>, cb: F) -> T where - F: FnOnce(*const u16) -> T, -{ +fn make_dirp(d: Option<&OsString>) -> (*const u16, Vec) { match d { - Some(dir) => { - let mut dir_str: Vec = dir.encode_wide().collect(); - dir_str.push(0); - cb(dir_str.as_ptr()) - }, - None => cb(ptr::null()) + Some(dir) => { + let mut dir_str: Vec = dir.encode_wide().collect(); + dir_str.push(0); + (dir_str.as_ptr(), dir_str) + }, + None => (ptr::null(), Vec::new()) + } +} + +impl Stdio { + fn to_handle(&self, stdio_id: libc::DWORD) -> io::Result { + use libc::DUPLICATE_SAME_ACCESS; + + match *self { + Stdio::Inherit => { + stdio::get(stdio_id).and_then(|io| { + io.handle().duplicate(0, true, DUPLICATE_SAME_ACCESS) + }) + } + Stdio::Piped(ref pipe) => { + pipe.handle().duplicate(0, true, DUPLICATE_SAME_ACCESS) + } + + // Similarly to unix, we don't actually leave holes for the + // stdio file descriptors, but rather open up /dev/null + // equivalents. These equivalents are drawn from libuv's + // windows process spawning. + Stdio::None => { + let size = mem::size_of::(); + let mut sa = libc::SECURITY_ATTRIBUTES { + nLength: size as libc::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); + File::open(Path::new("NUL"), &opts).map(|file| { + file.into_handle() + }) + } + } } } diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs index 91f6f328ff6e0..03547165f5d87 100644 --- a/src/libstd/sys/windows/stdio.rs +++ b/src/libstd/sys/windows/stdio.rs @@ -21,9 +21,9 @@ use sys::c; use sys::cvt; use sys::handle::Handle; -struct NoClose(Option); +pub struct NoClose(Option); -enum Output { +pub enum Output { Console(NoClose), Pipe(NoClose), } @@ -35,7 +35,7 @@ pub struct Stdin { pub struct Stdout(Output); pub struct Stderr(Output); -fn get(handle: libc::DWORD) -> io::Result { +pub fn get(handle: libc::DWORD) -> io::Result { let handle = unsafe { c::GetStdHandle(handle) }; if handle == libc::INVALID_HANDLE_VALUE { Err(io::Error::last_os_error()) @@ -159,6 +159,16 @@ impl Drop for NoClose { } } +impl Output { + pub fn handle(&self) -> &Handle { + let nc = match *self { + Output::Console(ref c) => c, + Output::Pipe(ref c) => c, + }; + nc.0.as_ref().unwrap() + } +} + fn invalid_encoding() -> io::Error { io::Error::new(io::ErrorKind::InvalidInput, "text was not valid unicode") }