]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/process.rs
Add verbose option to rustdoc in order to fix problem with --version
[rust.git] / src / libstd / sys / windows / process.rs
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
11 use libc::{pid_t, c_void, c_int};
12 use libc;
13 use c_str::CString;
14 use io;
15 use mem;
16 use os;
17 use ptr;
18 use prelude::*;
19 use io::process::{ProcessExit, ExitStatus, ExitSignal};
20 use collections;
21 use path::BytesContainer;
22 use hash::Hash;
23 use io::{IoResult, IoError};
24
25 use sys::fs;
26 use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer};
27 use sys::fs::FileDesc;
28 use sys_common::helper_thread::Helper;
29 use sys_common::{AsInner, mkerr_libc, timeout};
30
31 use io::fs::PathExtensions;
32
33 pub use sys_common::ProcessConfig;
34
35 /// A value representing a child process.
36 ///
37 /// The lifetime of this value is linked to the lifetime of the actual
38 /// process - the Process destructor calls self.finish() which waits
39 /// for the process to terminate.
40 pub struct Process {
41     /// The unique id of the process (this should never be negative).
42     pid: pid_t,
43
44     /// A HANDLE to the process, which will prevent the pid being
45     /// re-used until the handle is closed.
46     handle: *mut (),
47 }
48
49 impl Drop for Process {
50     fn drop(&mut self) {
51         free_handle(self.handle);
52     }
53 }
54
55 impl Process {
56     pub fn id(&self) -> pid_t {
57         self.pid
58     }
59
60     pub unsafe fn kill(&self, signal: int) -> IoResult<()> {
61         Process::killpid(self.pid, signal)
62     }
63
64     pub unsafe fn killpid(pid: pid_t, signal: int) -> IoResult<()> {
65         let handle = libc::OpenProcess(libc::PROCESS_TERMINATE |
66                                        libc::PROCESS_QUERY_INFORMATION,
67                                        libc::FALSE, pid as libc::DWORD);
68         if handle.is_null() {
69             return Err(super::last_error())
70         }
71         let ret = match signal {
72             // test for existence on signal 0
73             0 => {
74                 let mut status = 0;
75                 let ret = libc::GetExitCodeProcess(handle, &mut status);
76                 if ret == 0 {
77                     Err(super::last_error())
78                 } else if status != libc::STILL_ACTIVE {
79                     Err(IoError {
80                         kind: io::InvalidInput,
81                         desc: "no process to kill",
82                         detail: None,
83                     })
84                 } else {
85                     Ok(())
86                 }
87             }
88             15 | 9 => { // sigterm or sigkill
89                 let ret = libc::TerminateProcess(handle, 1);
90                 super::mkerr_winbool(ret)
91             }
92             _ => Err(IoError {
93                 kind: io::IoUnavailable,
94                 desc: "unsupported signal on windows",
95                 detail: None,
96             })
97         };
98         let _ = libc::CloseHandle(handle);
99         return ret;
100     }
101
102     pub fn spawn<K, V, C, P>(cfg: &C, in_fd: Option<P>,
103                               out_fd: Option<P>, err_fd: Option<P>)
104                               -> IoResult<Process>
105         where C: ProcessConfig<K, V>, P: AsInner<FileDesc>,
106               K: BytesContainer + Eq + Hash, V: BytesContainer
107     {
108         use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
109         use libc::consts::os::extra::{
110             TRUE, FALSE,
111             STARTF_USESTDHANDLES,
112             INVALID_HANDLE_VALUE,
113             DUPLICATE_SAME_ACCESS
114         };
115         use libc::funcs::extra::kernel32::{
116             GetCurrentProcess,
117             DuplicateHandle,
118             CloseHandle,
119             CreateProcessW
120         };
121         use libc::funcs::extra::msvcrt::get_osfhandle;
122
123         use mem;
124         use iter::{Iterator, IteratorExt};
125         use str::StrExt;
126
127         if cfg.gid().is_some() || cfg.uid().is_some() {
128             return Err(IoError {
129                 kind: io::IoUnavailable,
130                 desc: "unsupported gid/uid requested on windows",
131                 detail: None,
132             })
133         }
134
135         // To have the spawning semantics of unix/windows stay the same, we need to
136         // read the *child's* PATH if one is provided. See #15149 for more details.
137         let program = cfg.env().and_then(|env| {
138             for (key, v) in env.iter() {
139                 if b"PATH" != key.container_as_bytes() { continue }
140
141                 // Split the value and test each path to see if the
142                 // program exists.
143                 for path in os::split_paths(v.container_as_bytes()).into_iter() {
144                     let path = path.join(cfg.program().as_bytes_no_nul())
145                                    .with_extension(os::consts::EXE_EXTENSION);
146                     if path.exists() {
147                         return Some(path.to_c_str())
148                     }
149                 }
150                 break
151             }
152             None
153         });
154
155         unsafe {
156             let mut si = zeroed_startupinfo();
157             si.cb = mem::size_of::<STARTUPINFO>() as DWORD;
158             si.dwFlags = STARTF_USESTDHANDLES;
159
160             let cur_proc = GetCurrentProcess();
161
162             // Similarly to unix, we don't actually leave holes for the stdio file
163             // descriptors, but rather open up /dev/null equivalents. These
164             // equivalents are drawn from libuv's windows process spawning.
165             let set_fd = |fd: &Option<P>, slot: &mut HANDLE,
166                           is_stdin: bool| {
167                 match *fd {
168                     None => {
169                         let access = if is_stdin {
170                             libc::FILE_GENERIC_READ
171                         } else {
172                             libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES
173                         };
174                         let size = mem::size_of::<libc::SECURITY_ATTRIBUTES>();
175                         let mut sa = libc::SECURITY_ATTRIBUTES {
176                             nLength: size as libc::DWORD,
177                             lpSecurityDescriptor: ptr::null_mut(),
178                             bInheritHandle: 1,
179                         };
180                         let mut filename: Vec<u16> = "NUL".utf16_units().collect();
181                         filename.push(0);
182                         *slot = libc::CreateFileW(filename.as_ptr(),
183                                                   access,
184                                                   libc::FILE_SHARE_READ |
185                                                       libc::FILE_SHARE_WRITE,
186                                                   &mut sa,
187                                                   libc::OPEN_EXISTING,
188                                                   0,
189                                                   ptr::null_mut());
190                         if *slot == INVALID_HANDLE_VALUE {
191                             return Err(super::last_error())
192                         }
193                     }
194                     Some(ref fd) => {
195                         let orig = get_osfhandle(fd.as_inner().fd()) as HANDLE;
196                         if orig == INVALID_HANDLE_VALUE {
197                             return Err(super::last_error())
198                         }
199                         if DuplicateHandle(cur_proc, orig, cur_proc, slot,
200                                            0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
201                             return Err(super::last_error())
202                         }
203                     }
204                 }
205                 Ok(())
206             };
207
208             try!(set_fd(&in_fd, &mut si.hStdInput, true));
209             try!(set_fd(&out_fd, &mut si.hStdOutput, false));
210             try!(set_fd(&err_fd, &mut si.hStdError, false));
211
212             let cmd_str = make_command_line(program.as_ref().unwrap_or(cfg.program()),
213                                             cfg.args());
214             let mut pi = zeroed_process_information();
215             let mut create_err = None;
216
217             // stolen from the libuv code.
218             let mut flags = libc::CREATE_UNICODE_ENVIRONMENT;
219             if cfg.detach() {
220                 flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP;
221             }
222
223             with_envp(cfg.env(), |envp| {
224                 with_dirp(cfg.cwd(), |dirp| {
225                     let mut cmd_str: Vec<u16> = cmd_str.utf16_units().collect();
226                     cmd_str.push(0);
227                     let created = CreateProcessW(ptr::null(),
228                                                  cmd_str.as_mut_ptr(),
229                                                  ptr::null_mut(),
230                                                  ptr::null_mut(),
231                                                  TRUE,
232                                                  flags, envp, dirp,
233                                                  &mut si, &mut pi);
234                     if created == FALSE {
235                         create_err = Some(super::last_error());
236                     }
237                 })
238             });
239
240             assert!(CloseHandle(si.hStdInput) != 0);
241             assert!(CloseHandle(si.hStdOutput) != 0);
242             assert!(CloseHandle(si.hStdError) != 0);
243
244             match create_err {
245                 Some(err) => return Err(err),
246                 None => {}
247             }
248
249             // We close the thread handle because we don't care about keeping the
250             // thread id valid, and we aren't keeping the thread handle around to be
251             // able to close it later. We don't close the process handle however
252             // because std::we want the process id to stay valid at least until the
253             // calling code closes the process handle.
254             assert!(CloseHandle(pi.hThread) != 0);
255
256             Ok(Process {
257                 pid: pi.dwProcessId as pid_t,
258                 handle: pi.hProcess as *mut ()
259             })
260         }
261     }
262
263     /// Waits for a process to exit and returns the exit code, failing
264     /// if there is no process with the specified id.
265     ///
266     /// Note that this is private to avoid race conditions on unix where if
267     /// a user calls waitpid(some_process.get_id()) then some_process.finish()
268     /// and some_process.destroy() and some_process.finalize() will then either
269     /// operate on a none-existent process or, even worse, on a newer process
270     /// with the same id.
271     pub fn wait(&self, deadline: u64) -> IoResult<ProcessExit> {
272         use libc::types::os::arch::extra::DWORD;
273         use libc::consts::os::extra::{
274             SYNCHRONIZE,
275             PROCESS_QUERY_INFORMATION,
276             FALSE,
277             STILL_ACTIVE,
278             INFINITE,
279             WAIT_TIMEOUT,
280             WAIT_OBJECT_0,
281         };
282         use libc::funcs::extra::kernel32::{
283             OpenProcess,
284             GetExitCodeProcess,
285             CloseHandle,
286             WaitForSingleObject,
287         };
288
289         unsafe {
290             let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
291                                       FALSE,
292                                       self.pid as DWORD);
293             if process.is_null() {
294                 return Err(super::last_error())
295             }
296
297             loop {
298                 let mut status = 0;
299                 if GetExitCodeProcess(process, &mut status) == FALSE {
300                     let err = Err(super::last_error());
301                     assert!(CloseHandle(process) != 0);
302                     return err;
303                 }
304                 if status != STILL_ACTIVE {
305                     assert!(CloseHandle(process) != 0);
306                     return Ok(ExitStatus(status as int));
307                 }
308                 let interval = if deadline == 0 {
309                     INFINITE
310                 } else {
311                     let now = timer::now();
312                     if deadline < now {0} else {(deadline - now) as u32}
313                 };
314                 match WaitForSingleObject(process, interval) {
315                     WAIT_OBJECT_0 => {}
316                     WAIT_TIMEOUT => {
317                         assert!(CloseHandle(process) != 0);
318                         return Err(timeout("process wait timed out"))
319                     }
320                     _ => {
321                         let err = Err(super::last_error());
322                         assert!(CloseHandle(process) != 0);
323                         return err
324                     }
325                 }
326             }
327         }
328     }
329 }
330
331 fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
332     libc::types::os::arch::extra::STARTUPINFO {
333         cb: 0,
334         lpReserved: ptr::null_mut(),
335         lpDesktop: ptr::null_mut(),
336         lpTitle: ptr::null_mut(),
337         dwX: 0,
338         dwY: 0,
339         dwXSize: 0,
340         dwYSize: 0,
341         dwXCountChars: 0,
342         dwYCountCharts: 0,
343         dwFillAttribute: 0,
344         dwFlags: 0,
345         wShowWindow: 0,
346         cbReserved2: 0,
347         lpReserved2: ptr::null_mut(),
348         hStdInput: libc::INVALID_HANDLE_VALUE,
349         hStdOutput: libc::INVALID_HANDLE_VALUE,
350         hStdError: libc::INVALID_HANDLE_VALUE,
351     }
352 }
353
354 fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION {
355     libc::types::os::arch::extra::PROCESS_INFORMATION {
356         hProcess: ptr::null_mut(),
357         hThread: ptr::null_mut(),
358         dwProcessId: 0,
359         dwThreadId: 0
360     }
361 }
362
363 fn make_command_line(prog: &CString, args: &[CString]) -> String {
364     let mut cmd = String::new();
365     append_arg(&mut cmd, prog.as_str()
366                              .expect("expected program name to be utf-8 encoded"));
367     for arg in args.iter() {
368         cmd.push(' ');
369         append_arg(&mut cmd, arg.as_str()
370                                 .expect("expected argument to be utf-8 encoded"));
371     }
372     return cmd;
373
374     fn append_arg(cmd: &mut String, arg: &str) {
375         // If an argument has 0 characters then we need to quote it to ensure
376         // that it actually gets passed through on the command line or otherwise
377         // it will be dropped entirely when parsed on the other end.
378         let quote = arg.chars().any(|c| c == ' ' || c == '\t') || arg.len() == 0;
379         if quote {
380             cmd.push('"');
381         }
382         let argvec: Vec<char> = arg.chars().collect();
383         for i in range(0u, argvec.len()) {
384             append_char_at(cmd, argvec.as_slice(), i);
385         }
386         if quote {
387             cmd.push('"');
388         }
389     }
390
391     fn append_char_at(cmd: &mut String, arg: &[char], i: uint) {
392         match arg[i] {
393             '"' => {
394                 // Escape quotes.
395                 cmd.push_str("\\\"");
396             }
397             '\\' => {
398                 if backslash_run_ends_in_quote(arg, i) {
399                     // Double all backslashes that are in runs before quotes.
400                     cmd.push_str("\\\\");
401                 } else {
402                     // Pass other backslashes through unescaped.
403                     cmd.push('\\');
404                 }
405             }
406             c => {
407                 cmd.push(c);
408             }
409         }
410     }
411
412     fn backslash_run_ends_in_quote(s: &[char], mut i: uint) -> bool {
413         while i < s.len() && s[i] == '\\' {
414             i += 1;
415         }
416         return i < s.len() && s[i] == '"';
417     }
418 }
419
420 fn with_envp<K, V, T, F>(env: Option<&collections::HashMap<K, V>>, cb: F) -> T where
421     K: BytesContainer + Eq + Hash, V: BytesContainer, F: FnOnce(*mut c_void) -> T,
422 {
423     // On Windows we pass an "environment block" which is not a char**, but
424     // rather a concatenation of null-terminated k=v\0 sequences, with a final
425     // \0 to terminate.
426     match env {
427         Some(env) => {
428             let mut blk = Vec::new();
429
430             for pair in env.iter() {
431                 let kv = format!("{}={}",
432                                  pair.0.container_as_str().unwrap(),
433                                  pair.1.container_as_str().unwrap());
434                 blk.extend(kv.utf16_units());
435                 blk.push(0);
436             }
437
438             blk.push(0);
439
440             cb(blk.as_mut_ptr() as *mut c_void)
441         }
442         _ => cb(ptr::null_mut())
443     }
444 }
445
446 fn with_dirp<T, F>(d: Option<&CString>, cb: F) -> T where
447     F: FnOnce(*const u16) -> T,
448 {
449     match d {
450       Some(dir) => {
451           let dir_str = dir.as_str()
452                            .expect("expected workingdirectory to be utf-8 encoded");
453           let mut dir_str: Vec<u16> = dir_str.utf16_units().collect();
454           dir_str.push(0);
455           cb(dir_str.as_ptr())
456       },
457       None => cb(ptr::null())
458     }
459 }
460
461 fn free_handle(handle: *mut ()) {
462     assert!(unsafe {
463         libc::CloseHandle(mem::transmute(handle)) != 0
464     })
465 }
466
467 #[cfg(test)]
468 mod tests {
469
470     #[test]
471     fn test_make_command_line() {
472         use prelude::*;
473         use str;
474         use c_str::CString;
475         use super::make_command_line;
476
477         fn test_wrapper(prog: &str, args: &[&str]) -> String {
478             make_command_line(&prog.to_c_str(),
479                               args.iter()
480                                   .map(|a| a.to_c_str())
481                                   .collect::<Vec<CString>>()
482                                   .as_slice())
483         }
484
485         assert_eq!(
486             test_wrapper("prog", &["aaa", "bbb", "ccc"]),
487             "prog aaa bbb ccc"
488         );
489
490         assert_eq!(
491             test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]),
492             "\"C:\\Program Files\\blah\\blah.exe\" aaa"
493         );
494         assert_eq!(
495             test_wrapper("C:\\Program Files\\test", &["aa\"bb"]),
496             "\"C:\\Program Files\\test\" aa\\\"bb"
497         );
498         assert_eq!(
499             test_wrapper("echo", &["a b c"]),
500             "echo \"a b c\""
501         );
502         assert_eq!(
503             test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
504             "\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}"
505         );
506     }
507 }