diff --git a/context/context.rs b/context/context.rs
index 1093f1758b4497564ded54d15f95bcc6eaf38734..fd51e969ddea48f04902c1ace7d99b0599f0008f 100644
--- a/context/context.rs
+++ b/context/context.rs
@@ -55,6 +55,8 @@ pub struct Context {
     pub stack: Option<Memory>,
     /// User grants
     pub grants: Arc<Mutex<Vec<Grant>>>,
+    /// The name of the context
+    pub name: Arc<Mutex<Vec<u8>>>,
     /// The current working directory
     pub cwd: Arc<Mutex<Vec<u8>>>,
     /// Kernel events
@@ -87,6 +89,7 @@ impl Context {
             heap: None,
             stack: None,
             grants: Arc::new(Mutex::new(Vec::new())),
+            name: Arc::new(Mutex::new(Vec::new())),
             cwd: Arc::new(Mutex::new(Vec::new())),
             events: Arc::new(WaitQueue::new()),
             env: Arc::new(Mutex::new(BTreeMap::new())),
diff --git a/scheme/mod.rs b/scheme/mod.rs
index 1d61491d19b4c3c948bfa85cf875a71ebb7f41dc..b6b2c35dc04411292f8ca126e305f280b833af3d 100644
--- a/scheme/mod.rs
+++ b/scheme/mod.rs
@@ -22,6 +22,7 @@ use self::initfs::InitFsScheme;
 use self::irq::{IRQ_SCHEME_ID, IrqScheme};
 use self::pipe::{PIPE_SCHEME_ID, PipeScheme};
 use self::root::{ROOT_SCHEME_ID, RootScheme};
+use self::sys::SysScheme;
 
 /// Debug scheme
 pub mod debug;
@@ -44,6 +45,9 @@ pub mod pipe;
 /// Root scheme
 pub mod root;
 
+/// System information
+pub mod sys;
+
 /// Userspace schemes
 pub mod user;
 
@@ -121,6 +125,7 @@ fn init_schemes() -> RwLock<SchemeList> {
     list.insert(Box::new(*b"initfs"), Arc::new(Box::new(InitFsScheme::new()))).expect("failed to insert initfs scheme");
     IRQ_SCHEME_ID.store(list.insert(Box::new(*b"irq"), Arc::new(Box::new(IrqScheme))).expect("failed to insert irq scheme"), Ordering::SeqCst);
     PIPE_SCHEME_ID.store(list.insert(Box::new(*b"pipe"), Arc::new(Box::new(PipeScheme))).expect("failed to insert pipe scheme"), Ordering::SeqCst);
+    list.insert(Box::new(*b"sys"), Arc::new(Box::new(SysScheme::new()))).expect("failed to insert sys scheme");
     RwLock::new(list)
 }
 
diff --git a/scheme/sys/context.rs b/scheme/sys/context.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8c99a1601f4afd0675d16aa7d748505b4712f602
--- /dev/null
+++ b/scheme/sys/context.rs
@@ -0,0 +1,90 @@
+use collections::{String, Vec};
+use core::str;
+
+use context;
+use syscall::error::Result;
+
+pub fn resource() -> Result<Vec<u8>> {
+    let mut string = format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n",
+                             "PID",
+                             "PPID",
+                             "UID",
+                             "GID",
+                             "STAT",
+                             "MEM",
+                             "NAME");
+    {
+        let contexts = context::contexts();
+        for (_id, context_lock) in contexts.iter() {
+            let context = context_lock.read();
+
+            let mut memory = 0;
+            if let Some(ref kfx) = context.kstack {
+                memory += kfx.len();
+            }
+            if let Some(ref kstack) = context.kstack {
+                memory += kstack.len();
+            }
+            for shared_mem in context.image.iter() {
+                shared_mem.with(|mem| {
+                    memory += mem.size();
+                });
+            }
+            if let Some(ref heap) = context.heap {
+                heap.with(|heap| {
+                    memory += heap.size();
+                });
+            }
+            if let Some(ref stack) = context.stack {
+                memory += stack.size();
+            }
+
+            let memory_string = if memory >= 1024 * 1024 * 1024 {
+                format!("{} GB", memory / 1024 / 1024 / 1024)
+            } else if memory >= 1024 * 1024 {
+                format!("{} MB", memory / 1024 / 1024)
+            } else if memory >= 1024 {
+                format!("{} KB", memory / 1024)
+            } else {
+                format!("{} B", memory)
+            };
+
+            let mut stat_string = String::new();
+            if context.stack.is_some() {
+                stat_string.push('U');
+            } else {
+                stat_string.push('K');
+            }
+            match context.status {
+                context::Status::Runnable => {
+                    stat_string.push('R');
+                },
+                context::Status::Blocked => if context.wake.is_some() {
+                    stat_string.push('S');
+                } else {
+                    stat_string.push('B');
+                },
+                context::Status::Exited(_status) => {
+                    stat_string.push('Z');
+                }
+            }
+            if context.running {
+                stat_string.push('+');
+            }
+
+            let name_bytes = context.name.lock();
+            let name = str::from_utf8(&name_bytes).unwrap_or("");
+
+            string.push_str(&format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n",
+                               context.id,
+                               context.ppid,
+                               context.euid,
+                               context.egid,
+                               stat_string,
+                               memory_string,
+                               name));
+        }
+    }
+
+    Ok(string.into_bytes())
+}
diff --git a/scheme/sys/mod.rs b/scheme/sys/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..28b60b53116df0275d13575a87dfe3eac325c078
--- /dev/null
+++ b/scheme/sys/mod.rs
@@ -0,0 +1,162 @@
+use alloc::boxed::Box;
+use collections::{BTreeMap, Vec};
+use core::{cmp, str};
+use core::sync::atomic::{AtomicUsize, Ordering};
+use spin::RwLock;
+
+use syscall::data::Stat;
+use syscall::error::{Error, EBADF, EINVAL, ENOENT, Result};
+use syscall::flag::{MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET};
+use syscall::scheme::Scheme;
+
+mod context;
+//mod interrupt;
+//mod log;
+//mod memory;
+//mod test;
+
+struct Handle {
+    path: &'static [u8],
+    data: Vec<u8>,
+    mode: u16,
+    seek: usize
+}
+
+type SysFn = Fn() -> Result<Vec<u8>> + Send + Sync;
+
+/// System information scheme
+pub struct SysScheme {
+    next_id: AtomicUsize,
+    files: BTreeMap<&'static [u8], Box<SysFn>>,
+    handles: RwLock<BTreeMap<usize, Handle>>
+}
+
+impl SysScheme {
+    pub fn new() -> SysScheme {
+        let mut files: BTreeMap<&'static [u8], Box<SysFn>> = BTreeMap::new();
+
+        files.insert(b"context", Box::new(move || context::resource()));
+        //files.insert(b"interrupt", Box::new(move || interrupt::resource()));
+        //files.insert(b"log", Box::new(move || log::resource()));
+        //files.insert(b"memory", Box::new(move || memory::resource()));
+        //files.insert(b"test", Box::new(move || test::resource()));
+
+        SysScheme {
+            next_id: AtomicUsize::new(0),
+            files: files,
+            handles: RwLock::new(BTreeMap::new())
+        }
+    }
+}
+
+impl Scheme for SysScheme {
+    fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
+        let path_utf8 = str::from_utf8(path).map_err(|_err| Error::new(ENOENT))?;
+        let path_trimmed = path_utf8.trim_matches('/');
+
+        //Have to iterate to get the path without allocation
+        for entry in self.files.iter() {
+            if entry.0 == &path_trimmed.as_bytes() {
+                let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+                self.handles.write().insert(id, Handle {
+                    path: entry.0,
+                    data: entry.1()?,
+                    mode: MODE_FILE | 0o444,
+                    seek: 0
+                });
+
+                return Ok(id)
+            }
+        }
+
+        Err(Error::new(ENOENT))
+    }
+
+    fn dup(&self, id: usize) -> Result<usize> {
+        let (path, data, mode, seek) = {
+            let handles = self.handles.read();
+            let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
+            (handle.path, handle.data.clone(), handle.mode, handle.seek)
+        };
+
+        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+        self.handles.write().insert(id, Handle {
+            path: path,
+            data: data,
+            mode: mode,
+            seek: seek
+        });
+
+        Ok(id)
+    }
+
+    fn read(&self, id: usize, buffer: &mut [u8]) -> Result<usize> {
+        let mut handles = self.handles.write();
+        let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
+
+        let mut i = 0;
+        while i < buffer.len() && handle.seek < handle.data.len() {
+            buffer[i] = handle.data[handle.seek];
+            i += 1;
+            handle.seek += 1;
+        }
+
+        Ok(i)
+    }
+
+    fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
+        let mut handles = self.handles.write();
+        let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
+
+        handle.seek = match whence {
+            SEEK_SET => cmp::min(handle.data.len(), pos),
+            SEEK_CUR => cmp::max(0, cmp::min(handle.data.len() as isize, handle.seek as isize + pos as isize)) as usize,
+            SEEK_END => cmp::max(0, cmp::min(handle.data.len() as isize, handle.data.len() as isize + pos as isize)) as usize,
+            _ => return Err(Error::new(EINVAL))
+        };
+
+        Ok(handle.seek)
+    }
+
+    fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
+        let handles = self.handles.read();
+        let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
+
+        //TODO: Copy scheme part in kernel
+        let mut i = 0;
+        let scheme_path = b"initfs:";
+        while i < buf.len() && i < scheme_path.len() {
+            buf[i] = scheme_path[i];
+            i += 1;
+        }
+
+        let mut j = 0;
+        while i < buf.len() && j < handle.path.len() {
+            buf[i] = handle.path[j];
+            i += 1;
+            j += 1;
+        }
+
+        Ok(i)
+    }
+
+    fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
+        let handles = self.handles.read();
+        let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
+
+        stat.st_mode = handle.mode;
+        stat.st_uid = 0;
+        stat.st_gid = 0;
+        stat.st_size = handle.data.len() as u64;
+
+        Ok(0)
+    }
+
+    fn fsync(&self, _id: usize) -> Result<usize> {
+        Ok(0)
+    }
+
+    fn close(&self, id: usize) -> Result<usize> {
+        self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
+    }
+}
diff --git a/syscall/process.rs b/syscall/process.rs
index 765edb562a440a4db5f32f707622e5d65b45c37d..721211cb5b68de12cefbcd47fce9fa287932d47b 100644
--- a/syscall/process.rs
+++ b/syscall/process.rs
@@ -4,7 +4,7 @@ use alloc::boxed::Box;
 use collections::{BTreeMap, Vec};
 use core::{mem, str};
 use core::ops::DerefMut;
-use spin::Mutex;
+use spin::{Mutex, RwLock};
 
 use arch;
 use arch::externs::memcpy;
@@ -71,6 +71,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
         let mut heap_option = None;
         let mut stack_option = None;
         let grants;
+        let name;
         let cwd;
         let env;
         let files;
@@ -186,6 +187,12 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
                 grants = Arc::new(Mutex::new(Vec::new()));
             }
 
+            if flags & CLONE_VM == CLONE_VM {
+                name = context.name.clone();
+            } else {
+                name = Arc::new(Mutex::new(context.name.lock().clone()));
+            }
+
             if flags & CLONE_FS == CLONE_FS {
                 cwd = context.cwd.clone();
             } else {
@@ -382,6 +389,8 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
                 context.stack = Some(stack);
             }
 
+            context.name = name;
+
             context.cwd = cwd;
 
             context.env = env;
@@ -395,61 +404,6 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
     Ok(pid)
 }
 
-pub fn exit(status: usize) -> ! {
-    {
-        let mut close_files = Vec::new();
-        {
-            let contexts = context::contexts();
-            let (vfork, ppid) = {
-                let context_lock = contexts.current().expect("tried to exit without context");
-                let mut context = context_lock.write();
-                context.image.clear();
-                drop(context.heap.take());
-                drop(context.stack.take());
-                context.grants = Arc::new(Mutex::new(Vec::new()));
-                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.status = context::Status::Exited(status);
-
-                let vfork = context.vfork;
-                context.vfork = false;
-                context.waitpid.notify();
-                (vfork, context.ppid)
-            };
-            if vfork {
-                if let Some(context_lock) = contexts.get(ppid) {
-                    let mut context = context_lock.write();
-                    if ! context.unblock() {
-                        println!("{} not blocked for exit vfork unblock", ppid);
-                    }
-                } else {
-                    println!("{} not found for exit vfork unblock", ppid);
-                }
-            }
-        }
-
-        for (fd, file_option) in close_files.drain(..).enumerate() {
-            if let Some(file) = file_option {
-                context::event::unregister(fd, file.scheme, file.number);
-
-                let scheme_option = {
-                    let schemes = scheme::schemes();
-                    schemes.get(file.scheme).map(|scheme| scheme.clone())
-                };
-                if let Some(scheme) = scheme_option {
-                    let _ = scheme.close(file.number);
-                }
-            }
-        }
-    }
-
-    unsafe { context::switch(); }
-
-    unreachable!();
-}
-
 pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
     let entry;
     let mut sp = arch::USER_STACK_OFFSET + arch::USER_STACK_SIZE - 256;
@@ -461,14 +415,14 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
             args.push(arg.to_vec()); // Must be moved into kernel space before exec unmaps all memory
         }
 
-        let (uid, gid) = {
+        let (uid, gid, canonical) = {
             let contexts = context::contexts();
             let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
             let context = context_lock.read();
-            (context.euid, context.egid)
+            (context.euid, context.egid, context.canonicalize(path))
         };
 
-        let file = syscall::open(path, 0)?;
+        let file = syscall::open(&canonical, 0)?;
         let mut stat = Stat::default();
         syscall::file_op_mut_slice(syscall::number::SYS_FSTAT, file, &mut stat)?;
 
@@ -505,6 +459,9 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
                     let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
                     let mut context = context_lock.write();
 
+                    // Set name
+                    context.name = Arc::new(Mutex::new(canonical));
+
                     // Unmap previous image and stack
                     context.image.clear();
                     drop(context.heap.take());
@@ -639,6 +596,69 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
     unsafe { usermode(entry, sp); }
 }
 
+fn terminate(context_lock: Arc<RwLock<context::Context>>, status: usize) {
+    let mut close_files = Vec::new();
+    {
+        let (vfork, ppid) = {
+            let mut context = context_lock.write();
+            context.image.clear();
+            drop(context.heap.take());
+            drop(context.stack.take());
+            context.grants = Arc::new(Mutex::new(Vec::new()));
+            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.status = context::Status::Exited(status);
+
+            let vfork = context.vfork;
+            context.vfork = false;
+            context.waitpid.notify();
+            (vfork, context.ppid)
+        };
+        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);
+                }
+            } else {
+                println!("{} not found for exit vfork unblock", ppid);
+            }
+        }
+    }
+
+    for (fd, file_option) in close_files.drain(..).enumerate() {
+        if let Some(file) = file_option {
+            context::event::unregister(fd, file.scheme, file.number);
+
+            let scheme_option = {
+                let schemes = scheme::schemes();
+                schemes.get(file.scheme).map(|scheme| scheme.clone())
+            };
+            if let Some(scheme) = scheme_option {
+                let _ = scheme.close(file.number);
+            }
+        }
+    }
+}
+
+pub fn exit(status: usize) -> ! {
+    {
+        let context_lock = {
+            let contexts = context::contexts();
+            let context_lock = contexts.current().ok_or(Error::new(ESRCH)).expect("exit failed to find context");
+            context_lock.clone()
+        };
+        terminate(context_lock, status);
+    }
+
+    unsafe { context::switch(); }
+
+    unreachable!();
+}
+
 pub fn getegid() -> Result<usize> {
     let contexts = context::contexts();
     let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
@@ -679,6 +699,69 @@ pub fn iopl(_level: usize) -> Result<usize> {
     Ok(0)
 }
 
+pub fn kill(pid: usize, sig: usize) -> Result<usize> {
+    use syscall::flag::*;
+
+    let context_lock = {
+        let contexts = context::contexts();
+        let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?;
+        context_lock.clone()
+    };
+
+    let term = |context_lock| {
+        terminate(context_lock, !sig);
+    };
+
+    let core = |context_lock| {
+        terminate(context_lock, !sig);
+    };
+
+    let stop = || {
+
+    };
+
+    let cont = || {
+
+    };
+
+    match sig {
+        0 => (),
+        SIGHUP => term(context_lock),
+        SIGINT => term(context_lock),
+        SIGQUIT => core(context_lock),
+        SIGILL => core(context_lock),
+        SIGTRAP => core(context_lock),
+        SIGABRT => core(context_lock),
+        SIGBUS => core(context_lock),
+        SIGFPE => core(context_lock),
+        SIGKILL => term(context_lock),
+        SIGUSR1 => term(context_lock),
+        SIGSEGV => core(context_lock),
+        SIGPIPE => term(context_lock),
+        SIGALRM => term(context_lock),
+        SIGTERM => term(context_lock),
+        SIGSTKFLT => term(context_lock),
+        SIGCHLD => (),
+        SIGCONT => cont(),
+        SIGSTOP => stop(),
+        SIGTSTP => stop(),
+        SIGTTIN => stop(),
+        SIGTTOU => stop(),
+        SIGURG => (),
+        SIGXCPU => core(context_lock),
+        SIGXFSZ => core(context_lock),
+        SIGVTALRM => term(context_lock),
+        SIGPROF => term(context_lock),
+        SIGWINCH => (),
+        SIGIO => term(context_lock),
+        SIGPWR => term(context_lock),
+        SIGSYS => core(context_lock),
+        _ => return Err(Error::new(EINVAL))
+    }
+
+    Ok(0)
+}
+
 pub fn physalloc(size: usize) -> Result<usize> {
     allocate_frames((size + 4095)/4096).ok_or(Error::new(ENOMEM)).map(|frame| frame.start_address().get())
 }