async_readline/
raw.rs

1//! This should be moved to separate crate and API cleaned up
2
3use 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                    // continue
60                },
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                    // continue
75                },
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        // No buffering, no flushing
84        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, /* OPOST, */ 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        // disable BREAK interrupt, CR to NL conversion on input,
125        // input parity check, strip high bit (bit 8), output flow control
126        raw.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
127        // we don't want raw output, it turns newlines into straight linefeeds
128        // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
129        raw.c_cflag |=  CS8; // character-size mark (8 bits)
130        // disable echoing, canonical mode, extended input processing and signals
131        raw.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
132        raw.c_cc[VMIN] = 1; // One character-at-a-time input
133        raw.c_cc[VTIME] = 0; // with blocking read
134        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}