Implement VTIME
This commit is contained in:
+85
-14
@@ -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,
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user