about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorCorey Richardson <corey@octayn.net>2014-12-05 10:07:02 -0800
committerCorey Richardson <corey@octayn.net>2014-12-05 10:07:02 -0800
commit64d58dcac2e57bd93c868ea9d49a8b877e1be5df (patch)
treed25b9a9ee5e18c50136274b5046459d6251cdf91 /src/libstd
parentea8bb5d18d6bfff644631a1692eea1e429566298 (diff)
parent74fb798a200dc82cf5b4a18065e3ea565229adc3 (diff)
downloadrust-64d58dcac2e57bd93c868ea9d49a8b877e1be5df.tar.gz
rust-64d58dcac2e57bd93c868ea9d49a8b877e1be5df.zip
rollup merge of #19454: nodakai/libstd-reap-failed-child
After the library successfully called `fork(2)`, the child does several
setup works such as setting UID, GID and current directory before it
calls `exec(2)`.  When those setup works failed, the child exits but the
parent didn't call `waitpid(2)` and left it as a zombie.

This patch also add several sanity checks.  They shouldn't make any
noticeable impact to runtime performance.

The new test case in `libstd/io/process.rs` calls the ps command to check
if the new code can really reap a zombie.
The output of `ps -A -o pid,sid,command` should look like this:

```
  PID   SID COMMAND
    1     1 /sbin/init
    2     0 [kthreadd]
    3     0 [ksoftirqd/0]
...
12562  9237 ./spawn-failure
12563  9237 [spawn-failure] <defunct>
12564  9237 [spawn-failure] <defunct>
...
12592  9237 [spawn-failure] <defunct>
12593  9237 ps -A -o pid,sid,command
12884 12884 /bin/zsh
12922 12922 /bin/zsh
...
```

where `./spawn-failure` is my test program which intentionally leaves many
zombies.  Filtering the output with the "SID" (session ID) column is
a quick way to tell if a process (zombie) was spawned by my own test
program.  Then the number of "defunct" lines is the number of zombie
children.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/sys/unix/process.rs43
1 files changed, 33 insertions, 10 deletions
diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs
index 76c316076f9..7dde19a6476 100644
--- a/src/libstd/sys/unix/process.rs
+++ b/src/libstd/sys/unix/process.rs
@@ -11,7 +11,7 @@ use self::Req::*;
 
 use libc::{mod, pid_t, c_void, c_int};
 use c_str::CString;
-use io::{mod, IoResult, IoError};
+use io::{mod, IoResult, IoError, EndOfFile};
 use mem;
 use os;
 use ptr;
@@ -39,6 +39,8 @@ enum Req {
     NewChild(libc::pid_t, Sender<ProcessExit>, u64),
 }
 
+const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
+
 impl Process {
     pub fn id(&self) -> pid_t {
         self.pid
@@ -106,18 +108,36 @@ impl Process {
                 if pid < 0 {
                     return Err(super::last_error())
                 } else if pid > 0 {
+                    #[inline]
+                    fn combine(arr: &[u8]) -> i32 {
+                        let a = arr[0] as u32;
+                        let b = arr[1] as u32;
+                        let c = arr[2] as u32;
+                        let d = arr[3] as u32;
+
+                        ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
+                    }
+
+                    let p = Process{ pid: pid };
                     drop(output);
-                    let mut bytes = [0, ..4];
+                    let mut bytes = [0, ..8];
                     return match input.read(&mut bytes) {
-                        Ok(4) => {
-                            let errno = (bytes[0] as i32 << 24) |
-                                        (bytes[1] as i32 << 16) |
-                                        (bytes[2] as i32 <<  8) |
-                                        (bytes[3] as i32 <<  0);
+                        Ok(8) => {
+                            assert!(combine(CLOEXEC_MSG_FOOTER) == combine(bytes.slice(4, 8)),
+                                "Validation on the CLOEXEC pipe failed: {}", bytes);
+                            let errno = combine(bytes.slice(0, 4));
+                            assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic");
                             Err(super::decode_error(errno))
                         }
-                        Err(..) => Ok(Process { pid: pid }),
-                        Ok(..) => panic!("short read on the cloexec pipe"),
+                        Err(ref e) if e.kind == EndOfFile => Ok(p),
+                        Err(e) => {
+                            assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic");
+                            panic!("the CLOEXEC pipe failed: {}", e)
+                        },
+                        Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
+                            assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic");
+                            panic!("short read on the CLOEXEC pipe")
+                        }
                     };
                 }
 
@@ -154,13 +174,16 @@ impl Process {
                 let _ = libc::close(input.fd());
 
                 fn fail(output: &mut FileDesc) -> ! {
-                    let errno = sys::os::errno();
+                    let errno = sys::os::errno() as u32;
                     let bytes = [
                         (errno >> 24) as u8,
                         (errno >> 16) as u8,
                         (errno >>  8) as u8,
                         (errno >>  0) as u8,
+                        CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
+                        CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
                     ];
+                    // pipe I/O up to PIPE_BUF bytes should be atomic
                     assert!(output.write(&bytes).is_ok());
                     unsafe { libc::_exit(1) }
                 }