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