diff options
| author | Corey Richardson <corey@octayn.net> | 2014-12-05 10:07:02 -0800 |
|---|---|---|
| committer | Corey Richardson <corey@octayn.net> | 2014-12-05 10:07:02 -0800 |
| commit | 64d58dcac2e57bd93c868ea9d49a8b877e1be5df (patch) | |
| tree | d25b9a9ee5e18c50136274b5046459d6251cdf91 /src/libstd | |
| parent | ea8bb5d18d6bfff644631a1692eea1e429566298 (diff) | |
| parent | 74fb798a200dc82cf5b4a18065e3ea565229adc3 (diff) | |
| download | rust-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.rs | 43 |
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) } } |
