diff --git a/Cargo.lock b/Cargo.lock index 25de524..feddeab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index 8b1c820..a0f2b16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/src/main.rs b/src/main.rs index 2eabba4..86936b5 100644 --- a/src/main.rs +++ b/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>, output_buffer: Mutex>, - termios: Termios, - output_paused: AtomicBool, - column: AtomicUsize, + line_discipline: Mutex, } 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, ()> { - if self.output_paused.load(Ordering::Relaxed) { - return Ok(vec![]); + fn read_master(&self, len: usize) -> Vec { + 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, ()> { + fn read_slave(&self, len: usize) -> Vec { 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); diff --git a/src/termios.rs b/src/termios.rs deleted file mode 100644 index 181109b..0000000 --- a/src/termios.rs +++ /dev/null @@ -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, - } - } -}