]> git.lizzy.rs Git - rust.git/blob - src/librustuv/process.rs
Fix usage of libuv for windows
[rust.git] / src / librustuv / process.rs
1 // Copyright 2013 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 std::libc::c_int;
12 use std::libc;
13 use std::ptr;
14 use std::rt::BlockedTask;
15 use std::rt::io::IoError;
16 use std::rt::io::process::*;
17 use std::rt::local::Local;
18 use std::rt::rtio::RtioProcess;
19 use std::rt::sched::{Scheduler, SchedHandle};
20 use std::vec;
21
22 use super::{Loop, UvHandle, UvError, uv_error_to_io_error,
23             wait_until_woken_after};
24 use uvio::HomingIO;
25 use uvll;
26 use pipe::PipeWatcher;
27
28 pub struct Process {
29     handle: *uvll::uv_process_t,
30     home: SchedHandle,
31
32     /// Task to wake up (may be null) for when the process exits
33     to_wake: Option<BlockedTask>,
34
35     /// Collected from the exit_cb
36     exit_status: Option<int>,
37     term_signal: Option<int>,
38 }
39
40 impl Process {
41     /// Spawn a new process inside the specified event loop.
42     ///
43     /// Returns either the corresponding process object or an error which
44     /// occurred.
45     pub fn spawn(loop_: &Loop, config: ProcessConfig)
46                 -> Result<(~Process, ~[Option<PipeWatcher>]), UvError>
47     {
48         let cwd = config.cwd.map(|s| s.to_c_str());
49         let io = config.io;
50         let mut stdio = vec::with_capacity::<uvll::uv_stdio_container_t>(io.len());
51         let mut ret_io = vec::with_capacity(io.len());
52         unsafe {
53             vec::raw::set_len(&mut stdio, io.len());
54             for (slot, other) in stdio.iter().zip(io.iter()) {
55                 let io = set_stdio(slot as *uvll::uv_stdio_container_t, other,
56                                    loop_);
57                 ret_io.push(io);
58             }
59         }
60
61         let ret = do with_argv(config.program, config.args) |argv| {
62             do with_env(config.env) |envp| {
63                 let options = uvll::uv_process_options_t {
64                     exit_cb: on_exit,
65                     file: unsafe { *argv },
66                     args: argv,
67                     env: envp,
68                     cwd: match cwd {
69                         Some(ref cwd) => cwd.with_ref(|p| p),
70                         None => ptr::null(),
71                     },
72                     flags: 0,
73                     stdio_count: stdio.len() as libc::c_int,
74                     stdio: stdio.as_imm_buf(|p, _| p),
75                     uid: 0,
76                     gid: 0,
77                 };
78
79                 let handle = UvHandle::alloc(None::<Process>, uvll::UV_PROCESS);
80                 let process = ~Process {
81                     handle: handle,
82                     home: get_handle_to_current_scheduler!(),
83                     to_wake: None,
84                     exit_status: None,
85                     term_signal: None,
86                 };
87                 match unsafe {
88                     uvll::uv_spawn(loop_.handle, handle, &options)
89                 } {
90                     0 => Ok(process.install()),
91                     err => Err(UvError(err)),
92                 }
93             }
94         };
95
96         match ret {
97             Ok(p) => Ok((p, ret_io)),
98             Err(e) => Err(e),
99         }
100     }
101 }
102
103 extern fn on_exit(handle: *uvll::uv_process_t,
104                   exit_status: i64,
105                   term_signal: libc::c_int) {
106     let p: &mut Process = unsafe { UvHandle::from_uv_handle(&handle) };
107
108     assert!(p.exit_status.is_none());
109     assert!(p.term_signal.is_none());
110     p.exit_status = Some(exit_status as int);
111     p.term_signal = Some(term_signal as int);
112
113     match p.to_wake.take() {
114         Some(task) => {
115             let scheduler: ~Scheduler = Local::take();
116             scheduler.resume_blocked_task_immediately(task);
117         }
118         None => {}
119     }
120 }
121
122 unsafe fn set_stdio(dst: *uvll::uv_stdio_container_t,
123                     io: &StdioContainer,
124                     loop_: &Loop) -> Option<PipeWatcher> {
125     match *io {
126         Ignored => {
127             uvll::set_stdio_container_flags(dst, uvll::STDIO_IGNORE);
128             None
129         }
130         InheritFd(fd) => {
131             uvll::set_stdio_container_flags(dst, uvll::STDIO_INHERIT_FD);
132             uvll::set_stdio_container_fd(dst, fd);
133             None
134         }
135         CreatePipe(readable, writable) => {
136             let mut flags = uvll::STDIO_CREATE_PIPE as libc::c_int;
137             if readable {
138                 flags |= uvll::STDIO_READABLE_PIPE as libc::c_int;
139             }
140             if writable {
141                 flags |= uvll::STDIO_WRITABLE_PIPE as libc::c_int;
142             }
143             let pipe = PipeWatcher::new(loop_, false);
144             uvll::set_stdio_container_flags(dst, flags);
145             uvll::set_stdio_container_stream(dst, pipe.handle());
146             Some(pipe)
147         }
148     }
149 }
150
151 /// Converts the program and arguments to the argv array expected by libuv
152 fn with_argv<T>(prog: &str, args: &[~str], f: &fn(**libc::c_char) -> T) -> T {
153     // First, allocation space to put all the C-strings (we need to have
154     // ownership of them somewhere
155     let mut c_strs = vec::with_capacity(args.len() + 1);
156     c_strs.push(prog.to_c_str());
157     for arg in args.iter() {
158         c_strs.push(arg.to_c_str());
159     }
160
161     // Next, create the char** array
162     let mut c_args = vec::with_capacity(c_strs.len() + 1);
163     for s in c_strs.iter() {
164         c_args.push(s.with_ref(|p| p));
165     }
166     c_args.push(ptr::null());
167     c_args.as_imm_buf(|buf, _| f(buf))
168 }
169
170 /// Converts the environment to the env array expected by libuv
171 fn with_env<T>(env: Option<&[(~str, ~str)]>, f: &fn(**libc::c_char) -> T) -> T {
172     let env = match env {
173         Some(s) => s,
174         None => { return f(ptr::null()); }
175     };
176     // As with argv, create some temporary storage and then the actual array
177     let mut envp = vec::with_capacity(env.len());
178     for &(ref key, ref value) in env.iter() {
179         envp.push(format!("{}={}", *key, *value).to_c_str());
180     }
181     let mut c_envp = vec::with_capacity(envp.len() + 1);
182     for s in envp.iter() {
183         c_envp.push(s.with_ref(|p| p));
184     }
185     c_envp.push(ptr::null());
186     c_envp.as_imm_buf(|buf, _| f(buf))
187 }
188
189 impl HomingIO for Process {
190     fn home<'r>(&'r mut self) -> &'r mut SchedHandle { &mut self.home }
191 }
192
193 impl UvHandle<uvll::uv_process_t> for Process {
194     fn uv_handle(&self) -> *uvll::uv_process_t { self.handle }
195 }
196
197 impl RtioProcess for Process {
198     fn id(&self) -> libc::pid_t {
199         unsafe { uvll::process_pid(self.handle) as libc::pid_t }
200     }
201
202     fn kill(&mut self, signal: int) -> Result<(), IoError> {
203         let _m = self.fire_homing_missile();
204         match unsafe {
205             uvll::uv_process_kill(self.handle, signal as libc::c_int)
206         } {
207             0 => Ok(()),
208             err => Err(uv_error_to_io_error(UvError(err)))
209         }
210     }
211
212     fn wait(&mut self) -> int {
213         // Make sure (on the home scheduler) that we have an exit status listed
214         let _m = self.fire_homing_missile();
215         match self.exit_status {
216             Some(*) => {}
217             None => {
218                 // If there's no exit code previously listed, then the
219                 // process's exit callback has yet to be invoked. We just
220                 // need to deschedule ourselves and wait to be reawoken.
221                 wait_until_woken_after(&mut self.to_wake, || {});
222                 assert!(self.exit_status.is_some());
223             }
224         }
225
226         // FIXME(#10109): this is wrong
227         self.exit_status.unwrap()
228     }
229 }
230
231 impl Drop for Process {
232     fn drop(&mut self) {
233         let _m = self.fire_homing_missile();
234         assert!(self.to_wake.is_none());
235         self.close();
236     }
237 }