From 13e472b6ebfc3fb1d8aa009be10df711938aeb50 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 28 Jan 2020 11:05:46 -0700 Subject: [PATCH] Implement VTIME --- src/main.rs | 99 ++++++++++++++++++++++++++++++++++++++++++------- src/master.rs | 5 +++ src/pty.rs | 90 +++++++++++++++++++++++++++++++++++++++++--- src/resource.rs | 3 ++ src/slave.rs | 2 + 5 files changed, 180 insertions(+), 19 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1572793841..2fbdf74038 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,12 @@ extern crate redox_termios; extern crate syscall; -use std::fs::File; -use std::io::{Read, Write}; +use std::fs::{File, OpenOptions}; +use std::io::{self, Read, Write}; +use std::os::unix::fs::OpenOptionsExt; +use std::os::unix::io::AsRawFd; -use syscall::data::Packet; +use syscall::data::{Event, Packet, TimeSpec}; use syscall::scheme::SchemeBlockMut; mod master; @@ -21,21 +23,77 @@ use scheme::PtyScheme; fn main(){ // Daemonize if unsafe { syscall::clone(0).unwrap() } == 0 { - let mut socket = File::create(":pty").expect("pty: failed to create pty scheme"); - let mut scheme = PtyScheme::new(); + let mut event_file = OpenOptions::new() + .read(true) + .write(true) + .open("event:") + .expect("pty: failed to open event:"); + + let time_path = format!("time:{}", syscall::CLOCK_MONOTONIC); + let mut time_file = OpenOptions::new() + .read(true) + .write(true) + .custom_flags(syscall::O_NONBLOCK as i32) + .open(time_path) + .expect("pty: failed to open time:"); + + let mut socket = OpenOptions::new() + .create(true) + .read(true) + .write(true) + .custom_flags(syscall::O_NONBLOCK as i32) + .open(":pty") + .expect("pty: failed to create pty scheme"); syscall::setrens(0, 0).expect("ptyd: failed to enter null namespace"); - let mut todo = Vec::new(); - loop { - let mut packet = Packet::default(); - socket.read(&mut packet).expect("pty: failed to read events from pty scheme"); + event_file.write(&Event { + id: socket.as_raw_fd() as usize, + flags: syscall::EVENT_READ, + data: 1, + }).expect("pty: failed to watch events on pty:"); - if let Some(a) = scheme.handle(&mut packet) { - packet.a = a; - socket.write(&packet).expect("pty: failed to write responses to pty scheme"); - } else { - todo.push(packet); + event_file.write(&Event { + id: time_file.as_raw_fd() as usize, + flags: syscall::EVENT_READ, + data: 2, + }).expect("pty: failed to watch events on time:"); + + //TODO: do not set timeout if not necessary + timeout(&mut time_file) + .expect("pty: failed to set timeout"); + + let mut scheme = PtyScheme::new(); + let mut todo = Vec::new(); + let mut timeout_count = 0u64; + loop { + let mut event = Event::default(); + event_file.read(&mut event) + .expect("pty: failed to read event:"); + + match event.data { + 1 => { + let mut packet = Packet::default(); + socket.read(&mut packet).expect("pty: failed to read events from pty scheme"); + + if let Some(a) = scheme.handle(&mut packet) { + packet.a = a; + socket.write(&packet).expect("pty: failed to write responses to pty scheme"); + } else { + todo.push(packet); + } + }, + 2 => { + timeout(&mut time_file) + .expect("pty: failed to set timeout"); + + timeout_count.wrapping_add(1); + + for (_id, handle) in scheme.handles.iter_mut() { + handle.timeout(timeout_count); + } + } + _ => (), } let mut i = 0; @@ -59,6 +117,19 @@ fn main(){ } } +fn timeout(time_file: &mut File) -> io::Result<()> { + let mut time = TimeSpec::default(); + time_file.read_exact(&mut time)?; + + time.tv_nsec += 100_000_000; + while time.tv_nsec >= 1_000_000_000 { + time.tv_sec += 1; + time.tv_nsec -= 1_000_000_000; + } + + time_file.write_all(&time) +} + fn post_fevent(socket: &mut File, id: usize, flags: usize, count: usize) { socket.write(&Packet { id: 0, diff --git a/src/master.rs b/src/master.rs index 65db561a96..f465ecffe6 100644 --- a/src/master.rs +++ b/src/master.rs @@ -124,4 +124,9 @@ impl Resource for PtyMaster { events } + + fn timeout(&self, count: u64) { + let mut pty = self.pty.borrow_mut(); + pty.timeout(count); + } } diff --git a/src/pty.rs b/src/pty.rs index f61115bbf3..c863c59bbc 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -12,6 +12,8 @@ pub struct Pty { pub cooked: Vec, pub miso: VecDeque>, pub mosi: VecDeque>, + pub timeout_count: u64, + pub timeout_character: Option, } impl Pty { @@ -23,7 +25,9 @@ impl Pty { winsize: Winsize::default(), cooked: Vec::new(), miso: VecDeque::new(), - mosi: VecDeque::new() + mosi: VecDeque::new(), + timeout_count: 0, + timeout_character: None, } } @@ -202,14 +206,15 @@ impl Pty { if echo { self.output(&[b]); } + + // Restart timer after every byte + self.timeout_character = Some(self.timeout_count); + self.cooked.push(b); } } - if ! icanon && self.cooked.len() >= cc[VMIN] as usize { - self.mosi.push_back(self.cooked.clone()); - self.cooked.clear(); - } + self.update(); } pub fn output(&mut self, buf: &[u8]) { @@ -232,4 +237,79 @@ impl Pty { self.miso.push_back(vec); } + + pub fn update(&mut self) { + let lfl = self.termios.c_lflag; + let cc = self.termios.c_cc; + let icanon = lfl & ICANON == ICANON; + let vmin = cc[VMIN] as usize; + let vtime = cc[VTIME] as u64; + + // http://unixwiz.net/techtips/termios-vmin-vtime.html + if ! icanon { + if vtime == 0 { + // No timeout specified + if vmin == 0 { + // Polling read, return immediately with data + if self.mosi.is_empty() { + self.mosi.push_back(self.cooked.clone()); + self.cooked.clear(); + } + } else { + // Blocking read, wait until vmin bytes are available + if self.cooked.len() >= vmin { + self.mosi.push_back(self.cooked.clone()); + self.cooked.clear(); + } + } + } else { + // Timeout specified using vtime + if vmin == 0 { + // Return when any data is available or the timer expires + if ! self.cooked.is_empty() { + self.mosi.push_back(self.cooked.clone()); + self.cooked.clear(); + } else { + if let Some(timeout_character) = self.timeout_character { + if self.timeout_count >= timeout_character.wrapping_add(vtime) { + self.timeout_character = None; + + if self.mosi.is_empty() { + self.mosi.push_back(self.cooked.clone()); + self.cooked.clear(); + } + } + } else { + // Start timer if not already started + self.timeout_character = Some(self.timeout_count); + } + } + } else { + // Return when min bytes are received or the timer expires + // when any data is available + if self.cooked.len() >= vmin { + self.mosi.push_back(self.cooked.clone()); + self.cooked.clear(); + } else if ! self.cooked.is_empty() { + if let Some(timeout_character) = self.timeout_character { + if self.timeout_count >= timeout_character.wrapping_add(vtime) { + self.timeout_character = None; + + self.mosi.push_back(self.cooked.clone()); + self.cooked.clear(); + } + } + } + } + } + } + } + + pub fn timeout(&mut self, count: u64) { + if self.timeout_count != count { + self.timeout_count = count; + + self.update(); + } + } } diff --git a/src/resource.rs b/src/resource.rs index 39a24561f3..9abac98f77 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -17,4 +17,7 @@ pub trait Resource { fn fcntl(&mut self, cmd: usize, arg: usize) -> Result; fn fevent(&mut self) -> Result; fn events(&mut self) -> usize; + fn timeout(&self, _count: u64) { + // Handled only by PTY master + } } diff --git a/src/slave.rs b/src/slave.rs index a7232a34ee..6b55db3054 100644 --- a/src/slave.rs +++ b/src/slave.rs @@ -52,6 +52,8 @@ impl Resource for PtySlave { if let Some(pty_lock) = self.pty.upgrade() { let mut pty = pty_lock.borrow_mut(); + pty.update(); + if let Some(packet) = pty.mosi.pop_front() { let mut i = 0;