diff --git a/scheme/debug.rs b/scheme/debug.rs
index 0623b8b69322c59a63114e7fab292aca55c75c31..8f63bf654f6f832206032332014f2a14f09db3fa 100644
--- a/scheme/debug.rs
+++ b/scheme/debug.rs
@@ -1,8 +1,25 @@
+use collections::VecDeque;
 use core::str;
+use spin::{Mutex, MutexGuard, Once};
 
+use context;
 use syscall::Result;
 use super::Scheme;
 
+/// Input
+static INPUT: Once<Mutex<VecDeque<u8>>> = Once::new();
+
+/// Initialize contexts, called if needed
+fn init_input() -> Mutex<VecDeque<u8>> {
+    Mutex::new(VecDeque::new())
+}
+
+/// Get the global schemes list, const
+#[no_mangle]
+pub extern fn debug_input(b: u8) {
+    INPUT.call_once(init_input).lock().push_back(b)
+}
+
 pub struct DebugScheme;
 
 impl Scheme for DebugScheme {
@@ -17,8 +34,23 @@ impl Scheme for DebugScheme {
     /// Read the file `number` into the `buffer`
     ///
     /// Returns the number of bytes read
-    fn read(&mut self, _file: usize, _buffer: &mut [u8]) -> Result<usize> {
-        Ok(0)
+    fn read(&mut self, _file: usize, buf: &mut [u8]) -> Result<usize> {
+        loop {
+            let mut i = 0;
+            {
+                let mut input = INPUT.call_once(init_input).lock();
+                while i < buf.len() && ! input.is_empty() {
+                    buf[i] = input.pop_front().expect("debug_input lost byte");
+                    i += 1;
+                }
+            }
+
+            if i > 0 {
+                return Ok(i);
+            } else {
+                unsafe { context::switch(); }
+            }
+        }
     }
 
     /// Write the `buffer` to the `file`
@@ -30,6 +62,10 @@ impl Scheme for DebugScheme {
         Ok(buffer.len())
     }
 
+    fn fsync(&mut self, file: usize) -> Result<()> {
+        Ok(())
+    }
+
     /// Close the file `number`
     fn close(&mut self, _file: usize) -> Result<()> {
         Ok(())
diff --git a/scheme/env.rs b/scheme/env.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a04142f11e4ba4cff89fd675a0808560c98d11be
--- /dev/null
+++ b/scheme/env.rs
@@ -0,0 +1,88 @@
+use collections::BTreeMap;
+
+use syscall::{Error, Result};
+use super::Scheme;
+
+struct Handle {
+    data: &'static [u8],
+    seek: usize
+}
+
+pub struct EnvScheme {
+    next_id: usize,
+    files: BTreeMap<&'static [u8], &'static [u8]>,
+    handles: BTreeMap<usize, Handle>
+}
+
+impl EnvScheme {
+    pub fn new() -> EnvScheme {
+        let mut files: BTreeMap<&'static [u8], &'static [u8]> = BTreeMap::new();
+
+        files.insert(b"HOME", b"initfs:");
+        files.insert(b"PWD", b"initfs:");
+        files.insert(b"COLUMNS", b"80");
+        files.insert(b"LINES", b"30");
+
+        EnvScheme {
+            next_id: 0,
+            files: files,
+            handles: BTreeMap::new()
+        }
+    }
+}
+
+impl Scheme for EnvScheme {
+    fn open(&mut self, path: &[u8], _flags: usize) -> Result<usize> {
+        let data = self.files.get(path).ok_or(Error::NoEntry)?;
+
+        let id = self.next_id;
+        self.next_id += 1;
+        self.handles.insert(id, Handle {
+            data: data,
+            seek: 0
+        });
+
+        Ok(id)
+    }
+
+    fn dup(&mut self, file: usize) -> Result<usize> {
+        let (data, seek) = {
+            let handle = self.handles.get(&file).ok_or(Error::BadFile)?;
+            (handle.data, handle.seek)
+        };
+
+        let id = self.next_id;
+        self.next_id += 1;
+        self.handles.insert(id, Handle {
+            data: data,
+            seek: seek
+        });
+
+        Ok(id)
+    }
+
+    fn read(&mut self, file: usize, buffer: &mut [u8]) -> Result<usize> {
+        let mut handle = self.handles.get_mut(&file).ok_or(Error::BadFile)?;
+
+        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 write(&mut self, _file: usize, _buffer: &[u8]) -> Result<usize> {
+        Err(Error::NotPermitted)
+    }
+
+    fn fsync(&mut self, file: usize) -> Result<()> {
+        Ok(())
+    }
+
+    fn close(&mut self, file: usize) -> Result<()> {
+        self.handles.remove(&file).ok_or(Error::BadFile).and(Ok(()))
+    }
+}
diff --git a/scheme/initfs.rs b/scheme/initfs.rs
index 82e5f3581c23807d50a8f6746de5a7268dd14b7a..0b15bd239589060d272f3bd1999123c83b01fafa 100644
--- a/scheme/initfs.rs
+++ b/scheme/initfs.rs
@@ -78,6 +78,10 @@ impl Scheme for InitFsScheme {
         Err(Error::NotPermitted)
     }
 
+    fn fsync(&mut self, file: usize) -> Result<()> {
+        Ok(())
+    }
+
     fn close(&mut self, file: usize) -> Result<()> {
         self.handles.remove(&file).ok_or(Error::BadFile).and(Ok(()))
     }
diff --git a/scheme/mod.rs b/scheme/mod.rs
index a2a800975abe9e0bb956ff8c1531205635468473..af9f0a9a71e8c04fc25c3e078f1760aff71c71a0 100644
--- a/scheme/mod.rs
+++ b/scheme/mod.rs
@@ -16,11 +16,15 @@ use spin::{Once, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
 use syscall::{Error, Result};
 
 use self::debug::DebugScheme;
+use self::env::EnvScheme;
 use self::initfs::InitFsScheme;
 
 /// Debug scheme
 pub mod debug;
 
+/// Environmental variables
+pub mod env;
+
 /// InitFS scheme
 pub mod initfs;
 
@@ -57,7 +61,7 @@ impl SchemeList {
         }
     }
 
-    /// Create a new context.
+    /// Create a new scheme.
     pub fn insert(&mut self, name: Box<[u8]>, scheme: Arc<Mutex<Box<Scheme + Send>>>) -> Result<&Arc<Mutex<Box<Scheme + Send>>>> {
         if self.names.contains_key(&name) {
             return Err(Error::FileExists);
@@ -92,6 +96,7 @@ static SCHEMES: Once<RwLock<SchemeList>> = Once::new();
 fn init_schemes() -> RwLock<SchemeList> {
     let mut list: SchemeList = SchemeList::new();
     list.insert(Box::new(*b"debug"), Arc::new(Mutex::new(Box::new(DebugScheme)))).expect("failed to insert debug: scheme");
+    list.insert(Box::new(*b"env"), Arc::new(Mutex::new(Box::new(EnvScheme::new())))).expect("failed to insert env: scheme");
     list.insert(Box::new(*b"initfs"), Arc::new(Mutex::new(Box::new(InitFsScheme::new())))).expect("failed to insert initfs: scheme");
     RwLock::new(list)
 }
@@ -128,6 +133,9 @@ pub trait Scheme {
     /// Returns the number of bytes written
     fn write(&mut self, file: usize, buffer: &[u8]) -> Result<usize>;
 
+    /// Sync the file descriptor
+    fn fsync(&mut self, file: usize) -> Result<()>;
+
     /// Close the file descriptor
     fn close(&mut self, file: usize) -> Result<()>;
 }
diff --git a/syscall/call.rs b/syscall/call.rs
index e4a7e9263788639abf42a776310aef39a601e953..3bcefd4830cb0f681b1231b7bacc4bab3fb8a632 100644
--- a/syscall/call.rs
+++ b/syscall/call.rs
@@ -29,6 +29,8 @@ pub enum Call {
     Brk = 45,
     /// Set process I/O privilege level
     Iopl = 110,
+    /// Sync file descriptor
+    FSync = 118,
     /// Clone process
     Clone = 120,
     /// Yield to scheduler
@@ -55,6 +57,7 @@ impl Call {
             41 => Ok(Call::Dup),
             45 => Ok(Call::Brk),
             110 => Ok(Call::Iopl),
+            118 => Ok(Call::FSync),
             120 => Ok(Call::Clone),
             158 => Ok(Call::SchedYield),
             183 => Ok(Call::GetCwd),
diff --git a/syscall/fs.rs b/syscall/fs.rs
index c992bbaac7858fea7ca17ef1447680d23ec77dc7..d68727d500740d3b9218a8e222b4ec18780415fa 100644
--- a/syscall/fs.rs
+++ b/syscall/fs.rs
@@ -120,3 +120,18 @@ pub fn dup(fd: usize) -> Result<usize> {
     let result = scheme_mutex.lock().dup(file.number);
     result
 }
+
+pub fn fsync(fd: usize) -> Result<usize> {
+    let file = {
+        let contexts = context::contexts();
+        let context_lock = contexts.current().ok_or(Error::NoProcess)?;
+        let context = context_lock.read();
+        let file = context.get_file(fd).ok_or(Error::BadFile)?;
+        file
+    };
+
+    let schemes = scheme::schemes();
+    let scheme_mutex = schemes.get(file.scheme).ok_or(Error::BadFile)?;
+    let result = scheme_mutex.lock().fsync(file.number).and(Ok(0));
+    result
+}
diff --git a/syscall/mod.rs b/syscall/mod.rs
index c6ab11dfadef97cbd2299faea5f385118e2d29c6..396f366ac94daf343c2d0faeeb702a040b314763 100644
--- a/syscall/mod.rs
+++ b/syscall/mod.rs
@@ -39,6 +39,7 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
                 Call::Dup => dup(b),
                 Call::Brk => brk(b),
                 Call::Iopl => iopl(b),
+                Call::FSync => fsync(b),
                 Call::Clone => clone(b, stack),
                 Call::SchedYield => sched_yield(),
                 Call::GetCwd => getcwd(validate_slice_mut(b as *mut u8, c)?)