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.
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 std::c_str::CString;
16 use std::rt::rtio::IoResult;
17 use std::rt::task::BlockedTask;
19 use homing::{HomingIO, HomeHandle};
20 use pipe::PipeWatcher;
21 use super::{UvHandle, UvError, uv_error_to_io_error,
22 wait_until_woken_after, wakeup, Loop};
23 use timer::TimerWatcher;
24 use uvio::UvIoFactory;
28 handle: *mut uvll::uv_process_t,
31 /// Task to wake up (may be null) for when the process exits
32 to_wake: Option<BlockedTask>,
34 /// Collected from the exit_cb
35 exit_status: Option<rtio::ProcessExit>,
37 /// Lazily initialized timeout timer
38 timer: Option<Box<TimerWatcher>>,
39 timeout_state: TimeoutState,
49 /// Spawn a new process inside the specified event loop.
51 /// Returns either the corresponding process object or an error which
53 pub fn spawn(io_loop: &mut UvIoFactory, cfg: rtio::ProcessConfig)
54 -> Result<(Box<Process>, Vec<Option<PipeWatcher>>), UvError> {
55 let mut io = vec![cfg.stdin, cfg.stdout, cfg.stderr];
56 for slot in cfg.extra_io.iter() {
59 let mut stdio = Vec::<uvll::uv_stdio_container_t>::with_capacity(io.len());
60 let mut ret_io = Vec::with_capacity(io.len());
62 stdio.set_len(io.len());
63 for (slot, other) in stdio.mut_iter().zip(io.iter()) {
64 let io = set_stdio(slot as *mut uvll::uv_stdio_container_t, other,
70 let ret = with_argv(cfg.program, cfg.args, |argv| {
71 with_env(cfg.env, |envp| {
73 if cfg.uid.is_some() {
74 flags |= uvll::PROCESS_SETUID;
76 if cfg.gid.is_some() {
77 flags |= uvll::PROCESS_SETGID;
80 flags |= uvll::PROCESS_DETACHED;
82 let mut options = uvll::uv_process_options_t {
84 file: unsafe { *argv },
88 Some(cwd) => cwd.with_ref(|p| p),
91 flags: flags as libc::c_uint,
92 stdio_count: stdio.len() as libc::c_int,
93 stdio: stdio.as_mut_ptr(),
94 uid: cfg.uid.unwrap_or(0) as uvll::uv_uid_t,
95 gid: cfg.gid.unwrap_or(0) as uvll::uv_gid_t,
98 let handle = UvHandle::alloc(None::<Process>, uvll::UV_PROCESS);
99 let process = box Process {
101 home: io_loop.make_handle(),
105 timeout_state: NoTimeout,
108 uvll::uv_spawn(io_loop.uv_loop(), handle, &mut options)
110 0 => Ok(process.install()),
111 err => Err(UvError(err)),
117 Ok(p) => Ok((p, ret_io)),
122 pub fn kill(pid: libc::pid_t, signum: int) -> Result<(), UvError> {
124 uvll::uv_kill(pid as libc::c_int, signum as libc::c_int)
132 extern fn on_exit(handle: *mut uvll::uv_process_t,
134 term_signal: libc::c_int) {
135 let p: &mut Process = unsafe { UvHandle::from_uv_handle(&handle) };
137 assert!(p.exit_status.is_none());
138 p.exit_status = Some(match term_signal {
139 0 => rtio::ExitStatus(exit_status as int),
140 n => rtio::ExitSignal(n as int),
143 if p.to_wake.is_none() { return }
144 wakeup(&mut p.to_wake);
147 unsafe fn set_stdio(dst: *mut uvll::uv_stdio_container_t,
148 io: &rtio::StdioContainer,
149 io_loop: &mut UvIoFactory) -> Option<PipeWatcher> {
152 uvll::set_stdio_container_flags(dst, uvll::STDIO_IGNORE);
155 rtio::InheritFd(fd) => {
156 uvll::set_stdio_container_flags(dst, uvll::STDIO_INHERIT_FD);
157 uvll::set_stdio_container_fd(dst, fd);
160 rtio::CreatePipe(readable, writable) => {
161 let mut flags = uvll::STDIO_CREATE_PIPE as libc::c_int;
163 flags |= uvll::STDIO_READABLE_PIPE as libc::c_int;
166 flags |= uvll::STDIO_WRITABLE_PIPE as libc::c_int;
168 let pipe = PipeWatcher::new(io_loop, false);
169 uvll::set_stdio_container_flags(dst, flags);
170 uvll::set_stdio_container_stream(dst, pipe.handle());
176 /// Converts the program and arguments to the argv array expected by libuv.
177 fn with_argv<T>(prog: &CString, args: &[CString],
178 cb: |*const *const libc::c_char| -> T) -> T {
179 let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1);
181 // Convert the CStrings into an array of pointers. Note: the
182 // lifetime of the various CStrings involved is guaranteed to be
183 // larger than the lifetime of our invocation of cb, but this is
184 // technically unsafe as the callback could leak these pointers
186 ptrs.push(prog.with_ref(|buf| buf));
187 ptrs.extend(args.iter().map(|tmp| tmp.with_ref(|buf| buf)));
189 // Add a terminating null pointer (required by libc).
190 ptrs.push(ptr::null());
195 /// Converts the environment to the env array expected by libuv
196 fn with_env<T>(env: Option<&[(CString, CString)]>,
197 cb: |*const *const libc::c_char| -> T) -> T {
198 // We can pass a char** for envp, which is a null-terminated array
199 // of "k=v\0" strings. Since we must create these strings locally,
200 // yet expose a raw pointer to them, we create a temporary vector
201 // to own the CStrings that outlives the call to cb.
204 let mut tmps = Vec::with_capacity(env.len());
206 for pair in env.iter() {
207 let mut kv = Vec::new();
208 kv.push_all(pair.ref0().as_bytes_no_nul());
210 kv.push_all(pair.ref1().as_bytes()); // includes terminal \0
214 // As with `with_argv`, this is unsafe, since cb could leak the pointers.
215 let mut ptrs: Vec<*const libc::c_char> =
217 .map(|tmp| tmp.as_ptr() as *const libc::c_char)
219 ptrs.push(ptr::null());
227 impl HomingIO for Process {
228 fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
231 impl UvHandle<uvll::uv_process_t> for Process {
232 fn uv_handle(&self) -> *mut uvll::uv_process_t { self.handle }
235 impl rtio::RtioProcess for Process {
236 fn id(&self) -> libc::pid_t {
237 unsafe { uvll::process_pid(self.handle) as libc::pid_t }
240 fn kill(&mut self, signal: int) -> IoResult<()> {
241 let _m = self.fire_homing_missile();
243 uvll::uv_process_kill(self.handle, signal as libc::c_int)
246 err => Err(uv_error_to_io_error(UvError(err)))
250 fn wait(&mut self) -> IoResult<rtio::ProcessExit> {
251 // Make sure (on the home scheduler) that we have an exit status listed
252 let _m = self.fire_homing_missile();
253 match self.exit_status {
254 Some(status) => return Ok(status),
258 // If there's no exit code previously listed, then the process's exit
259 // callback has yet to be invoked. We just need to deschedule ourselves
260 // and wait to be reawoken.
261 match self.timeout_state {
262 NoTimeout | TimeoutPending => {
263 wait_until_woken_after(&mut self.to_wake, &self.uv_loop(), || {});
268 // If there's still no exit status listed, then we timed out, and we
270 match self.exit_status {
271 Some(status) => Ok(status),
272 None => Err(uv_error_to_io_error(UvError(uvll::ECANCELED)))
276 fn set_timeout(&mut self, timeout: Option<u64>) {
277 let _m = self.fire_homing_missile();
278 self.timeout_state = NoTimeout;
279 let ms = match timeout {
283 Some(ref mut timer) => timer.stop(),
289 if self.timer.is_none() {
290 let loop_ = Loop::wrap(unsafe {
291 uvll::get_loop_for_uv_handle(self.uv_handle())
293 let mut timer = box TimerWatcher::new_home(&loop_, self.home().clone());
295 timer.set_data(self as *mut _);
297 self.timer = Some(timer);
300 let timer = self.timer.get_mut_ref();
302 timer.start(timer_cb, ms, 0);
303 self.timeout_state = TimeoutPending;
305 extern fn timer_cb(timer: *mut uvll::uv_timer_t) {
306 let p: &mut Process = unsafe {
307 &mut *(uvll::get_data_for_uv_handle(timer) as *mut Process)
309 p.timeout_state = TimeoutElapsed;
310 match p.to_wake.take() {
311 Some(task) => { let _t = task.wake().map(|t| t.reawaken()); }
318 impl Drop for Process {
320 let _m = self.fire_homing_missile();
321 assert!(self.to_wake.is_none());