Implement VTIME

This commit is contained in:
Jeremy Soller
2020-01-28 11:05:46 -07:00
parent ce1d87e2aa
commit 13e472b6eb
5 changed files with 180 additions and 19 deletions
+85 -14
View File
@@ -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,
+5
View File
@@ -124,4 +124,9 @@ impl Resource for PtyMaster {
events
}
fn timeout(&self, count: u64) {
let mut pty = self.pty.borrow_mut();
pty.timeout(count);
}
}
+85 -5
View File
@@ -12,6 +12,8 @@ pub struct Pty {
pub cooked: Vec<u8>,
pub miso: VecDeque<Vec<u8>>,
pub mosi: VecDeque<Vec<u8>>,
pub timeout_count: u64,
pub timeout_character: Option<u64>,
}
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();
}
}
}
+3
View File
@@ -17,4 +17,7 @@ pub trait Resource {
fn fcntl(&mut self, cmd: usize, arg: usize) -> Result<usize>;
fn fevent(&mut self) -> Result<usize>;
fn events(&mut self) -> usize;
fn timeout(&self, _count: u64) {
// Handled only by PTY master
}
}
+2
View File
@@ -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;