]> git.lizzy.rs Git - rust.git/blob - src/libcore/run.rs
435feb160235192f24cad1a39f6ab36685b3aa74
[rust.git] / src / libcore / run.rs
1 // Copyright 2012 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 // NB: transitionary, de-mode-ing.
12 #[forbid(deprecated_mode)];
13 #[forbid(deprecated_pattern)];
14
15 //! Process spawning
16 use io;
17 use io::ReaderUtil;
18 use libc;
19 use libc::{pid_t, c_void, c_int};
20 use oldcomm;
21 use option::{Some, None};
22 use os;
23 use prelude::*;
24 use ptr;
25 use run;
26 use str;
27 use task;
28 use vec;
29
30 #[abi = "cdecl"]
31 extern mod rustrt {
32     unsafe fn rust_run_program(argv: **libc::c_char, envp: *c_void,
33                                dir: *libc::c_char,
34                                in_fd: c_int, out_fd: c_int, err_fd: c_int)
35                             -> pid_t;
36 }
37
38 /// A value representing a child process
39 pub trait Program {
40     /// Returns the process id of the program
41     fn get_id() -> pid_t;
42
43     /// Returns an io::writer that can be used to write to stdin
44     fn input() -> io::Writer;
45
46     /// Returns an io::reader that can be used to read from stdout
47     fn output() -> io::Reader;
48
49     /// Returns an io::reader that can be used to read from stderr
50     fn err() -> io::Reader;
51
52     /// Closes the handle to the child processes standard input
53     fn close_input();
54
55     /**
56      * Waits for the child process to terminate. Closes the handle
57      * to stdin if necessary.
58      */
59     fn finish() -> int;
60
61     /// Closes open handles
62     fn destroy();
63 }
64
65
66 /**
67  * Run a program, providing stdin, stdout and stderr handles
68  *
69  * # Arguments
70  *
71  * * prog - The path to an executable
72  * * args - Vector of arguments to pass to the child process
73  * * env - optional env-modification for child
74  * * dir - optional dir to run child in (default current dir)
75  * * in_fd - A file descriptor for the child to use as std input
76  * * out_fd - A file descriptor for the child to use as std output
77  * * err_fd - A file descriptor for the child to use as std error
78  *
79  * # Return value
80  *
81  * The process id of the spawned process
82  */
83 pub fn spawn_process(prog: &str, args: &[~str],
84                  env: &Option<~[(~str,~str)]>,
85                  dir: &Option<~str>,
86                  in_fd: c_int, out_fd: c_int, err_fd: c_int)
87               -> pid_t {
88     unsafe {
89         do with_argv(prog, args) |argv| {
90             do with_envp(env) |envp| {
91                 do with_dirp(dir) |dirp| {
92                     rustrt::rust_run_program(argv, envp, dirp,
93                                              in_fd, out_fd, err_fd)
94                 }
95             }
96         }
97     }
98 }
99
100 fn with_argv<T>(prog: &str, args: &[~str],
101                 cb: fn(**libc::c_char) -> T) -> T {
102     let mut argptrs = str::as_c_str(prog, |b| ~[b]);
103     let mut tmps = ~[];
104     for vec::each(args) |arg| {
105         let t = @copy *arg;
106         tmps.push(t);
107         argptrs.push_all(str::as_c_str(*t, |b| ~[b]));
108     }
109     argptrs.push(ptr::null());
110     vec::as_imm_buf(argptrs, |buf, _len| cb(buf))
111 }
112
113 #[cfg(unix)]
114 fn with_envp<T>(env: &Option<~[(~str,~str)]>,
115                 cb: fn(*c_void) -> T) -> T {
116     // On posixy systems we can pass a char** for envp, which is
117     // a null-terminated array of "k=v\n" strings.
118     match *env {
119       Some(ref es) if !vec::is_empty(*es) => {
120         let mut tmps = ~[];
121         let mut ptrs = ~[];
122
123         for vec::each(*es) |e| {
124             let (k,v) = copy *e;
125             let t = @(fmt!("%s=%s", k, v));
126             tmps.push(t);
127             ptrs.push_all(str::as_c_str(*t, |b| ~[b]));
128         }
129         ptrs.push(ptr::null());
130         vec::as_imm_buf(ptrs, |p, _len|
131             unsafe { cb(::cast::reinterpret_cast(&p)) }
132         )
133       }
134       _ => cb(ptr::null())
135     }
136 }
137
138 #[cfg(windows)]
139 fn with_envp<T>(env: &Option<~[(~str,~str)]>,
140                 cb: fn(*c_void) -> T) -> T {
141     // On win32 we pass an "environment block" which is not a char**, but
142     // rather a concatenation of null-terminated k=v\0 sequences, with a final
143     // \0 to terminate.
144     unsafe {
145         match *env {
146           Some(ref es) if !vec::is_empty(*es) => {
147             let mut blk : ~[u8] = ~[];
148             for vec::each(*es) |e| {
149                 let (k,v) = copy *e;
150                 let t = fmt!("%s=%s", k, v);
151                 let mut v : ~[u8] = ::cast::reinterpret_cast(&t);
152                 blk += v;
153                 ::cast::forget(v);
154             }
155             blk += ~[0_u8];
156             vec::as_imm_buf(blk, |p, _len| cb(::cast::reinterpret_cast(&p)))
157           }
158           _ => cb(ptr::null())
159         }
160     }
161 }
162
163 fn with_dirp<T>(d: &Option<~str>,
164                 cb: fn(*libc::c_char) -> T) -> T {
165     match *d {
166       Some(ref dir) => str::as_c_str(*dir, cb),
167       None => cb(ptr::null())
168     }
169 }
170
171 /**
172  * Spawns a process and waits for it to terminate
173  *
174  * # Arguments
175  *
176  * * prog - The path to an executable
177  * * args - Vector of arguments to pass to the child process
178  *
179  * # Return value
180  *
181  * The process id
182  */
183 pub fn run_program(prog: &str, args: &[~str]) -> int {
184     let pid = spawn_process(prog, args, &None, &None,
185                             0i32, 0i32, 0i32);
186     if pid == -1 as pid_t { fail; }
187     return waitpid(pid);
188 }
189
190 /**
191  * Spawns a process and returns a program
192  *
193  * The returned value is a boxed class containing a <program> object that can
194  * be used for sending and receiving data over the standard file descriptors.
195  * The class will ensure that file descriptors are closed properly.
196  *
197  * # Arguments
198  *
199  * * prog - The path to an executable
200  * * args - Vector of arguments to pass to the child process
201  *
202  * # Return value
203  *
204  * A class with a <program> field
205  */
206 pub fn start_program(prog: &str, args: &[~str]) -> Program {
207     unsafe {
208         let pipe_input = os::pipe();
209         let pipe_output = os::pipe();
210         let pipe_err = os::pipe();
211         let pid =
212             spawn_process(prog, args, &None, &None,
213                           pipe_input.in, pipe_output.out,
214                           pipe_err.out);
215
216         unsafe {
217             if pid == -1 as pid_t { fail; }
218             libc::close(pipe_input.in);
219             libc::close(pipe_output.out);
220             libc::close(pipe_err.out);
221         }
222
223         type ProgRepr = {pid: pid_t,
224                          mut in_fd: c_int,
225                          out_file: *libc::FILE,
226                          err_file: *libc::FILE,
227                          mut finished: bool};
228
229         fn close_repr_input(r: &ProgRepr) {
230             let invalid_fd = -1i32;
231             if r.in_fd != invalid_fd {
232                 unsafe {
233                     libc::close(r.in_fd);
234                 }
235                 r.in_fd = invalid_fd;
236             }
237         }
238         fn finish_repr(r: &ProgRepr) -> int {
239             if r.finished { return 0; }
240             r.finished = true;
241             close_repr_input(r);
242             return waitpid(r.pid);
243         }
244         fn destroy_repr(r: &ProgRepr) {
245             unsafe {
246                 finish_repr(r);
247                 libc::fclose(r.out_file);
248                 libc::fclose(r.err_file);
249             }
250         }
251         struct ProgRes {
252             r: ProgRepr,
253             drop { destroy_repr(&self.r); }
254         }
255
256         fn ProgRes(r: ProgRepr) -> ProgRes {
257             ProgRes {
258                 r: move r
259             }
260         }
261
262         impl ProgRes: Program {
263             fn get_id() -> pid_t { return self.r.pid; }
264             fn input() -> io::Writer {
265                 io::fd_writer(self.r.in_fd, false)
266             }
267             fn output() -> io::Reader {
268                 io::FILE_reader(self.r.out_file, false)
269             }
270             fn err() -> io::Reader {
271                 io::FILE_reader(self.r.err_file, false)
272             }
273             fn close_input() { close_repr_input(&self.r); }
274             fn finish() -> int { finish_repr(&self.r) }
275             fn destroy() { destroy_repr(&self.r); }
276         }
277         let repr = {pid: pid,
278                     mut in_fd: pipe_input.out,
279                     out_file: os::fdopen(pipe_output.in),
280                     err_file: os::fdopen(pipe_err.in),
281                     mut finished: false};
282         return ProgRes(move repr) as Program;
283     }
284 }
285
286 fn read_all(rd: io::Reader) -> ~str {
287     let buf = io::with_bytes_writer(|wr| {
288         let mut bytes = [mut 0, ..4096];
289         while !rd.eof() {
290             let nread = rd.read(bytes, bytes.len());
291             wr.write(bytes.view(0, nread));
292         }
293     });
294     str::from_bytes(buf)
295 }
296
297 /**
298  * Spawns a process, waits for it to exit, and returns the exit code, and
299  * contents of stdout and stderr.
300  *
301  * # Arguments
302  *
303  * * prog - The path to an executable
304  * * args - Vector of arguments to pass to the child process
305  *
306  * # Return value
307  *
308  * A record, {status: int, out: str, err: str} containing the exit code,
309  * the contents of stdout and the contents of stderr.
310  */
311 pub fn program_output(prog: &str, args: &[~str]) ->
312    {status: int, out: ~str, err: ~str} {
313     unsafe {
314         let pipe_in = os::pipe();
315         let pipe_out = os::pipe();
316         let pipe_err = os::pipe();
317         let pid = spawn_process(prog, args, &None, &None,
318                                 pipe_in.in, pipe_out.out, pipe_err.out);
319
320         os::close(pipe_in.in);
321         os::close(pipe_out.out);
322         os::close(pipe_err.out);
323         if pid == -1i32 {
324             os::close(pipe_in.out);
325             os::close(pipe_out.in);
326             os::close(pipe_err.in);
327             fail;
328         }
329
330         os::close(pipe_in.out);
331
332         // Spawn two entire schedulers to read both stdout and sterr
333         // in parallel so we don't deadlock while blocking on one
334         // or the other. FIXME (#2625): Surely there's a much more
335         // clever way to do this.
336         let p = oldcomm::Port();
337         let ch = oldcomm::Chan(&p);
338         do task::spawn_sched(task::SingleThreaded) {
339             let errput = readclose(pipe_err.in);
340             oldcomm::send(ch, (2, move errput));
341         };
342         do task::spawn_sched(task::SingleThreaded) {
343             let output = readclose(pipe_out.in);
344             oldcomm::send(ch, (1, move output));
345         };
346         let status = run::waitpid(pid);
347         let mut errs = ~"";
348         let mut outs = ~"";
349         let mut count = 2;
350         while count > 0 {
351             let stream = oldcomm::recv(p);
352             match stream {
353                 (1, copy s) => {
354                     outs = move s;
355                 }
356                 (2, copy s) => {
357                     errs = move s;
358                 }
359                 (n, _) => {
360                     fail(fmt!("program_output received an unexpected file \
361                                number: %u", n));
362                 }
363             };
364             count -= 1;
365         };
366         return {status: status, out: move outs, err: move errs};
367     }
368 }
369
370 pub fn writeclose(fd: c_int, s: ~str) {
371     use io::WriterUtil;
372
373     error!("writeclose %d, %s", fd as int, s);
374     let writer = io::fd_writer(fd, false);
375     writer.write_str(s);
376
377     os::close(fd);
378 }
379
380 pub fn readclose(fd: c_int) -> ~str {
381     unsafe {
382         let file = os::fdopen(fd);
383         let reader = io::FILE_reader(file, false);
384         let buf = io::with_bytes_writer(|writer| {
385             let mut bytes = [mut 0, ..4096];
386             while !reader.eof() {
387                 let nread = reader.read(bytes, bytes.len());
388                 writer.write(bytes.view(0, nread));
389             }
390         });
391         os::fclose(file);
392         str::from_bytes(buf)
393     }
394 }
395
396 /// Waits for a process to exit and returns the exit code
397 pub fn waitpid(pid: pid_t) -> int {
398     return waitpid_os(pid);
399
400     #[cfg(windows)]
401     fn waitpid_os(pid: pid_t) -> int {
402         os::waitpid(pid) as int
403     }
404
405     #[cfg(unix)]
406     fn waitpid_os(pid: pid_t) -> int {
407         #[cfg(target_os = "linux")]
408         fn WIFEXITED(status: i32) -> bool {
409             (status & 0xffi32) == 0i32
410         }
411
412         #[cfg(target_os = "macos")]
413         #[cfg(target_os = "freebsd")]
414         fn WIFEXITED(status: i32) -> bool {
415             (status & 0x7fi32) == 0i32
416         }
417
418         #[cfg(target_os = "linux")]
419         fn WEXITSTATUS(status: i32) -> i32 {
420             (status >> 8i32) & 0xffi32
421         }
422
423         #[cfg(target_os = "macos")]
424         #[cfg(target_os = "freebsd")]
425         fn WEXITSTATUS(status: i32) -> i32 {
426             status >> 8i32
427         }
428
429         let status = os::waitpid(pid);
430         return if WIFEXITED(status) {
431             WEXITSTATUS(status) as int
432         } else {
433             1
434         };
435     }
436 }
437
438 #[cfg(test)]
439 mod tests {
440     use debug;
441     use io::WriterUtil;
442     use option::{None, Some};
443     use os;
444     use run::{readclose, writeclose};
445     use run;
446
447     // Regression test for memory leaks
448     #[ignore(cfg(windows))] // FIXME (#2626)
449     pub fn test_leaks() {
450         run::run_program("echo", []);
451         run::start_program("echo", []);
452         run::program_output("echo", []);
453     }
454
455     #[test]
456     #[allow(non_implicitly_copyable_typarams)]
457     pub fn test_pipes() {
458         let pipe_in = os::pipe();
459         let pipe_out = os::pipe();
460         let pipe_err = os::pipe();
461
462         let pid =
463             run::spawn_process(
464                 "cat", [], &None, &None,
465                 pipe_in.in, pipe_out.out, pipe_err.out);
466         os::close(pipe_in.in);
467         os::close(pipe_out.out);
468         os::close(pipe_err.out);
469
470         if pid == -1i32 { fail; }
471         let expected = ~"test";
472         writeclose(pipe_in.out, copy expected);
473         let actual = readclose(pipe_out.in);
474         readclose(pipe_err.in);
475         os::waitpid(pid);
476
477         log(debug, copy expected);
478         log(debug, copy actual);
479         assert (expected == actual);
480     }
481
482     #[test]
483     pub fn waitpid() {
484         let pid = run::spawn_process("false", [],
485                                      &None, &None,
486                                      0i32, 0i32, 0i32);
487         let status = run::waitpid(pid);
488         assert status == 1;
489     }
490
491 }
492
493 // Local Variables:
494 // mode: rust
495 // fill-column: 78;
496 // indent-tabs-mode: nil
497 // c-basic-offset: 4
498 // buffer-file-coding-system: utf-8-unix
499 // End: