about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-02-06 22:38:05 -0800
committerAlex Crichton <alex@alexcrichton.com>2014-02-16 16:01:03 -0800
commit553b7e67d70dbf72dd705616acaa496a20fba765 (patch)
tree4274ee5030cea6f05dbbb23f75c1c9eb4e58c2f1 /src/libstd
parent13dc521861f6c3be909ba9317fde50f946e9f85d (diff)
downloadrust-553b7e67d70dbf72dd705616acaa496a20fba765.tar.gz
rust-553b7e67d70dbf72dd705616acaa496a20fba765.zip
Allow configuration of uid/gid/detach on processes
This just copies the libuv implementation for libnative which seems reasonable
enough (uid/gid fail on windows).

Closes #12082
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/io/process.rs129
-rw-r--r--src/libstd/libc.rs6
-rw-r--r--src/libstd/run.rs34
3 files changed, 138 insertions, 31 deletions
diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs
index b515cd9d31c..6540fcd85d3 100644
--- a/src/libstd/io/process.rs
+++ b/src/libstd/io/process.rs
@@ -58,7 +58,21 @@ pub struct ProcessConfig<'a> {
     ///     0 - stdin
     ///     1 - stdout
     ///     2 - stderr
-    io: &'a [StdioContainer]
+    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.
+    uid: Option<uint>,
+
+    /// Similar to `uid`, but sets the group id of the child process. This has
+    /// the same semantics as the `uid` field.
+    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.
+    detach: bool,
 }
 
 /// Describes what to do with a standard io stream for a child process.
@@ -115,6 +129,36 @@ 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: &'static [~"-c", ~"echo hello"],
+    ///     .. ProcessConfig::new()
+    /// };
+    ///
+    /// let p = Process::new(config);
+    /// ```
+    ///
+    pub fn new() -> ProcessConfig<'static> {
+        ProcessConfig {
+            program: "",
+            args: &'static [],
+            env: None,
+            cwd: None,
+            io: &'static [],
+            uid: None,
+            gid: None,
+            detach: false,
+        }
+    }
+}
+
 impl Process {
     /// Creates a new pipe initialized, but not bound to any particular
     /// source/destination
@@ -175,13 +219,10 @@ mod tests {
     // FIXME(#10380)
     #[cfg(unix, not(target_os="android"))]
     iotest!(fn smoke() {
-        let io = ~[];
         let args = ProcessConfig {
             program: "/bin/sh",
             args: &[~"-c", ~"true"],
-            env: None,
-            cwd: None,
-            io: io,
+            .. ProcessConfig::new()
         };
         let p = Process::new(args);
         assert!(p.is_ok());
@@ -192,13 +233,9 @@ mod tests {
     // FIXME(#10380)
     #[cfg(unix, not(target_os="android"))]
     iotest!(fn smoke_failure() {
-        let io = ~[];
         let args = ProcessConfig {
             program: "if-this-is-a-binary-then-the-world-has-ended",
-            args: &[],
-            env: None,
-            cwd: None,
-            io: io,
+            .. ProcessConfig::new()
         };
         match Process::new(args) {
             Ok(..) => fail!(),
@@ -209,13 +246,10 @@ mod tests {
     // FIXME(#10380)
     #[cfg(unix, not(target_os="android"))]
     iotest!(fn exit_reported_right() {
-        let io = ~[];
         let args = ProcessConfig {
             program: "/bin/sh",
             args: &[~"-c", ~"exit 1"],
-            env: None,
-            cwd: None,
-            io: io,
+            .. ProcessConfig::new()
         };
         let p = Process::new(args);
         assert!(p.is_ok());
@@ -225,13 +259,10 @@ mod tests {
 
     #[cfg(unix, not(target_os="android"))]
     iotest!(fn signal_reported_right() {
-        let io = ~[];
         let args = ProcessConfig {
             program: "/bin/sh",
             args: &[~"-c", ~"kill -1 $$"],
-            env: None,
-            cwd: None,
-            io: io,
+            .. ProcessConfig::new()
         };
         let p = Process::new(args);
         assert!(p.is_ok());
@@ -264,9 +295,8 @@ mod tests {
         let args = ProcessConfig {
             program: "/bin/sh",
             args: &[~"-c", ~"echo foobar"],
-            env: None,
-            cwd: None,
             io: io,
+            .. ProcessConfig::new()
         };
         assert_eq!(run_output(args), ~"foobar\n");
     })
@@ -279,9 +309,9 @@ mod tests {
         let args = ProcessConfig {
             program: "/bin/sh",
             args: &[~"-c", ~"pwd"],
-            env: None,
             cwd: cwd,
             io: io,
+            .. ProcessConfig::new()
         };
         assert_eq!(run_output(args), ~"/\n");
     })
@@ -294,9 +324,8 @@ mod tests {
         let args = ProcessConfig {
             program: "/bin/sh",
             args: &[~"-c", ~"read line; echo $line"],
-            env: None,
-            cwd: None,
             io: io,
+            .. ProcessConfig::new()
         };
         let mut p = Process::new(args).unwrap();
         p.io[0].get_mut_ref().write("foobar".as_bytes()).unwrap();
@@ -306,4 +335,58 @@ mod tests {
         assert_eq!(out, ~"foobar\n");
     })
 
+    // FIXME(#10380)
+    #[cfg(unix, not(target_os="android"))]
+    iotest!(fn detach_works() {
+        let args = ProcessConfig {
+            program: "/bin/sh",
+            args: &[~"-c", ~"true"],
+            detach: true,
+            .. ProcessConfig::new()
+        };
+        let mut p = Process::new(args).unwrap();
+        assert!(p.wait().success());
+    })
+
+    #[cfg(windows)]
+    iotest!(fn uid_fails_on_windows() {
+        let args = ProcessConfig {
+            program: "test",
+            uid: Some(10),
+            .. ProcessConfig::new()
+        };
+        assert!(Process::new(args).is_err());
+    })
+
+    // FIXME(#10380)
+    #[cfg(unix, not(target_os="android"))]
+    iotest!(fn uid_works() {
+        use libc;
+        let args = ProcessConfig {
+            program: "/bin/sh",
+            args: &[~"-c", ~"true"],
+            uid: Some(unsafe { libc::getuid() as uint }),
+            gid: Some(unsafe { libc::getgid() as uint }),
+            .. ProcessConfig::new()
+        };
+        let mut p = Process::new(args).unwrap();
+        assert!(p.wait().success());
+    })
+
+    // FIXME(#10380)
+    #[cfg(unix, not(target_os="android"))]
+    iotest!(fn uid_to_root_fails() {
+        use libc;
+
+        // 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::new(args).is_err());
+    })
 }
diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs
index 39383f99392..7dc4c692f63 100644
--- a/src/libstd/libc.rs
+++ b/src/libstd/libc.rs
@@ -159,7 +159,7 @@ pub use libc::funcs::c95::stdio::{fread, freopen, fseek, fsetpos, ftell};
 pub use libc::funcs::c95::stdio::{fwrite, perror, puts, remove, rewind};
 pub use libc::funcs::c95::stdio::{setbuf, setvbuf, tmpfile, ungetc};
 
-pub use libc::funcs::c95::stdlib::{abs, atof, atoi, calloc, exit};
+pub use libc::funcs::c95::stdlib::{abs, atof, atoi, calloc, exit, _exit};
 pub use libc::funcs::c95::stdlib::{free, getenv, labs, malloc, rand};
 pub use libc::funcs::c95::stdlib::{realloc, srand, strtod, strtol};
 pub use libc::funcs::c95::stdlib::{strtoul, system};
@@ -1769,6 +1769,9 @@ pub mod consts {
             pub static MAX_PROTOCOL_CHAIN: DWORD = 7;
             pub static WSAPROTOCOL_LEN: DWORD = 255;
             pub static INVALID_SOCKET: DWORD = !0;
+
+            pub static DETACHED_PROCESS: DWORD = 0x00000008;
+            pub static CREATE_NEW_PROCESS_GROUP: DWORD = 0x00000200;
         }
         pub mod sysconf {
         }
@@ -3340,6 +3343,7 @@ pub mod funcs {
                 pub fn realloc(p: *mut c_void, size: size_t) -> *mut c_void;
                 pub fn free(p: *mut c_void);
                 pub fn exit(status: c_int) -> !;
+                pub fn _exit(status: c_int) -> !;
                 // Omitted: atexit.
                 pub fn system(s: *c_char) -> c_int;
                 pub fn getenv(s: *c_char) -> *c_char;
diff --git a/src/libstd/run.rs b/src/libstd/run.rs
index 6f684f23d47..9ea8f6447dd 100644
--- a/src/libstd/run.rs
+++ b/src/libstd/run.rs
@@ -88,6 +88,20 @@ pub struct ProcessOptions<'a> {
      * and Process.error() will fail.
      */
     err_fd: Option<c_int>,
+
+    /// The uid to assume for the child process. For more information, see the
+    /// documentation in `io::process::ProcessConfig` about this field.
+    uid: Option<uint>,
+
+    /// The gid to assume for the child process. For more information, see the
+    /// documentation in `io::process::ProcessConfig` about this field.
+    gid: Option<uint>,
+
+    /// Flag as to whether the child process will be the leader of a new process
+    /// group or not. This allows the parent process to exit while the child is
+    /// still running. For more information, see the documentation in
+    /// `io::process::ProcessConfig` about this field.
+    detach: bool,
 }
 
 impl <'a> ProcessOptions<'a> {
@@ -99,6 +113,9 @@ impl <'a> ProcessOptions<'a> {
             in_fd: None,
             out_fd: None,
             err_fd: None,
+            uid: None,
+            gid: None,
+            detach: false,
         }
     }
 }
@@ -128,7 +145,9 @@ impl Process {
      */
     pub fn new(prog: &str, args: &[~str],
                options: ProcessOptions) -> io::IoResult<Process> {
-        let ProcessOptions { env, dir, in_fd, out_fd, err_fd } = options;
+        let ProcessOptions {
+            env, dir, in_fd, out_fd, err_fd, uid, gid, detach
+        } = options;
         let env = env.as_ref().map(|a| a.as_slice());
         let cwd = dir.as_ref().map(|a| a.as_str().unwrap());
         fn rtify(fd: Option<c_int>, input: bool) -> process::StdioContainer {
@@ -145,6 +164,9 @@ impl Process {
             env: env,
             cwd: cwd,
             io: rtio,
+            uid: uid,
+            gid: gid,
+            detach: detach,
         };
         process::Process::new(rtconfig).map(|p| Process { inner: p })
     }
@@ -302,11 +324,10 @@ impl Process {
  */
 pub fn process_status(prog: &str, args: &[~str]) -> io::IoResult<ProcessExit> {
     Process::new(prog, args, ProcessOptions {
-        env: None,
-        dir: None,
         in_fd: Some(unsafe { libc::dup(libc::STDIN_FILENO) }),
         out_fd: Some(unsafe { libc::dup(libc::STDOUT_FILENO) }),
-        err_fd: Some(unsafe { libc::dup(libc::STDERR_FILENO) })
+        err_fd: Some(unsafe { libc::dup(libc::STDERR_FILENO) }),
+        .. ProcessOptions::new()
     }).map(|mut p| p.finish())
 }
 
@@ -396,11 +417,10 @@ mod tests {
         let pipe_err = os::pipe();
 
         let mut process = run::Process::new("cat", [], run::ProcessOptions {
-            dir: None,
-            env: None,
             in_fd: Some(pipe_in.input),
             out_fd: Some(pipe_out.out),
-            err_fd: Some(pipe_err.out)
+            err_fd: Some(pipe_err.out),
+            .. run::ProcessOptions::new()
         }).unwrap();
 
         os::close(pipe_in.input as int);