about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-02-16 16:46:39 -0800
committerbors <bors@rust-lang.org>2014-02-16 16:46:39 -0800
commitc8489069b43191c5298f17430933b3b88fb79c3c (patch)
treef23c432774f94a6295fe9b37449fb339f888df00
parent425f57477f3e1e4850c7e3c92954148e7585d2ca (diff)
parent74b42c646eb90ff267b131fcb2ac8d25428538c5 (diff)
downloadrust-c8489069b43191c5298f17430933b3b88fb79c3c.tar.gz
rust-c8489069b43191c5298f17430933b3b88fb79c3c.zip
auto merge of #12085 : alexcrichton/rust/issue-12082, r=brson
This just copies the libuv implementation for libnative which seems reasonable
enough (uid/gid fail on windows).

Closes #12082
-rw-r--r--src/compiletest/procsrv.rs10
-rw-r--r--src/libnative/io/process.rs95
-rw-r--r--src/librustuv/process.rs16
-rw-r--r--src/libstd/io/process.rs129
-rw-r--r--src/libstd/libc.rs6
-rw-r--r--src/libstd/run.rs34
m---------src/libuv0
-rw-r--r--src/rt/libuv-auto-clean-trigger2
-rw-r--r--src/test/run-pass/issue-10626.rs5
-rw-r--r--src/test/run-pass/process-detach.rs56
10 files changed, 288 insertions, 65 deletions
diff --git a/src/compiletest/procsrv.rs b/src/compiletest/procsrv.rs
index 019803a9337..1016c3cf0e6 100644
--- a/src/compiletest/procsrv.rs
+++ b/src/compiletest/procsrv.rs
@@ -51,10 +51,7 @@ pub fn run(lib_path: &str,
     let env = env + target_env(lib_path, prog);
     let mut opt_process = run::Process::new(prog, args, run::ProcessOptions {
         env: Some(env),
-        dir: None,
-        in_fd: None,
-        out_fd: None,
-        err_fd: None
+        .. run::ProcessOptions::new()
     });
 
     match opt_process {
@@ -83,10 +80,7 @@ pub fn run_background(lib_path: &str,
     let env = env + target_env(lib_path, prog);
     let opt_process = run::Process::new(prog, args, run::ProcessOptions {
         env: Some(env),
-        dir: None,
-        in_fd: None,
-        out_fd: None,
-        err_fd: None
+        .. run::ProcessOptions::new()
     });
 
     match opt_process {
diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs
index b796535371f..affa3ebf544 100644
--- a/src/libnative/io/process.rs
+++ b/src/libnative/io/process.rs
@@ -98,8 +98,8 @@ impl Process {
 
         let env = config.env.map(|a| a.to_owned());
         let cwd = config.cwd.map(|a| Path::new(a));
-        let res = spawn_process_os(config.program, config.args, env,
-                                   cwd.as_ref(), in_fd, out_fd, err_fd);
+        let res = spawn_process_os(config, env, cwd.as_ref(), in_fd, out_fd,
+                                   err_fd);
 
         unsafe {
             for pipe in in_pipe.iter() { let _ = libc::close(pipe.input); }
@@ -180,7 +180,7 @@ struct SpawnProcessResult {
 }
 
 #[cfg(windows)]
-fn spawn_process_os(prog: &str, args: &[~str],
+fn spawn_process_os(config: p::ProcessConfig,
                     env: Option<~[(~str, ~str)]>,
                     dir: Option<&Path>,
                     in_fd: c_int, out_fd: c_int,
@@ -202,6 +202,14 @@ fn spawn_process_os(prog: &str, args: &[~str],
 
     use std::mem;
 
+    if config.gid.is_some() || config.uid.is_some() {
+        return Err(io::IoError {
+            kind: io::OtherIoError,
+            desc: "unsupported gid/uid requested on windows",
+            detail: None,
+        })
+    }
+
     unsafe {
 
         let mut si = zeroed_startupinfo();
@@ -237,16 +245,23 @@ fn spawn_process_os(prog: &str, args: &[~str],
             fail!("failure in DuplicateHandle: {}", os::last_os_error());
         }
 
-        let cmd = make_command_line(prog, args);
+        let cmd = make_command_line(config.program, config.args);
         let mut pi = zeroed_process_information();
         let mut create_err = None;
 
+        // stolen from the libuv code.
+        let mut flags = 0;
+        if config.detach {
+            flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP;
+        }
+
         with_envp(env, |envp| {
             with_dirp(dir, |dirp| {
                 cmd.with_c_str(|cmdp| {
                     let created = CreateProcessA(ptr::null(), cast::transmute(cmdp),
                                                  ptr::mut_null(), ptr::mut_null(), TRUE,
-                                                 0, envp, dirp, &mut si, &mut pi);
+                                                 flags, envp, dirp, &mut si,
+                                                 &mut pi);
                     if created == FALSE {
                         create_err = Some(super::last_error());
                     }
@@ -364,7 +379,7 @@ fn make_command_line(prog: &str, args: &[~str]) -> ~str {
 }
 
 #[cfg(unix)]
-fn spawn_process_os(prog: &str, args: &[~str],
+fn spawn_process_os(config: p::ProcessConfig,
                     env: Option<~[(~str, ~str)]>,
                     dir: Option<&Path>,
                     in_fd: c_int, out_fd: c_int,
@@ -372,7 +387,6 @@ fn spawn_process_os(prog: &str, args: &[~str],
     use std::libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
     use std::libc::funcs::bsd44::getdtablesize;
     use std::libc::c_ulong;
-    use std::unstable::intrinsics;
 
     mod rustrt {
         extern {
@@ -441,22 +455,34 @@ fn spawn_process_os(prog: &str, args: &[~str],
         }
         drop(input);
 
+        fn fail(output: &mut file::FileDesc) -> ! {
+            let errno = os::errno();
+            let bytes = [
+                (errno << 24) as u8,
+                (errno << 16) as u8,
+                (errno <<  8) as u8,
+                (errno <<  0) as u8,
+            ];
+            assert!(output.inner_write(bytes).is_ok());
+            unsafe { libc::_exit(1) }
+        }
+
         rustrt::rust_unset_sigprocmask();
 
         if in_fd == -1 {
             let _ = libc::close(libc::STDIN_FILENO);
         } else if retry(|| dup2(in_fd, 0)) == -1 {
-            fail!("failure in dup2(in_fd, 0): {}", os::last_os_error());
+            fail(&mut output);
         }
         if out_fd == -1 {
             let _ = libc::close(libc::STDOUT_FILENO);
         } else if retry(|| dup2(out_fd, 1)) == -1 {
-            fail!("failure in dup2(out_fd, 1): {}", os::last_os_error());
+            fail(&mut output);
         }
         if err_fd == -1 {
             let _ = libc::close(libc::STDERR_FILENO);
         } else if retry(|| dup2(err_fd, 2)) == -1 {
-            fail!("failure in dup3(err_fd, 2): {}", os::last_os_error());
+            fail(&mut output);
         }
         // close all other fds
         for fd in range(3, getdtablesize()).rev() {
@@ -465,9 +491,44 @@ fn spawn_process_os(prog: &str, args: &[~str],
             }
         }
 
+        match config.gid {
+            Some(u) => {
+                if libc::setgid(u as libc::gid_t) != 0 {
+                    fail(&mut output);
+                }
+            }
+            None => {}
+        }
+        match config.uid {
+            Some(u) => {
+                // When dropping privileges from root, the `setgroups` call will
+                // remove any extraneous groups. If we don't call this, then
+                // even though our uid has dropped, we may still have groups
+                // that enable us to do super-user things. This will fail if we
+                // aren't root, so don't bother checking the return value, this
+                // is just done as an optimistic privilege dropping function.
+                extern {
+                    fn setgroups(ngroups: libc::c_int,
+                                 ptr: *libc::c_void) -> libc::c_int;
+                }
+                let _ = setgroups(0, 0 as *libc::c_void);
+
+                if libc::setuid(u as libc::uid_t) != 0 {
+                    fail(&mut output);
+                }
+            }
+            None => {}
+        }
+        if config.detach {
+            // Don't check the error of setsid because it fails if we're the
+            // process leader already. We just forked so it shouldn't return
+            // error, but ignore it anyway.
+            let _ = libc::setsid();
+        }
+
         with_dirp(dir, |dirp| {
             if !dirp.is_null() && chdir(dirp) == -1 {
-                fail!("failure in chdir: {}", os::last_os_error());
+                fail(&mut output);
             }
         });
 
@@ -476,17 +537,9 @@ fn spawn_process_os(prog: &str, args: &[~str],
                 set_environ(envp);
             }
         });
-        with_argv(prog, args, |argv| {
+        with_argv(config.program, config.args, |argv| {
             let _ = execvp(*argv, argv);
-            let errno = os::errno();
-            let bytes = [
-                (errno << 24) as u8,
-                (errno << 16) as u8,
-                (errno <<  8) as u8,
-                (errno <<  0) as u8,
-            ];
-            assert!(output.inner_write(bytes).is_ok());
-            intrinsics::abort();
+            fail(&mut output);
         })
     }
 }
diff --git a/src/librustuv/process.rs b/src/librustuv/process.rs
index e1f94d8c4df..a0623059bd7 100644
--- a/src/librustuv/process.rs
+++ b/src/librustuv/process.rs
@@ -58,6 +58,16 @@ impl Process {
 
         let ret = with_argv(config.program, config.args, |argv| {
             with_env(config.env, |envp| {
+                let mut flags = 0;
+                if config.uid.is_some() {
+                    flags |= uvll::PROCESS_SETUID;
+                }
+                if config.gid.is_some() {
+                    flags |= uvll::PROCESS_SETGID;
+                }
+                if config.detach {
+                    flags |= uvll::PROCESS_DETACHED;
+                }
                 let options = uvll::uv_process_options_t {
                     exit_cb: on_exit,
                     file: unsafe { *argv },
@@ -67,11 +77,11 @@ impl Process {
                         Some(ref cwd) => cwd.with_ref(|p| p),
                         None => ptr::null(),
                     },
-                    flags: 0,
+                    flags: flags as libc::c_uint,
                     stdio_count: stdio.len() as libc::c_int,
                     stdio: stdio.as_ptr(),
-                    uid: 0,
-                    gid: 0,
+                    uid: config.uid.unwrap_or(0) as uvll::uv_uid_t,
+                    gid: config.gid.unwrap_or(0) as uvll::uv_gid_t,
                 };
 
                 let handle = UvHandle::alloc(None::<Process>, uvll::UV_PROCESS);
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);
diff --git a/src/libuv b/src/libuv
-Subproject fd5308383c575472edb2163d823dc6670bf5960
+Subproject 800b56fe6af21ffd8e56aee8cf12dd758f5bbdf
diff --git a/src/rt/libuv-auto-clean-trigger b/src/rt/libuv-auto-clean-trigger
index ca0809cb090..961455b9093 100644
--- a/src/rt/libuv-auto-clean-trigger
+++ b/src/rt/libuv-auto-clean-trigger
@@ -1,2 +1,2 @@
 # Change the contents of this file to force a full rebuild of libuv
-2013-12-23
+2014-02-16
diff --git a/src/test/run-pass/issue-10626.rs b/src/test/run-pass/issue-10626.rs
index 3529551de81..94964fbc89b 100644
--- a/src/test/run-pass/issue-10626.rs
+++ b/src/test/run-pass/issue-10626.rs
@@ -33,7 +33,10 @@ pub fn main () {
         args : &[~"child"],
         env : None,
         cwd : None,
-        io : &[]
+        io : &[],
+        uid: None,
+        gid: None,
+        detach: false,
     };
 
     let mut p = process::Process::new(config).unwrap();
diff --git a/src/test/run-pass/process-detach.rs b/src/test/run-pass/process-detach.rs
new file mode 100644
index 00000000000..91f77caf8a3
--- /dev/null
+++ b/src/test/run-pass/process-detach.rs
@@ -0,0 +1,56 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-fast
+// ignore-win32
+// ignore-android
+
+// This test ensures that the 'detach' field on processes does the right thing.
+// By detaching the child process, they should be put into a separate process
+// group. We test this by spawning a detached process, then killing our own
+// group with a signal.
+//
+// Note that the first thing we do is put ourselves in our own process group so
+// we don't interfere with other running tests.
+
+use std::libc;
+use std::io::process;
+use std::io::signal::{Listener, Interrupt};
+
+fn main() {
+    unsafe { libc::setsid(); }
+
+    let config = process::ProcessConfig {
+        program : "/bin/sh",
+        args : &[~"-c", ~"read a"],
+        io : &[process::CreatePipe(true, false)],
+        detach: true,
+        .. process::ProcessConfig::new()
+    };
+
+    // we shouldn't die because of an interrupt
+    let mut l = Listener::new();
+    l.register(Interrupt).unwrap();
+
+    // spawn the child
+    let mut p = process::Process::new(config).unwrap();
+
+    // send an interrupt to everyone in our process group
+    unsafe { libc::funcs::posix88::signal::kill(0, libc::SIGINT); }
+
+    // Wait for the child process to die (terminate it's stdin and the read
+    // should fail).
+    drop(p.io[0].take());
+    match p.wait() {
+        process::ExitStatus(..) => {}
+        process::ExitSignal(..) => fail!()
+    }
+}
+