diff --git a/kernel/context/context.rs b/kernel/context/context.rs
index 1563e6f8203e67035b8db3ab7f34fe56c9f06b08..a8678d7ac68425357433f358d9375ce92310488d 100644
--- a/kernel/context/context.rs
+++ b/kernel/context/context.rs
@@ -7,7 +7,7 @@ use arch;
 use context::file::File;
 use context::memory::{Grant, Memory, SharedMemory, Tls};
 use syscall::data::Event;
-use sync::{WaitCondition, WaitQueue};
+use sync::{WaitMap, WaitQueue};
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub enum Status {
@@ -40,7 +40,7 @@ pub struct Context {
     /// Context is halting parent
     pub vfork: bool,
     /// Context is being waited on
-    pub waitpid: Arc<WaitCondition>,
+    pub waitpid: Arc<WaitMap<usize, usize>>,
     /// Context should wake up at specified time
     pub wake: Option<(u64, u64)>,
     /// The architecture specific context
@@ -85,7 +85,7 @@ impl Context {
             running: false,
             cpu_id: None,
             vfork: false,
-            waitpid: Arc::new(WaitCondition::new()),
+            waitpid: Arc::new(WaitMap::new()),
             wake: None,
             arch: arch::context::Context::new(),
             kfx: None,
diff --git a/kernel/sync/wait_map.rs b/kernel/sync/wait_map.rs
index 468ad8c0e02e83e37d21aac65c7c8ce544a1f8a6..c0198a175b0c10ac3e0aea506979ccaec1e6dd30 100644
--- a/kernel/sync/wait_map.rs
+++ b/kernel/sync/wait_map.rs
@@ -1,4 +1,5 @@
 use collections::BTreeMap;
+use core::mem;
 use spin::Mutex;
 
 use sync::WaitCondition;
@@ -9,7 +10,7 @@ pub struct WaitMap<K, V> {
     condition: WaitCondition
 }
 
-impl<K, V> WaitMap<K, V> where K: Ord {
+impl<K, V> WaitMap<K, V> where K: Clone + Ord {
     pub fn new() -> WaitMap<K, V> {
         WaitMap {
             inner: Mutex::new(BTreeMap::new()),
@@ -17,17 +18,45 @@ impl<K, V> WaitMap<K, V> where K: Ord {
         }
     }
 
-    pub fn send(&self, key: K, value: V) {
-        self.inner.lock().insert(key, value);
-        self.condition.notify();
+    pub fn receive_nonblock(&self, key: &K) -> Option<V> {
+        self.inner.lock().remove(key)
     }
 
     pub fn receive(&self, key: &K) -> V {
         loop {
-            if let Some(value) = self.inner.lock().remove(key) {
+            if let Some(value) = self.receive_nonblock(key) {
                 return value;
             }
             self.condition.wait();
         }
     }
+
+    pub fn receive_any_nonblock(&self) -> Option<(K, V)> {
+        let mut inner = self.inner.lock();
+        if let Some(key) = inner.keys().next().map(|key| key.clone()) {
+            inner.remove(&key).map(|value| (key, value))
+        } else {
+            None
+        }
+    }
+
+    pub fn receive_any(&self) -> (K, V) {
+        loop {
+            if let Some(entry) = self.receive_any_nonblock() {
+                return entry;
+            }
+            self.condition.wait();
+        }
+    }
+
+    pub fn receive_all(&self) -> BTreeMap<K, V> {
+        let mut ret = BTreeMap::new();
+        mem::swap(&mut ret, &mut *self.inner.lock());
+        ret
+    }
+
+    pub fn send(&self, key: K, value: V) {
+        self.inner.lock().insert(key, value);
+        self.condition.notify();
+    }
 }
diff --git a/kernel/syscall/process.rs b/kernel/syscall/process.rs
index 6fa2fade82c3cf58a13eb52da67144f59dc9dc7d..39ed2addd04fdbb4c098d48bdc5abfec7f0e1498 100644
--- a/kernel/syscall/process.rs
+++ b/kernel/syscall/process.rs
@@ -691,13 +691,14 @@ pub fn exit(status: usize) -> ! {
         };
 
         let mut close_files = Vec::new();
-        {
+        let (pid, ppid) = {
             let mut context = context_lock.write();
             if Arc::strong_count(&context.files) == 1 {
                 mem::swap(context.files.lock().deref_mut(), &mut close_files);
             }
             context.files = Arc::new(Mutex::new(Vec::new()));
-        }
+            (context.id, context.ppid)
+        };
 
         /// Files must be closed while context is valid so that messages can be passed
         for (fd, file_option) in close_files.drain(..).enumerate() {
@@ -716,7 +717,19 @@ pub fn exit(status: usize) -> ! {
             }
         }
 
-        let (vfork, ppid) = {
+        /// Transfer child processes to parent
+        {
+            let contexts = context::contexts();
+            for (_id, context_lock) in contexts.iter() {
+                let mut context = context_lock.write();
+                if context.ppid == pid {
+                    context.ppid = ppid;
+                    context.vfork = false;
+                }
+            }
+        }
+
+        let (vfork, children) = {
             let mut context = context_lock.write();
 
             context.image.clear();
@@ -730,18 +743,28 @@ pub fn exit(status: usize) -> ! {
 
             context.status = context::Status::Exited(status);
 
-            context.waitpid.notify();
+            let children = context.waitpid.receive_all();
 
-            (vfork, context.ppid)
+            (vfork, children)
         };
 
-        if vfork {
+        {
             let contexts = context::contexts();
             if let Some(parent_lock) = contexts.get(ppid) {
-                let mut parent = parent_lock.write();
-                if ! parent.unblock() {
-                    println!("{} not blocked for exit vfork unblock", ppid);
+                let waitpid = {
+                    let mut parent = parent_lock.write();
+                    if vfork {
+                        if ! parent.unblock() {
+                            println!("{} not blocked for exit vfork unblock", ppid);
+                        }
+                    }
+                    parent.waitpid.clone()
+                };
+
+                for (c_pid, c_status) in children {
+                    waitpid.send(c_pid, c_status);
                 }
+                waitpid.send(pid, status);
             } else {
                 println!("{} not found for exit vfork unblock", ppid);
             }
@@ -977,45 +1000,64 @@ pub fn virttophys(virtual_address: usize) -> Result<usize> {
     }
 }
 
-pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result<usize> {
-    loop {
-        let mut exited = false;
-        let mut running;
-        let waitpid;
+fn reap(pid: usize) -> Result<usize> {
+    // Spin until not running
+    let mut running = false;
+    while running {
         {
             let contexts = context::contexts();
             let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?;
             let context = context_lock.read();
-            if let context::Status::Exited(status) = context.status {
-                if status_ptr != 0 {
-                    let status_slice = validate_slice_mut(status_ptr as *mut usize, 1)?;
-                    status_slice[0] = status;
-                }
-                exited = true;
-            }
             running = context.running;
-            waitpid = context.waitpid.clone();
         }
 
-        if exited {
-            // Spin until not running
-            while running {
-                {
-                    let contexts = context::contexts();
-                    let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?;
-                    let context = context_lock.read();
-                    running = context.running;
-                }
+        arch::interrupt::pause();
+    }
 
-                arch::interrupt::pause();
-            }
+    let mut contexts = context::contexts_mut();
+    contexts.remove(pid).ok_or(Error::new(ESRCH)).and(Ok(pid))
+}
 
-            let mut contexts = context::contexts_mut();
-            return contexts.remove(pid).ok_or(Error::new(ESRCH)).and(Ok(pid));
-        } else if flags & WNOHANG == WNOHANG {
-            return Ok(0);
+pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result<usize> {
+    let waitpid = {
+        let contexts = context::contexts();
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+        let context = context_lock.read();
+        context.waitpid.clone()
+    };
+
+    let mut tmp = [0];
+    let status_slice = if status_ptr != 0 {
+        validate_slice_mut(status_ptr as *mut usize, 1)?
+    } else {
+        &mut tmp
+    };
+
+    if pid == 0 {
+        if flags & WNOHANG == WNOHANG {
+            if let Some((w_pid, status)) = waitpid.receive_any_nonblock() {
+                status_slice[0] = status;
+                reap(w_pid)
+            } else {
+                Ok(0)
+            }
+        } else {
+            let (w_pid, status) = waitpid.receive_any();
+            status_slice[0] = status;
+            reap(w_pid)
+        }
+    } else {
+        if flags & WNOHANG == WNOHANG {
+            if let Some(status) = waitpid.receive_nonblock(&pid) {
+                status_slice[0] = status;
+                reap(pid)
+            } else {
+                Ok(0)
+            }
         } else {
-            waitpid.wait();
+            let status = waitpid.receive(&pid);
+            status_slice[0] = status;
+            reap(pid)
         }
     }
 }
diff --git a/programs/init/src/main.rs b/programs/init/src/main.rs
index f6e6a98f00ad5cd51697bed0f474b91585af559f..1b010ce63db7880e17097733d62ec25cbb286380 100644
--- a/programs/init/src/main.rs
+++ b/programs/init/src/main.rs
@@ -89,4 +89,9 @@ pub fn main() {
     if let Err(err) = run("initfs:etc/init.rc") {
         println!("init: failed to run initfs:etc/init.rc: {}", err);
     }
+
+    loop {
+        let mut status = 0;
+        syscall::waitpid(0, &mut status, 0).unwrap();
+    }
 }