1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//! Pseudo Terminal manipulation
use crate::common::Result;
use async_std::fs;
use async_std::os::unix::io::RawFd;
use std::convert::TryFrom;
use termios::{self, Termios};
#[derive(Debug)]
pub struct PTTY {
fd: RawFd,
termios: Termios,
}
impl PTTY {
/// In termios terms, noncanonical mode means:
///
/// > In noncanonical mode input is available immediately
/// > (without the user having to type a line-delimiter character),
/// > no input processing is performed, and line editing is disabled
///
/// What we want with this method is to have total control on how to
/// process input and how to print to the pseudo terminal.
///
/// More info in [termios' webpage inside "Canonical and noncanonical mode" section][termios]
///
/// [termios]: https://linux.die.net/man/3/termios
pub fn noncanonical_mode(&self) -> Result<()> {
let mut raw_tty = self.termios;
raw_tty.c_lflag &= !(termios::ICANON | termios::ECHO | termios::ECHONL | termios::IEXTEN);
termios::tcsetattr(self.fd, termios::TCSANOW, &raw_tty)?;
Ok(())
}
}
impl TryFrom<RawFd> for PTTY {
type Error = std::io::Error;
fn try_from(fd: RawFd) -> std::result::Result<Self, Self::Error> {
let termios = Termios::from_fd(fd)?;
let tty = PTTY { fd, termios };
Ok(tty)
}
}
impl Drop for PTTY {
fn drop(&mut self) {
log::trace!("dropping: {:?}", self);
// Make sure we restore termios settings after the PTTY is dropped
let _r = termios::tcsetattr(self.fd, termios::TCSANOW, &self.termios);
}
}
/// Get PTTY file with full access
pub async fn file() -> Result<fs::File> {
get(true, true).await
}
/// Get PTTY file reader
pub async fn reader() -> Result<fs::File> {
get(true, false).await
}
/// Get PTTY file writer
pub async fn writer() -> Result<fs::File> {
get(false, true).await
}
/// Get PTTY file representation
pub async fn get(read: bool, write: bool) -> Result<fs::File> {
let tty = fs::OpenOptions::new()
.read(read)
.write(write)
.open("/dev/tty")
.await?;
Ok(tty)
}