1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
14 use collections
::HashMap
;
17 use ffi
::{OsString, OsStr}
;
20 use io
::{self, Error}
;
21 use libc
::{self, c_void}
;
22 use os
::windows
::ffi
::OsStrExt
;
24 use sync
::{StaticMutex, MUTEX_INIT}
;
25 use sys
::handle
::Handle
;
26 use sys
::pipe2
::AnonPipe
;
28 use sys_common
::{AsInner, FromInner}
;
30 ////////////////////////////////////////////////////////////////////////////////
32 ////////////////////////////////////////////////////////////////////////////////
34 fn mk_key(s
: &OsStr
) -> OsString
{
35 FromInner
::from_inner(sys
::os_str
::Buf
{
36 inner
: s
.as_inner().inner
.to_ascii_uppercase()
42 pub program
: OsString
,
43 pub args
: Vec
<OsString
>,
44 pub env
: Option
<HashMap
<OsString
, OsString
>>,
45 pub cwd
: Option
<OsString
>,
46 pub detach
: bool
, // not currently exposed in std::process
50 pub fn new(program
: &OsStr
) -> Command
{
52 program
: program
.to_os_string(),
60 pub fn arg(&mut self, arg
: &OsStr
) {
61 self.args
.push(arg
.to_os_string())
63 pub fn args
<'a
, I
: Iterator
<Item
= &'a OsStr
>>(&mut self, args
: I
) {
64 self.args
.extend(args
.map(OsStr
::to_os_string
))
66 fn init_env_map(&mut self){
67 if self.env
.is_none() {
68 self.env
= Some(env
::vars_os().map(|(key
, val
)| {
73 pub fn env(&mut self, key
: &OsStr
, val
: &OsStr
) {
75 self.env
.as_mut().unwrap().insert(mk_key(key
), val
.to_os_string());
77 pub fn env_remove(&mut self, key
: &OsStr
) {
79 self.env
.as_mut().unwrap().remove(&mk_key(key
));
81 pub fn env_clear(&mut self) {
82 self.env
= Some(HashMap
::new())
84 pub fn cwd(&mut self, dir
: &OsStr
) {
85 self.cwd
= Some(dir
.to_os_string())
89 ////////////////////////////////////////////////////////////////////////////////
91 ////////////////////////////////////////////////////////////////////////////////
93 // `CreateProcess` is racy!
94 // http://support.microsoft.com/kb/315939
95 static CREATE_PROCESS_LOCK
: StaticMutex
= MUTEX_INIT
;
97 /// A value representing a child process.
99 /// The lifetime of this value is linked to the lifetime of the actual
100 /// process - the Process destructor calls self.finish() which waits
101 /// for the process to terminate.
103 /// A HANDLE to the process, which will prevent the pid being
104 /// re-used until the handle is closed.
116 pub fn spawn(cfg
: &Command
,
119 err_fd
: Stdio
) -> io
::Result
<Process
>
121 use libc
::types
::os
::arch
::extra
::{DWORD, HANDLE, STARTUPINFO}
;
122 use libc
::consts
::os
::extra
::{
124 STARTF_USESTDHANDLES
,
125 INVALID_HANDLE_VALUE
,
126 DUPLICATE_SAME_ACCESS
128 use libc
::funcs
::extra
::kernel32
::{
135 use env
::split_paths
;
139 // To have the spawning semantics of unix/windows stay the same, we need to
140 // read the *child's* PATH if one is provided. See #15149 for more details.
141 let program
= cfg
.env
.as_ref().and_then(|env
| {
142 for (key
, v
) in env
{
143 if OsStr
::new("PATH") != &**key { continue }
145 // Split the value and test each path to see if the
147 for path
in split_paths(&v
) {
148 let path
= path
.join(cfg
.program
.to_str().unwrap())
149 .with_extension(env
::consts
::EXE_EXTENSION
);
150 if fs
::metadata(&path
).is_ok() {
151 return Some(path
.into_os_string())
160 let mut si
= zeroed_startupinfo();
161 si
.cb
= mem
::size_of
::<STARTUPINFO
>() as DWORD
;
162 si
.dwFlags
= STARTF_USESTDHANDLES
;
164 let cur_proc
= GetCurrentProcess();
166 let set_fd
= |fd
: &Stdio
, slot
: &mut HANDLE
,
171 // Similarly to unix, we don't actually leave holes for the
172 // stdio file descriptors, but rather open up /dev/null
173 // equivalents. These equivalents are drawn from libuv's
174 // windows process spawning.
176 let access
= if is_stdin
{
177 libc
::FILE_GENERIC_READ
179 libc
::FILE_GENERIC_WRITE
| libc
::FILE_READ_ATTRIBUTES
181 let size
= mem
::size_of
::<libc
::SECURITY_ATTRIBUTES
>();
182 let mut sa
= libc
::SECURITY_ATTRIBUTES
{
183 nLength
: size
as libc
::DWORD
,
184 lpSecurityDescriptor
: ptr
::null_mut(),
187 let mut filename
: Vec
<u16> = "NUL".utf16_units().collect();
189 *slot
= libc
::CreateFileW(filename
.as_ptr(),
191 libc
::FILE_SHARE_READ
|
192 libc
::FILE_SHARE_WRITE
,
197 if *slot
== INVALID_HANDLE_VALUE
{
198 return Err(Error
::last_os_error())
201 Stdio
::Piped(ref pipe
) => {
202 let orig
= pipe
.handle().raw();
203 if DuplicateHandle(cur_proc
, orig
, cur_proc
, slot
,
204 0, TRUE
, DUPLICATE_SAME_ACCESS
) == FALSE
{
205 return Err(Error
::last_os_error())
212 try
!(set_fd(&in_fd
, &mut si
.hStdInput
, true));
213 try
!(set_fd(&out_fd
, &mut si
.hStdOutput
, false));
214 try
!(set_fd(&err_fd
, &mut si
.hStdError
, false));
216 let mut cmd_str
= make_command_line(program
.as_ref().unwrap_or(&cfg
.program
),
218 cmd_str
.push(0); // add null terminator
220 let mut pi
= zeroed_process_information();
221 let mut create_err
= None
;
223 // stolen from the libuv code.
224 let mut flags
= libc
::CREATE_UNICODE_ENVIRONMENT
;
226 flags
|= libc
::DETACHED_PROCESS
| libc
::CREATE_NEW_PROCESS_GROUP
;
229 with_envp(cfg
.env
.as_ref(), |envp
| {
230 with_dirp(cfg
.cwd
.as_ref(), |dirp
| {
231 let _lock
= CREATE_PROCESS_LOCK
.lock().unwrap();
232 let created
= CreateProcessW(ptr
::null(),
233 cmd_str
.as_mut_ptr(),
239 if created
== FALSE
{
240 create_err
= Some(Error
::last_os_error());
245 if !in_fd
.inherited() {
246 assert
!(CloseHandle(si
.hStdInput
) != 0);
248 if !out_fd
.inherited() {
249 assert
!(CloseHandle(si
.hStdOutput
) != 0);
251 if !err_fd
.inherited() {
252 assert
!(CloseHandle(si
.hStdError
) != 0);
256 Some(err
) => return Err(err
),
260 // We close the thread handle because we don't care about keeping the
261 // thread id valid, and we aren't keeping the thread handle around to be
262 // able to close it later. We don't close the process handle however
263 // because std::we want the process id to stay valid at least until the
264 // calling code closes the process handle.
265 assert
!(CloseHandle(pi
.hThread
) != 0);
268 handle
: Handle
::new(pi
.hProcess
)
273 pub unsafe fn kill(&self) -> io
::Result
<()> {
274 try
!(cvt(libc
::TerminateProcess(self.handle
.raw(), 1)));
278 pub fn wait(&self) -> io
::Result
<ExitStatus
> {
279 use libc
::consts
::os
::extra
::{
285 use libc
::funcs
::extra
::kernel32
::{
293 if GetExitCodeProcess(self.handle
.raw(), &mut status
) == FALSE
{
294 let err
= Err(Error
::last_os_error());
297 if status
!= STILL_ACTIVE
{
298 return Ok(ExitStatus(status
as i32));
300 match WaitForSingleObject(self.handle
.raw(), INFINITE
) {
303 let err
= Err(Error
::last_os_error());
313 fn inherited(&self) -> bool
{
314 match *self { Stdio::Inherit => true, _ => false }
318 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
319 pub struct ExitStatus(i32);
322 pub fn success(&self) -> bool
{
325 pub fn code(&self) -> Option
<i32> {
330 impl fmt
::Display
for ExitStatus
{
331 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
332 write
!(f
, "exit code: {}", self.0)
336 fn zeroed_startupinfo() -> libc
::types
::os
::arch
::extra
::STARTUPINFO
{
337 libc
::types
::os
::arch
::extra
::STARTUPINFO
{
339 lpReserved
: ptr
::null_mut(),
340 lpDesktop
: ptr
::null_mut(),
341 lpTitle
: ptr
::null_mut(),
352 lpReserved2
: ptr
::null_mut(),
353 hStdInput
: libc
::INVALID_HANDLE_VALUE
,
354 hStdOutput
: libc
::INVALID_HANDLE_VALUE
,
355 hStdError
: libc
::INVALID_HANDLE_VALUE
,
359 fn zeroed_process_information() -> libc
::types
::os
::arch
::extra
::PROCESS_INFORMATION
{
360 libc
::types
::os
::arch
::extra
::PROCESS_INFORMATION
{
361 hProcess
: ptr
::null_mut(),
362 hThread
: ptr
::null_mut(),
368 // Produces a wide string *without terminating null*
369 fn make_command_line(prog
: &OsStr
, args
: &[OsString
]) -> Vec
<u16> {
370 let mut cmd
: Vec
<u16> = Vec
::new();
371 append_arg(&mut cmd
, prog
);
373 cmd
.push(' '
as u16);
374 append_arg(&mut cmd
, arg
);
378 fn append_arg(cmd
: &mut Vec
<u16>, arg
: &OsStr
) {
379 // If an argument has 0 characters then we need to quote it to ensure
380 // that it actually gets passed through on the command line or otherwise
381 // it will be dropped entirely when parsed on the other end.
382 let arg_bytes
= &arg
.as_inner().inner
.as_inner();
383 let quote
= arg_bytes
.iter().any(|c
| *c
== b' '
|| *c
== b'
\t'
)
384 || arg_bytes
.is_empty();
386 cmd
.push('
"' as u16);
389 let mut iter = arg.encode_wide();
390 while let Some(x) = iter.next() {
393 cmd
.push('
\\'
as u16);
394 cmd
.push('
"' as u16);
395 } else if x == '\\' as u16 {
396 // is this a run of backslashes followed by a " ?
397 if iter
.clone().skip_while(|y
| *y
== '
\\'
as u16).next() == Some('
"' as u16) {
398 // Double it ... NOTE: this behavior is being
399 // preserved as it's been part of Rust for a long
400 // time, but no one seems to know exactly why this
401 // is the right thing to do.
402 cmd.push('\\' as u16);
403 cmd.push('\\' as u16);
405 // Push it through unescaped
406 cmd.push('\\' as u16);
414 cmd.push('"'
as u16);
419 fn with_envp
<F
, T
>(env
: Option
<&collections
::HashMap
<OsString
, OsString
>>, cb
: F
) -> T
420 where F
: FnOnce(*mut c_void
) -> T
,
422 // On Windows we pass an "environment block" which is not a char**, but
423 // rather a concatenation of null-terminated k=v\0 sequences, with a final
427 let mut blk
= Vec
::new();
430 blk
.extend(pair
.0.encode_wide());
431 blk
.push('
='
as u16);
432 blk
.extend(pair
.1.encode_wide());
436 cb(blk
.as_mut_ptr() as *mut c_void
)
438 _
=> cb(ptr
::null_mut())
442 fn with_dirp
<T
, F
>(d
: Option
<&OsString
>, cb
: F
) -> T
where
443 F
: FnOnce(*const u16) -> T
,
447 let mut dir_str
: Vec
<u16> = dir
.encode_wide().collect();
451 None
=> cb(ptr
::null())
459 use ffi
::{OsStr, OsString}
;
460 use super::make_command_line
;
463 fn test_make_command_line() {
464 fn test_wrapper(prog
: &str, args
: &[&str]) -> String
{
466 &make_command_line(OsStr
::new(prog
),
468 .map(|a
| OsString
::from(a
))
469 .collect
::<Vec
<OsString
>>())).unwrap()
473 test_wrapper("prog", &["aaa", "bbb", "ccc"]),
478 test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]),
479 "\"C:\\Program Files\\blah\\blah.exe\" aaa"
482 test_wrapper("C:\\Program Files\\test", &["aa\"bb"]),
483 "\"C:\\Program Files\\test\" aa\\\"bb"
486 test_wrapper("echo", &["a b c"]),
490 test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
491 "\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}"