]> git.lizzy.rs Git - rust.git/blob - src/libcore/run.rs
d0c495dd19e44f77606b97f98fbdca87fcfba268
[rust.git] / src / libcore / run.rs
1 // Copyright 2012-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 //! Process spawning
12 use cast;
13 use io;
14 use libc;
15 use libc::{pid_t, c_void, c_int};
16 use comm::{stream, SharedChan, GenericChan, GenericPort};
17 use option::{Some, None};
18 use os;
19 use prelude::*;
20 use ptr;
21 use str;
22 use task;
23 use vec;
24
25 pub mod rustrt {
26     use libc::{c_int, c_void};
27     use libc;
28     use run;
29
30     #[abi = "cdecl"]
31     pub extern {
32         unsafe fn rust_run_program(argv: **libc::c_char,
33                                    envp: *c_void,
34                                    dir: *libc::c_char,
35                                    in_fd: c_int,
36                                    out_fd: c_int,
37                                    err_fd: c_int) -> run::RunProgramResult;
38         unsafe fn rust_process_wait(pid: c_int) -> c_int;
39     }
40 }
41
42 pub struct RunProgramResult {
43     // the process id of the program, or -1 if in case of errors
44     pid: pid_t,
45     // a handle to the process - on unix this will always be NULL, but on windows it will be a
46     // HANDLE to the process, which will prevent the pid being re-used until the handle is closed.
47     handle: *(),
48 }
49
50 /// A value representing a child process
51 pub struct Program {
52     priv pid: pid_t,
53     priv handle: *(),
54     priv in_fd: c_int,
55     priv out_file: *libc::FILE,
56     priv err_file: *libc::FILE,
57     priv finished: bool,
58 }
59
60 impl Drop for Program {
61     fn finalize(&self) {
62         // FIXME #4943: transmute is bad.
63         let mut_self: &mut Program = unsafe { cast::transmute(self) };
64
65         mut_self.finish();
66         mut_self.close_outputs();
67         free_handle(self.handle);
68     }
69 }
70
71 pub impl Program {
72
73     /// Returns the process id of the program
74     fn get_id(&mut self) -> pid_t { self.pid }
75
76     /// Returns an io::Writer that can be used to write to stdin
77     fn input(&mut self) -> @io::Writer {
78         io::fd_writer(self.in_fd, false)
79     }
80
81     /// Returns an io::Reader that can be used to read from stdout
82     fn output(&mut self) -> @io::Reader {
83         io::FILE_reader(self.out_file, false)
84     }
85
86     /// Returns an io::Reader that can be used to read from stderr
87     fn err(&mut self) -> @io::Reader {
88         io::FILE_reader(self.err_file, false)
89     }
90
91     /// Closes the handle to the child processes standard input
92     fn close_input(&mut self) {
93         let invalid_fd = -1i32;
94         if self.in_fd != invalid_fd {
95             unsafe {
96                 libc::close(self.in_fd);
97             }
98             self.in_fd = invalid_fd;
99         }
100     }
101
102     priv fn close_outputs(&mut self) {
103         unsafe {
104             fclose_and_null(&mut self.out_file);
105             fclose_and_null(&mut self.err_file);
106         }
107     }
108
109     /**
110      * Waits for the child process to terminate. Closes the handle
111      * to stdin if necessary.
112      */
113     fn finish(&mut self) -> int {
114         if self.finished { return 0; }
115         self.finished = true;
116         self.close_input();
117         return waitpid(self.pid);
118     }
119
120     priv fn destroy_internal(&mut self, force: bool) {
121         killpid(self.pid, force);
122         self.finish();
123         self.close_outputs();
124
125         #[cfg(windows)]
126         fn killpid(pid: pid_t, _force: bool) {
127             unsafe {
128                 libc::funcs::extra::kernel32::TerminateProcess(
129                     cast::transmute(pid), 1);
130             }
131         }
132
133         #[cfg(unix)]
134         fn killpid(pid: pid_t, force: bool) {
135             let signal = if force {
136                 libc::consts::os::posix88::SIGKILL
137             } else {
138                 libc::consts::os::posix88::SIGTERM
139             };
140
141             unsafe {
142                 libc::funcs::posix88::signal::kill(pid, signal as c_int);
143             }
144         }
145     }
146
147     /**
148      * Terminate the program, giving it a chance to clean itself up if
149      * this is supported by the operating system.
150      *
151      * On Posix OSs SIGTERM will be sent to the process. On Win32
152      * TerminateProcess(..) will be called.
153      */
154     fn destroy(&mut self) { self.destroy_internal(false); }
155
156     /**
157      * Terminate the program as soon as possible without giving it a
158      * chance to clean itself up.
159      *
160      * On Posix OSs SIGKILL will be sent to the process. On Win32
161      * TerminateProcess(..) will be called.
162      */
163     fn force_destroy(&mut self) { self.destroy_internal(true); }
164 }
165
166
167 /**
168  * Run a program, providing stdin, stdout and stderr handles
169  *
170  * # Arguments
171  *
172  * * prog - The path to an executable
173  * * args - Vector of arguments to pass to the child process
174  * * env - optional env-modification for child
175  * * dir - optional dir to run child in (default current dir)
176  * * in_fd - A file descriptor for the child to use as std input
177  * * out_fd - A file descriptor for the child to use as std output
178  * * err_fd - A file descriptor for the child to use as std error
179  *
180  * # Return value
181  *
182  * The process id of the spawned process
183  */
184 pub fn spawn_process(prog: &str, args: &[~str],
185                      env: &Option<~[(~str,~str)]>,
186                      dir: &Option<~str>,
187                      in_fd: c_int, out_fd: c_int, err_fd: c_int) -> pid_t {
188
189     let res = spawn_process_internal(prog, args, env, dir, in_fd, out_fd, err_fd);
190     free_handle(res.handle);
191     return res.pid;
192 }
193
194 fn spawn_process_internal(prog: &str, args: &[~str],
195                           env: &Option<~[(~str,~str)]>,
196                           dir: &Option<~str>,
197                           in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult {
198     unsafe {
199         do with_argv(prog, args) |argv| {
200             do with_envp(env) |envp| {
201                 do with_dirp(dir) |dirp| {
202                     rustrt::rust_run_program(argv, envp, dirp, in_fd, out_fd, err_fd)
203                 }
204             }
205         }
206     }
207 }
208
209 fn with_argv<T>(prog: &str, args: &[~str],
210                 cb: &fn(**libc::c_char) -> T) -> T {
211     let mut argptrs = str::as_c_str(prog, |b| ~[b]);
212     let mut tmps = ~[];
213     for vec::each(args) |arg| {
214         let t = @copy *arg;
215         tmps.push(t);
216         argptrs.push_all(str::as_c_str(*t, |b| ~[b]));
217     }
218     argptrs.push(ptr::null());
219     vec::as_imm_buf(argptrs, |buf, _len| cb(buf))
220 }
221
222 #[cfg(unix)]
223 fn with_envp<T>(env: &Option<~[(~str,~str)]>,
224                 cb: &fn(*c_void) -> T) -> T {
225     // On posixy systems we can pass a char** for envp, which is
226     // a null-terminated array of "k=v\n" strings.
227     match *env {
228       Some(ref es) if !vec::is_empty(*es) => {
229         let mut tmps = ~[];
230         let mut ptrs = ~[];
231
232         for vec::each(*es) |e| {
233             let (k,v) = copy *e;
234             let t = @(fmt!("%s=%s", k, v));
235             tmps.push(t);
236             ptrs.push_all(str::as_c_str(*t, |b| ~[b]));
237         }
238         ptrs.push(ptr::null());
239         vec::as_imm_buf(ptrs, |p, _len|
240             unsafe { cb(::cast::transmute(p)) }
241         )
242       }
243       _ => cb(ptr::null())
244     }
245 }
246
247 #[cfg(windows)]
248 fn with_envp<T>(env: &Option<~[(~str,~str)]>,
249                 cb: &fn(*c_void) -> T) -> T {
250     // On win32 we pass an "environment block" which is not a char**, but
251     // rather a concatenation of null-terminated k=v\0 sequences, with a final
252     // \0 to terminate.
253     unsafe {
254         match *env {
255           Some(ref es) if !vec::is_empty(*es) => {
256             let mut blk : ~[u8] = ~[];
257             for vec::each(*es) |e| {
258                 let (k,v) = copy *e;
259                 let t = fmt!("%s=%s", k, v);
260                 let mut v : ~[u8] = ::cast::transmute(t);
261                 blk += v;
262                 ::cast::forget(v);
263             }
264             blk += ~[0_u8];
265             vec::as_imm_buf(blk, |p, _len| cb(::cast::transmute(p)))
266           }
267           _ => cb(ptr::null())
268         }
269     }
270 }
271
272 fn with_dirp<T>(d: &Option<~str>,
273                 cb: &fn(*libc::c_char) -> T) -> T {
274     match *d {
275       Some(ref dir) => str::as_c_str(*dir, cb),
276       None => cb(ptr::null())
277     }
278 }
279
280 /// helper function that closes non-NULL files and then makes them NULL
281 priv unsafe fn fclose_and_null(f: &mut *libc::FILE) {
282     if *f != 0 as *libc::FILE {
283         libc::fclose(*f);
284         *f = 0 as *libc::FILE;
285     }
286 }
287
288 #[cfg(windows)]
289 priv fn free_handle(handle: *()) {
290     unsafe {
291         libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle));
292     }
293 }
294
295 #[cfg(unix)]
296 priv fn free_handle(_handle: *()) {
297     // unix has no process handle object, just a pid
298 }
299
300 /**
301  * Spawns a process and waits for it to terminate
302  *
303  * # Arguments
304  *
305  * * prog - The path to an executable
306  * * args - Vector of arguments to pass to the child process
307  *
308  * # Return value
309  *
310  * The process's exit code
311  */
312 pub fn run_program(prog: &str, args: &[~str]) -> int {
313     let res = spawn_process_internal(prog, args, &None, &None,
314                                      0i32, 0i32, 0i32);
315     if res.pid == -1 as pid_t { fail!(); }
316
317     let code = waitpid(res.pid);
318     free_handle(res.handle);
319     return code;
320 }
321
322 /**
323  * Spawns a process and returns a Program
324  *
325  * The returned value is a <Program> object that can be used for sending and
326  * receiving data over the standard file descriptors.  The class will ensure
327  * that file descriptors are closed properly.
328  *
329  * # Arguments
330  *
331  * * prog - The path to an executable
332  * * args - Vector of arguments to pass to the child process
333  *
334  * # Return value
335  *
336  * A <Program> object
337  */
338 pub fn start_program(prog: &str, args: &[~str]) -> Program {
339     let pipe_input = os::pipe();
340     let pipe_output = os::pipe();
341     let pipe_err = os::pipe();
342     let res =
343         spawn_process_internal(prog, args, &None, &None,
344                                pipe_input.in, pipe_output.out,
345                                pipe_err.out);
346
347     unsafe {
348         if res.pid == -1 as pid_t { fail!(); }
349         libc::close(pipe_input.in);
350         libc::close(pipe_output.out);
351         libc::close(pipe_err.out);
352     }
353
354     Program {
355         pid: res.pid,
356         handle: res.handle,
357         in_fd: pipe_input.out,
358         out_file: os::fdopen(pipe_output.in),
359         err_file: os::fdopen(pipe_err.in),
360         finished: false,
361     }
362 }
363
364 fn read_all(rd: @io::Reader) -> ~str {
365     let buf = io::with_bytes_writer(|wr| {
366         let mut bytes = [0, ..4096];
367         while !rd.eof() {
368             let nread = rd.read(bytes, bytes.len());
369             wr.write(bytes.slice(0, nread));
370         }
371     });
372     str::from_bytes(buf)
373 }
374
375 pub struct ProgramOutput {status: int, out: ~str, err: ~str}
376
377 /**
378  * Spawns a process, waits for it to exit, and returns the exit code, and
379  * contents of stdout and stderr.
380  *
381  * # Arguments
382  *
383  * * prog - The path to an executable
384  * * args - Vector of arguments to pass to the child process
385  *
386  * # Return value
387  *
388  * A record, {status: int, out: str, err: str} containing the exit code,
389  * the contents of stdout and the contents of stderr.
390  */
391 pub fn program_output(prog: &str, args: &[~str]) -> ProgramOutput {
392     let pipe_in = os::pipe();
393     let pipe_out = os::pipe();
394     let pipe_err = os::pipe();
395     let res = spawn_process_internal(prog, args, &None, &None,
396                                      pipe_in.in, pipe_out.out, pipe_err.out);
397
398     os::close(pipe_in.in);
399     os::close(pipe_out.out);
400     os::close(pipe_err.out);
401     if res.pid == -1i32 {
402         os::close(pipe_in.out);
403         os::close(pipe_out.in);
404         os::close(pipe_err.in);
405         fail!();
406     }
407
408     os::close(pipe_in.out);
409
410     // Spawn two entire schedulers to read both stdout and sterr
411     // in parallel so we don't deadlock while blocking on one
412     // or the other. FIXME (#2625): Surely there's a much more
413     // clever way to do this.
414     let (p, ch) = stream();
415     let ch = SharedChan::new(ch);
416     let ch_clone = ch.clone();
417     do task::spawn_sched(task::SingleThreaded) {
418         let errput = readclose(pipe_err.in);
419         ch.send((2, errput));
420     };
421     do task::spawn_sched(task::SingleThreaded) {
422         let output = readclose(pipe_out.in);
423         ch_clone.send((1, output));
424     };
425
426     let status = waitpid(res.pid);
427     free_handle(res.handle);
428
429     let mut errs = ~"";
430     let mut outs = ~"";
431     let mut count = 2;
432     while count > 0 {
433         let stream = p.recv();
434         match stream {
435             (1, copy s) => {
436                 outs = s;
437             }
438             (2, copy s) => {
439                 errs = s;
440             }
441             (n, _) => {
442                 fail!(fmt!("program_output received an unexpected file \
443                            number: %u", n));
444             }
445         };
446         count -= 1;
447     };
448     return ProgramOutput {status: status,
449                           out: outs,
450                           err: errs};
451 }
452
453 pub fn writeclose(fd: c_int, s: ~str) {
454     use io::WriterUtil;
455
456     error!("writeclose %d, %s", fd as int, s);
457     let writer = io::fd_writer(fd, false);
458     writer.write_str(s);
459
460     os::close(fd);
461 }
462
463 pub fn readclose(fd: c_int) -> ~str {
464     unsafe {
465         let file = os::fdopen(fd);
466         let reader = io::FILE_reader(file, false);
467         let buf = io::with_bytes_writer(|writer| {
468             let mut bytes = [0, ..4096];
469             while !reader.eof() {
470                 let nread = reader.read(bytes, bytes.len());
471                 writer.write(bytes.slice(0, nread));
472             }
473         });
474         os::fclose(file);
475         str::from_bytes(buf)
476     }
477 }
478
479 /**
480  * Waits for a process to exit and returns the exit code, failing
481  * if there is no process with the specified id.
482  */
483 pub fn waitpid(pid: pid_t) -> int {
484     return waitpid_os(pid);
485
486     #[cfg(windows)]
487     fn waitpid_os(pid: pid_t) -> int {
488         let status = unsafe { rustrt::rust_process_wait(pid) };
489         if status < 0 {
490             fail!(fmt!("failure in rust_process_wait: %s", os::last_os_error()));
491         }
492         return status as int;
493     }
494
495     #[cfg(unix)]
496     fn waitpid_os(pid: pid_t) -> int {
497
498         use libc::funcs::posix01::wait::*;
499
500         #[cfg(target_os = "linux")]
501         #[cfg(target_os = "android")]
502         fn WIFEXITED(status: i32) -> bool {
503             (status & 0xffi32) == 0i32
504         }
505
506         #[cfg(target_os = "macos")]
507         #[cfg(target_os = "freebsd")]
508         fn WIFEXITED(status: i32) -> bool {
509             (status & 0x7fi32) == 0i32
510         }
511
512         #[cfg(target_os = "linux")]
513         #[cfg(target_os = "android")]
514         fn WEXITSTATUS(status: i32) -> i32 {
515             (status >> 8i32) & 0xffi32
516         }
517
518         #[cfg(target_os = "macos")]
519         #[cfg(target_os = "freebsd")]
520         fn WEXITSTATUS(status: i32) -> i32 {
521             status >> 8i32
522         }
523
524         let mut status = 0 as c_int;
525         if unsafe { waitpid(pid, &mut status, 0) } == -1 {
526             fail!(fmt!("failure in waitpid: %s", os::last_os_error()));
527         }
528
529         return if WIFEXITED(status) {
530             WEXITSTATUS(status) as int
531         } else {
532             1
533         };
534     }
535 }
536
537 #[cfg(test)]
538 mod tests {
539     use libc;
540     use option::None;
541     use os;
542     use path::Path;
543     use run::{readclose, writeclose};
544     use run;
545
546     // Regression test for memory leaks
547     #[test]
548     fn test_leaks() {
549         run::run_program("echo", []);
550         run::start_program("echo", []);
551         run::program_output("echo", []);
552     }
553
554     #[test]
555     #[allow(non_implicitly_copyable_typarams)]
556     fn test_pipes() {
557         let pipe_in = os::pipe();
558         let pipe_out = os::pipe();
559         let pipe_err = os::pipe();
560
561         let pid =
562             run::spawn_process(
563                 "cat", [], &None, &None,
564                 pipe_in.in, pipe_out.out, pipe_err.out);
565         os::close(pipe_in.in);
566         os::close(pipe_out.out);
567         os::close(pipe_err.out);
568
569         if pid == -1i32 { fail!(); }
570         let expected = ~"test";
571         writeclose(pipe_in.out, copy expected);
572         let actual = readclose(pipe_out.in);
573         readclose(pipe_err.in);
574         run::waitpid(pid);
575
576         debug!(copy expected);
577         debug!(copy actual);
578         assert!((expected == actual));
579     }
580
581     #[test]
582     fn waitpid() {
583         let pid = run::spawn_process("false", [],
584                                      &None, &None,
585                                      0i32, 0i32, 0i32);
586         let status = run::waitpid(pid);
587         assert!(status == 1);
588     }
589
590     #[test]
591     #[should_fail]
592     #[ignore(cfg(windows))]
593     fn waitpid_non_existant_pid() {
594         run::waitpid(123456789); // assume that this pid doesn't exist
595     }
596
597     #[test]
598     fn test_destroy_once() {
599         let mut p = run::start_program("echo", []);
600         p.destroy(); // this shouldn't crash (and nor should the destructor)
601     }
602
603     #[test]
604     fn test_destroy_twice() {
605         let mut p = run::start_program("echo", []);
606         p.destroy(); // this shouldnt crash...
607         p.destroy(); // ...and nor should this (and nor should the destructor)
608     }
609
610     #[cfg(unix)] // there is no way to sleep on windows from inside libcore...
611     fn test_destroy_actually_kills(force: bool) {
612         let path = Path(fmt!("test/core-run-test-destroy-actually-kills-%?.tmp", force));
613
614         os::remove_file(&path);
615
616         let cmd = fmt!("sleep 5 && echo MurderDeathKill > %s", path.to_str());
617         let mut p = run::start_program("sh", [~"-c", cmd]);
618
619         p.destroy(); // destroy the program before it has a chance to echo its message
620
621         unsafe {
622             // wait to ensure the program is really destroyed and not just waiting itself
623             libc::sleep(10);
624         }
625
626         // the program should not have had chance to echo its message
627         assert!(!path.exists());
628     }
629
630     #[test]
631     #[cfg(unix)]
632     fn test_unforced_destroy_actually_kills() {
633         test_destroy_actually_kills(false);
634     }
635
636     #[test]
637     #[cfg(unix)]
638     fn test_forced_destroy_actually_kills() {
639         test_destroy_actually_kills(true);
640     }
641 }