rust/src/librustuv/process.rs

203 lines
7.1 KiB
Rust
Raw Normal View History

// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::cell::Cell;
use std::libc;
use std::ptr;
use std::vec;
use std::rt::io::process::*;
use super::{Watcher, Loop, NativeHandle, UvError};
use super::{status_to_maybe_uv_error, ExitCallback};
use uvio::{UvPipeStream, UvUnboundPipe};
use uvll;
/// A process wraps the handle of the underlying uv_process_t.
pub struct Process(*uvll::uv_process_t);
impl 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 = 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_: &Loop, config: ProcessConfig,
exit_cb: ExitCallback)
-> Result<~[Option<~UvPipeStream>], UvError>
{
let cwd = config.cwd.map(|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 = NativeHandle::from_native_handle(p);
let err = match exit_status {
0 => None,
_ => 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 = config.io;
let mut stdio = vec::with_capacity::<uvll::uv_stdio_container_t>(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.iter()) {
let io = set_stdio(slot as *uvll::uv_stdio_container_t, other,
loop_);
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 {
2013-11-03 10:39:39 -08:00
uvll::uv_spawn(loop_.native_handle(), **self, options)
} {
0 => {
(*self).get_watcher_data().exit_cb = Some(exit_cb.take());
Ok(ret_io.take())
}
err => Err(UvError(err))
}
}
}
}
/// Sends a signal to this process.
///
/// This is a wrapper around `uv_process_kill`
pub fn kill(&self, signum: int) -> Result<(), UvError> {
match unsafe {
2013-11-03 10:39:39 -08:00
uvll::uv_process_kill(self.native_handle(), signum as libc::c_int)
} {
0 => Ok(()),
err => Err(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 }
}
}
unsafe fn set_stdio(dst: *uvll::uv_stdio_container_t,
io: &StdioContainer,
loop_: &Loop) -> Option<~UvPipeStream> {
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(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 pipe = UvUnboundPipe::new(loop_);
let handle = pipe.pipe.as_stream().native_handle();
uvll::set_stdio_container_flags(dst, flags);
uvll::set_stdio_container_stream(dst, handle);
Some(~UvPipeStream::new(pipe))
}
}
}
/// Converts the program and arguments to the argv array expected by libuv
fn with_argv<T>(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<T>(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(format!("{}={}", *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 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 }
}
}