diff --git a/src/header/sys_ioctl/linux.rs b/src/header/sys_ioctl/linux.rs
index 02cc8c9fc2c77b5534a35530066abc1311e00067..e5f39c682898db036c7112af1b98fb6ca5788853 100644
--- a/src/header/sys_ioctl/linux.rs
+++ b/src/header/sys_ioctl/linux.rs
@@ -2,7 +2,7 @@ use platform::types::*;
 use platform::Sys;
 
 #[no_mangle]
-pub extern "C" fn ioctl(fd: c_int, request: c_ulong, out: *mut c_void) -> c_int {
+pub unsafe extern "C" fn ioctl(fd: c_int, request: c_ulong, out: *mut c_void) -> c_int {
     // TODO: Somehow support varargs to syscall??
     Sys::ioctl(fd, request, out)
 }
diff --git a/src/header/sys_ioctl/redox.rs b/src/header/sys_ioctl/redox.rs
index 6d0f0257302454ebe7b631a5a93502ec7e438cca..4494fa80a2f35c73a3dee6d43abae9c42b026bd5 100644
--- a/src/header/sys_ioctl/redox.rs
+++ b/src/header/sys_ioctl/redox.rs
@@ -11,6 +11,39 @@ use super::winsize;
 #[no_mangle]
 pub unsafe extern "C" fn ioctl(fd: c_int, request: c_ulong, out: *mut c_void) -> c_int {
     match request {
+        TCGETS => {
+            let dup = e(syscall::dup(fd as usize, b"termios"));
+            if dup == !0 {
+                return -1;
+            }
+
+            let count = e(syscall::read(dup, unsafe {
+                slice::from_raw_parts_mut(out as *mut u8, mem::size_of::<termios>())
+            }));
+            let _ = syscall::close(dup);
+
+            if count == !0 {
+                return -1;
+            }
+            0
+        }
+
+        TCSETS => {
+            let dup = e(syscall::dup(fd as usize, b"termios"));
+            if dup == !0 {
+                return -1;
+            }
+
+            let count = e(syscall::write(dup, unsafe {
+                slice::from_raw_parts(out as *const u8, mem::size_of::<termios>())
+            }));
+            let _ = syscall::close(dup);
+
+            if count == !0 {
+                return -1;
+            }
+            0
+        },
         TIOCGPGRP => {
             let dup = e(syscall::dup(fd as usize, b"pgrp"));
             if dup == !0 {
@@ -86,6 +119,9 @@ pub unsafe extern "C" fn ioctl(fd: c_int, request: c_ulong, out: *mut c_void) ->
     }
 }
 
+pub const TCGETS: c_ulong = 0x5401;
+pub const TCSETS: c_ulong = 0x5402;
+
 pub const TIOCGPGRP: c_ulong = 0x540F;
 pub const TIOCSPGRP: c_ulong = 0x5410;
 
diff --git a/src/header/termios/mod.rs b/src/header/termios/mod.rs
index e60e35a2d163cfedc9798ac228ac088108d81c33..eb8978b564e1e97f8736ff6e3f43e4d17c4e8779 100644
--- a/src/header/termios/mod.rs
+++ b/src/header/termios/mod.rs
@@ -1,9 +1,9 @@
 //! termios implementation, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html
 
 use header::errno;
+use header::sys_ioctl;
 use platform;
 use platform::types::*;
-use platform::{Pal, Sys};
 
 pub type cc_t = u8;
 pub type speed_t = u32;
@@ -12,6 +12,7 @@ pub type tcflag_t = u32;
 pub const NCCS: usize = 32;
 
 #[repr(C)]
+#[derive(Default)]
 pub struct termios {
     c_iflag: tcflag_t,
     c_oflag: tcflag_t,
@@ -24,13 +25,18 @@ pub struct termios {
 }
 
 #[no_mangle]
-pub extern "C" fn tcgetattr(fd: c_int, out: *mut termios) -> c_int {
-    Sys::tcgetattr(fd, out)
+pub unsafe extern "C" fn tcgetattr(fd: c_int, out: *mut termios) -> c_int {
+    sys_ioctl::ioctl(fd, sys_ioctl::TCGETS, out as *mut c_void)
 }
 
 #[no_mangle]
-pub extern "C" fn tcsetattr(fd: c_int, act: c_int, value: *mut termios) -> c_int {
-    Sys::tcsetattr(fd, act, value)
+pub unsafe extern "C" fn tcsetattr(fd: c_int, act: c_int, value: *mut termios) -> c_int {
+    if act < 0 || act > 2 {
+        platform::errno = errno::EINVAL;
+        return -1;
+    }
+    // This is safe because ioctl shouldn't modify the value
+    sys_ioctl::ioctl(fd, sys_ioctl::TCSETS + act as c_ulong, value as *mut c_void)
 }
 
 #[no_mangle]
diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs
index e8bf0fd9611eae256de0e4a4d9fb59f531559666..173104adcf48e8dbe8128a166f378a111aeaad33 100644
--- a/src/header/unistd/mod.rs
+++ b/src/header/unistd/mod.rs
@@ -8,6 +8,7 @@ use header::limits;
 use header::stdlib::getenv;
 use header::sys_ioctl;
 use header::sys_time;
+use header::termios;
 use header::time::timespec;
 use platform;
 use platform::types::*;
@@ -378,7 +379,12 @@ pub extern "C" fn getwd(path_name: *mut c_char) -> *mut c_char {
 
 #[no_mangle]
 pub extern "C" fn isatty(fd: c_int) -> c_int {
-    Sys::isatty(fd)
+    let mut t = termios::termios::default();
+    if unsafe { termios::tcgetattr(fd, &mut t as *mut termios::termios) == 0 } {
+        1
+    } else {
+        0
+    }
 }
 
 // #[no_mangle]
diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs
index 26003962eb43fe148e858a98b0e5204f3a6e7d3e..d9611da1c02b24917b4268f35212e245ecaf97ec 100644
--- a/src/platform/linux/mod.rs
+++ b/src/platform/linux/mod.rs
@@ -6,11 +6,9 @@ use super::{errno, Pal};
 use c_str::CStr;
 use fs::File;
 use header::dirent::dirent;
-use header::errno::EINVAL;
 use header::fcntl;
 use header::poll::{nfds_t, pollfd};
 use header::signal::SIGCHLD;
-use header::sys_ioctl::{winsize, TCGETS, TCSETS, TIOCGWINSZ};
 // use header::sys_resource::rusage;
 use header::sys_select::fd_set;
 use header::sys_stat::stat;
@@ -18,7 +16,6 @@ use header::sys_statvfs::statvfs;
 use header::sys_time::{timeval, timezone};
 // use header::sys_times::tms;
 use header::sys_utsname::utsname;
-use header::termios::termios;
 use header::time::timespec;
 
 mod signal;
@@ -68,9 +65,9 @@ impl Sys {
     //     e(unsafe { syscall!(GETRUSAGE, who, r_usage) }) as c_int
     // }
 
-    pub fn ioctl(fd: c_int, request: c_ulong, out: *mut c_void) -> c_int {
+    pub unsafe fn ioctl(fd: c_int, request: c_ulong, out: *mut c_void) -> c_int {
         // TODO: Somehow support varargs to syscall??
-        e(unsafe { syscall!(IOCTL, fd, request, out) }) as c_int
+        e(syscall!(IOCTL, fd, request, out)) as c_int
     }
 
     // fn times(out: *mut tms) -> clock_t {
@@ -260,11 +257,6 @@ impl Pal for Sys {
         e(unsafe { syscall!(GETUID) }) as uid_t
     }
 
-    fn isatty(fd: c_int) -> c_int {
-        let mut winsize = winsize::default();
-        (Self::ioctl(fd, TIOCGWINSZ, &mut winsize as *mut _ as *mut c_void) == 0) as c_int
-    }
-
     fn link(path1: &CStr, path2: &CStr) -> c_int {
         e(unsafe {
             syscall!(
@@ -424,21 +416,6 @@ impl Pal for Sys {
         e(unsafe { syscall!(SYMLINKAT, path1.as_ptr(), AT_FDCWD, path2.as_ptr()) }) as c_int
     }
 
-    fn tcgetattr(fd: c_int, out: *mut termios) -> c_int {
-        Self::ioctl(fd, TCGETS, out as *mut c_void)
-    }
-
-    fn tcsetattr(fd: c_int, act: c_int, value: *const termios) -> c_int {
-        if act < 0 || act > 2 {
-            unsafe {
-                errno = EINVAL;
-            }
-            return -1;
-        }
-        // This is safe because ioctl shouldn't modify the value
-        Self::ioctl(fd, TCSETS + act as c_ulong, value as *mut c_void)
-    }
-
     fn umask(mask: mode_t) -> mode_t {
         unsafe { syscall!(UMASK, mask) as mode_t }
     }
diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs
index bae5380a9d4904c0bf1bc668f8996339c5821b2d..211b9554b97491bc52707ce40fd319bc7ae0a977 100644
--- a/src/platform/pal/mod.rs
+++ b/src/platform/pal/mod.rs
@@ -7,7 +7,6 @@ use header::sys_stat::stat;
 use header::sys_statvfs::statvfs;
 use header::sys_time::{timeval, timezone};
 use header::sys_utsname::utsname;
-use header::termios::termios;
 use header::time::timespec;
 
 pub use self::signal::PalSignal;
@@ -87,8 +86,6 @@ pub trait Pal {
 
     fn getuid() -> uid_t;
 
-    fn isatty(fd: c_int) -> c_int;
-
     fn link(path1: &CStr, path2: &CStr) -> c_int;
 
     fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t;
@@ -149,10 +146,6 @@ pub trait Pal {
 
     fn symlink(path1: &CStr, path2: &CStr) -> c_int;
 
-    fn tcgetattr(fd: c_int, out: *mut termios) -> c_int;
-
-    fn tcsetattr(fd: c_int, act: c_int, value: *const termios) -> c_int;
-
     fn umask(mask: mode_t) -> mode_t;
 
     fn uname(utsname: *mut utsname) -> c_int;
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
index 5fb4d4e618ed4ba93b74279ea264ba198ac10cbe..86f2396a0f66d64de9c6d9e690508907e84b5506 100644
--- a/src/platform/redox/mod.rs
+++ b/src/platform/redox/mod.rs
@@ -21,7 +21,6 @@ use header::sys_stat::stat;
 use header::sys_statvfs::statvfs;
 use header::sys_time::{timeval, timezone};
 use header::sys_utsname::{utsname, UTSLENGTH};
-use header::termios::termios;
 use header::time::timespec;
 use header::unistd::{F_OK, R_OK, W_OK, X_OK};
 use io::prelude::*;
@@ -576,15 +575,6 @@ impl Pal for Sys {
         e(syscall::getuid()) as pid_t
     }
 
-    fn isatty(fd: c_int) -> c_int {
-        syscall::dup(fd as usize, b"termios")
-            .map(|fd| {
-                let _ = syscall::close(fd);
-                1
-            })
-            .unwrap_or(0)
-    }
-
     fn link(path1: &CStr, path2: &CStr) -> c_int {
         e(unsafe { syscall::link(path1.as_ptr() as *const u8, path2.as_ptr() as *const u8) })
             as c_int
@@ -1064,40 +1054,6 @@ impl Pal for Sys {
         0
     }
 
-    fn tcgetattr(fd: c_int, out: *mut termios) -> c_int {
-        let dup = e(syscall::dup(fd as usize, b"termios"));
-        if dup == !0 {
-            return -1;
-        }
-
-        let read = e(syscall::read(dup, unsafe {
-            slice::from_raw_parts_mut(out as *mut u8, mem::size_of::<termios>())
-        }));
-        let _ = syscall::close(dup);
-
-        if read == !0 {
-            return -1;
-        }
-        0
-    }
-
-    fn tcsetattr(fd: c_int, _act: c_int, value: *const termios) -> c_int {
-        let dup = e(syscall::dup(fd as usize, b"termios"));
-        if dup == !0 {
-            return -1;
-        }
-
-        let write = e(syscall::write(dup, unsafe {
-            slice::from_raw_parts(value as *const u8, mem::size_of::<termios>())
-        }));
-        let _ = syscall::close(dup);
-
-        if write == !0 {
-            return -1;
-        }
-        0
-    }
-
     fn umask(mask: mode_t) -> mode_t {
         e(syscall::umask(mask as usize)) as mode_t
     }