Correct the behavior about closing pty
This commit is contained in:
parent
1c3e83bbaa
commit
cd981a0a8d
|
|
@ -14,7 +14,7 @@ pub mod tdxguest;
|
|||
|
||||
use alloc::format;
|
||||
|
||||
pub use pty::{new_pty_pair, PtyMaster, PtySlave};
|
||||
pub use pty::{new_pty_pair, PtyMaster, PtySlave, PtySlaveFile};
|
||||
pub use random::Random;
|
||||
pub use urandom::Urandom;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
|
||||
use aster_console::AnyConsoleDevice;
|
||||
use ostd::sync::SpinLock;
|
||||
|
||||
use crate::{
|
||||
device::tty::{PushCharError, Tty, TtyDriver},
|
||||
device::tty::{Tty, TtyDriver},
|
||||
events::IoEvents,
|
||||
prelude::{return_errno_with_message, Errno, Result},
|
||||
process::signal::Pollee,
|
||||
|
|
@ -15,12 +17,14 @@ const BUFFER_CAPACITY: usize = 8192;
|
|||
|
||||
/// A pseudoterminal driver.
|
||||
///
|
||||
/// This is contained in the PTY slave, but it maintains the output buffer and the pollee of the
|
||||
/// This is contained in the pty slave, but it maintains the output buffer and the pollee of the
|
||||
/// master. The pollee of the slave is part of the [`Tty`] structure (see the definition of
|
||||
/// [`PtySlave`]).
|
||||
pub struct PtyDriver {
|
||||
output: SpinLock<RingBuffer<u8>>,
|
||||
pollee: Pollee,
|
||||
is_master_closed: AtomicBool,
|
||||
opened_slaves: AtomicUsize,
|
||||
}
|
||||
|
||||
/// A pseudoterminal slave.
|
||||
|
|
@ -31,6 +35,8 @@ impl PtyDriver {
|
|||
Self {
|
||||
output: SpinLock::new(RingBuffer::new(BUFFER_CAPACITY)),
|
||||
pollee: Pollee::new(),
|
||||
is_master_closed: AtomicBool::new(false),
|
||||
opened_slaves: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -41,6 +47,12 @@ impl PtyDriver {
|
|||
|
||||
let mut output = self.output.lock();
|
||||
if output.is_empty() {
|
||||
if self.opened_slaves.load(Ordering::Relaxed) == 0 {
|
||||
return_errno_with_message!(
|
||||
Errno::EIO,
|
||||
"the pty master does not have opened slaves"
|
||||
);
|
||||
}
|
||||
return_errno_with_message!(Errno::EAGAIN, "the buffer is empty");
|
||||
}
|
||||
|
||||
|
|
@ -57,10 +69,22 @@ impl PtyDriver {
|
|||
pub(super) fn buffer_len(&self) -> usize {
|
||||
self.output.lock().len()
|
||||
}
|
||||
|
||||
pub(super) fn set_master_closed(&self) {
|
||||
self.is_master_closed.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub(super) fn opened_slaves(&self) -> &AtomicUsize {
|
||||
&self.opened_slaves
|
||||
}
|
||||
}
|
||||
|
||||
impl TtyDriver for PtyDriver {
|
||||
fn push_output(&self, chs: &[u8]) -> core::result::Result<usize, PushCharError> {
|
||||
fn push_output(&self, chs: &[u8]) -> Result<usize> {
|
||||
if self.is_closed() {
|
||||
return_errno_with_message!(Errno::EIO, "the pty master has been closed");
|
||||
}
|
||||
|
||||
let mut output = self.output.lock();
|
||||
|
||||
let mut len = 0;
|
||||
|
|
@ -73,7 +97,7 @@ impl TtyDriver for PtyDriver {
|
|||
} else if *ch != b'\n' && !output.is_full() {
|
||||
output.push(*ch).unwrap();
|
||||
} else if len == 0 {
|
||||
return Err(PushCharError);
|
||||
return_errno_with_message!(Errno::EAGAIN, "the output buffer is full");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
@ -110,6 +134,10 @@ impl TtyDriver for PtyDriver {
|
|||
output.capacity() - output.len() >= 2
|
||||
}
|
||||
|
||||
fn is_closed(&self) -> bool {
|
||||
self.is_master_closed.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn notify_input(&self) {
|
||||
self.pollee.notify(IoEvents::OUT);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use inherit_methods_macro::inherit_methods;
|
||||
|
||||
use crate::{
|
||||
device::PtySlave,
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
inode_handle::FileIo,
|
||||
utils::{IoctlCmd, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
};
|
||||
|
||||
/// The file for a pseudoterminal slave.
|
||||
pub struct PtySlaveFile(Arc<PtySlave>);
|
||||
|
||||
impl PtySlaveFile {
|
||||
pub fn new(slave: Arc<PtySlave>) -> PtySlaveFile {
|
||||
slave
|
||||
.driver()
|
||||
.opened_slaves()
|
||||
.fetch_add(1, Ordering::Relaxed);
|
||||
slave.driver().pollee().invalidate();
|
||||
PtySlaveFile(slave)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PtySlaveFile {
|
||||
fn drop(&mut self) {
|
||||
let driver = self.0.driver();
|
||||
driver.opened_slaves().fetch_sub(1, Ordering::Relaxed);
|
||||
driver.pollee().notify(IoEvents::HUP);
|
||||
}
|
||||
}
|
||||
|
||||
#[inherit_methods(from = "self.0")]
|
||||
impl FileIo for PtySlaveFile {
|
||||
fn read(&self, writer: &mut VmWriter, status_flags: StatusFlags) -> Result<usize>;
|
||||
fn write(&self, reader: &mut VmReader, status_flags: StatusFlags) -> Result<usize>;
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32>;
|
||||
}
|
||||
|
||||
#[inherit_methods(from = "self.0")]
|
||||
impl Pollable for PtySlaveFile {
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents;
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::format;
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use ostd::task::Task;
|
||||
|
||||
|
|
@ -61,6 +62,10 @@ impl PtyMaster {
|
|||
events |= IoEvents::OUT;
|
||||
}
|
||||
|
||||
if self.slave.driver().opened_slaves().load(Ordering::Relaxed) == 0 {
|
||||
events |= IoEvents::HUP;
|
||||
}
|
||||
|
||||
events
|
||||
}
|
||||
}
|
||||
|
|
@ -95,7 +100,7 @@ impl FileIo for PtyMaster {
|
|||
|
||||
// TODO: Add support for non-blocking mode and timeout
|
||||
let len = self.wait_events(IoEvents::OUT, None, || {
|
||||
Ok(self.slave.push_input(&buf[..write_len])?)
|
||||
self.slave.push_input(&buf[..write_len])
|
||||
})?;
|
||||
self.slave.driver().pollee().invalidate();
|
||||
Ok(len)
|
||||
|
|
@ -111,7 +116,7 @@ impl FileIo for PtyMaster {
|
|||
| IoctlCmd::TIOCSWINSZ
|
||||
| IoctlCmd::TIOCGPTN => return self.slave.ioctl(cmd, arg),
|
||||
IoctlCmd::TIOCSPTLCK => {
|
||||
// TODO: Lock or unlock the PTY.
|
||||
// TODO: Lock or unlock the pty.
|
||||
}
|
||||
IoctlCmd::TIOCGPTPEER => {
|
||||
let current_task = Task::current().unwrap();
|
||||
|
|
@ -164,5 +169,8 @@ impl Drop for PtyMaster {
|
|||
|
||||
let index = self.slave.index();
|
||||
devpts.remove_slave(index);
|
||||
|
||||
self.slave.driver().set_master_closed();
|
||||
self.slave.notify_hup();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@ use crate::{
|
|||
};
|
||||
|
||||
mod driver;
|
||||
mod file;
|
||||
mod master;
|
||||
|
||||
pub use driver::PtySlave;
|
||||
pub use file::PtySlaveFile;
|
||||
pub use master::PtyMaster;
|
||||
use spin::Once;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,17 +2,7 @@
|
|||
|
||||
use aster_console::AnyConsoleDevice;
|
||||
|
||||
use crate::prelude::{Errno, Error};
|
||||
|
||||
/// An error indicating that no characters can be pushed because the buffer is full.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PushCharError;
|
||||
|
||||
impl From<PushCharError> for Error {
|
||||
fn from(_value: PushCharError) -> Self {
|
||||
Error::with_message(Errno::EAGAIN, "the buffer is full")
|
||||
}
|
||||
}
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A TTY driver.
|
||||
///
|
||||
|
|
@ -26,8 +16,8 @@ pub trait TtyDriver: Send + Sync + 'static {
|
|||
/// Pushes characters into the output buffer.
|
||||
///
|
||||
/// This method returns the number of bytes pushed or fails with an error if no bytes can be
|
||||
/// pushed because the buffer is full.
|
||||
fn push_output(&self, chs: &[u8]) -> core::result::Result<usize, PushCharError>;
|
||||
/// pushed.
|
||||
fn push_output(&self, chs: &[u8]) -> Result<usize>;
|
||||
|
||||
/// Drains the output buffer.
|
||||
fn drain_output(&self);
|
||||
|
|
@ -43,6 +33,12 @@ pub trait TtyDriver: Send + Sync + 'static {
|
|||
/// This method should return `false` if the output buffer is full.
|
||||
fn can_push(&self) -> bool;
|
||||
|
||||
/// Returns whether the TTY is closed.
|
||||
///
|
||||
/// For a pty slave, this method returns `true` only if its associated master has been closed.
|
||||
/// For other TTY types, this method returns `false`.
|
||||
fn is_closed(&self) -> bool;
|
||||
|
||||
/// Notifies that the input buffer now has room for new characters.
|
||||
///
|
||||
/// This method should be called when the state of [`Tty::can_push`] changes from `false` to
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@
|
|||
|
||||
use ostd::const_assert;
|
||||
|
||||
use super::{
|
||||
termio::{CCtrlCharId, CTermios, CWinSize},
|
||||
PushCharError,
|
||||
};
|
||||
use super::termio::{CCtrlCharId, CTermios, CWinSize};
|
||||
use crate::{
|
||||
prelude::*,
|
||||
process::signal::{
|
||||
|
|
@ -100,7 +97,7 @@ impl LineDiscipline {
|
|||
ch: u8,
|
||||
mut signal_callback: F1,
|
||||
echo_callback: F2,
|
||||
) -> core::result::Result<(), PushCharError> {
|
||||
) -> Result<()> {
|
||||
let ch = if self.termios.contains_icrnl() && ch == b'\r' {
|
||||
b'\n'
|
||||
} else {
|
||||
|
|
@ -122,7 +119,7 @@ impl LineDiscipline {
|
|||
// If the buffer is full, we should not push the character into the buffer. The caller
|
||||
// can silently ignore the error (if the input comes from the keyboard) or block the
|
||||
// user space (if the input comes from the pseduoterminal master).
|
||||
return Err(PushCharError);
|
||||
return_errno_with_message!(Errno::EAGAIN, "the buffer is full");
|
||||
}
|
||||
|
||||
// Raw mode
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ mod n_tty;
|
|||
mod termio;
|
||||
|
||||
pub use device::TtyDevice;
|
||||
pub use driver::{PushCharError, TtyDriver};
|
||||
pub use driver::TtyDriver;
|
||||
pub(super) use n_tty::init;
|
||||
pub use n_tty::{iter_n_tty, system_console};
|
||||
|
||||
|
|
@ -100,6 +100,11 @@ impl<D> Tty<D> {
|
|||
pub fn notify_output(&self) {
|
||||
self.pollee.notify(IoEvents::OUT);
|
||||
}
|
||||
|
||||
/// Notifies that the other end has been closed.
|
||||
pub fn notify_hup(&self) {
|
||||
self.pollee.notify(IoEvents::HUP);
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: TtyDriver> Tty<D> {
|
||||
|
|
@ -107,7 +112,7 @@ impl<D: TtyDriver> Tty<D> {
|
|||
///
|
||||
/// This method returns the number of bytes pushed or fails with an error if no bytes can be
|
||||
/// pushed because the buffer is full.
|
||||
pub fn push_input(&self, chs: &[u8]) -> core::result::Result<usize, PushCharError> {
|
||||
pub fn push_input(&self, chs: &[u8]) -> Result<usize> {
|
||||
let mut ldisc = self.ldisc.lock();
|
||||
let mut echo = self.driver.echo_callback();
|
||||
|
||||
|
|
@ -123,7 +128,7 @@ impl<D: TtyDriver> Tty<D> {
|
|||
&mut echo,
|
||||
);
|
||||
if res.is_err() && len == 0 {
|
||||
return Err(PushCharError);
|
||||
return_errno_with_message!(Errno::EAGAIN, "the line discipline is full");
|
||||
} else if res.is_err() {
|
||||
break;
|
||||
} else {
|
||||
|
|
@ -146,6 +151,10 @@ impl<D: TtyDriver> Tty<D> {
|
|||
events |= IoEvents::OUT;
|
||||
}
|
||||
|
||||
if self.driver.is_closed() {
|
||||
events |= IoEvents::HUP;
|
||||
}
|
||||
|
||||
events
|
||||
}
|
||||
}
|
||||
|
|
@ -233,6 +242,10 @@ impl<D: TtyDriver> Pollable for Tty<D> {
|
|||
|
||||
impl<D: TtyDriver> FileIo for Tty<D> {
|
||||
fn read(&self, writer: &mut VmWriter, _status_flags: StatusFlags) -> Result<usize> {
|
||||
if self.driver.is_closed() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
self.job_control.wait_until_in_foreground()?;
|
||||
|
||||
// TODO: Add support for non-blocking mode and timeout
|
||||
|
|
@ -253,7 +266,7 @@ impl<D: TtyDriver> FileIo for Tty<D> {
|
|||
|
||||
// TODO: Add support for non-blocking mode and timeout
|
||||
let len = self.wait_events(IoEvents::OUT, None, || {
|
||||
Ok(self.driver.push_output(&buf[..write_len])?)
|
||||
self.driver.push_output(&buf[..write_len])
|
||||
})?;
|
||||
self.pollee.invalidate();
|
||||
Ok(len)
|
||||
|
|
@ -304,6 +317,10 @@ impl<D: TtyDriver> FileIo for Tty<D> {
|
|||
current_userspace!().write_val(arg, &idx)?;
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
if self.driver().is_closed() {
|
||||
return_errno_with_message!(Errno::EIO, "the TTY is closed");
|
||||
}
|
||||
|
||||
let buffer_len = self.ldisc.lock().buffer_len() as u32;
|
||||
|
||||
current_userspace!().write_val(arg, &buffer_len)?;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc, vec};
|
||||
|
||||
use aster_console::AnyConsoleDevice;
|
||||
use ostd::mm::{Infallible, VmReader, VmWriter};
|
||||
use spin::Once;
|
||||
|
||||
use super::{PushCharError, Tty, TtyDriver};
|
||||
use super::{Tty, TtyDriver};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct ConsoleDriver {
|
||||
console: Arc<dyn AnyConsoleDevice>,
|
||||
}
|
||||
|
||||
impl TtyDriver for ConsoleDriver {
|
||||
fn push_output(&self, chs: &[u8]) -> core::result::Result<usize, PushCharError> {
|
||||
fn push_output(&self, chs: &[u8]) -> Result<usize> {
|
||||
self.console.send(chs);
|
||||
Ok(chs.len())
|
||||
}
|
||||
|
|
@ -28,6 +27,10 @@ impl TtyDriver for ConsoleDriver {
|
|||
true
|
||||
}
|
||||
|
||||
fn is_closed(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn notify_input(&self) {}
|
||||
|
||||
fn console(&self) -> Option<&dyn AnyConsoleDevice> {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use super::*;
|
||||
use crate::{
|
||||
device::PtySlave,
|
||||
device::{PtySlave, PtySlaveFile},
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
inode_handle::FileIo,
|
||||
|
|
@ -152,6 +152,6 @@ impl Inode for PtySlaveInode {
|
|||
access_mode: AccessMode,
|
||||
status_flags: StatusFlags,
|
||||
) -> Option<Result<Arc<dyn FileIo>>> {
|
||||
self.device.open()
|
||||
Some(Ok(Arc::new(PtySlaveFile::new(self.device.clone()))))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use ostd::sync::{LocalIrqDisabled, WaitQueue};
|
|||
use super::{ProcessGroup, Session};
|
||||
use crate::prelude::*;
|
||||
|
||||
/// The job control for terminals like TTY and PTY.
|
||||
/// The job control for terminals like TTY and pty.
|
||||
///
|
||||
/// This structure is used to support the shell job control, allowing users to
|
||||
/// run commands in the foreground or in the background. To achieve this, this
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
|
||||
/// A terminal.
|
||||
///
|
||||
/// We currently support two kinds of terminal, the TTY and PTY. They're associated with a
|
||||
/// We currently support two kinds of terminal, the TTY and pty. They're associated with a
|
||||
/// `JobControl` to track the session and the foreground process group.
|
||||
pub trait Terminal: FileIo + Device {
|
||||
/// Returns the job control of the terminal.
|
||||
|
|
|
|||
Loading…
Reference in New Issue