diff options
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/io/mod.rs | 2 | ||||
| -rw-r--r-- | src/libstd/io/process.rs | 595 | ||||
| -rw-r--r-- | src/libstd/rt/rtio.rs | 59 | ||||
| -rw-r--r-- | src/libstd/unstable/dynamic_lib.rs | 2 |
4 files changed, 348 insertions, 310 deletions
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 2ee3ee59731..5c9d5feab10 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -245,7 +245,7 @@ pub use self::net::tcp::TcpListener; pub use self::net::tcp::TcpStream; pub use self::net::udp::UdpStream; pub use self::pipe::PipeStream; -pub use self::process::{Process, ProcessConfig}; +pub use self::process::{Process, Command}; pub use self::tempfile::TempDir; pub use self::mem::{MemReader, BufReader, MemWriter, BufWriter}; diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs index 349cac723ff..fe51615285a 100644 --- a/src/libstd/io/process.rs +++ b/src/libstd/io/process.rs @@ -14,13 +14,15 @@ use prelude::*; +use std::str; use fmt; use io::IoResult; use io; use libc; use mem; use owned::Box; -use rt::rtio::{RtioProcess, IoFactory, LocalIo}; +use rt::rtio::{RtioProcess, ProcessConfig, IoFactory, LocalIo}; +use c_str::CString; /// Signal a process to exit, without forcibly killing it. Corresponds to /// SIGTERM on unix platforms. @@ -37,16 +39,16 @@ use rt::rtio::{RtioProcess, IoFactory, LocalIo}; /// Representation of a running or exited child process. /// -/// This structure is used to create, run, and manage child processes. A process -/// is configured with the `ProcessConfig` struct which contains specific -/// options for dictating how the child is spawned. +/// This structure is used to represent and manage child processes. A child +/// process is created via the `Command` struct, which configures the spawning +/// process and can itself be constructed using a builder-style interface. /// /// # Example /// /// ```should_fail -/// use std::io::Process; +/// use std::io::Command; /// -/// let mut child = match Process::new("/bin/cat", ["file.txt".to_owned()]) { +/// let mut child = match Command::new("/bin/cat").arg("file.txt").spawn() { /// Ok(child) => child, /// Err(e) => fail!("failed to execute child: {}", e), /// }; @@ -74,71 +76,244 @@ pub struct Process { pub extra_io: Vec<Option<io::PipeStream>>, } -/// This configuration describes how a new process should be spawned. A blank -/// configuration can be created with `ProcessConfig::new()`. It is also -/// recommented to use a functional struct update pattern when creating process -/// configuration: +/// The `Command` type acts as a process builder, providing fine-grained control +/// over how a new process should be spawned. A default configuration can be +/// generated using `Command::new(program)`, where `program` gives a path to the +/// program to be executed. Additional builder methods allow the configuration +/// to be changed (for example, by adding arguments) prior to spawning: /// /// ``` -/// use std::io::ProcessConfig; +/// use std::io::Command; /// -/// let config = ProcessConfig { -/// program: "/bin/sh", -/// args: &["-c".to_owned(), "true".to_owned()], -/// .. ProcessConfig::new() +/// let mut process = match Command::new("sh").arg("-c").arg("echo hello").spawn() { +/// Ok(p) => p, +/// Err(e) => fail!("failed to execute process: {}", e), /// }; +/// +/// let output = process.stdout.get_mut_ref().read_to_end(); /// ``` -pub struct ProcessConfig<'a> { - /// Path to the program to run - pub program: &'a str, +pub struct Command { + // The internal data for the builder. Documented by the builder + // methods below, and serialized into rt::rtio::ProcessConfig. + program: CString, + args: Vec<CString>, + env: Option<Vec<(CString, CString)>>, + cwd: Option<CString>, + stdin: StdioContainer, + stdout: StdioContainer, + stderr: StdioContainer, + extra_io: Vec<StdioContainer>, + uid: Option<uint>, + gid: Option<uint>, + detach: bool, +} + +// FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so +// we cannot usefully take ToCStr arguments by reference (without forcing an +// additional & around &str). So we are instead temporarily adding an instance +// for &Path, so that we can take ToCStr as owned. When DST lands, the &Path +// instance should be removed, and arguments bound by ToCStr should be passed by +// reference. (Here: {new, arg, args, env}.) + +impl Command { + /// Constructs a new `Command` for launching the program at + /// path `program`, with the following default configuration: + /// + /// * No arguments to the program + /// * Inherit the current process's environment + /// * Inherit the current process's working directory + /// * A readable pipe for stdin (file descriptor 0) + /// * A writeable pipe for stdour and stderr (file descriptors 1 and 2) + /// + /// Builder methods are provided to change these defaults and + /// otherwise configure the process. + pub fn new<T:ToCStr>(program: T) -> Command { + Command { + program: program.to_c_str(), + args: Vec::new(), + env: None, + cwd: None, + stdin: CreatePipe(true, false), + stdout: CreatePipe(false, true), + stderr: CreatePipe(false, true), + extra_io: Vec::new(), + uid: None, + gid: None, + detach: false, + } + } + + /// Add an argument to pass to the program. + pub fn arg<'a, T:ToCStr>(&'a mut self, arg: T) -> &'a mut Command { + self.args.push(arg.to_c_str()); + self + } - /// Arguments to pass to the program (doesn't include the program itself) - pub args: &'a [~str], + /// Add multiple arguments to pass to the program. + pub fn args<'a, T:ToCStr>(&'a mut self, args: &[T]) -> &'a mut Command { + self.args.extend(args.iter().map(|arg| arg.to_c_str()));; + self + } - /// Optional environment to specify for the program. If this is None, then - /// it will inherit the current process's environment. - pub env: Option<&'a [(~str, ~str)]>, + /// Sets the environment for the child process (rather than inheriting it + /// from the current process). + + // FIXME (#13851): We should change this interface to allow clients to (1) + // build up the env vector incrementally and (2) allow both inheriting the + // current process's environment AND overriding/adding additional + // environment variables. The underlying syscalls assume that the + // environment has no duplicate names, so we really want to use a hashtable + // to compute the environment to pass down to the syscall; resolving issue + // #13851 will make it possible to use the standard hashtable. + pub fn env<'a, T:ToCStr>(&'a mut self, env: &[(T,T)]) -> &'a mut Command { + self.env = Some(env.iter().map(|&(ref name, ref val)| { + (name.to_c_str(), val.to_c_str()) + }).collect()); + self + } - /// Optional working directory for the new process. If this is None, then - /// the current directory of the running process is inherited. - pub cwd: Option<&'a Path>, + /// Set the working directory for the child process. + pub fn cwd<'a>(&'a mut self, dir: &Path) -> &'a mut Command { + self.cwd = Some(dir.to_c_str()); + self + } /// Configuration for the child process's stdin handle (file descriptor 0). - /// This field defaults to `CreatePipe(true, false)` so the input can be - /// written to. - pub stdin: StdioContainer, + /// Defaults to `CreatePipe(true, false)` so the input can be written to. + pub fn stdin<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { + self.stdin = cfg; + self + } /// Configuration for the child process's stdout handle (file descriptor 1). - /// This field defaults to `CreatePipe(false, true)` so the output can be - /// collected. - pub stdout: StdioContainer, - - /// Configuration for the child process's stdout handle (file descriptor 2). - /// This field defaults to `CreatePipe(false, true)` so the output can be - /// collected. - pub stderr: StdioContainer, - - /// Any number of streams/file descriptors/pipes may be attached to this - /// process. This list enumerates the file descriptors and such for the - /// process to be spawned, and the file descriptors inherited will start at - /// 3 and go to the length of this array. The first three file descriptors - /// (stdin/stdout/stderr) are configured with the `stdin`, `stdout`, and - /// `stderr` fields. - pub extra_io: &'a [StdioContainer], + /// Defaults to `CreatePipe(false, true)` so the output can be collected. + pub fn stdout<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { + self.stdout = cfg; + self + } + + /// Configuration for the child process's stderr handle (file descriptor 2). + /// Defaults to `CreatePipe(false, true)` so the output can be collected. + pub fn stderr<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { + self.stderr = cfg; + self + } + /// Attaches a stream/file descriptor/pipe to the child process. Inherited + /// file descriptors are numbered consecutively, starting at 3; the first + /// three file descriptors (stdin/stdout/stderr) are configured with the + /// `stdin`, `stdout`, and `stderr` methods. + pub fn extra_io<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { + self.extra_io.push(cfg); + self + } /// Sets the child process's user id. This translates to a `setuid` call in /// the child process. Setting this value on windows will cause the spawn to /// fail. Failure in the `setuid` call on unix will also cause the spawn to /// fail. - pub uid: Option<uint>, + pub fn uid<'a>(&'a mut self, id: uint) -> &'a mut Command { + self.uid = Some(id); + self + } /// Similar to `uid`, but sets the group id of the child process. This has /// the same semantics as the `uid` field. - pub gid: Option<uint>, + pub fn gid<'a>(&'a mut self, id: uint) -> &'a mut Command { + self.gid = Some(id); + self + } - /// If true, the child process is spawned in a detached state. On unix, this + /// Sets the child process to be spawned in a detached state. On unix, this /// means that the child is the leader of a new process group. - pub detach: bool, + pub fn detached<'a>(&'a mut self) -> &'a mut Command { + self.detach = true; + self + } + + /// Executes the command as a child process, which is returned. + pub fn spawn(&self) -> IoResult<Process> { + LocalIo::maybe_raise(|io| { + let cfg = ProcessConfig { + program: &self.program, + args: self.args.as_slice(), + env: self.env.as_ref().map(|env| env.as_slice()), + cwd: self.cwd.as_ref(), + stdin: self.stdin, + stdout: self.stdout, + stderr: self.stderr, + extra_io: self.extra_io.as_slice(), + uid: self.uid, + gid: self.gid, + detach: self.detach, + }; + io.spawn(cfg).map(|(p, io)| { + let mut io = io.move_iter().map(|p| { + p.map(|p| io::PipeStream::new(p)) + }); + Process { + handle: p, + stdin: io.next().unwrap(), + stdout: io.next().unwrap(), + stderr: io.next().unwrap(), + extra_io: io.collect(), + } + }) + }) + } + + /// Executes the command as a child process, waiting for it to finish and + /// collecting all of its output. + /// + /// # Example + /// + /// ``` + /// use std::io::Command; + /// use std::str; + /// + /// let output = match Command::new("cat").arg("foot.txt").output() { + /// Ok(output) => output, + /// Err(e) => fail!("failed to execute process: {}", e), + /// }; + /// + /// println!("status: {}", output.status); + /// println!("stdout: {}", str::from_utf8_lossy(output.output.as_slice())); + /// println!("stderr: {}", str::from_utf8_lossy(output.error.as_slice())); + /// ``` + pub fn output(&self) -> IoResult<ProcessOutput> { + self.spawn().and_then(|p| p.wait_with_output()) + } + + /// Executes a command as a child process, waiting for it to finish and + /// collecting its exit status. + /// + /// # Example + /// + /// ``` + /// use std::io::Command; + /// + /// let status = match Command::new("ls").status() { + /// Ok(status) => status, + /// Err(e) => fail!("failed to execute process: {}", e), + /// }; + /// + /// println!("process exited with: {}", status); + /// ``` + pub fn status(&self) -> IoResult<ProcessExit> { + self.spawn().and_then(|mut p| p.wait()) + } +} + +impl fmt::Show for Command { + /// Format the program and arguments of a Command for display. Any + /// non-utf8 data is lossily converted using the utf8 replacement + /// character. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(write!(f.buf, "{}", str::from_utf8_lossy(self.program.as_bytes_no_nul()))); + for arg in self.args.iter() { + try!(write!(f.buf, " '{}'", str::from_utf8_lossy(arg.as_bytes_no_nul()))); + } + Ok(()) + } } /// The output of a finished process. @@ -206,127 +381,7 @@ impl ProcessExit { } } -impl<'a> ProcessConfig<'a> { - /// Creates a new configuration with blanks as all of the defaults. This is - /// useful when using functional struct updates: - /// - /// ```rust - /// use std::io::process::{ProcessConfig, Process}; - /// - /// let config = ProcessConfig { - /// program: "/bin/sh", - /// args: &["-c".to_owned(), "echo hello".to_owned()], - /// .. ProcessConfig::new() - /// }; - /// - /// let p = Process::configure(config); - /// ``` - /// - pub fn new<'a>() -> ProcessConfig<'a> { - ProcessConfig { - program: "", - args: &[], - env: None, - cwd: None, - stdin: CreatePipe(true, false), - stdout: CreatePipe(false, true), - stderr: CreatePipe(false, true), - extra_io: &[], - uid: None, - gid: None, - detach: false, - } - } -} - impl Process { - /// Creates a new process for the specified program/arguments, using - /// otherwise default configuration. - /// - /// By default, new processes have their stdin/stdout/stderr handles created - /// as pipes the can be manipulated through the respective fields of the - /// returned `Process`. - /// - /// # Example - /// - /// ``` - /// use std::io::Process; - /// - /// let mut process = match Process::new("sh", &["c".to_owned(), "echo hello".to_owned()]) { - /// Ok(p) => p, - /// Err(e) => fail!("failed to execute process: {}", e), - /// }; - /// - /// let output = process.stdout.get_mut_ref().read_to_end(); - /// ``` - pub fn new(prog: &str, args: &[~str]) -> IoResult<Process> { - Process::configure(ProcessConfig { - program: prog, - args: args, - .. ProcessConfig::new() - }) - } - - /// Executes the specified program with arguments, waiting for it to finish - /// and collecting all of its output. - /// - /// # Example - /// - /// ``` - /// use std::io::Process; - /// use std::str; - /// - /// let output = match Process::output("cat", ["foo.txt".to_owned()]) { - /// Ok(output) => output, - /// Err(e) => fail!("failed to execute process: {}", e), - /// }; - /// - /// println!("status: {}", output.status); - /// println!("stdout: {}", str::from_utf8_lossy(output.output.as_slice())); - /// println!("stderr: {}", str::from_utf8_lossy(output.error.as_slice())); - /// ``` - pub fn output(prog: &str, args: &[~str]) -> IoResult<ProcessOutput> { - Process::new(prog, args).and_then(|p| p.wait_with_output()) - } - - /// Executes a child process and collects its exit status. This will block - /// waiting for the child to exit. - /// - /// # Example - /// - /// ``` - /// use std::io::Process; - /// - /// let status = match Process::status("ls", []) { - /// Ok(status) => status, - /// Err(e) => fail!("failed to execute process: {}", e), - /// }; - /// - /// println!("process exited with: {}", status); - /// ``` - pub fn status(prog: &str, args: &[~str]) -> IoResult<ProcessExit> { - Process::new(prog, args).and_then(|mut p| p.wait()) - } - - /// Creates a new process with the specified configuration. - pub fn configure(config: ProcessConfig) -> IoResult<Process> { - let mut config = Some(config); - LocalIo::maybe_raise(|io| { - io.spawn(config.take_unwrap()).map(|(p, io)| { - let mut io = io.move_iter().map(|p| { - p.map(|p| io::PipeStream::new(p)) - }); - Process { - handle: p, - stdin: io.next().unwrap(), - stdout: io.next().unwrap(), - stderr: io.next().unwrap(), - extra_io: io.collect(), - } - }) - }) - } - /// Sends `signal` to another process in the system identified by `id`. /// /// Note that windows doesn't quite have the same model as unix, so some @@ -403,11 +458,11 @@ impl Process { /// /// ```no_run /// # #![allow(experimental)] - /// use std::io::process::{Process, ProcessExit}; + /// use std::io::process::{Command, ProcessExit}; /// use std::io::IoResult; /// /// fn run_gracefully(prog: &str) -> IoResult<ProcessExit> { - /// let mut p = try!(Process::new("long-running-process", [])); + /// let mut p = try!(Command::new("long-running-process").spawn()); /// /// // give the process 10 seconds to finish completely /// p.set_timeout(Some(10_000)); @@ -487,18 +542,14 @@ impl Drop for Process { #[cfg(test)] mod tests { - use io::process::{ProcessConfig, Process}; + use io::process::{Command, Process}; use prelude::*; // FIXME(#10380) these tests should not all be ignored on android. #[cfg(not(target_os="android"))] iotest!(fn smoke() { - let args = ProcessConfig { - program: "true", - .. ProcessConfig::new() - }; - let p = Process::configure(args); + let p = Command::new("true").spawn(); assert!(p.is_ok()); let mut p = p.unwrap(); assert!(p.wait().unwrap().success()); @@ -506,11 +557,7 @@ mod tests { #[cfg(not(target_os="android"))] iotest!(fn smoke_failure() { - let args = ProcessConfig { - program: "if-this-is-a-binary-then-the-world-has-ended", - .. ProcessConfig::new() - }; - match Process::configure(args) { + match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() { Ok(..) => fail!(), Err(..) => {} } @@ -518,11 +565,7 @@ mod tests { #[cfg(not(target_os="android"))] iotest!(fn exit_reported_right() { - let args = ProcessConfig { - program: "false", - .. ProcessConfig::new() - }; - let p = Process::configure(args); + let p = Command::new("false").spawn(); assert!(p.is_ok()); let mut p = p.unwrap(); assert!(p.wait().unwrap().matches_exit_status(1)); @@ -531,12 +574,7 @@ mod tests { #[cfg(unix, not(target_os="android"))] iotest!(fn signal_reported_right() { - let args = ProcessConfig { - program: "/bin/sh", - args: &["-c".to_owned(), "kill -1 $$".to_owned()], - .. ProcessConfig::new() - }; - let p = Process::configure(args); + let p = Command::new("/bin/sh").arg("-c").arg("kill -1 $$").spawn(); assert!(p.is_ok()); let mut p = p.unwrap(); match p.wait().unwrap() { @@ -549,8 +587,8 @@ mod tests { input.read_to_str().unwrap() } - pub fn run_output(args: ProcessConfig) -> ~str { - let p = Process::configure(args); + pub fn run_output(cmd: Command) -> ~str { + let p = cmd.spawn(); assert!(p.is_ok()); let mut p = p.unwrap(); assert!(p.stdout.is_some()); @@ -561,38 +599,27 @@ mod tests { #[cfg(not(target_os="android"))] iotest!(fn stdout_works() { - let args = ProcessConfig { - program: "echo", - args: &["foobar".to_owned()], - stdout: CreatePipe(false, true), - .. ProcessConfig::new() - }; - assert_eq!(run_output(args), "foobar\n".to_owned()); + let mut cmd = Command::new("echo"); + cmd.arg("foobar").stdout(CreatePipe(false, true)); + assert_eq!(run_output(cmd), "foobar\n".to_owned()); }) #[cfg(unix, not(target_os="android"))] iotest!(fn set_cwd_works() { - let cwd = Path::new("/"); - let args = ProcessConfig { - program: "/bin/sh", - args: &["-c".to_owned(), "pwd".to_owned()], - cwd: Some(&cwd), - stdout: CreatePipe(false, true), - .. ProcessConfig::new() - }; - assert_eq!(run_output(args), "/\n".to_owned()); + let mut cmd = Command::new("/bin/sh"); + cmd.arg("-c").arg("pwd") + .cwd(&Path::new("/")) + .stdout(CreatePipe(false, true)); + assert_eq!(run_output(cmd), "/\n".to_owned()); }) #[cfg(unix, not(target_os="android"))] iotest!(fn stdin_works() { - let args = ProcessConfig { - program: "/bin/sh", - args: &["-c".to_owned(), "read line; echo $line".to_owned()], - stdin: CreatePipe(true, false), - stdout: CreatePipe(false, true), - .. ProcessConfig::new() - }; - let mut p = Process::configure(args).unwrap(); + let mut p = Command::new("/bin/sh") + .arg("-c").arg("read line; echo $line") + .stdin(CreatePipe(true, false)) + .stdout(CreatePipe(false, true)) + .spawn().unwrap(); p.stdin.get_mut_ref().write("foobar".as_bytes()).unwrap(); drop(p.stdin.take()); let out = read_all(p.stdout.get_mut_ref() as &mut Reader); @@ -602,36 +629,23 @@ mod tests { #[cfg(not(target_os="android"))] iotest!(fn detach_works() { - let args = ProcessConfig { - program: "true", - detach: true, - .. ProcessConfig::new() - }; - let mut p = Process::configure(args).unwrap(); + let mut p = Command::new("true").detached().spawn().unwrap(); assert!(p.wait().unwrap().success()); }) #[cfg(windows)] iotest!(fn uid_fails_on_windows() { - let args = ProcessConfig { - program: "test", - uid: Some(10), - .. ProcessConfig::new() - }; - assert!(Process::configure(args).is_err()); + assert!(Command::new("test").uid(10).spawn().is_err()); }) #[cfg(unix, not(target_os="android"))] iotest!(fn uid_works() { use libc; - let args = ProcessConfig { - program: "/bin/sh", - args: &["-c".to_owned(), "true".to_owned()], - uid: Some(unsafe { libc::getuid() as uint }), - gid: Some(unsafe { libc::getgid() as uint }), - .. ProcessConfig::new() - }; - let mut p = Process::configure(args).unwrap(); + let mut p = Command::new("/bin/sh") + .arg("-c").arg("true") + .uid(unsafe { libc::getuid() as uint }) + .gid(unsafe { libc::getgid() as uint }) + .spawn().unwrap(); assert!(p.wait().unwrap().success()); }) @@ -642,26 +656,20 @@ mod tests { // if we're already root, this isn't a valid test. Most of the bots run // as non-root though (android is an exception). if unsafe { libc::getuid() == 0 } { return } - let args = ProcessConfig { - program: "/bin/ls", - uid: Some(0), - gid: Some(0), - .. ProcessConfig::new() - }; - assert!(Process::configure(args).is_err()); + assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err()); }) #[cfg(not(target_os="android"))] iotest!(fn test_process_status() { - let mut status = Process::status("false", []).unwrap(); + let mut status = Command::new("false").status().unwrap(); assert!(status.matches_exit_status(1)); - status = Process::status("true", []).unwrap(); + status = Command::new("true").status().unwrap(); assert!(status.success()); }) iotest!(fn test_process_output_fail_to_start() { - match Process::output("/no-binary-by-this-name-should-exist", []) { + match Command::new("/no-binary-by-this-name-should-exist").output() { Err(e) => assert_eq!(e.kind, FileNotFound), Ok(..) => fail!() } @@ -669,9 +677,8 @@ mod tests { #[cfg(not(target_os="android"))] iotest!(fn test_process_output_output() { - let ProcessOutput {status, output, error} - = Process::output("echo", ["hello".to_owned()]).unwrap(); + = Command::new("echo").arg("hello").output().unwrap(); let output_str = str::from_utf8(output.as_slice()).unwrap(); assert!(status.success()); @@ -685,7 +692,7 @@ mod tests { #[cfg(not(target_os="android"))] iotest!(fn test_process_output_error() { let ProcessOutput {status, output, error} - = Process::output("mkdir", [".".to_owned()]).unwrap(); + = Command::new("mkdir").arg(".").output().unwrap(); assert!(status.matches_exit_status(1)); assert_eq!(output, Vec::new()); @@ -694,21 +701,20 @@ mod tests { #[cfg(not(target_os="android"))] iotest!(fn test_finish_once() { - let mut prog = Process::new("false", []).unwrap(); + let mut prog = Command::new("false").spawn().unwrap(); assert!(prog.wait().unwrap().matches_exit_status(1)); }) #[cfg(not(target_os="android"))] iotest!(fn test_finish_twice() { - let mut prog = Process::new("false", []).unwrap(); + let mut prog = Command::new("false").spawn().unwrap(); assert!(prog.wait().unwrap().matches_exit_status(1)); assert!(prog.wait().unwrap().matches_exit_status(1)); }) #[cfg(not(target_os="android"))] iotest!(fn test_wait_with_output_once() { - - let prog = Process::new("echo", ["hello".to_owned()]).unwrap(); + let prog = Command::new("echo").arg("hello").spawn().unwrap(); let ProcessOutput {status, output, error} = prog.wait_with_output().unwrap(); let output_str = str::from_utf8(output.as_slice()).unwrap(); @@ -721,36 +727,26 @@ mod tests { }) #[cfg(unix,not(target_os="android"))] - pub fn run_pwd(dir: Option<&Path>) -> Process { - Process::configure(ProcessConfig { - program: "pwd", - cwd: dir, - .. ProcessConfig::new() - }).unwrap() + pub fn pwd_cmd() -> Command { + Command::new("pwd") } #[cfg(target_os="android")] - pub fn run_pwd(dir: Option<&Path>) -> Process { - Process::configure(ProcessConfig { - program: "/system/bin/sh", - args: &["-c".to_owned(),"pwd".to_owned()], - cwd: dir.map(|a| &*a), - .. ProcessConfig::new() - }).unwrap() + pub fn pwd_cmd() -> Command { + let mut cmd = Command::new("/system/bin/sh"); + cmd.arg("-c").arg("pwd"); + cmd } #[cfg(windows)] - pub fn run_pwd(dir: Option<&Path>) -> Process { - Process::configure(ProcessConfig { - program: "cmd", - args: &["/c".to_owned(), "cd".to_owned()], - cwd: dir.map(|a| &*a), - .. ProcessConfig::new() - }).unwrap() + pub fn pwd_cmd() -> Command { + let mut cmd = Command::new("cmd"); + cmd.arg("/c").arg("cd"); + cmd } iotest!(fn test_keep_current_working_dir() { use os; - let prog = run_pwd(None); + let prog = pwd_cmd().spawn().unwrap(); let output = str::from_utf8(prog.wait_with_output().unwrap() .output.as_slice()).unwrap().to_owned(); @@ -769,7 +765,7 @@ mod tests { // test changing to the parent of os::getcwd() because we know // the path exists (and os::getcwd() is not expected to be root) let parent_dir = os::getcwd().dir_path(); - let prog = run_pwd(Some(&parent_dir)); + let prog = pwd_cmd().cwd(&parent_dir).spawn().unwrap(); let output = str::from_utf8(prog.wait_with_output().unwrap() .output.as_slice()).unwrap().to_owned(); @@ -783,31 +779,21 @@ mod tests { }) #[cfg(unix,not(target_os="android"))] - pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process { - Process::configure(ProcessConfig { - program: "env", - env: env.as_ref().map(|e| e.as_slice()), - .. ProcessConfig::new() - }).unwrap() + pub fn env_cmd() -> Command { + Command::new("env") } #[cfg(target_os="android")] - pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process { - Process::configure(ProcessConfig { - program: "/system/bin/sh", - args: &["-c".to_owned(),"set".to_owned()], - env: env.as_ref().map(|e| e.as_slice()), - .. ProcessConfig::new() - }).unwrap() + pub fn env_cmd() -> Command { + let mut cmd = Command::new("/system/bin/sh"); + cmd.arg("-c").arg("set"); + cmd } #[cfg(windows)] - pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process { - Process::configure(ProcessConfig { - program: "cmd", - args: &["/c".to_owned(), "set".to_owned()], - env: env.as_ref().map(|e| e.as_slice()), - .. ProcessConfig::new() - }).unwrap() + pub fn env_cmd() -> Command { + let mut cmd = Command::new("cmd"); + cmd.arg("/c").arg("set"); + cmd } #[cfg(not(target_os="android"))] @@ -815,7 +801,7 @@ mod tests { use os; if running_on_valgrind() { return; } - let prog = run_env(None); + let prog = env_cmd().spawn().unwrap(); let output = str::from_utf8(prog.wait_with_output().unwrap() .output.as_slice()).unwrap().to_owned(); @@ -830,7 +816,7 @@ mod tests { use os; if running_on_valgrind() { return; } - let prog = run_env(None); + let mut prog = env_cmd().spawn().unwrap(); let output = str::from_utf8(prog.wait_with_output() .unwrap().output.as_slice()) .unwrap().to_owned(); @@ -846,9 +832,8 @@ mod tests { }) iotest!(fn test_add_to_env() { - let new_env = box [("RUN_TEST_NEW_ENV".to_owned(), "123".to_owned())]; - - let prog = run_env(Some(new_env)); + let new_env = box [("RUN_TEST_NEW_ENV", "123")]; + let prog = env_cmd().env(new_env).spawn().unwrap(); let result = prog.wait_with_output().unwrap(); let output = str::from_utf8_lossy(result.output.as_slice()).into_owned(); @@ -858,14 +843,14 @@ mod tests { #[cfg(unix)] pub fn sleeper() -> Process { - Process::new("sleep", ["1000".to_owned()]).unwrap() + Command::new("sleep").arg("1000").spawn().unwrap() } #[cfg(windows)] pub fn sleeper() -> Process { // There's a `timeout` command on windows, but it doesn't like having // its output piped, so instead just ping ourselves a few times with // gaps inbetweeen so we're sure this process is alive for awhile - Process::new("ping", ["127.0.0.1".to_owned(), "-n".to_owned(), "1000".to_owned()]).unwrap() + Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn().unwrap() } iotest!(fn test_kill() { diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index 90f97e59caa..a6c60df2642 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -29,7 +29,7 @@ use ai = io::net::addrinfo; use io; use io::IoResult; use io::net::ip::{IpAddr, SocketAddr}; -use io::process::{ProcessConfig, ProcessExit}; +use io::process::{StdioContainer, ProcessExit}; use io::signal::Signum; use io::{FileMode, FileAccess, FileStat, FilePermission}; use io::{SeekStyle}; @@ -87,6 +87,61 @@ pub enum CloseBehavior { CloseAsynchronously, } +/// Data needed to spawn a process. Serializes the `std::io::process::Command` +/// builder. +pub struct ProcessConfig<'a> { + /// Path to the program to run. + pub program: &'a CString, + + /// Arguments to pass to the program (doesn't include the program itself). + pub args: &'a [CString], + + /// Optional environment to specify for the program. If this is None, then + /// it will inherit the current process's environment. + pub env: Option<&'a [(CString, CString)]>, + + /// Optional working directory for the new process. If this is None, then + /// the current directory of the running process is inherited. + pub cwd: Option<&'a CString>, + + /// Configuration for the child process's stdin handle (file descriptor 0). + /// This field defaults to `CreatePipe(true, false)` so the input can be + /// written to. + pub stdin: StdioContainer, + + /// Configuration for the child process's stdout handle (file descriptor 1). + /// This field defaults to `CreatePipe(false, true)` so the output can be + /// collected. + pub stdout: StdioContainer, + + /// Configuration for the child process's stdout handle (file descriptor 2). + /// This field defaults to `CreatePipe(false, true)` so the output can be + /// collected. + pub stderr: StdioContainer, + + /// Any number of streams/file descriptors/pipes may be attached to this + /// process. This list enumerates the file descriptors and such for the + /// process to be spawned, and the file descriptors inherited will start at + /// 3 and go to the length of this array. The first three file descriptors + /// (stdin/stdout/stderr) are configured with the `stdin`, `stdout`, and + /// `stderr` fields. + pub extra_io: &'a [StdioContainer], + + /// Sets the child process's user id. This translates to a `setuid` call in + /// the child process. Setting this value on windows will cause the spawn to + /// fail. Failure in the `setuid` call on unix will also cause the spawn to + /// fail. + pub uid: Option<uint>, + + /// Similar to `uid`, but sets the group id of the child process. This has + /// the same semantics as the `uid` field. + pub gid: Option<uint>, + + /// If true, the child process is spawned in a detached state. On unix, this + /// means that the child is the leader of a new process group. + pub detach: bool, +} + pub struct LocalIo<'a> { factory: &'a mut IoFactory, } @@ -189,7 +244,7 @@ pub trait IoFactory { // misc fn timer_init(&mut self) -> IoResult<Box<RtioTimer:Send>>; - fn spawn(&mut self, config: ProcessConfig) + fn spawn(&mut self, cfg: ProcessConfig) -> IoResult<(Box<RtioProcess:Send>, Vec<Option<Box<RtioPipe:Send>>>)>; fn kill(&mut self, pid: libc::pid_t, signal: int) -> IoResult<()>; diff --git a/src/libstd/unstable/dynamic_lib.rs b/src/libstd/unstable/dynamic_lib.rs index 87d531cc627..1e88bffd00e 100644 --- a/src/libstd/unstable/dynamic_lib.rs +++ b/src/libstd/unstable/dynamic_lib.rs @@ -221,8 +221,6 @@ pub mod dl { pub mod dl { use libc; use os; - use path::GenericPath; - use path; use ptr; use result::{Ok, Err, Result}; |
