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::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};
22 use super::{Loop, UvHandle, UvError, uv_error_to_io_error,
23 wait_until_woken_after};
26 use pipe::PipeWatcher;
29 handle: *uvll::uv_process_t,
32 /// Task to wake up (may be null) for when the process exits
33 to_wake: Option<BlockedTask>,
35 /// Collected from the exit_cb
36 exit_status: Option<int>,
37 term_signal: Option<int>,
41 /// Spawn a new process inside the specified event loop.
43 /// Returns either the corresponding process object or an error which
45 pub fn spawn(loop_: &Loop, config: ProcessConfig)
46 -> Result<(~Process, ~[Option<PipeWatcher>]), UvError>
48 let cwd = config.cwd.map(|s| s.to_c_str());
50 let mut stdio = vec::with_capacity::<uvll::uv_stdio_container_t>(io.len());
51 let mut ret_io = vec::with_capacity(io.len());
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,
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 {
65 file: unsafe { *argv },
69 Some(ref cwd) => cwd.with_ref(|p| p),
73 stdio_count: stdio.len() as libc::c_int,
74 stdio: stdio.as_imm_buf(|p, _| p),
79 let handle = UvHandle::alloc(None::<Process>, uvll::UV_PROCESS);
80 let process = ~Process {
82 home: get_handle_to_current_scheduler!(),
88 uvll::uv_spawn(loop_.handle, handle, &options)
90 0 => Ok(process.install()),
91 err => Err(UvError(err)),
97 Ok(p) => Ok((p, ret_io)),
103 extern fn on_exit(handle: *uvll::uv_process_t,
105 term_signal: libc::c_int) {
106 let p: &mut Process = unsafe { UvHandle::from_uv_handle(&handle) };
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);
113 match p.to_wake.take() {
115 let scheduler: ~Scheduler = Local::take();
116 scheduler.resume_blocked_task_immediately(task);
122 unsafe fn set_stdio(dst: *uvll::uv_stdio_container_t,
124 loop_: &Loop) -> Option<PipeWatcher> {
127 uvll::set_stdio_container_flags(dst, uvll::STDIO_IGNORE);
131 uvll::set_stdio_container_flags(dst, uvll::STDIO_INHERIT_FD);
132 uvll::set_stdio_container_fd(dst, fd);
135 CreatePipe(readable, writable) => {
136 let mut flags = uvll::STDIO_CREATE_PIPE as libc::c_int;
138 flags |= uvll::STDIO_READABLE_PIPE as libc::c_int;
141 flags |= uvll::STDIO_WRITABLE_PIPE as libc::c_int;
143 let pipe = PipeWatcher::new(loop_, false);
144 uvll::set_stdio_container_flags(dst, flags);
145 uvll::set_stdio_container_stream(dst, pipe.handle());
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());
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));
166 c_args.push(ptr::null());
167 c_args.as_imm_buf(|buf, _| f(buf))
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 {
174 None => { return f(ptr::null()); }
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());
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));
185 c_envp.push(ptr::null());
186 c_envp.as_imm_buf(|buf, _| f(buf))
189 impl HomingIO for Process {
190 fn home<'r>(&'r mut self) -> &'r mut SchedHandle { &mut self.home }
193 impl UvHandle<uvll::uv_process_t> for Process {
194 fn uv_handle(&self) -> *uvll::uv_process_t { self.handle }
197 impl RtioProcess for Process {
198 fn id(&self) -> libc::pid_t {
199 unsafe { uvll::process_pid(self.handle) as libc::pid_t }
202 fn kill(&mut self, signal: int) -> Result<(), IoError> {
203 let _m = self.fire_homing_missile();
205 uvll::uv_process_kill(self.handle, signal as libc::c_int)
208 err => Err(uv_error_to_io_error(UvError(err)))
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 {
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());
226 // FIXME(#10109): this is wrong
227 self.exit_status.unwrap()
231 impl Drop for Process {
233 let _m = self.fire_homing_missile();
234 assert!(self.to_wake.is_none());