mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-17 02:26:46 +00:00
Refactor the pty implementation
This commit is contained in:
parent
40056f0692
commit
038c19b693
@ -15,8 +15,6 @@ int main() {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("master fd: %d\n", master);
|
||||
printf("slave fd: %d\n", slave);
|
||||
printf("slave name: %s\n", name);
|
||||
|
||||
// Set pty slave terminal attributes
|
||||
@ -33,7 +31,7 @@ int main() {
|
||||
char buf[256];
|
||||
ssize_t n = read(master, buf, sizeof(buf));
|
||||
if (n > 0) {
|
||||
printf("read %ld bytes from slave: %.*s\n", n, (int)n, buf);
|
||||
printf("read %ld bytes from slave: %.*s", n, (int)n, buf);
|
||||
}
|
||||
|
||||
// Write to pty master
|
||||
@ -43,7 +41,7 @@ int main() {
|
||||
char nbuf[256];
|
||||
ssize_t nn = read(slave, nbuf, sizeof(nbuf));
|
||||
if (nn > 0) {
|
||||
printf("read %ld bytes from master: %.*s\n", nn, (int)nn, nbuf);
|
||||
printf("read %ld bytes from master: %.*s", nn, (int)nn, nbuf);
|
||||
}
|
||||
|
||||
close(master);
|
||||
|
@ -6,7 +6,7 @@ SCRIPT_DIR=/regression
|
||||
cd ${SCRIPT_DIR}/..
|
||||
|
||||
echo "Running tests......"
|
||||
tests="hello_world/hello_world fork/fork execve/execve fork_c/fork signal_c/signal_test pthread/pthread_test hello_pie/hello"
|
||||
tests="hello_world/hello_world fork/fork execve/execve fork_c/fork signal_c/signal_test pthread/pthread_test hello_pie/hello pty/open_pty"
|
||||
for testcase in ${tests}
|
||||
do
|
||||
echo "Running test ${testcase}......"
|
||||
|
@ -7,6 +7,7 @@ mod zero;
|
||||
|
||||
use crate::fs::device::{add_node, Device, DeviceId, DeviceType};
|
||||
use crate::prelude::*;
|
||||
pub use pty::new_pty_pair;
|
||||
pub use pty::{PtyMaster, PtySlave};
|
||||
pub use random::Random;
|
||||
pub use urandom::Urandom;
|
||||
|
@ -1,260 +0,0 @@
|
||||
use crate::{
|
||||
fs::{
|
||||
file_handle::FileLike,
|
||||
fs_resolver::FsPath,
|
||||
utils::{AccessMode, Inode, InodeMode, IoEvents, IoctlCmd, Poller},
|
||||
},
|
||||
prelude::*,
|
||||
util::{read_val_from_user, write_val_to_user},
|
||||
};
|
||||
use alloc::format;
|
||||
use jinux_frame::sync::SpinLock;
|
||||
use ringbuf::{ring_buffer::RbBase, HeapRb, Rb};
|
||||
|
||||
use crate::{device::tty::line_discipline::LineDiscipline, fs::utils::Pollee};
|
||||
|
||||
use super::slave::PtySlave;
|
||||
|
||||
const PTS_DIR: &str = "/dev/pts";
|
||||
const BUFFER_CAPACITY: usize = 4096;
|
||||
|
||||
/// Pesudo terminal master.
|
||||
/// Internally, it has two buffers.
|
||||
/// One is inside ldisc, which is written by master and read by slave,
|
||||
/// the other is a ring buffer, which is written by slave and read by master.
|
||||
pub struct PtyMaster {
|
||||
ptmx: Arc<dyn Inode>,
|
||||
index: usize,
|
||||
ldisc: LineDiscipline,
|
||||
master_buffer: SpinLock<HeapRb<u8>>,
|
||||
/// The state of master buffer
|
||||
pollee: Pollee,
|
||||
}
|
||||
|
||||
impl PtyMaster {
|
||||
pub fn new_pair(index: u32, ptmx: Arc<dyn Inode>) -> Result<(Arc<PtyMaster>, Arc<PtySlave>)> {
|
||||
debug!("allocate pty index = {}", index);
|
||||
let master = Arc::new(PtyMaster {
|
||||
ptmx,
|
||||
index: index as usize,
|
||||
master_buffer: SpinLock::new(HeapRb::new(BUFFER_CAPACITY)),
|
||||
pollee: Pollee::new(IoEvents::OUT),
|
||||
ldisc: LineDiscipline::new(),
|
||||
});
|
||||
let slave = Arc::new(PtySlave::new(master.clone()));
|
||||
Ok((master, slave))
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
self.index
|
||||
}
|
||||
|
||||
pub fn ptmx(&self) -> &Arc<dyn Inode> {
|
||||
&self.ptmx
|
||||
}
|
||||
|
||||
pub(super) fn slave_push_char(&self, item: u8) -> Result<()> {
|
||||
let mut buf = self.master_buffer.lock_irq_disabled();
|
||||
buf.push_overwrite(item);
|
||||
self.update_state(&buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn slave_read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.ldisc.read(buf)
|
||||
}
|
||||
|
||||
pub(super) fn slave_poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let poll_out_mask = mask & IoEvents::OUT;
|
||||
let poll_in_mask = mask & IoEvents::IN;
|
||||
|
||||
loop {
|
||||
let mut poll_status = IoEvents::empty();
|
||||
|
||||
if !poll_in_mask.is_empty() {
|
||||
let poll_in_status = self.ldisc.poll(poll_in_mask, poller);
|
||||
poll_status |= poll_in_status;
|
||||
}
|
||||
|
||||
if !poll_out_mask.is_empty() {
|
||||
let poll_out_status = self.pollee.poll(poll_out_mask, poller);
|
||||
poll_status |= poll_out_status;
|
||||
}
|
||||
|
||||
if !poll_status.is_empty() || poller.is_none() {
|
||||
return poll_status;
|
||||
}
|
||||
|
||||
poller.unwrap().wait();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn slave_buf_len(&self) -> usize {
|
||||
self.ldisc.buffer_len()
|
||||
}
|
||||
|
||||
fn update_state(&self, buf: &HeapRb<u8>) {
|
||||
if buf.is_empty() {
|
||||
self.pollee.del_events(IoEvents::IN)
|
||||
} else {
|
||||
self.pollee.add_events(IoEvents::IN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileLike for PtyMaster {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
// TODO: deal with nonblocking read
|
||||
if buf.len() == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let poller = Poller::new();
|
||||
loop {
|
||||
let mut master_buf = self.master_buffer.lock_irq_disabled();
|
||||
|
||||
if master_buf.is_empty() {
|
||||
self.update_state(&master_buf);
|
||||
let events = self.pollee.poll(IoEvents::IN, Some(&poller));
|
||||
if !events.contains(IoEvents::IN) {
|
||||
drop(master_buf);
|
||||
poller.wait();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let read_len = master_buf.len().min(buf.len());
|
||||
master_buf.pop_slice(&mut buf[..read_len]);
|
||||
self.update_state(&master_buf);
|
||||
return Ok(read_len);
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
let mut master_buf = self.master_buffer.lock();
|
||||
|
||||
for item in buf {
|
||||
self.ldisc.push_char(*item, |content| {
|
||||
for byte in content.as_bytes() {
|
||||
master_buf.push_overwrite(*byte);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
self.update_state(&master_buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS => {
|
||||
let termios = self.ldisc.termios();
|
||||
write_val_to_user(arg, &termios)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TCSETS => {
|
||||
let termios = read_val_from_user(arg)?;
|
||||
self.ldisc.set_termios(termios);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSPTLCK => {
|
||||
// TODO: lock/unlock pty
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGPTN => {
|
||||
let idx = self.index() as u32;
|
||||
write_val_to_user(arg, &idx)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGPTPEER => {
|
||||
let current = current!();
|
||||
|
||||
// TODO: deal with open options
|
||||
let slave = {
|
||||
let slave_name = format!("{}/{}", PTS_DIR, self.index());
|
||||
let fs_path = FsPath::try_from(slave_name.as_str())?;
|
||||
|
||||
let inode_handle = {
|
||||
let fs = current.fs().read();
|
||||
let flags = AccessMode::O_RDWR as u32;
|
||||
let mode = (InodeMode::S_IRUSR | InodeMode::S_IWUSR).bits();
|
||||
fs.open(&fs_path, flags, mode)?
|
||||
};
|
||||
Arc::new(inode_handle)
|
||||
};
|
||||
|
||||
let fd = {
|
||||
let mut file_table = current.file_table().lock();
|
||||
file_table.insert(slave)
|
||||
};
|
||||
Ok(fd)
|
||||
}
|
||||
IoctlCmd::TIOCGWINSZ => {
|
||||
let winsize = self.ldisc.window_size();
|
||||
write_val_to_user(arg, &winsize)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSWINSZ => {
|
||||
let winsize = read_val_from_user(arg)?;
|
||||
self.ldisc.set_window_size(winsize);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSCTTY => {
|
||||
// TODO: reimplement when adding session.
|
||||
let foreground = {
|
||||
let current = current!();
|
||||
let process_group = current.process_group().lock();
|
||||
process_group.clone()
|
||||
};
|
||||
self.ldisc.set_fg(foreground);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGPGRP => {
|
||||
let Some(fg_pgid) = self.ldisc.fg_pgid() else {
|
||||
return_errno_with_message!(
|
||||
Errno::ESRCH,
|
||||
"the foreground process group does not exist"
|
||||
);
|
||||
};
|
||||
write_val_to_user(arg, &fg_pgid)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCNOTTY => {
|
||||
// TODO: reimplement when adding session.
|
||||
self.ldisc.set_fg(Weak::new());
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
let len = self.master_buffer.lock().len() as i32;
|
||||
write_val_to_user(arg, &len)?;
|
||||
Ok(0)
|
||||
}
|
||||
_ => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let poll_out_mask = mask & IoEvents::OUT;
|
||||
let poll_in_mask = mask & IoEvents::IN;
|
||||
|
||||
loop {
|
||||
let mut poll_status = IoEvents::empty();
|
||||
|
||||
if !poll_in_mask.is_empty() {
|
||||
let poll_in_status = self.pollee.poll(poll_in_mask, poller);
|
||||
poll_status |= poll_in_status;
|
||||
}
|
||||
|
||||
if !poll_out_mask.is_empty() {
|
||||
let poll_out_status = self.ldisc.poll(poll_out_mask, poller);
|
||||
poll_status |= poll_out_status;
|
||||
}
|
||||
|
||||
if !poll_status.is_empty() || poller.is_none() {
|
||||
return poll_status;
|
||||
}
|
||||
|
||||
poller.unwrap().wait();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
mod master;
|
||||
mod slave;
|
||||
|
||||
pub use master::PtyMaster;
|
||||
pub use slave::PtySlave;
|
||||
|
||||
use crate::fs::{
|
||||
devpts::DevPts,
|
||||
fs_resolver::{FsPath, FsResolver},
|
||||
utils::{InodeMode, InodeType},
|
||||
};
|
||||
use crate::fs::devpts::DevPts;
|
||||
use crate::fs::fs_resolver::{FsPath, FsResolver};
|
||||
use crate::fs::utils::{Dentry, Inode, InodeMode, InodeType};
|
||||
use crate::prelude::*;
|
||||
|
||||
mod pty;
|
||||
|
||||
pub use pty::{PtyMaster, PtySlave};
|
||||
use spin::Once;
|
||||
|
||||
static DEV_PTS: Once<Arc<Dentry>> = Once::new();
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
let fs = FsResolver::new();
|
||||
|
||||
@ -19,6 +18,8 @@ pub fn init() -> Result<()> {
|
||||
let devpts = dev.create("pts", InodeType::Dir, InodeMode::from_bits_truncate(0o755))?;
|
||||
devpts.mount(DevPts::new())?;
|
||||
|
||||
DEV_PTS.call_once(|| devpts);
|
||||
|
||||
// Create the "ptmx" symlink.
|
||||
let ptmx = dev.create(
|
||||
"ptmx",
|
||||
@ -28,3 +29,10 @@ pub fn init() -> Result<()> {
|
||||
ptmx.write_link("pts/ptmx")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn new_pty_pair(index: u32, ptmx: Arc<dyn Inode>) -> Result<(Arc<PtyMaster>, Arc<PtySlave>)> {
|
||||
debug!("pty index = {}", index);
|
||||
let master = Arc::new(PtyMaster::new(ptmx, index));
|
||||
let slave = Arc::new(PtySlave::new(master.clone()));
|
||||
Ok((master, slave))
|
||||
}
|
||||
|
312
services/libs/jinux-std/src/device/pty/pty.rs
Normal file
312
services/libs/jinux-std/src/device/pty/pty.rs
Normal file
@ -0,0 +1,312 @@
|
||||
use alloc::format;
|
||||
use ringbuf::{ring_buffer::RbBase, HeapRb, Rb};
|
||||
|
||||
use crate::device::tty::line_discipline::LineDiscipline;
|
||||
use crate::fs::device::{Device, DeviceId, DeviceType};
|
||||
use crate::fs::file_handle::FileLike;
|
||||
use crate::fs::fs_resolver::FsPath;
|
||||
use crate::fs::utils::{AccessMode, Inode, InodeMode, IoEvents, IoctlCmd, Pollee, Poller};
|
||||
use crate::prelude::*;
|
||||
use crate::util::{read_val_from_user, write_val_to_user};
|
||||
|
||||
const PTS_DIR: &str = "/dev/pts";
|
||||
const BUFFER_CAPACITY: usize = 4096;
|
||||
|
||||
/// Pesudo terminal master.
|
||||
/// Internally, it has two buffers.
|
||||
/// One is inside ldisc, which is written by master and read by slave,
|
||||
/// the other is a ring buffer, which is written by slave and read by master.
|
||||
pub struct PtyMaster {
|
||||
ptmx: Arc<dyn Inode>,
|
||||
index: u32,
|
||||
output: LineDiscipline,
|
||||
input: SpinLock<HeapRb<u8>>,
|
||||
/// The state of input buffer
|
||||
pollee: Pollee,
|
||||
}
|
||||
|
||||
impl PtyMaster {
|
||||
pub fn new(ptmx: Arc<dyn Inode>, index: u32) -> Self {
|
||||
Self {
|
||||
ptmx,
|
||||
index,
|
||||
output: LineDiscipline::new(),
|
||||
input: SpinLock::new(HeapRb::new(BUFFER_CAPACITY)),
|
||||
pollee: Pollee::new(IoEvents::OUT),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u32 {
|
||||
self.index
|
||||
}
|
||||
|
||||
pub fn ptmx(&self) -> &Arc<dyn Inode> {
|
||||
&self.ptmx
|
||||
}
|
||||
|
||||
pub(super) fn slave_push_byte(&self, byte: u8) {
|
||||
let mut input = self.input.lock_irq_disabled();
|
||||
input.push_overwrite(byte);
|
||||
self.update_state(&input);
|
||||
}
|
||||
|
||||
pub(super) fn slave_read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.output.read(buf)
|
||||
}
|
||||
|
||||
pub(super) fn slave_poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let mut poll_status = IoEvents::empty();
|
||||
|
||||
let poll_in_mask = mask & IoEvents::IN;
|
||||
if !poll_in_mask.is_empty() {
|
||||
let poll_in_status = self.output.poll(poll_in_mask, poller);
|
||||
poll_status |= poll_in_status;
|
||||
}
|
||||
|
||||
let poll_out_mask = mask & IoEvents::OUT;
|
||||
if !poll_out_mask.is_empty() {
|
||||
let poll_out_status = self.pollee.poll(poll_out_mask, poller);
|
||||
poll_status |= poll_out_status;
|
||||
}
|
||||
|
||||
poll_status
|
||||
}
|
||||
|
||||
pub(super) fn slave_buf_len(&self) -> usize {
|
||||
self.output.buffer_len()
|
||||
}
|
||||
|
||||
fn update_state(&self, buf: &HeapRb<u8>) {
|
||||
if buf.is_empty() {
|
||||
self.pollee.del_events(IoEvents::IN)
|
||||
} else {
|
||||
self.pollee.add_events(IoEvents::IN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileLike for PtyMaster {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
// TODO: deal with nonblocking read
|
||||
if buf.len() == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let poller = Poller::new();
|
||||
loop {
|
||||
let mut input = self.input.lock_irq_disabled();
|
||||
|
||||
if input.is_empty() {
|
||||
let events = self.pollee.poll(IoEvents::IN, Some(&poller));
|
||||
|
||||
if events.contains(IoEvents::ERR) {
|
||||
return_errno_with_message!(Errno::EACCES, "unexpected err");
|
||||
}
|
||||
|
||||
if events.is_empty() {
|
||||
drop(input);
|
||||
poller.wait();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let read_len = input.len().min(buf.len());
|
||||
input.pop_slice(&mut buf[..read_len]);
|
||||
self.update_state(&input);
|
||||
return Ok(read_len);
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
let mut input = self.input.lock();
|
||||
|
||||
for character in buf {
|
||||
self.output.push_char(*character, |content| {
|
||||
for byte in content.as_bytes() {
|
||||
input.push_overwrite(*byte);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
self.update_state(&input);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS => {
|
||||
let termios = self.output.termios();
|
||||
write_val_to_user(arg, &termios)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TCSETS => {
|
||||
let termios = read_val_from_user(arg)?;
|
||||
self.output.set_termios(termios);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSPTLCK => {
|
||||
// TODO: lock/unlock pty
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGPTN => {
|
||||
let idx = self.index();
|
||||
write_val_to_user(arg, &idx)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGPTPEER => {
|
||||
let current = current!();
|
||||
|
||||
// TODO: deal with open options
|
||||
let slave = {
|
||||
let slave_name = {
|
||||
let devpts_path = super::DEV_PTS.get().unwrap().abs_path();
|
||||
format!("{}/{}", devpts_path, self.index())
|
||||
};
|
||||
|
||||
let fs_path = FsPath::try_from(slave_name.as_str())?;
|
||||
|
||||
let inode_handle = {
|
||||
let fs = current.fs().read();
|
||||
let flags = AccessMode::O_RDWR as u32;
|
||||
let mode = (InodeMode::S_IRUSR | InodeMode::S_IWUSR).bits();
|
||||
fs.open(&fs_path, flags, mode)?
|
||||
};
|
||||
Arc::new(inode_handle)
|
||||
};
|
||||
|
||||
let fd = {
|
||||
let mut file_table = current.file_table().lock();
|
||||
file_table.insert(slave)
|
||||
};
|
||||
Ok(fd)
|
||||
}
|
||||
IoctlCmd::TIOCGWINSZ => {
|
||||
let winsize = self.output.window_size();
|
||||
write_val_to_user(arg, &winsize)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSWINSZ => {
|
||||
let winsize = read_val_from_user(arg)?;
|
||||
self.output.set_window_size(winsize);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSCTTY => {
|
||||
// TODO: reimplement when adding session.
|
||||
let foreground = {
|
||||
let current = current!();
|
||||
let process_group = current.process_group().lock();
|
||||
process_group.clone()
|
||||
};
|
||||
self.output.set_fg(foreground);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGPGRP => {
|
||||
let Some(fg_pgid) = self.output.fg_pgid() else {
|
||||
return_errno_with_message!(
|
||||
Errno::ESRCH,
|
||||
"the foreground process group does not exist"
|
||||
);
|
||||
};
|
||||
write_val_to_user(arg, &fg_pgid)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCNOTTY => {
|
||||
// TODO: reimplement when adding session.
|
||||
self.output.set_fg(Weak::new());
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
let len = self.input.lock().len() as i32;
|
||||
write_val_to_user(arg, &len)?;
|
||||
Ok(0)
|
||||
}
|
||||
_ => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let mut poll_status = IoEvents::empty();
|
||||
|
||||
let poll_in_mask = mask & IoEvents::IN;
|
||||
if !poll_in_mask.is_empty() {
|
||||
let poll_in_status = self.pollee.poll(poll_in_mask, poller);
|
||||
poll_status |= poll_in_status;
|
||||
}
|
||||
|
||||
let poll_out_mask = mask & IoEvents::OUT;
|
||||
if !poll_out_mask.is_empty() {
|
||||
let poll_out_status = self.output.poll(poll_out_mask, poller);
|
||||
poll_status |= poll_out_status;
|
||||
}
|
||||
|
||||
poll_status
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PtySlave(Arc<PtyMaster>);
|
||||
|
||||
impl PtySlave {
|
||||
pub fn new(master: Arc<PtyMaster>) -> Self {
|
||||
PtySlave(master)
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u32 {
|
||||
self.0.index()
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for PtySlave {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> crate::fs::device::DeviceId {
|
||||
DeviceId::new(88, self.index() as u32)
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.0.slave_read(buf)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
for ch in buf {
|
||||
// do we need to add '\r' here?
|
||||
if *ch == b'\n' {
|
||||
self.0.slave_push_byte(b'\r');
|
||||
self.0.slave_push_byte(b'\n');
|
||||
} else {
|
||||
self.0.slave_push_byte(*ch);
|
||||
}
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS
|
||||
| IoctlCmd::TCSETS
|
||||
| IoctlCmd::TIOCGPGRP
|
||||
| IoctlCmd::TIOCGPTN
|
||||
| IoctlCmd::TIOCGWINSZ
|
||||
| IoctlCmd::TIOCSWINSZ => self.0.ioctl(cmd, arg),
|
||||
IoctlCmd::TIOCSCTTY => {
|
||||
// TODO:
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCNOTTY => {
|
||||
// TODO:
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
let buffer_len = self.0.slave_buf_len() as i32;
|
||||
write_val_to_user(arg, &buffer_len)?;
|
||||
Ok(0)
|
||||
}
|
||||
_ => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.0.slave_poll(mask, poller)
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::fs::device::{Device, DeviceId, DeviceType};
|
||||
use crate::fs::file_handle::FileLike;
|
||||
use crate::fs::utils::{IoEvents, IoctlCmd, Poller};
|
||||
use crate::prelude::*;
|
||||
use crate::util::write_val_to_user;
|
||||
|
||||
use super::master::PtyMaster;
|
||||
|
||||
pub struct PtySlave(Arc<PtyMaster>);
|
||||
|
||||
impl PtySlave {
|
||||
pub fn new(master: Arc<PtyMaster>) -> Self {
|
||||
PtySlave(master)
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
self.0.index()
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for PtySlave {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> crate::fs::device::DeviceId {
|
||||
DeviceId::new(88, self.index() as u32)
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.0.slave_read(buf)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
for ch in buf {
|
||||
// do we need to add '\r' here?
|
||||
if *ch == b'\n' {
|
||||
self.0.slave_push_char(b'\r')?;
|
||||
self.0.slave_push_char(b'\n')?;
|
||||
} else {
|
||||
self.0.slave_push_char(*ch)?;
|
||||
}
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS
|
||||
| IoctlCmd::TCSETS
|
||||
| IoctlCmd::TIOCGPGRP
|
||||
| IoctlCmd::TIOCGPTN
|
||||
| IoctlCmd::TIOCGWINSZ
|
||||
| IoctlCmd::TIOCSWINSZ => self.0.ioctl(cmd, arg),
|
||||
IoctlCmd::TIOCSCTTY => {
|
||||
// TODO:
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCNOTTY => {
|
||||
// TODO:
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
let buffer_len = self.0.slave_buf_len() as i32;
|
||||
write_val_to_user(arg, &buffer_len)?;
|
||||
Ok(0)
|
||||
}
|
||||
_ => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.0.slave_poll(mask, poller)
|
||||
}
|
||||
}
|
@ -14,27 +14,27 @@ bitflags! {
|
||||
#[repr(C)]
|
||||
pub struct C_IFLAGS: u32 {
|
||||
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits-common.h
|
||||
const IGNBRK = 0x001; /* Ignore break condition */
|
||||
const BRKINT = 0x002; /* Signal interrupt on break */
|
||||
const IGNPAR = 0x004; /* Ignore characters with parity errors */
|
||||
const PARMRK = 0x008; /* Mark parity and framing errors */
|
||||
const INPCK = 0x010; /* Enable input parity check */
|
||||
const ISTRIP = 0x020; /* Strip 8th bit off characters */
|
||||
const INLCR = 0x040; /* Map NL to CR on input */
|
||||
const IGNCR = 0x080; /* Ignore CR */
|
||||
const ICRNL = 0x100; /* Map CR to NL on input */
|
||||
const IXANY = 0x800; /* Any character will restart after stop */
|
||||
const IGNBRK = 0x001; /* Ignore break condition */
|
||||
const BRKINT = 0x002; /* Signal interrupt on break */
|
||||
const IGNPAR = 0x004; /* Ignore characters with parity errors */
|
||||
const PARMRK = 0x008; /* Mark parity and framing errors */
|
||||
const INPCK = 0x010; /* Enable input parity check */
|
||||
const ISTRIP = 0x020; /* Strip 8th bit off characters */
|
||||
const INLCR = 0x040; /* Map NL to CR on input */
|
||||
const IGNCR = 0x080; /* Ignore CR */
|
||||
const ICRNL = 0x100; /* Map CR to NL on input */
|
||||
const IXANY = 0x800; /* Any character will restart after stop */
|
||||
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits.h
|
||||
const IUCLC = 0x0200;
|
||||
const IXON = 0x0400;
|
||||
const IXOFF = 0x1000;
|
||||
const IMAXBEL = 0x2000;
|
||||
const IUTF8 = 0x4000;
|
||||
const IUCLC = 0x0200;
|
||||
const IXON = 0x0400;
|
||||
const IXOFF = 0x1000;
|
||||
const IMAXBEL = 0x2000;
|
||||
const IUTF8 = 0x4000;
|
||||
}
|
||||
}
|
||||
|
||||
impl C_IFLAGS {
|
||||
pub fn new_default() -> Self {
|
||||
impl Default for C_IFLAGS {
|
||||
fn default() -> Self {
|
||||
C_IFLAGS::ICRNL | C_IFLAGS::IXON
|
||||
}
|
||||
}
|
||||
@ -43,19 +43,19 @@ bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Pod)]
|
||||
pub struct C_OFLAGS: u32 {
|
||||
const OPOST = 0x1 << 0; /* Perform output processing */
|
||||
const OLCUC = 0x1 << 1;
|
||||
const ONLCR = 0x1 << 2;
|
||||
const OCRNL = 0x1 << 3;
|
||||
const ONOCR = 0x1 << 4;
|
||||
const ONLRET= 0x1 << 5;
|
||||
const OFILL = 0x1 << 6;
|
||||
const OFDEL = 0x1 << 7;
|
||||
const OPOST = 1 << 0; /* Perform output processing */
|
||||
const OLCUC = 1 << 1;
|
||||
const ONLCR = 1 << 2;
|
||||
const OCRNL = 1 << 3;
|
||||
const ONOCR = 1 << 4;
|
||||
const ONLRET = 1 << 5;
|
||||
const OFILL = 1 << 6;
|
||||
const OFDEL = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
impl C_OFLAGS {
|
||||
pub fn new_default() -> Self {
|
||||
impl Default for C_OFLAGS {
|
||||
fn default() -> Self {
|
||||
C_OFLAGS::OPOST | C_OFLAGS::ONLCR
|
||||
}
|
||||
}
|
||||
@ -64,14 +64,16 @@ impl C_OFLAGS {
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
pub struct C_CFLAGS(u32);
|
||||
|
||||
impl C_CFLAGS {
|
||||
pub fn new_default() -> Self {
|
||||
impl Default for C_CFLAGS {
|
||||
fn default() -> Self {
|
||||
let cbaud = C_CFLAGS_BAUD::B38400 as u32;
|
||||
let csize = C_CFLAGS_CSIZE::CS8 as u32;
|
||||
let c_cflags = cbaud | csize | CREAD;
|
||||
Self(c_cflags)
|
||||
}
|
||||
}
|
||||
|
||||
impl C_CFLAGS {
|
||||
pub fn cbaud(&self) -> Result<C_CFLAGS_BAUD> {
|
||||
let cbaud = self.0 & CBAUD_MASK;
|
||||
Ok(C_CFLAGS_BAUD::try_from(cbaud)?)
|
||||
@ -125,27 +127,27 @@ bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Pod)]
|
||||
pub struct C_LFLAGS: u32 {
|
||||
const ISIG = 0x00001;
|
||||
const ICANON= 0x00002;
|
||||
const XCASE = 0x00004;
|
||||
const ECHO = 0x00008;
|
||||
const ECHOE = 0x00010;
|
||||
const ECHOK = 0x00020;
|
||||
const ECHONL= 0x00040;
|
||||
const NOFLSH= 0x00080;
|
||||
const TOSTOP= 0x00100;
|
||||
const ECHOCTL= 0x00200;
|
||||
const ECHOPRT= 0x00400;
|
||||
const ECHOKE= 0x00800;
|
||||
const FLUSHO= 0x01000;
|
||||
const PENDIN= 0x04000;
|
||||
const IEXTEN= 0x08000;
|
||||
const EXTPROC= 0x10000;
|
||||
const ISIG = 0x00001;
|
||||
const ICANON = 0x00002;
|
||||
const XCASE = 0x00004;
|
||||
const ECHO = 0x00008;
|
||||
const ECHOE = 0x00010;
|
||||
const ECHOK = 0x00020;
|
||||
const ECHONL = 0x00040;
|
||||
const NOFLSH = 0x00080;
|
||||
const TOSTOP = 0x00100;
|
||||
const ECHOCTL = 0x00200;
|
||||
const ECHOPRT = 0x00400;
|
||||
const ECHOKE = 0x00800;
|
||||
const FLUSHO = 0x01000;
|
||||
const PENDIN = 0x04000;
|
||||
const IEXTEN = 0x08000;
|
||||
const EXTPROC = 0x10000;
|
||||
}
|
||||
}
|
||||
|
||||
impl C_LFLAGS {
|
||||
pub fn new_default() -> Self {
|
||||
impl Default for C_LFLAGS {
|
||||
fn default() -> Self {
|
||||
C_LFLAGS::ICANON
|
||||
| C_LFLAGS::ECHO
|
||||
| C_LFLAGS::ISIG
|
||||
@ -159,7 +161,7 @@ impl C_LFLAGS {
|
||||
|
||||
/* c_cc characters index*/
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
#[derive(Debug, Clone, Copy, TryFromInt)]
|
||||
pub enum CC_C_CHAR {
|
||||
VINTR = 0,
|
||||
VQUIT = 1,
|
||||
@ -203,39 +205,6 @@ impl CC_C_CHAR {
|
||||
CC_C_CHAR::VEOL2 => '\0' as u8,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_usize(&self) -> usize {
|
||||
*self as usize
|
||||
}
|
||||
|
||||
pub fn from_char(item: u8) -> Result<Self> {
|
||||
if item == Self::VINTR.default_char() {
|
||||
return Ok(Self::VINTR);
|
||||
}
|
||||
if item == Self::VQUIT.default_char() {
|
||||
return Ok(Self::VQUIT);
|
||||
}
|
||||
if item == Self::VINTR.default_char() {
|
||||
return Ok(Self::VINTR);
|
||||
}
|
||||
if item == Self::VERASE.default_char() {
|
||||
return Ok(Self::VERASE);
|
||||
}
|
||||
if item == Self::VEOF.default_char() {
|
||||
return Ok(Self::VEOF);
|
||||
}
|
||||
if item == Self::VSTART.default_char() {
|
||||
return Ok(Self::VSTART);
|
||||
}
|
||||
if item == Self::VSTOP.default_char() {
|
||||
return Ok(Self::VSTOP);
|
||||
}
|
||||
if item == Self::VSUSP.default_char() {
|
||||
return Ok(Self::VSUSP);
|
||||
}
|
||||
|
||||
return_errno_with_message!(Errno::EINVAL, "Not a valid cc_char");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
@ -252,12 +221,12 @@ pub struct KernelTermios {
|
||||
impl KernelTermios {
|
||||
pub fn default() -> Self {
|
||||
let mut termios = Self {
|
||||
c_iflags: C_IFLAGS::new_default(),
|
||||
c_oflags: C_OFLAGS::new_default(),
|
||||
c_cflags: C_CFLAGS::new_default(),
|
||||
c_lflags: C_LFLAGS::new_default(),
|
||||
c_iflags: C_IFLAGS::default(),
|
||||
c_oflags: C_OFLAGS::default(),
|
||||
c_cflags: C_CFLAGS::default(),
|
||||
c_lflags: C_LFLAGS::default(),
|
||||
c_line: 0,
|
||||
c_cc: [0; KERNEL_NCCS],
|
||||
c_cc: [CcT::default(); KERNEL_NCCS],
|
||||
};
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VINTR) = CC_C_CHAR::VINTR.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VQUIT) = CC_C_CHAR::VQUIT.default_char();
|
||||
@ -280,11 +249,11 @@ impl KernelTermios {
|
||||
}
|
||||
|
||||
pub fn get_special_char(&self, cc_c_char: CC_C_CHAR) -> &CcT {
|
||||
&self.c_cc[cc_c_char.as_usize()]
|
||||
&self.c_cc[cc_c_char as usize]
|
||||
}
|
||||
|
||||
pub fn get_special_char_mut(&mut self, cc_c_char: CC_C_CHAR) -> &mut CcT {
|
||||
&mut self.c_cc[cc_c_char.as_usize()]
|
||||
&mut self.c_cc[cc_c_char as usize]
|
||||
}
|
||||
|
||||
/// Canonical mode means we will handle input by lines, not by single character
|
||||
|
@ -12,7 +12,6 @@ use jinux_util::{id_allocator::IdAlloc, slot_vec::SlotVec};
|
||||
use self::master::PtyMasterInode;
|
||||
use self::ptmx::Ptmx;
|
||||
use self::slave::PtySlaveInode;
|
||||
use crate::device::PtyMaster;
|
||||
|
||||
mod master;
|
||||
mod ptmx;
|
||||
@ -61,7 +60,7 @@ impl DevPts {
|
||||
.alloc()
|
||||
.ok_or_else(|| Error::with_message(Errno::EIO, "cannot alloc index"))?;
|
||||
|
||||
let (master, slave) = PtyMaster::new_pair(index as u32, self.root.ptmx.clone())?;
|
||||
let (master, slave) = crate::device::new_pty_pair(index as u32, self.root.ptmx.clone())?;
|
||||
|
||||
let master_inode = PtyMasterInode::new(master);
|
||||
let slave_inode = PtySlaveInode::new(slave, self.this.clone());
|
||||
@ -73,10 +72,10 @@ impl DevPts {
|
||||
/// Remove the slave from fs.
|
||||
///
|
||||
/// This is called when the master is being dropped.
|
||||
fn remove_slave(&self, index: usize) -> Option<Arc<PtySlaveInode>> {
|
||||
fn remove_slave(&self, index: u32) -> Option<Arc<PtySlaveInode>> {
|
||||
let removed_slave = self.root.remove_slave(&index.to_string());
|
||||
if removed_slave.is_some() {
|
||||
self.index_alloc.lock().free(index);
|
||||
self.index_alloc.lock().free(index as usize);
|
||||
}
|
||||
removed_slave
|
||||
}
|
||||
|
@ -151,6 +151,7 @@ impl VmMapping {
|
||||
pub fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||
let vmo_read_offset = self.vmo_offset() + offset;
|
||||
|
||||
// TODO: the current logic is vulnerable to TOCTTOU attack, since the permission may change after check.
|
||||
let page_idx_range = get_page_idx_range(&(vmo_read_offset..vmo_read_offset + buf.len()));
|
||||
let read_perm = VmPerm::R;
|
||||
for page_idx in page_idx_range {
|
||||
|
Loading…
x
Reference in New Issue
Block a user