1use libc;
4use tokio_core;
5use nix;
6use mio;
7use termios;
8
9use mio::unix::EventedFd;
10use tokio_core::reactor::PollEvented;
11use std::io;
12
13static STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
14static STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;
15static STDERR_FILENO: libc::c_int = libc::STDERR_FILENO;
16
17pub type Mode = termios::Termios;
18
19
20pub struct StdioFd(mio::unix::EventedFd<'static>);
21
22impl StdioFd {
23 fn stdin() -> Self {
24 StdioFd(EventedFd(&STDIN_FILENO)).set_nonblocking()
25 }
26 fn stdout() -> Self {
27 StdioFd(EventedFd(&STDOUT_FILENO)).set_nonblocking()
28 }
29 fn stderr() -> Self {
30 StdioFd(EventedFd(&STDERR_FILENO)).set_nonblocking()
31 }
32
33 fn set_nonblocking(self) -> Self {
34 use nix::fcntl::{fcntl, FcntlArg, O_NONBLOCK};
35 fcntl(*(self.0).0, FcntlArg::F_SETFL(O_NONBLOCK)).expect("fcntl");
36 self
37 }
38}
39
40impl mio::Evented for StdioFd {
41 fn register(&self, poll: &mio::Poll, token: mio::Token, interest: mio::Ready, opts: mio::PollOpt) -> io::Result<()> {
42 self.0.register(poll, token, interest, opts)
43 }
44
45 fn reregister(&self, poll: &mio::Poll, token: mio::Token, interest: mio::Ready, opts: mio::PollOpt) -> io::Result<()> {
46 self.0.reregister(poll, token, interest, opts)
47 }
48
49 fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
50 self.0.deregister(poll)
51 }
52}
53
54impl io::Read for StdioFd {
55 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
56 loop {
57 match nix::unistd::read(*(self.0).0, buf) {
58 Err(nix::Error::Sys(nix::errno::EINTR)) => {
59 },
61 Err(nix::Error::Sys(e)) => return Err(e.into()),
62 Err(nix::Error::InvalidPath) => return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid path")),
63 Ok(count) => return Ok(count),
64 }
65 }
66 }
67}
68
69impl io::Write for StdioFd {
70 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
71 loop {
72 match nix::unistd::write(*(self.0).0, buf) {
73 Err(nix::Error::Sys(nix::errno::EINTR)) => {
74 },
76 Err(nix::Error::Sys(e)) => return Err(e.into()),
77 Err(nix::Error::InvalidPath) => return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid path")),
78 Ok(count) => return Ok(count),
79 }
80 }
81 }
82 fn flush(&mut self) -> io::Result<()> {
83 Ok(())
85 }
86}
87
88pub struct RawStdio {
89 stdin : PollEvented<StdioFd>,
90 stdout : PollEvented<StdioFd>,
91 stderr : PollEvented<StdioFd>,
92 stdin_isatty : bool,
93}
94
95pub type PollFd = PollEvented<StdioFd>;
96
97impl RawStdio {
98
99 pub fn new(handle : &tokio_core::reactor::Handle) -> io::Result<Self> {
100 let stdin_poll_evented = PollEvented::new(StdioFd::stdin(), handle)?;
101 let stdout_poll_evented = PollEvented::new(StdioFd::stdout(), handle)?;
102 let stderr_poll_evented = PollEvented::new(StdioFd::stderr(), handle)?;
103 let raw_stdio = RawStdio {
104 stdin : stdin_poll_evented,
105 stdout : stdout_poll_evented,
106 stderr : stderr_poll_evented,
107 stdin_isatty : true,
108 };
109 raw_stdio.enable_raw_mode()?;
110 Ok(raw_stdio)
111 }
112
113 fn enable_raw_mode(&self) -> io::Result<termios::Termios> {
114 let mut orig_term = termios::Termios::from_fd(STDIN_FILENO)?;
115
116 use nix::errno::Errno::ENOTTY;
117 use termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP,
118 IXON, VMIN, VTIME};
119 if !self.stdin_isatty {
120 try!(Err(nix::Error::from_errno(ENOTTY)));
121 }
122 termios::tcgetattr(STDIN_FILENO, &mut orig_term)?;
123 let mut raw = orig_term;
124 raw.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
127 raw.c_cflag |= CS8; raw.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
132 raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, &raw));
135 Ok(orig_term)
136 }
137
138 pub fn split(self) -> (PollEvented<StdioFd>, PollEvented<StdioFd>, PollEvented<StdioFd>) {
139 (self.stdin, self.stdout, self.stderr)
140 }
141}