]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/windows/process.rs
Imported Upstream version 1.6.0+dfsg1
[rustc.git] / src / libstd / sys / windows / process.rs
CommitLineData
85aaf69f
SL
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.
4//
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.
10
11use prelude::v1::*;
12
13use ascii::*;
14use collections::HashMap;
15use collections;
bd371182 16use env::split_paths;
85aaf69f
SL
17use env;
18use ffi::{OsString, OsStr};
19use fmt;
c34b1796 20use fs;
85aaf69f 21use io::{self, Error};
92a42be0 22use libc::c_void;
bd371182 23use mem;
c34b1796 24use os::windows::ffi::OsStrExt;
bd371182 25use path::Path;
85aaf69f 26use ptr;
62682a34 27use sync::StaticMutex;
bd371182 28use sys::c;
92a42be0 29
d9579d0f 30use sys::fs::{OpenOptions, File};
62682a34 31use sys::handle::{Handle, RawHandle};
bd371182 32use sys::stdio;
85aaf69f 33use sys::{self, cvt};
85aaf69f
SL
34use sys_common::{AsInner, FromInner};
35
36////////////////////////////////////////////////////////////////////////////////
37// Command
38////////////////////////////////////////////////////////////////////////////////
39
40fn mk_key(s: &OsStr) -> OsString {
41 FromInner::from_inner(sys::os_str::Buf {
42 inner: s.as_inner().inner.to_ascii_uppercase()
43 })
44}
45
46#[derive(Clone)]
47pub struct Command {
48 pub program: OsString,
49 pub args: Vec<OsString>,
50 pub env: Option<HashMap<OsString, OsString>>,
51 pub cwd: Option<OsString>,
52 pub detach: bool, // not currently exposed in std::process
53}
54
55impl Command {
56 pub fn new(program: &OsStr) -> Command {
57 Command {
58 program: program.to_os_string(),
59 args: Vec::new(),
60 env: None,
61 cwd: None,
62 detach: false,
63 }
64 }
65
66 pub fn arg(&mut self, arg: &OsStr) {
67 self.args.push(arg.to_os_string())
68 }
69 pub fn args<'a, I: Iterator<Item = &'a OsStr>>(&mut self, args: I) {
70 self.args.extend(args.map(OsStr::to_os_string))
71 }
72 fn init_env_map(&mut self){
73 if self.env.is_none() {
74 self.env = Some(env::vars_os().map(|(key, val)| {
75 (mk_key(&key), val)
76 }).collect());
77 }
78 }
79 pub fn env(&mut self, key: &OsStr, val: &OsStr) {
80 self.init_env_map();
81 self.env.as_mut().unwrap().insert(mk_key(key), val.to_os_string());
82 }
83 pub fn env_remove(&mut self, key: &OsStr) {
84 self.init_env_map();
85 self.env.as_mut().unwrap().remove(&mk_key(key));
86 }
87 pub fn env_clear(&mut self) {
88 self.env = Some(HashMap::new())
89 }
90 pub fn cwd(&mut self, dir: &OsStr) {
91 self.cwd = Some(dir.to_os_string())
92 }
93}
94
95////////////////////////////////////////////////////////////////////////////////
96// Processes
97////////////////////////////////////////////////////////////////////////////////
98
85aaf69f
SL
99/// A value representing a child process.
100///
101/// The lifetime of this value is linked to the lifetime of the actual
102/// process - the Process destructor calls self.finish() which waits
103/// for the process to terminate.
104pub struct Process {
85aaf69f
SL
105 handle: Handle,
106}
107
9346a6ac
AL
108pub enum Stdio {
109 Inherit,
9346a6ac 110 None,
92a42be0 111 Raw(c::HANDLE),
9346a6ac
AL
112}
113
62682a34
SL
114pub type RawStdio = Handle;
115
85aaf69f 116impl Process {
85aaf69f 117 pub fn spawn(cfg: &Command,
bd371182
AL
118 in_handle: Stdio,
119 out_handle: Stdio,
120 err_handle: Stdio) -> io::Result<Process>
85aaf69f 121 {
bd371182
AL
122 // To have the spawning semantics of unix/windows stay the same, we need
123 // to read the *child's* PATH if one is provided. See #15149 for more
124 // details.
85aaf69f
SL
125 let program = cfg.env.as_ref().and_then(|env| {
126 for (key, v) in env {
9346a6ac 127 if OsStr::new("PATH") != &**key { continue }
85aaf69f
SL
128
129 // Split the value and test each path to see if the
130 // program exists.
131 for path in split_paths(&v) {
132 let path = path.join(cfg.program.to_str().unwrap())
133 .with_extension(env::consts::EXE_EXTENSION);
c34b1796
AL
134 if fs::metadata(&path).is_ok() {
135 return Some(path.into_os_string())
85aaf69f
SL
136 }
137 }
138 break
139 }
140 None
141 });
142
bd371182 143 let mut si = zeroed_startupinfo();
92a42be0
SL
144 si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
145 si.dwFlags = c::STARTF_USESTDHANDLES;
85aaf69f 146
bd371182
AL
147 let stdin = try!(in_handle.to_handle(c::STD_INPUT_HANDLE));
148 let stdout = try!(out_handle.to_handle(c::STD_OUTPUT_HANDLE));
149 let stderr = try!(err_handle.to_handle(c::STD_ERROR_HANDLE));
85aaf69f 150
bd371182
AL
151 si.hStdInput = stdin.raw();
152 si.hStdOutput = stdout.raw();
153 si.hStdError = stderr.raw();
85aaf69f 154
bd371182
AL
155 let program = program.as_ref().unwrap_or(&cfg.program);
156 let mut cmd_str = make_command_line(program, &cfg.args);
157 cmd_str.push(0); // add null terminator
85aaf69f 158
bd371182 159 // stolen from the libuv code.
92a42be0 160 let mut flags = c::CREATE_UNICODE_ENVIRONMENT;
bd371182 161 if cfg.detach {
92a42be0 162 flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
bd371182 163 }
85aaf69f 164
bd371182
AL
165 let (envp, _data) = make_envp(cfg.env.as_ref());
166 let (dirp, _data) = make_dirp(cfg.cwd.as_ref());
167 let mut pi = zeroed_process_information();
168 try!(unsafe {
169 // `CreateProcess` is racy!
170 // http://support.microsoft.com/kb/315939
62682a34 171 static CREATE_PROCESS_LOCK: StaticMutex = StaticMutex::new();
bd371182
AL
172 let _lock = CREATE_PROCESS_LOCK.lock();
173
92a42be0
SL
174 cvt(c::CreateProcessW(ptr::null(),
175 cmd_str.as_mut_ptr(),
176 ptr::null_mut(),
177 ptr::null_mut(),
178 c::TRUE, flags, envp, dirp,
179 &mut si, &mut pi))
bd371182 180 });
85aaf69f 181
bd371182
AL
182 // We close the thread handle because we don't care about keeping
183 // the thread id valid, and we aren't keeping the thread handle
184 // around to be able to close it later.
185 drop(Handle::new(pi.hThread));
85aaf69f 186
bd371182 187 Ok(Process { handle: Handle::new(pi.hProcess) })
85aaf69f
SL
188 }
189
190 pub unsafe fn kill(&self) -> io::Result<()> {
92a42be0 191 try!(cvt(c::TerminateProcess(self.handle.raw(), 1)));
85aaf69f
SL
192 Ok(())
193 }
194
62682a34
SL
195 pub fn id(&self) -> u32 {
196 unsafe {
197 c::GetProcessId(self.handle.raw()) as u32
198 }
199 }
200
85aaf69f 201 pub fn wait(&self) -> io::Result<ExitStatus> {
85aaf69f 202 unsafe {
92a42be0
SL
203 let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE);
204 if res != c::WAIT_OBJECT_0 {
205 return Err(Error::last_os_error())
85aaf69f 206 }
92a42be0
SL
207 let mut status = 0;
208 try!(cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status)));
209 Ok(ExitStatus(status))
85aaf69f
SL
210 }
211 }
62682a34
SL
212
213 pub fn handle(&self) -> &Handle { &self.handle }
c1a9b12d
SL
214
215 pub fn into_handle(self) -> Handle { self.handle }
85aaf69f
SL
216}
217
218#[derive(PartialEq, Eq, Clone, Copy, Debug)]
92a42be0 219pub struct ExitStatus(c::DWORD);
85aaf69f
SL
220
221impl ExitStatus {
222 pub fn success(&self) -> bool {
223 self.0 == 0
224 }
225 pub fn code(&self) -> Option<i32> {
92a42be0 226 Some(self.0 as i32)
85aaf69f
SL
227 }
228}
229
230impl fmt::Display for ExitStatus {
231 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
232 write!(f, "exit code: {}", self.0)
233 }
234}
235
92a42be0
SL
236fn zeroed_startupinfo() -> c::STARTUPINFO {
237 c::STARTUPINFO {
85aaf69f
SL
238 cb: 0,
239 lpReserved: ptr::null_mut(),
240 lpDesktop: ptr::null_mut(),
241 lpTitle: ptr::null_mut(),
242 dwX: 0,
243 dwY: 0,
244 dwXSize: 0,
245 dwYSize: 0,
246 dwXCountChars: 0,
247 dwYCountCharts: 0,
248 dwFillAttribute: 0,
249 dwFlags: 0,
250 wShowWindow: 0,
251 cbReserved2: 0,
252 lpReserved2: ptr::null_mut(),
92a42be0
SL
253 hStdInput: c::INVALID_HANDLE_VALUE,
254 hStdOutput: c::INVALID_HANDLE_VALUE,
255 hStdError: c::INVALID_HANDLE_VALUE,
85aaf69f
SL
256 }
257}
258
92a42be0
SL
259fn zeroed_process_information() -> c::PROCESS_INFORMATION {
260 c::PROCESS_INFORMATION {
85aaf69f
SL
261 hProcess: ptr::null_mut(),
262 hThread: ptr::null_mut(),
263 dwProcessId: 0,
264 dwThreadId: 0
265 }
266}
267
268// Produces a wide string *without terminating null*
269fn make_command_line(prog: &OsStr, args: &[OsString]) -> Vec<u16> {
d9579d0f
AL
270 // Encode the command and arguments in a command line string such
271 // that the spawned process may recover them using CommandLineToArgvW.
85aaf69f
SL
272 let mut cmd: Vec<u16> = Vec::new();
273 append_arg(&mut cmd, prog);
274 for arg in args {
275 cmd.push(' ' as u16);
276 append_arg(&mut cmd, arg);
277 }
278 return cmd;
279
280 fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr) {
281 // If an argument has 0 characters then we need to quote it to ensure
282 // that it actually gets passed through on the command line or otherwise
283 // it will be dropped entirely when parsed on the other end.
284 let arg_bytes = &arg.as_inner().inner.as_inner();
285 let quote = arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t')
9346a6ac 286 || arg_bytes.is_empty();
85aaf69f
SL
287 if quote {
288 cmd.push('"' as u16);
289 }
290
291 let mut iter = arg.encode_wide();
d9579d0f 292 let mut backslashes: usize = 0;
85aaf69f 293 while let Some(x) = iter.next() {
d9579d0f
AL
294 if x == '\\' as u16 {
295 backslashes += 1;
85aaf69f 296 } else {
d9579d0f
AL
297 if x == '"' as u16 {
298 // Add n+1 backslashes to total 2n+1 before internal '"'.
299 for _ in 0..(backslashes+1) {
300 cmd.push('\\' as u16);
301 }
302 }
303 backslashes = 0;
85aaf69f 304 }
d9579d0f 305 cmd.push(x);
85aaf69f
SL
306 }
307
308 if quote {
d9579d0f
AL
309 // Add n backslashes to total 2n before ending '"'.
310 for _ in 0..backslashes {
311 cmd.push('\\' as u16);
312 }
85aaf69f
SL
313 cmd.push('"' as u16);
314 }
315 }
316}
317
bd371182
AL
318fn make_envp(env: Option<&collections::HashMap<OsString, OsString>>)
319 -> (*mut c_void, Vec<u16>) {
85aaf69f
SL
320 // On Windows we pass an "environment block" which is not a char**, but
321 // rather a concatenation of null-terminated k=v\0 sequences, with a final
322 // \0 to terminate.
323 match env {
324 Some(env) => {
325 let mut blk = Vec::new();
326
327 for pair in env {
328 blk.extend(pair.0.encode_wide());
329 blk.push('=' as u16);
330 blk.extend(pair.1.encode_wide());
331 blk.push(0);
332 }
333 blk.push(0);
bd371182 334 (blk.as_mut_ptr() as *mut c_void, blk)
85aaf69f 335 }
bd371182 336 _ => (ptr::null_mut(), Vec::new())
85aaf69f
SL
337 }
338}
339
bd371182 340fn make_dirp(d: Option<&OsString>) -> (*const u16, Vec<u16>) {
85aaf69f 341 match d {
bd371182
AL
342 Some(dir) => {
343 let mut dir_str: Vec<u16> = dir.encode_wide().collect();
344 dir_str.push(0);
345 (dir_str.as_ptr(), dir_str)
346 },
347 None => (ptr::null(), Vec::new())
348 }
349}
350
351impl Stdio {
92a42be0 352 fn to_handle(&self, stdio_id: c::DWORD) -> io::Result<Handle> {
bd371182
AL
353 match *self {
354 Stdio::Inherit => {
355 stdio::get(stdio_id).and_then(|io| {
92a42be0 356 io.handle().duplicate(0, true, c::DUPLICATE_SAME_ACCESS)
bd371182
AL
357 })
358 }
62682a34 359 Stdio::Raw(handle) => {
92a42be0 360 RawHandle::new(handle).duplicate(0, true, c::DUPLICATE_SAME_ACCESS)
bd371182
AL
361 }
362
363 // Similarly to unix, we don't actually leave holes for the
364 // stdio file descriptors, but rather open up /dev/null
365 // equivalents. These equivalents are drawn from libuv's
366 // windows process spawning.
367 Stdio::None => {
92a42be0
SL
368 let size = mem::size_of::<c::SECURITY_ATTRIBUTES>();
369 let mut sa = c::SECURITY_ATTRIBUTES {
370 nLength: size as c::DWORD,
bd371182
AL
371 lpSecurityDescriptor: ptr::null_mut(),
372 bInheritHandle: 1,
373 };
374 let mut opts = OpenOptions::new();
375 opts.read(stdio_id == c::STD_INPUT_HANDLE);
376 opts.write(stdio_id != c::STD_INPUT_HANDLE);
377 opts.security_attributes(&mut sa);
378 File::open(Path::new("NUL"), &opts).map(|file| {
379 file.into_handle()
380 })
381 }
382 }
85aaf69f
SL
383 }
384}
385
386#[cfg(test)]
387mod tests {
388 use prelude::v1::*;
389 use str;
390 use ffi::{OsStr, OsString};
391 use super::make_command_line;
392
393 #[test]
394 fn test_make_command_line() {
395 fn test_wrapper(prog: &str, args: &[&str]) -> String {
396 String::from_utf16(
9346a6ac 397 &make_command_line(OsStr::new(prog),
c34b1796
AL
398 &args.iter()
399 .map(|a| OsString::from(a))
400 .collect::<Vec<OsString>>())).unwrap()
85aaf69f
SL
401 }
402
403 assert_eq!(
404 test_wrapper("prog", &["aaa", "bbb", "ccc"]),
405 "prog aaa bbb ccc"
406 );
407
408 assert_eq!(
409 test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]),
410 "\"C:\\Program Files\\blah\\blah.exe\" aaa"
411 );
412 assert_eq!(
413 test_wrapper("C:\\Program Files\\test", &["aa\"bb"]),
414 "\"C:\\Program Files\\test\" aa\\\"bb"
415 );
416 assert_eq!(
417 test_wrapper("echo", &["a b c"]),
418 "echo \"a b c\""
419 );
d9579d0f
AL
420 assert_eq!(
421 test_wrapper("echo", &["\" \\\" \\", "\\"]),
422 "echo \"\\\" \\\\\\\" \\\\\" \\"
423 );
85aaf69f
SL
424 assert_eq!(
425 test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
426 "\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}"
427 );
428 }
429}