Factor out termios and the line discipline into their own crates

This commit is contained in:
pjht 2024-09-23 14:46:04 -05:00
parent 84786479c2
commit 142c1d2aab
Signed by: pjht
GPG Key ID: CA239FC6934E6F3A
4 changed files with 62 additions and 369 deletions

23
Cargo.lock generated
View File

@ -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",

View File

@ -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" }

View File

@ -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(())
}
}

View File

@ -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,
}
}
}