Factor out termios and the line discipline into their own crates
This commit is contained in:
parent
84786479c2
commit
142c1d2aab
23
Cargo.lock
generated
23
Cargo.lock
generated
@ -68,6 +68,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.4.0"
|
||||
@ -113,12 +119,28 @@ dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.158"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||
|
||||
[[package]]
|
||||
name = "line_discipline"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
@ -193,6 +215,7 @@ dependencies = [
|
||||
"dev_driver_rpc",
|
||||
"devfs_rpc",
|
||||
"file_rpc",
|
||||
"line_discipline",
|
||||
"parking_lot",
|
||||
"slab",
|
||||
"syslog_rpc",
|
||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||
dev_driver_rpc = { version = "0.1.0", path = "../dev_driver_rpc" }
|
||||
devfs_rpc = { version = "0.1.0", path = "../devfs/devfs_rpc" }
|
||||
file_rpc = { version = "0.1.0", path = "../file_rpc" }
|
||||
line_discipline = { version = "0.1.0", path = "../line_discipline" }
|
||||
parking_lot = "0.12.3"
|
||||
slab = "0.4.9"
|
||||
syslog_rpc = { version = "0.1.0", path = "../syslog/syslog_rpc" }
|
||||
|
195
src/main.rs
195
src/main.rs
@ -1,23 +1,20 @@
|
||||
mod termios;
|
||||
|
||||
use std::{
|
||||
borrow::Cow, collections::HashMap, io::empty, os::mikros::{ipc, syscalls}, path::{Path, PathBuf}, sync::{
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
Arc,
|
||||
}
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
os::mikros::{ipc, syscalls},
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use file_rpc::PollEvents;
|
||||
use line_discipline::LineDiscipline;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use slab::Slab;
|
||||
use termios::Termios;
|
||||
|
||||
struct Pty {
|
||||
input_buffer: Mutex<Vec<u8>>,
|
||||
output_buffer: Mutex<Vec<u8>>,
|
||||
termios: Termios,
|
||||
output_paused: AtomicBool,
|
||||
column: AtomicUsize,
|
||||
line_discipline: Mutex<LineDiscipline>,
|
||||
}
|
||||
|
||||
impl Pty {
|
||||
@ -25,169 +22,48 @@ impl Pty {
|
||||
Pty {
|
||||
input_buffer: Mutex::new(Vec::new()),
|
||||
output_buffer: Mutex::new(Vec::new()),
|
||||
termios: Termios::default(),
|
||||
output_paused: AtomicBool::new(false),
|
||||
column: AtomicUsize::new(0),
|
||||
line_discipline: Mutex::new(LineDiscipline::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_master(&self, len: usize) -> Result<Vec<u8>, ()> {
|
||||
if self.output_paused.load(Ordering::Relaxed) {
|
||||
return Ok(vec![]);
|
||||
fn read_master(&self, len: usize) -> Vec<u8> {
|
||||
if self.line_discipline.lock().output_paused() {
|
||||
return Vec::new();
|
||||
}
|
||||
let mut output_buffer = self.output_buffer.lock();
|
||||
output_buffer.extend_from_slice(&self.line_discipline.lock().get_terminal_output());
|
||||
let buf_len = output_buffer.len();
|
||||
let rem_buf = output_buffer.split_off(usize::min(buf_len, len));
|
||||
let buf = std::mem::replace(&mut *output_buffer, rem_buf);
|
||||
Ok(buf)
|
||||
std::mem::replace(&mut *output_buffer, rem_buf)
|
||||
}
|
||||
|
||||
fn write_master(&self, data: &[u8]) -> Result<(), ()> {
|
||||
if !self.termios.cflags.cread {
|
||||
return Ok(());
|
||||
}
|
||||
for &(mut ch) in data {
|
||||
if self.termios.iflags.istrip {
|
||||
ch &= 0x7F;
|
||||
}
|
||||
if ch == b'\r' && self.termios.iflags.igncr {
|
||||
continue;
|
||||
} else if ch == b'\r' && self.termios.iflags.icrnl {
|
||||
ch = b'\n';
|
||||
} else if ch == b'\n' && self.termios.iflags.inlcr {
|
||||
ch = b'\r';
|
||||
}
|
||||
if ch as char == self.termios.verase && self.termios.lflags.icanon {
|
||||
let buf_last = self.input_buffer.lock().last().copied();
|
||||
if let Some(erased_ch) = buf_last {
|
||||
let erased_ch = erased_ch as char;
|
||||
if erased_ch != '\n' {
|
||||
self.input_buffer.lock().pop();
|
||||
self.erase_char(erased_ch);
|
||||
}
|
||||
}
|
||||
} else if ch as char == self.termios.vkill && self.termios.lflags.icanon {
|
||||
let buf_last = self.input_buffer.lock().last().copied();
|
||||
if let Some(erased_ch) = buf_last {
|
||||
let erased_ch = erased_ch as char;
|
||||
if erased_ch != '\n' {
|
||||
self.input_buffer.lock().pop();
|
||||
self.erase_char(erased_ch);
|
||||
}
|
||||
}
|
||||
} else if ch as char == self.termios.vstop && self.termios.iflags.ixon {
|
||||
self.output_paused.store(true, Ordering::Relaxed);
|
||||
} else if (ch as char == self.termios.vstart && self.termios.iflags.ixon)
|
||||
|| (self.termios.iflags.ixany && self.output_paused.load(Ordering::Relaxed))
|
||||
{
|
||||
self.output_paused.store(false, Ordering::Relaxed);
|
||||
} else {
|
||||
self.input_buffer.lock().push(ch);
|
||||
if self.termios.lflags.echo || (ch == b'\n' && self.termios.lflags.echonl) {
|
||||
let mut output_buffer = self.output_buffer.lock();
|
||||
if Self::is_nonprinting_control(ch as char) {
|
||||
output_buffer.push(b'^');
|
||||
output_buffer.push(ch + b'@');
|
||||
self.column.fetch_add(2, Ordering::Relaxed);
|
||||
} else if ch == b'\t' {
|
||||
let curr_col = self.column.load(Ordering::Relaxed);
|
||||
let num_cols = 8 - (curr_col % 8);
|
||||
for _ in 0..num_cols {
|
||||
output_buffer.push(b' ');
|
||||
}
|
||||
self.column.fetch_add(num_cols, Ordering::Relaxed);
|
||||
} else {
|
||||
output_buffer.push(ch);
|
||||
self.column.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
fn write_master(&self, data: &[u8]) {
|
||||
self.line_discipline.lock().process_terminal_input(data);
|
||||
}
|
||||
|
||||
fn read_slave(&self, len: usize) -> Result<Vec<u8>, ()> {
|
||||
fn read_slave(&self, len: usize) -> Vec<u8> {
|
||||
while !self.input_buffer.lock().contains(&b'\n') {
|
||||
ipc::process_messages();
|
||||
let mut input_buffer = self.input_buffer.lock();
|
||||
input_buffer.extend_from_slice(&self.line_discipline.lock().get_program_input());
|
||||
}
|
||||
let mut input_buffer = self.input_buffer.lock();
|
||||
input_buffer.extend_from_slice(&self.line_discipline.lock().get_program_input());
|
||||
let Some(line_end) = input_buffer.iter().position(|&x| x == b'\n') else {
|
||||
return Ok(vec![]);
|
||||
return Vec::new();
|
||||
};
|
||||
let rem_buf = input_buffer.split_off(usize::min(len, line_end + 1));
|
||||
let buf = std::mem::replace(&mut *input_buffer, rem_buf);
|
||||
Ok(buf)
|
||||
std::mem::replace(&mut *input_buffer, rem_buf)
|
||||
}
|
||||
|
||||
fn write_slave(&self, data: &[u8]) -> Result<(), ()> {
|
||||
let mut output_buffer = self.output_buffer.lock();
|
||||
for &byte in data {
|
||||
if self.termios.oflags.opost {
|
||||
if byte == b'\n' && self.termios.oflags.onlcr {
|
||||
if self.column.load(Ordering::Relaxed) != 0 {
|
||||
output_buffer.push(b'\r');
|
||||
}
|
||||
output_buffer.push(b'\n');
|
||||
self.column.store(0, Ordering::Relaxed);
|
||||
} else if byte == b'\r' && self.termios.oflags.ocrnl {
|
||||
output_buffer.push(b'\n');
|
||||
if self.termios.oflags.onlret {
|
||||
self.column.store(0, Ordering::Relaxed);
|
||||
}
|
||||
} else {
|
||||
output_buffer.push(byte);
|
||||
}
|
||||
} else if (byte == b'\n' && self.termios.oflags.onlret) || byte == b'\r' {
|
||||
self.column.store(0, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_nonprinting_control(ch: char) -> bool {
|
||||
ch.is_ascii_control() && ch != '\r' && ch != '\n' && ch != '\t'
|
||||
}
|
||||
|
||||
fn erase_char(&self, ch: char) {
|
||||
if !self.termios.lflags.echoe {
|
||||
return;
|
||||
}
|
||||
let mut output_buffer = self.output_buffer.lock();
|
||||
if Self::is_nonprinting_control(ch) {
|
||||
output_buffer.push(8);
|
||||
output_buffer.push(8);
|
||||
self.column.fetch_sub(2, Ordering::Relaxed);
|
||||
} else if ch == '\t' {
|
||||
let curr_col = self.column.load(Ordering::Relaxed);
|
||||
let mut before_tab_cols = 0;
|
||||
let mut after_tab = false;
|
||||
let input_buffer = self.input_buffer.lock();
|
||||
for &byte in input_buffer.iter().rev() {
|
||||
if Self::is_nonprinting_control(byte as char) {
|
||||
before_tab_cols += 2;
|
||||
} else if byte == b'\t' {
|
||||
after_tab = true;
|
||||
break;
|
||||
} else {
|
||||
before_tab_cols += 1;
|
||||
}
|
||||
}
|
||||
let num_cols = if after_tab {
|
||||
8 - before_tab_cols
|
||||
} else {
|
||||
curr_col - before_tab_cols
|
||||
};
|
||||
for _ in 0..num_cols {
|
||||
output_buffer.push(8);
|
||||
}
|
||||
self.column.fetch_sub(num_cols, Ordering::Relaxed);
|
||||
} else {
|
||||
output_buffer.push(8);
|
||||
self.column.fetch_sub(1, Ordering::Relaxed);
|
||||
}
|
||||
fn write_slave(&self, data: &[u8]) {
|
||||
self.line_discipline.lock().process_program_output(data);
|
||||
}
|
||||
|
||||
fn curr_slave_poll(&self) -> PollEvents {
|
||||
self.input_buffer
|
||||
.lock()
|
||||
.extend_from_slice(&self.line_discipline.lock().get_program_input());
|
||||
let mut events = PollEvents::POLLOUT;
|
||||
if self.input_buffer.lock().contains(&b'\n') {
|
||||
events |= PollEvents::POLLIN;
|
||||
@ -196,6 +72,9 @@ impl Pty {
|
||||
}
|
||||
|
||||
fn curr_master_poll(&self) -> PollEvents {
|
||||
self.output_buffer
|
||||
.lock()
|
||||
.extend_from_slice(&self.line_discipline.lock().get_terminal_output());
|
||||
let mut events = PollEvents::POLLOUT;
|
||||
if !self.output_buffer.lock().is_empty() {
|
||||
events |= PollEvents::POLLIN;
|
||||
@ -239,11 +118,11 @@ impl file_rpc::Server for Serv {
|
||||
if fd as usize >= usize::MAX / 2 {
|
||||
let ptys = self.ptys.read();
|
||||
let pty = &ptys[fd as usize - usize::MAX / 2];
|
||||
pty.read_slave(len).map(|x| x.into())
|
||||
Ok(pty.read_slave(len).into())
|
||||
} else {
|
||||
let ptys = self.ptys.read();
|
||||
let pty = &ptys[fd as usize];
|
||||
pty.read_master(len).map(|x| x.into())
|
||||
Ok(pty.read_master(len).into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,11 +130,13 @@ impl file_rpc::Server for Serv {
|
||||
if fd as usize >= usize::MAX / 2 {
|
||||
let ptys = self.ptys.read();
|
||||
let pty = &ptys[fd as usize - usize::MAX / 2];
|
||||
pty.write_slave(data)
|
||||
pty.write_slave(data);
|
||||
Ok(())
|
||||
} else {
|
||||
let ptys = self.ptys.read();
|
||||
let pty = &ptys[fd as usize];
|
||||
pty.write_master(data)
|
||||
pty.write_master(data);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,7 +163,7 @@ impl file_rpc::Server for Serv {
|
||||
let pty_no = if fd as usize >= usize::MAX / 2 {
|
||||
fd as usize - usize::MAX / 2
|
||||
} else {
|
||||
fd as usize
|
||||
fd as usize
|
||||
};
|
||||
let slave = fd as usize >= usize::MAX / 2;
|
||||
loop {
|
||||
@ -290,9 +171,9 @@ impl file_rpc::Server for Serv {
|
||||
return None;
|
||||
}
|
||||
let curr_poll = if slave {
|
||||
self.ptys.read()[pty_no].curr_slave_poll()
|
||||
self.ptys.read()[pty_no].curr_slave_poll()
|
||||
} else {
|
||||
self.ptys.read()[pty_no].curr_master_poll()
|
||||
self.ptys.read()[pty_no].curr_master_poll()
|
||||
};
|
||||
if !((curr_poll & req_events).is_empty()) {
|
||||
self.polls.lock().remove(poll_id);
|
||||
|
212
src/termios.rs
212
src/termios.rs
@ -1,212 +0,0 @@
|
||||
#[allow(unused)]
|
||||
pub struct Iflags {
|
||||
pub brkint: bool,
|
||||
pub icrnl: bool,
|
||||
pub ignbrk: bool,
|
||||
pub igncr: bool,
|
||||
pub ignpar: bool,
|
||||
pub inlcr: bool,
|
||||
pub inpck: bool,
|
||||
pub istrip: bool,
|
||||
pub ixany: bool,
|
||||
pub ixoff: bool,
|
||||
pub ixon: bool,
|
||||
pub parmark: bool,
|
||||
}
|
||||
|
||||
impl Default for Iflags {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
brkint: false,
|
||||
icrnl: true,
|
||||
ignbrk: false,
|
||||
igncr: false,
|
||||
ignpar: true,
|
||||
inlcr: false,
|
||||
inpck: true,
|
||||
istrip: false,
|
||||
ixany: false,
|
||||
ixoff: false,
|
||||
ixon: true,
|
||||
parmark: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct Oflags {
|
||||
pub opost: bool,
|
||||
pub onlcr: bool,
|
||||
pub ocrnl: bool,
|
||||
pub onocr: bool,
|
||||
pub onlret: bool,
|
||||
pub ofdel: bool,
|
||||
pub nldly: NlDelay,
|
||||
pub crdly: CrDelay,
|
||||
pub tabdly: TabDelay,
|
||||
pub bsdly: BsDelay,
|
||||
pub vtdly: VtDelay,
|
||||
pub ffdly: FfDelay,
|
||||
}
|
||||
|
||||
impl Default for Oflags {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
opost: true,
|
||||
onlcr: true,
|
||||
ocrnl: false,
|
||||
onocr: false,
|
||||
onlret: false,
|
||||
ofdel: false,
|
||||
nldly: NlDelay::Nl0,
|
||||
crdly: CrDelay::Cr0,
|
||||
tabdly: TabDelay::Tab0,
|
||||
bsdly: BsDelay::Bs0,
|
||||
vtdly: VtDelay::Vt0,
|
||||
ffdly: FfDelay::Ff0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub enum NlDelay {
|
||||
Nl0,
|
||||
Nl1,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub enum CrDelay {
|
||||
Cr0,
|
||||
Cr1,
|
||||
Cr2,
|
||||
Cr3,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub enum TabDelay {
|
||||
Tab0,
|
||||
Tab1,
|
||||
Tab2,
|
||||
Tab3,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub enum BsDelay {
|
||||
Bs0,
|
||||
Bs1,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub enum VtDelay {
|
||||
Vt0,
|
||||
Vt1,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub enum FfDelay {
|
||||
Ff0,
|
||||
Ff1,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct Cflags {
|
||||
pub csize: CharSize,
|
||||
pub cstopb: bool,
|
||||
pub cread: bool,
|
||||
pub parenb: bool,
|
||||
pub parodd: bool,
|
||||
pub hupcl: bool,
|
||||
pub clocal: bool,
|
||||
}
|
||||
|
||||
impl Default for Cflags {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
csize: CharSize::Cs8,
|
||||
cstopb: false,
|
||||
cread: true,
|
||||
parenb: false,
|
||||
parodd: false,
|
||||
hupcl: true,
|
||||
clocal: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub enum CharSize {
|
||||
Cs5,
|
||||
Cs6,
|
||||
Cs7,
|
||||
Cs8,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct Lflags {
|
||||
pub echo: bool,
|
||||
pub echoe: bool,
|
||||
pub echok: bool,
|
||||
pub echonl: bool,
|
||||
pub icanon: bool,
|
||||
pub iexten: bool,
|
||||
pub isig: bool,
|
||||
pub noflsh: bool,
|
||||
pub tostop: bool,
|
||||
}
|
||||
|
||||
impl Default for Lflags {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
echo: true,
|
||||
echoe: true,
|
||||
echok: true,
|
||||
echonl: false,
|
||||
icanon: true,
|
||||
iexten: true,
|
||||
isig: true,
|
||||
noflsh: false,
|
||||
tostop: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct Termios {
|
||||
pub iflags: Iflags,
|
||||
pub oflags: Oflags,
|
||||
pub cflags: Cflags,
|
||||
pub lflags: Lflags,
|
||||
pub veof: char,
|
||||
pub veol: char,
|
||||
pub verase: char,
|
||||
pub vintr: char,
|
||||
pub vkill: char,
|
||||
pub vmin: u32,
|
||||
pub vquit: char,
|
||||
pub vstart: char,
|
||||
pub vstop: char,
|
||||
pub vsusp: char,
|
||||
pub vtime: u32,
|
||||
}
|
||||
|
||||
impl Default for Termios {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
iflags: Iflags::default(),
|
||||
oflags: Oflags::default(),
|
||||
cflags: Cflags::default(),
|
||||
lflags: Lflags::default(),
|
||||
veof: '\u{4}', //ctrl-D (EOT)
|
||||
veol: '\n',
|
||||
verase: '\u{8}', //ctrl-H (BS)
|
||||
vintr: '\u{3}', //ctrl-C (ETX)
|
||||
vkill: '\u{15}', //ctrl-U (NAK)
|
||||
vmin: 0,
|
||||
vquit: '\u{1C}', //ctrl-\
|
||||
vstart: '\u{11}', // ctrl-S (XONN)
|
||||
vstop: '\u{13}', // ctrl-Q (XOFF)
|
||||
vsusp: '\u{1A}', // ctrl-Z (SUB)
|
||||
vtime: 0,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user