ptyd: implement tcflush, tcdrain, and tcsendbreak
This commit is contained in:
Generated
+1
@@ -1658,6 +1658,7 @@ name = "ptyd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"daemon",
|
||||
"libc",
|
||||
"libredox",
|
||||
"redox-scheme",
|
||||
"redox_event",
|
||||
|
||||
@@ -12,6 +12,7 @@ scheme-utils = { path = "../scheme-utils" }
|
||||
redox_event.workspace = true
|
||||
redox_syscall.workspace = true
|
||||
redox_termios.workspace = true
|
||||
libc.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -6,7 +6,6 @@ use syscall::flag::{EventFlags, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK};
|
||||
|
||||
use crate::pty::Pty;
|
||||
use crate::resource::Resource;
|
||||
use scheme_utils::FpathWriter;
|
||||
|
||||
/// Read side of a pipe
|
||||
pub struct PtyControlTerm {
|
||||
@@ -106,6 +105,11 @@ impl Resource for PtyControlTerm {
|
||||
let mut events = EventFlags::empty();
|
||||
|
||||
let pty = self.pty.borrow();
|
||||
// If pty is stopped (e.g. `tcflush(fd, TCOOFF)`), do not send the event
|
||||
// to the terminal to write the characters to the screen.
|
||||
if pty.stopped {
|
||||
return EventFlags::empty();
|
||||
}
|
||||
if pty.miso.front().is_some() {
|
||||
if !self.notified_read {
|
||||
self.notified_read = true;
|
||||
|
||||
@@ -7,8 +7,11 @@ use syscall::data::TimeSpec;
|
||||
|
||||
mod controlterm;
|
||||
mod pgrp;
|
||||
mod ptflow;
|
||||
mod ptflush;
|
||||
mod ptlock;
|
||||
mod ptname;
|
||||
mod ptsendbreak;
|
||||
mod pty;
|
||||
mod resource;
|
||||
mod scheme;
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
use core::ops::DerefMut;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Weak;
|
||||
|
||||
use libc::{c_int, TCIOFF, TCION, TCOOFF, TCOON};
|
||||
use syscall::error::{Error, Result, EBADF, EINVAL, EPIPE};
|
||||
use syscall::flag::{EventFlags, F_GETFL, F_SETFL, O_ACCMODE};
|
||||
|
||||
use crate::pty::Pty;
|
||||
use crate::resource::Resource;
|
||||
|
||||
/// Read side of a pipe
|
||||
pub struct PtFlow {
|
||||
pty: Weak<RefCell<Pty>>,
|
||||
flags: usize,
|
||||
}
|
||||
|
||||
impl PtFlow {
|
||||
pub fn new(pty: Weak<RefCell<Pty>>, flags: usize) -> Self {
|
||||
PtFlow { pty, flags }
|
||||
}
|
||||
|
||||
fn flow(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
let action = u32::from_ne_bytes(
|
||||
buf.get(..4)
|
||||
.and_then(|b| <[u8; 4]>::try_from(b).ok())
|
||||
.ok_or(Error::new(EINVAL))?,
|
||||
);
|
||||
let action = action as c_int;
|
||||
|
||||
match action {
|
||||
TCOON | TCOOFF => {
|
||||
let pty_lock = self.pty.upgrade().ok_or(Error::new(EPIPE))?;
|
||||
let mut pty = pty_lock.borrow_mut();
|
||||
let pty = pty.deref_mut();
|
||||
|
||||
if action == TCOON {
|
||||
pty.stopped = false;
|
||||
} else if action == TCOOFF {
|
||||
pty.stopped = true;
|
||||
}
|
||||
}
|
||||
TCION | TCIOFF => {
|
||||
// We are a pty, and the start and stop characters
|
||||
// only have to be written if we are a tty
|
||||
}
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
}
|
||||
Ok(4)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for PtFlow {
|
||||
fn pty(&self) -> Weak<RefCell<Pty>> {
|
||||
self.pty.clone()
|
||||
}
|
||||
|
||||
fn flags(&self) -> usize {
|
||||
self.flags
|
||||
}
|
||||
|
||||
fn path(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
pty_lock.borrow_mut().path(buf)
|
||||
} else {
|
||||
Err(Error::new(EPIPE))
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
self.flow(buf)
|
||||
}
|
||||
|
||||
fn sync(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fcntl(&mut self, cmd: usize, arg: usize) -> Result<usize> {
|
||||
match cmd {
|
||||
F_GETFL => Ok(self.flags),
|
||||
F_SETFL => {
|
||||
self.flags = (self.flags & O_ACCMODE) | (arg & !O_ACCMODE);
|
||||
Ok(0)
|
||||
}
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
fn fevent(&mut self) -> Result<EventFlags> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
fn events(&mut self) -> EventFlags {
|
||||
EventFlags::empty()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Weak;
|
||||
|
||||
use libc::{c_int, TCIFLUSH, TCIOFLUSH, TCOFLUSH};
|
||||
use syscall::error::{Error, Result, EBADF, EINVAL, EPIPE};
|
||||
use syscall::flag::{EventFlags, F_GETFL, F_SETFL, O_ACCMODE};
|
||||
|
||||
use crate::pty::Pty;
|
||||
use crate::resource::Resource;
|
||||
|
||||
/// Read side of a pipe
|
||||
pub struct PtFlush {
|
||||
pty: Weak<RefCell<Pty>>,
|
||||
flags: usize,
|
||||
}
|
||||
|
||||
impl PtFlush {
|
||||
pub fn new(pty: Weak<RefCell<Pty>>, flags: usize) -> Self {
|
||||
PtFlush { pty, flags }
|
||||
}
|
||||
fn flush_write(&mut self) -> Result<usize> {
|
||||
if let Some(pty) = self.pty.upgrade() {
|
||||
pty.borrow_mut().miso.clear();
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(Error::new(EPIPE))
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_read(&mut self) -> Result<usize> {
|
||||
if let Some(pty) = self.pty.upgrade() {
|
||||
pty.borrow_mut().mosi.clear();
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(Error::new(EPIPE))
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
let action = u32::from_ne_bytes(
|
||||
buf.get(..4)
|
||||
.and_then(|b| <[u8; 4]>::try_from(b).ok())
|
||||
.ok_or(Error::new(EINVAL))?,
|
||||
);
|
||||
|
||||
match action as c_int {
|
||||
TCIFLUSH => {
|
||||
self.flush_read()?;
|
||||
}
|
||||
TCOFLUSH => {
|
||||
self.flush_write()?;
|
||||
}
|
||||
TCIOFLUSH => {
|
||||
self.flush_read()?;
|
||||
self.flush_write()?;
|
||||
}
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
}
|
||||
Ok(4)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for PtFlush {
|
||||
fn pty(&self) -> Weak<RefCell<Pty>> {
|
||||
self.pty.clone()
|
||||
}
|
||||
|
||||
fn flags(&self) -> usize {
|
||||
self.flags
|
||||
}
|
||||
|
||||
fn path(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
pty_lock.borrow_mut().path(buf)
|
||||
} else {
|
||||
Err(Error::new(EPIPE))
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
self.flush(buf)
|
||||
}
|
||||
|
||||
fn sync(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fcntl(&mut self, cmd: usize, arg: usize) -> Result<usize> {
|
||||
match cmd {
|
||||
F_GETFL => Ok(self.flags),
|
||||
F_SETFL => {
|
||||
self.flags = (self.flags & O_ACCMODE) | (arg & !O_ACCMODE);
|
||||
Ok(0)
|
||||
}
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
fn fevent(&mut self) -> Result<EventFlags> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
fn events(&mut self) -> EventFlags {
|
||||
EventFlags::empty()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Weak;
|
||||
|
||||
use libc::{c_int, nanosleep, timespec};
|
||||
use syscall::error::{Error, Result, EBADF, EINVAL, EPIPE};
|
||||
use syscall::flag::{EventFlags, F_GETFL, F_SETFL, O_ACCMODE};
|
||||
|
||||
use crate::pty::Pty;
|
||||
use crate::resource::Resource;
|
||||
|
||||
/// Read side of a pipe
|
||||
pub struct PtSendbreak {
|
||||
pty: Weak<RefCell<Pty>>,
|
||||
flags: usize,
|
||||
}
|
||||
|
||||
impl PtSendbreak {
|
||||
pub fn new(pty: Weak<RefCell<Pty>>, flags: usize) -> Self {
|
||||
PtSendbreak { pty, flags }
|
||||
}
|
||||
|
||||
fn sendbreak(&mut self, _duration: c_int) -> Result<usize> {
|
||||
// TODO: send break here
|
||||
let _ = unsafe {
|
||||
// POSIX specifies that we need to sleep for 0.25 to 0.5 seconds.
|
||||
// FreeBSD uses 0.4, and that seems reasonable.
|
||||
let tm = timespec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: 400000000,
|
||||
};
|
||||
nanosleep(&tm, core::ptr::null_mut())
|
||||
};
|
||||
// TODO: end break here
|
||||
|
||||
Ok(4)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for PtSendbreak {
|
||||
fn pty(&self) -> Weak<RefCell<Pty>> {
|
||||
self.pty.clone()
|
||||
}
|
||||
|
||||
fn flags(&self) -> usize {
|
||||
self.flags
|
||||
}
|
||||
|
||||
fn path(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
pty_lock.borrow_mut().path(buf)
|
||||
} else {
|
||||
Err(Error::new(EPIPE))
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
let duration = c_int::from_ne_bytes(
|
||||
buf.get(..4)
|
||||
.and_then(|b| <[u8; 4]>::try_from(b).ok())
|
||||
.ok_or(Error::new(EINVAL))?,
|
||||
);
|
||||
self.sendbreak(duration)
|
||||
}
|
||||
|
||||
fn sync(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fcntl(&mut self, cmd: usize, arg: usize) -> Result<usize> {
|
||||
match cmd {
|
||||
F_GETFL => Ok(self.flags),
|
||||
F_SETFL => {
|
||||
self.flags = (self.flags & O_ACCMODE) | (arg & !O_ACCMODE);
|
||||
Ok(0)
|
||||
}
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
fn fevent(&mut self) -> Result<EventFlags> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
fn events(&mut self) -> EventFlags {
|
||||
EventFlags::empty()
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ pub struct Pty {
|
||||
pub id: usize,
|
||||
pub pgrp: usize,
|
||||
pub locked: bool,
|
||||
pub stopped: bool,
|
||||
pub termios: Termios,
|
||||
pub winsize: Winsize,
|
||||
pub cooked: Vec<u8>,
|
||||
@@ -23,6 +24,7 @@ impl Pty {
|
||||
id,
|
||||
pgrp: 0,
|
||||
locked: true,
|
||||
stopped: false,
|
||||
termios: Termios::default(),
|
||||
winsize: Winsize::default(),
|
||||
cooked: Vec::new(),
|
||||
|
||||
@@ -12,8 +12,11 @@ use syscall::schemev2::NewFdFlags;
|
||||
|
||||
use crate::controlterm::PtyControlTerm;
|
||||
use crate::pgrp::PtyPgrp;
|
||||
use crate::ptflow::PtFlow;
|
||||
use crate::ptflush::PtFlush;
|
||||
use crate::ptlock::PtyLock;
|
||||
use crate::ptname::PtsName;
|
||||
use crate::ptsendbreak::PtSendbreak;
|
||||
use crate::pty::Pty;
|
||||
use crate::resource::Resource;
|
||||
use crate::subterm::PtySubTerm;
|
||||
@@ -130,6 +133,12 @@ impl SchemeSync for PtyScheme {
|
||||
Box::new(PtyLock::new(old_resource.pty(), old_resource.flags()))
|
||||
} else if buf == b"ptsname" {
|
||||
Box::new(PtsName::new(old_resource.pty(), old_resource.flags()))
|
||||
} else if buf == b"flush" {
|
||||
Box::new(PtFlush::new(old_resource.pty(), old_resource.flags()))
|
||||
} else if buf == b"sendbreak" {
|
||||
Box::new(PtSendbreak::new(old_resource.pty(), old_resource.flags()))
|
||||
} else if buf == b"flow" {
|
||||
Box::new(PtFlow::new(old_resource.pty(), old_resource.flags()))
|
||||
} else {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user