summary refs log tree commit diff
path: root/src/libstd/rt/uv/process.rs
blob: ccfa1ff87dbd05a3d5a4504978fc1c304945068e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// 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 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<UvPipeStream>], 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::<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.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<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(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<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(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 }
    }
}