(index<- ) ./librustuv/process.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
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.
4 //
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.
10
11 use libc::c_int;
12 use libc;
13 use std::io::IoError;
14 use std::io::process;
15 use std::ptr;
16 use std::rt::rtio::RtioProcess;
17 use std::rt::task::BlockedTask;
18
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};
23 use uvio::UvIoFactory;
24 use uvll;
25
26 pub struct Process {
27 handle: *uvll::uv_process_t,
28 home: HomeHandle,
29
30 /// Task to wake up (may be null) for when the process exits
31 to_wake: Option<BlockedTask>,
32
33 /// Collected from the exit_cb
34 exit_status: Option<process::ProcessExit>,
35 }
36
37 impl Process {
38 /// Spawn a new process inside the specified event loop.
39 ///
40 /// Returns either the corresponding process object or an error which
41 /// occurred.
42 pub fn spawn(io_loop: &mut UvIoFactory, config: process::ProcessConfig)
43 -> Result<(Box<Process>, Vec<Option<PipeWatcher>>), UvError>
44 {
45 let cwd = config.cwd.map(|s| s.to_c_str());
46 let mut io = vec![config.stdin, config.stdout, config.stderr];
47 for slot in config.extra_io.iter() {
48 io.push(*slot);
49 }
50 let mut stdio = Vec::<uvll::uv_stdio_container_t>::with_capacity(io.len());
51 let mut ret_io = Vec::with_capacity(io.len());
52 unsafe {
53 stdio.set_len(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,
56 io_loop);
57 ret_io.push(io);
58 }
59 }
60
61 let ret = with_argv(config.program, config.args, |argv| {
62 with_env(config.env, |envp| {
63 let mut flags = 0;
64 if config.uid.is_some() {
65 flags |= uvll::PROCESS_SETUID;
66 }
67 if config.gid.is_some() {
68 flags |= uvll::PROCESS_SETGID;
69 }
70 if config.detach {
71 flags |= uvll::PROCESS_DETACHED;
72 }
73 let options = uvll::uv_process_options_t {
74 exit_cb: on_exit,
75 file: unsafe { *argv },
76 args: argv,
77 env: envp,
78 cwd: match cwd {
79 Some(ref cwd) => cwd.with_ref(|p| p),
80 None => ptr::null(),
81 },
82 flags: flags as libc::c_uint,
83 stdio_count: stdio.len() as libc::c_int,
84 stdio: stdio.as_ptr(),
85 uid: config.uid.unwrap_or(0) as uvll::uv_uid_t,
86 gid: config.gid.unwrap_or(0) as uvll::uv_gid_t,
87 };
88
89 let handle = UvHandle::alloc(None::<Process>, uvll::UV_PROCESS);
90 let process = box Process {
91 handle: handle,
92 home: io_loop.make_handle(),
93 to_wake: None,
94 exit_status: None,
95 };
96 match unsafe {
97 uvll::uv_spawn(io_loop.uv_loop(), handle, &options)
98 } {
99 0 => Ok(process.install()),
100 err => Err(UvError(err)),
101 }
102 })
103 });
104
105 match ret {
106 Ok(p) => Ok((p, ret_io)),
107 Err(e) => Err(e),
108 }
109 }
110
111 pub fn kill(pid: libc::pid_t, signum: int) -> Result<(), UvError> {
112 match unsafe {
113 uvll::uv_kill(pid as libc::c_int, signum as libc::c_int)
114 } {
115 0 => Ok(()),
116 n => Err(UvError(n))
117 }
118 }
119 }
120
121 extern fn on_exit(handle: *uvll::uv_process_t,
122 exit_status: i64,
123 term_signal: libc::c_int) {
124 let p: &mut Process = unsafe { UvHandle::from_uv_handle(&handle) };
125
126 assert!(p.exit_status.is_none());
127 p.exit_status = Some(match term_signal {
128 0 => process::ExitStatus(exit_status as int),
129 n => process::ExitSignal(n as int),
130 });
131
132 if p.to_wake.is_none() { return }
133 wakeup(&mut p.to_wake);
134 }
135
136 unsafe fn set_stdio(dst: *uvll::uv_stdio_container_t,
137 io: &process::StdioContainer,
138 io_loop: &mut UvIoFactory) -> Option<PipeWatcher> {
139 match *io {
140 process::Ignored => {
141 uvll::set_stdio_container_flags(dst, uvll::STDIO_IGNORE);
142 None
143 }
144 process::InheritFd(fd) => {
145 uvll::set_stdio_container_flags(dst, uvll::STDIO_INHERIT_FD);
146 uvll::set_stdio_container_fd(dst, fd);
147 None
148 }
149 process::CreatePipe(readable, writable) => {
150 let mut flags = uvll::STDIO_CREATE_PIPE as libc::c_int;
151 if readable {
152 flags |= uvll::STDIO_READABLE_PIPE as libc::c_int;
153 }
154 if writable {
155 flags |= uvll::STDIO_WRITABLE_PIPE as libc::c_int;
156 }
157 let pipe = PipeWatcher::new(io_loop, false);
158 uvll::set_stdio_container_flags(dst, flags);
159 uvll::set_stdio_container_stream(dst, pipe.handle());
160 Some(pipe)
161 }
162 }
163 }
164
165 /// Converts the program and arguments to the argv array expected by libuv
166 fn with_argv<T>(prog: &str, args: &[~str], f: |**libc::c_char| -> T) -> T {
167 // First, allocation space to put all the C-strings (we need to have
168 // ownership of them somewhere
169 let mut c_strs = Vec::with_capacity(args.len() + 1);
170 c_strs.push(prog.to_c_str());
171 for arg in args.iter() {
172 c_strs.push(arg.to_c_str());
173 }
174
175 // Next, create the char** array
176 let mut c_args = Vec::with_capacity(c_strs.len() + 1);
177 for s in c_strs.iter() {
178 c_args.push(s.with_ref(|p| p));
179 }
180 c_args.push(ptr::null());
181 f(c_args.as_ptr())
182 }
183
184 /// Converts the environment to the env array expected by libuv
185 fn with_env<T>(env: Option<&[(~str, ~str)]>, f: |**libc::c_char| -> T) -> T {
186 let env = match env {
187 Some(s) => s,
188 None => { return f(ptr::null()); }
189 };
190 // As with argv, create some temporary storage and then the actual array
191 let mut envp = Vec::with_capacity(env.len());
192 for &(ref key, ref value) in env.iter() {
193 envp.push(format!("{}={}", *key, *value).to_c_str());
194 }
195 let mut c_envp = Vec::with_capacity(envp.len() + 1);
196 for s in envp.iter() {
197 c_envp.push(s.with_ref(|p| p));
198 }
199 c_envp.push(ptr::null());
200 f(c_envp.as_ptr())
201 }
202
203 impl HomingIO for Process {
204 fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
205 }
206
207 impl UvHandle<uvll::uv_process_t> for Process {
208 fn uv_handle(&self) -> *uvll::uv_process_t { self.handle }
209 }
210
211 impl RtioProcess for Process {
212 fn id(&self) -> libc::pid_t {
213 unsafe { uvll::process_pid(self.handle) as libc::pid_t }
214 }
215
216 fn kill(&mut self, signal: int) -> Result<(), IoError> {
217 let _m = self.fire_homing_missile();
218 match unsafe {
219 uvll::uv_process_kill(self.handle, signal as libc::c_int)
220 } {
221 0 => Ok(()),
222 err => Err(uv_error_to_io_error(UvError(err)))
223 }
224 }
225
226 fn wait(&mut self) -> process::ProcessExit {
227 // Make sure (on the home scheduler) that we have an exit status listed
228 let _m = self.fire_homing_missile();
229 match self.exit_status {
230 Some(..) => {}
231 None => {
232 // If there's no exit code previously listed, then the
233 // process's exit callback has yet to be invoked. We just
234 // need to deschedule ourselves and wait to be reawoken.
235 wait_until_woken_after(&mut self.to_wake, &self.uv_loop(), || {});
236 assert!(self.exit_status.is_some());
237 }
238 }
239
240 self.exit_status.unwrap()
241 }
242 }
243
244 impl Drop for Process {
245 fn drop(&mut self) {
246 let _m = self.fire_homing_missile();
247 assert!(self.to_wake.is_none());
248 self.close();
249 }
250 }