// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use prelude::*; use cell::Cell; use libc; use ptr; use util; use vec; use rt::io::process::*; use rt::uv; use rt::uv::uvio::UvPipeStream; use rt::uv::uvll; /// A process wraps the handle of the underlying uv_process_t. pub struct Process(*uvll::uv_process_t); impl uv::Watcher for Process {} impl Process { /// Creates a new process, ready to spawn inside an event loop pub fn new() -> Process { let handle = unsafe { uvll::malloc_handle(uvll::UV_PROCESS) }; assert!(handle.is_not_null()); let mut ret: Process = uv::NativeHandle::from_native_handle(handle); ret.install_watcher_data(); return ret; } /// Spawn a new process inside the specified event loop. /// /// The `config` variable will be passed down to libuv, and the `exit_cb` /// will be run only once, when the process exits. /// /// Returns either the corresponding process object or an error which /// occurred. pub fn spawn(&mut self, loop_: &uv::Loop, mut config: ProcessConfig, exit_cb: uv::ExitCallback) -> Result<~[Option], uv::UvError> { let cwd = config.cwd.map_move(|s| s.to_c_str()); extern fn on_exit(p: *uvll::uv_process_t, exit_status: libc::c_int, term_signal: libc::c_int) { let mut p: Process = uv::NativeHandle::from_native_handle(p); let err = match exit_status { 0 => None, _ => uv::status_to_maybe_uv_error(-1) }; p.get_watcher_data().exit_cb.take_unwrap()(p, exit_status as int, term_signal as int, err); } let io = util::replace(&mut config.io, ~[]); let mut stdio = vec::with_capacity::(io.len()); let mut ret_io = vec::with_capacity(io.len()); unsafe { vec::raw::set_len(&mut stdio, io.len()); for (slot, other) in stdio.iter().zip(io.move_iter()) { let io = set_stdio(slot as *uvll::uv_stdio_container_t, other); ret_io.push(io); } } let exit_cb = Cell::new(exit_cb); let ret_io = Cell::new(ret_io); do with_argv(config.program, config.args) |argv| { do with_env(config.env) |envp| { let options = uvll::uv_process_options_t { exit_cb: on_exit, file: unsafe { *argv }, args: argv, env: envp, cwd: match cwd { Some(ref cwd) => cwd.with_ref(|p| p), None => ptr::null(), }, flags: 0, stdio_count: stdio.len() as libc::c_int, stdio: stdio.as_imm_buf(|p, _| p), uid: 0, gid: 0, }; match unsafe { uvll::spawn(loop_.native_handle(), **self, options) } { 0 => { (*self).get_watcher_data().exit_cb = Some(exit_cb.take()); Ok(ret_io.take()) } err => Err(uv::UvError(err)) } } } } /// Sends a signal to this process. /// /// This is a wrapper around `uv_process_kill` pub fn kill(&self, signum: int) -> Result<(), uv::UvError> { match unsafe { uvll::process_kill(self.native_handle(), signum as libc::c_int) } { 0 => Ok(()), err => Err(uv::UvError(err)) } } /// Returns the process id of a spawned process pub fn pid(&self) -> libc::pid_t { unsafe { uvll::process_pid(**self) as libc::pid_t } } /// Closes this handle, invoking the specified callback once closed pub fn close(self, cb: uv::NullCallback) { { let mut this = self; let data = this.get_watcher_data(); assert!(data.close_cb.is_none()); data.close_cb = Some(cb); } unsafe { uvll::close(self.native_handle(), close_cb); } extern fn close_cb(handle: *uvll::uv_process_t) { let mut process: Process = uv::NativeHandle::from_native_handle(handle); process.get_watcher_data().close_cb.take_unwrap()(); process.drop_watcher_data(); unsafe { uvll::free_handle(handle as *libc::c_void) } } } } unsafe fn set_stdio(dst: *uvll::uv_stdio_container_t, io: StdioContainer) -> Option { match io { Ignored => { uvll::set_stdio_container_flags(dst, uvll::STDIO_IGNORE); None } InheritFd(fd) => { uvll::set_stdio_container_flags(dst, uvll::STDIO_INHERIT_FD); uvll::set_stdio_container_fd(dst, fd); None } CreatePipe(pipe, readable, writable) => { let mut flags = uvll::STDIO_CREATE_PIPE as libc::c_int; if readable { flags |= uvll::STDIO_READABLE_PIPE as libc::c_int; } if writable { flags |= uvll::STDIO_WRITABLE_PIPE as libc::c_int; } let handle = pipe.pipe.as_stream().native_handle(); uvll::set_stdio_container_flags(dst, flags); uvll::set_stdio_container_stream(dst, handle); Some(pipe.bind()) } } } /// Converts the program and arguments to the argv array expected by libuv fn with_argv(prog: &str, args: &[~str], f: &fn(**libc::c_char) -> T) -> T { // First, allocation space to put all the C-strings (we need to have // ownership of them somewhere let mut c_strs = vec::with_capacity(args.len() + 1); c_strs.push(prog.to_c_str()); for arg in args.iter() { c_strs.push(arg.to_c_str()); } // Next, create the char** array let mut c_args = vec::with_capacity(c_strs.len() + 1); for s in c_strs.iter() { c_args.push(s.with_ref(|p| p)); } c_args.push(ptr::null()); c_args.as_imm_buf(|buf, _| f(buf)) } /// Converts the environment to the env array expected by libuv fn with_env(env: Option<&[(~str, ~str)]>, f: &fn(**libc::c_char) -> T) -> T { let env = match env { Some(s) => s, None => { return f(ptr::null()); } }; // As with argv, create some temporary storage and then the actual array let mut envp = vec::with_capacity(env.len()); for &(ref key, ref value) in env.iter() { envp.push(fmt!("%s=%s", *key, *value).to_c_str()); } let mut c_envp = vec::with_capacity(envp.len() + 1); for s in envp.iter() { c_envp.push(s.with_ref(|p| p)); } c_envp.push(ptr::null()); c_envp.as_imm_buf(|buf, _| f(buf)) } impl uv::NativeHandle<*uvll::uv_process_t> for Process { fn from_native_handle(handle: *uvll::uv_process_t) -> Process { Process(handle) } fn native_handle(&self) -> *uvll::uv_process_t { match self { &Process(ptr) => ptr } } }