milestone: desktop path Phases 1-5
Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests Phase 2 (Wayland Compositor): bounded scaffold, zero warnings Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick) Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/target/
|
||||
@@ -0,0 +1,26 @@
|
||||
# This file is a template, and might need editing before it works on your project.
|
||||
# Official language image. Look for the different tagged releases at:
|
||||
# https://hub.docker.com/r/library/rust/tags/
|
||||
image: "rust:latest"
|
||||
|
||||
# Optional: Pick zero or more services to be used on all builds.
|
||||
# Only needed when using a docker container to run your tests in.
|
||||
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
|
||||
#services:
|
||||
# - mysql:latest
|
||||
# - redis:latest
|
||||
# - postgres:latest
|
||||
|
||||
# Optional: Install a C compiler, cmake and git into the container.
|
||||
# You will often need this when you (or any of your dependencies) depends on C code.
|
||||
#before_script:
|
||||
#- apt-get update -yqq
|
||||
#- apt-get install -yqq --no-install-recommends build-essential
|
||||
|
||||
# Use cargo to test the project
|
||||
test:cargo:
|
||||
script:
|
||||
- rustup toolchain add nightly
|
||||
- rustup target add x86_64-unknown-redox --toolchain nightly
|
||||
- rustup show # Print version info for debugging
|
||||
- cargo +nightly check --verbose --target x86_64-unknown-redox
|
||||
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "ptyd"
|
||||
description = "Pseudo-terminal daemon"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libredox.workspace = true
|
||||
daemon = { path = "../daemon" }
|
||||
redox-scheme.workspace = true
|
||||
scheme-utils = { path = "../scheme-utils" }
|
||||
redox_event.workspace = true
|
||||
redox_syscall.workspace = true
|
||||
redox_termios.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,129 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use syscall::error::{Error, Result, EAGAIN, EINVAL, EWOULDBLOCK};
|
||||
use syscall::flag::{EventFlags, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK};
|
||||
|
||||
use crate::pty::Pty;
|
||||
use crate::resource::Resource;
|
||||
|
||||
/// Read side of a pipe
|
||||
pub struct PtyControlTerm {
|
||||
pty: Rc<RefCell<Pty>>,
|
||||
flags: usize,
|
||||
notified_read: bool,
|
||||
notified_write: bool,
|
||||
}
|
||||
|
||||
impl PtyControlTerm {
|
||||
pub fn new(pty: Rc<RefCell<Pty>>, flags: usize) -> Self {
|
||||
PtyControlTerm {
|
||||
pty,
|
||||
flags,
|
||||
notified_read: false,
|
||||
notified_write: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for PtyControlTerm {
|
||||
fn pty(&self) -> Weak<RefCell<Pty>> {
|
||||
Rc::downgrade(&self.pty)
|
||||
}
|
||||
|
||||
fn flags(&self) -> usize {
|
||||
self.flags
|
||||
}
|
||||
|
||||
fn path(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.pty.borrow_mut().path(buf)
|
||||
}
|
||||
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.notified_read = false;
|
||||
|
||||
let mut pty = self.pty.borrow_mut();
|
||||
|
||||
if let Some(packet) = pty.miso.pop_front() {
|
||||
let mut i = 0;
|
||||
|
||||
while i < buf.len() && i < packet.len() {
|
||||
buf[i] = packet[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if i < packet.len() {
|
||||
let packet_remaining = &packet[i..];
|
||||
let mut new_packet = Vec::with_capacity(packet_remaining.len() + 1);
|
||||
new_packet.push(packet[0]);
|
||||
new_packet.extend(packet_remaining);
|
||||
pty.miso.push_front(new_packet);
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
} else if self.flags & O_NONBLOCK == O_NONBLOCK || Rc::weak_count(&self.pty) == 0 {
|
||||
Err(Error::new(EAGAIN))
|
||||
} else {
|
||||
Err(Error::new(EWOULDBLOCK))
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
let mut pty = self.pty.borrow_mut();
|
||||
|
||||
if pty.mosi.len() >= 64 {
|
||||
return Err(Error::new(EWOULDBLOCK));
|
||||
}
|
||||
|
||||
pty.input(buf);
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
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> {
|
||||
self.notified_read = false; // resend
|
||||
self.notified_write = false;
|
||||
Ok(self.events())
|
||||
}
|
||||
|
||||
fn events(&mut self) -> EventFlags {
|
||||
let mut events = EventFlags::empty();
|
||||
|
||||
let pty = self.pty.borrow();
|
||||
if pty.miso.front().is_some() {
|
||||
if !self.notified_read {
|
||||
self.notified_read = true;
|
||||
events |= syscall::EVENT_READ;
|
||||
}
|
||||
} else {
|
||||
self.notified_read = false;
|
||||
}
|
||||
|
||||
if !self.notified_write {
|
||||
self.notified_write = true;
|
||||
events |= syscall::EVENT_WRITE;
|
||||
}
|
||||
|
||||
events
|
||||
}
|
||||
|
||||
fn timeout(&self, count: u64) {
|
||||
let mut pty = self.pty.borrow_mut();
|
||||
pty.timeout(count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
use event::{user_data, EventFlags, EventQueue};
|
||||
use libredox::{flag, Fd};
|
||||
|
||||
use redox_scheme::{scheme::register_sync_scheme, Response, SignalBehavior, Socket};
|
||||
use scheme_utils::ReadinessBased;
|
||||
use syscall::data::TimeSpec;
|
||||
|
||||
mod controlterm;
|
||||
mod pgrp;
|
||||
mod pty;
|
||||
mod resource;
|
||||
mod scheme;
|
||||
mod subterm;
|
||||
mod termios;
|
||||
mod winsize;
|
||||
|
||||
use scheme::{Handle, PtyScheme};
|
||||
|
||||
fn main() {
|
||||
daemon::Daemon::new(daemon);
|
||||
}
|
||||
|
||||
fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
user_data! {
|
||||
enum EventSource {
|
||||
Socket,
|
||||
Time,
|
||||
}
|
||||
}
|
||||
|
||||
let event_queue = EventQueue::<EventSource>::new().expect("pty: failed to open event:");
|
||||
|
||||
let time_path = format!("/scheme/time/{}", flag::CLOCK_MONOTONIC);
|
||||
let mut time_file =
|
||||
Fd::open(&time_path, flag::O_NONBLOCK, 0).expect("pty: failed to open time:");
|
||||
|
||||
let socket = redox_scheme::Socket::nonblock().expect("pty: failed to create pty scheme");
|
||||
let mut handler = ReadinessBased::new(&socket, 16);
|
||||
|
||||
let mut scheme = PtyScheme::new();
|
||||
register_sync_scheme(&socket, "pty", &mut scheme)
|
||||
.expect("ptyd: failed to register scheme to namespace");
|
||||
daemon.ready();
|
||||
|
||||
libredox::call::setrens(0, 0).expect("ptyd: failed to enter null namespace");
|
||||
|
||||
event_queue
|
||||
.subscribe(socket.inner().raw(), EventSource::Socket, EventFlags::READ)
|
||||
.expect("pty: failed to watch events on pty:");
|
||||
event_queue
|
||||
.subscribe(time_file.raw(), EventSource::Time, EventFlags::READ)
|
||||
.expect("pty: failed to watch events on time:");
|
||||
|
||||
//TODO: do not set timeout if not necessary
|
||||
timeout(&mut time_file).expect("pty: failed to set timeout");
|
||||
|
||||
let mut timeout_count = 0u64;
|
||||
|
||||
scan_requests(&mut handler, &mut scheme).expect("pty: could not scan requests");
|
||||
issue_events(&socket, &mut scheme);
|
||||
|
||||
for event_res in event_queue {
|
||||
let event = event_res.expect("pty: failed to read from event queue");
|
||||
|
||||
match event.user_data {
|
||||
EventSource::Socket => {
|
||||
if scan_requests(&mut handler, &mut scheme).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
EventSource::Time => {
|
||||
timeout(&mut time_file).expect("pty: failed to set timeout");
|
||||
|
||||
timeout_count = timeout_count.wrapping_add(1);
|
||||
|
||||
for (_id, handle) in scheme.handles.iter_mut() {
|
||||
if let Handle::Resource(res) = handle {
|
||||
res.timeout(timeout_count);
|
||||
}
|
||||
}
|
||||
|
||||
handler
|
||||
.poll_all_requests(&mut scheme)
|
||||
.expect("ihdad: failed to poll requests");
|
||||
}
|
||||
}
|
||||
|
||||
issue_events(&socket, &mut scheme);
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
fn scan_requests(
|
||||
handler: &mut ReadinessBased<'_>,
|
||||
scheme: &mut PtyScheme,
|
||||
) -> libredox::error::Result<()> {
|
||||
handler
|
||||
.read_and_process_requests(scheme)
|
||||
.expect("pty: failed to read from socket");
|
||||
handler
|
||||
.poll_all_requests(scheme)
|
||||
.expect("pty: error occured in poll_all_requests");
|
||||
handler
|
||||
.write_responses()
|
||||
.expect("pty: failed to write to socket");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn issue_events(socket: &Socket, scheme: &mut PtyScheme) {
|
||||
for (id, handle) in scheme.handles.iter_mut() {
|
||||
if let Handle::Resource(ref mut res) = handle {
|
||||
let events = res.events();
|
||||
if events != syscall::EventFlags::empty() {
|
||||
socket
|
||||
.write_response(
|
||||
Response::post_fevent(*id, events.bits()),
|
||||
SignalBehavior::Restart,
|
||||
)
|
||||
.expect("pty: failed to send scheme event");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn timeout(time_file: &mut Fd) -> libredox::error::Result<()> {
|
||||
let mut time = TimeSpec::default();
|
||||
time_file.read(&mut time)?;
|
||||
|
||||
// Set timeout for 100ms
|
||||
time.tv_nsec += 100_000_000;
|
||||
while time.tv_nsec >= 1_000_000_000 {
|
||||
time.tv_sec += 1;
|
||||
time.tv_nsec -= 1_000_000_000;
|
||||
}
|
||||
|
||||
time_file.write(&time)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Weak;
|
||||
|
||||
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 PtyPgrp {
|
||||
pty: Weak<RefCell<Pty>>,
|
||||
flags: usize,
|
||||
}
|
||||
|
||||
impl PtyPgrp {
|
||||
pub fn new(pty: Weak<RefCell<Pty>>, flags: usize) -> Self {
|
||||
PtyPgrp { pty, flags }
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for PtyPgrp {
|
||||
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> {
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
let pty = pty_lock.borrow();
|
||||
|
||||
//println!("READ PGRP {}: {}", pty.id, pty.pgrp);
|
||||
let dst_buf = buf
|
||||
.get_mut(..4)
|
||||
.and_then(|b| <&mut [u8; 4]>::try_from(b).ok())
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
*dst_buf = (pty.pgrp as u32).to_ne_bytes();
|
||||
|
||||
Ok(4)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
let mut pty = pty_lock.borrow_mut();
|
||||
|
||||
let new_pgrp = u32::from_ne_bytes(
|
||||
buf.get(..4)
|
||||
.and_then(|b| <[u8; 4]>::try_from(b).ok())
|
||||
.ok_or(Error::new(EBADF))?,
|
||||
);
|
||||
pty.pgrp = new_pgrp as usize;
|
||||
//println!("WRITE PGRP {}: {} => {}", pty.id, pty.pgrp, new_pgrp);
|
||||
|
||||
Ok(4)
|
||||
} else {
|
||||
Err(Error::new(EPIPE))
|
||||
}
|
||||
}
|
||||
|
||||
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,317 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use redox_termios::*;
|
||||
use scheme_utils::FpathWriter;
|
||||
use syscall::error::Result;
|
||||
|
||||
pub struct Pty {
|
||||
pub id: usize,
|
||||
pub pgrp: usize,
|
||||
pub termios: Termios,
|
||||
pub winsize: Winsize,
|
||||
pub cooked: Vec<u8>,
|
||||
pub miso: VecDeque<Vec<u8>>,
|
||||
pub mosi: VecDeque<Vec<u8>>,
|
||||
pub timeout_count: u64,
|
||||
pub timeout_character: Option<u64>,
|
||||
}
|
||||
|
||||
impl Pty {
|
||||
pub fn new(id: usize) -> Self {
|
||||
Pty {
|
||||
id,
|
||||
pgrp: 0,
|
||||
termios: Termios::default(),
|
||||
winsize: Winsize::default(),
|
||||
cooked: Vec::new(),
|
||||
miso: VecDeque::new(),
|
||||
mosi: VecDeque::new(),
|
||||
timeout_count: 0,
|
||||
timeout_character: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
FpathWriter::with(buf, "pty", |w| {
|
||||
write!(w, "{}", self.id).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn input(&mut self, buf: &[u8]) {
|
||||
let ifl = self.termios.c_iflag;
|
||||
//let ofl = &self.termios.c_oflag;
|
||||
//let cfl = &self.termios.c_cflag;
|
||||
let lfl = self.termios.c_lflag;
|
||||
let cc = self.termios.c_cc;
|
||||
|
||||
let is_cc = |b: u8, i: usize| -> bool { b != 0 && b == cc[i] };
|
||||
// TODO: Delete this constant once termios is bumped (it's in termios).
|
||||
const _POSIX_VDISABLE: u8 = 0;
|
||||
let is_vdisable = |i: usize| -> bool { cc[i] == _POSIX_VDISABLE };
|
||||
|
||||
let inlcr = ifl & INLCR == INLCR;
|
||||
let igncr = ifl & IGNCR == IGNCR;
|
||||
let icrnl = ifl & ICRNL == ICRNL;
|
||||
|
||||
let echo = lfl & ECHO == ECHO;
|
||||
let echoe = lfl & ECHOE == ECHOE;
|
||||
let echonl = lfl & ECHONL == ECHONL;
|
||||
let icanon = lfl & ICANON == ICANON;
|
||||
let isig = lfl & ISIG == ISIG;
|
||||
let iexten = lfl & IEXTEN == IEXTEN;
|
||||
let ixon = lfl & IXON == IXON;
|
||||
|
||||
for &byte in buf.iter() {
|
||||
let mut b = byte;
|
||||
|
||||
// Input translation
|
||||
if b == b'\n' {
|
||||
if inlcr {
|
||||
b = b'\r';
|
||||
}
|
||||
} else if b == b'\r' {
|
||||
if igncr {
|
||||
b = 0;
|
||||
} else if icrnl {
|
||||
b = b'\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Link settings
|
||||
if icanon {
|
||||
if b == b'\n' {
|
||||
if echo || echonl {
|
||||
self.output(&[b]);
|
||||
}
|
||||
|
||||
self.cooked.push(b);
|
||||
self.mosi.push_back(self.cooked.clone());
|
||||
self.cooked.clear();
|
||||
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VEOF) {
|
||||
self.mosi.push_back(self.cooked.clone());
|
||||
self.cooked.clear();
|
||||
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VEOL) {
|
||||
if echo {
|
||||
self.output(&[b]);
|
||||
}
|
||||
|
||||
self.cooked.push(b);
|
||||
self.mosi.push_back(self.cooked.clone());
|
||||
self.cooked.clear();
|
||||
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VEOL2) {
|
||||
if echo {
|
||||
self.output(&[b]);
|
||||
}
|
||||
|
||||
self.cooked.push(b);
|
||||
self.mosi.push_back(self.cooked.clone());
|
||||
self.cooked.clear();
|
||||
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VERASE) && !is_vdisable(VERASE) {
|
||||
if let Some(_c) = self.cooked.pop() {
|
||||
if echoe {
|
||||
self.output(&[8, b' ', 8]);
|
||||
}
|
||||
}
|
||||
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VWERASE) && iexten {
|
||||
println!("VWERASE");
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VKILL) {
|
||||
println!("VKILL");
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VREPRINT) && iexten {
|
||||
println!("VREPRINT");
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if isig {
|
||||
if is_cc(b, VINTR) && !is_vdisable(VINTR) {
|
||||
if self.pgrp != 0 {
|
||||
let _ = libredox::call::kill(
|
||||
-(self.pgrp as isize) as usize,
|
||||
libredox::flag::SIGINT as _,
|
||||
);
|
||||
}
|
||||
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VQUIT) && !is_vdisable(VQUIT) {
|
||||
if self.pgrp != 0 {
|
||||
let _ = libredox::call::kill(
|
||||
-(self.pgrp as isize) as usize,
|
||||
libredox::flag::SIGQUIT as _,
|
||||
);
|
||||
}
|
||||
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VSUSP) && !is_vdisable(VSUSP) {
|
||||
if self.pgrp != 0 {
|
||||
let _ = libredox::call::kill(
|
||||
-(self.pgrp as isize) as usize,
|
||||
libredox::flag::SIGTSTP as _,
|
||||
);
|
||||
}
|
||||
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ixon {
|
||||
if is_cc(b, VSTART) {
|
||||
println!("VSTART");
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VSTOP) {
|
||||
println!("VSTOP");
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if is_cc(b, VLNEXT) && iexten {
|
||||
println!("VLNEXT");
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if is_cc(b, VDISCARD) && iexten {
|
||||
println!("VDISCARD");
|
||||
b = 0;
|
||||
}
|
||||
|
||||
if b != 0 {
|
||||
if echo {
|
||||
self.output(&[b]);
|
||||
}
|
||||
|
||||
// Restart timer after every byte
|
||||
self.timeout_character = Some(self.timeout_count);
|
||||
|
||||
self.cooked.push(b);
|
||||
}
|
||||
}
|
||||
|
||||
self.update();
|
||||
}
|
||||
|
||||
pub fn output(&mut self, buf: &[u8]) {
|
||||
//TODO: more output flags
|
||||
|
||||
let ofl = &self.termios.c_oflag;
|
||||
|
||||
let opost = ofl & OPOST == OPOST;
|
||||
let onlcr = ofl & ONLCR == ONLCR;
|
||||
|
||||
let mut vec = Vec::with_capacity(buf.len() + 1);
|
||||
vec.push(0);
|
||||
|
||||
for &b in buf.iter() {
|
||||
if opost && onlcr && b == b'\n' {
|
||||
vec.push(b'\r');
|
||||
}
|
||||
vec.push(b);
|
||||
}
|
||||
|
||||
self.miso.push_back(vec);
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
let lfl = self.termios.c_lflag;
|
||||
let cc = self.termios.c_cc;
|
||||
let icanon = lfl & ICANON == ICANON;
|
||||
let vmin = usize::from(cc[VMIN]);
|
||||
let vtime = u64::from(cc[VTIME]);
|
||||
|
||||
// http://unixwiz.net/techtips/termios-vmin-vtime.html
|
||||
if !icanon {
|
||||
if vtime == 0 {
|
||||
// No timeout specified
|
||||
if vmin == 0 {
|
||||
// Polling read, return immediately with data
|
||||
if self.mosi.is_empty() {
|
||||
self.mosi.push_back(self.cooked.clone());
|
||||
self.cooked.clear();
|
||||
}
|
||||
} else {
|
||||
// Blocking read, wait until vmin bytes are available
|
||||
if self.cooked.len() >= vmin {
|
||||
self.mosi.push_back(self.cooked.clone());
|
||||
self.cooked.clear();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Timeout specified using vtime
|
||||
if vmin == 0 {
|
||||
// Return when any data is available or the timer expires
|
||||
if !self.cooked.is_empty() {
|
||||
self.mosi.push_back(self.cooked.clone());
|
||||
self.cooked.clear();
|
||||
} else if let Some(timeout_character) = self.timeout_character {
|
||||
if self.timeout_count >= timeout_character.wrapping_add(vtime) {
|
||||
self.timeout_character = None;
|
||||
|
||||
if self.mosi.is_empty() {
|
||||
self.mosi.push_back(self.cooked.clone());
|
||||
self.cooked.clear();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Start timer if not already started
|
||||
self.timeout_character = Some(self.timeout_count);
|
||||
}
|
||||
} else {
|
||||
// Return when min bytes are received or the timer expires
|
||||
// when any data is available
|
||||
if self.cooked.len() >= vmin {
|
||||
self.mosi.push_back(self.cooked.clone());
|
||||
self.cooked.clear();
|
||||
} else if !self.cooked.is_empty() {
|
||||
if let Some(timeout_character) = self.timeout_character {
|
||||
if self.timeout_count >= timeout_character.wrapping_add(vtime) {
|
||||
self.timeout_character = None;
|
||||
|
||||
self.mosi.push_back(self.cooked.clone());
|
||||
self.cooked.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timeout(&mut self, count: u64) {
|
||||
if self.timeout_count != count {
|
||||
self.timeout_count = count;
|
||||
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Weak;
|
||||
|
||||
use syscall::error::Result;
|
||||
use syscall::flag::EventFlags;
|
||||
|
||||
use crate::pty::Pty;
|
||||
|
||||
pub trait Resource {
|
||||
fn pty(&self) -> Weak<RefCell<Pty>>;
|
||||
fn flags(&self) -> usize;
|
||||
|
||||
fn path(&mut self, buf: &mut [u8]) -> Result<usize>;
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize>;
|
||||
fn sync(&mut self) -> Result<()>;
|
||||
fn fcntl(&mut self, cmd: usize, arg: usize) -> Result<usize>;
|
||||
fn fevent(&mut self) -> Result<EventFlags>;
|
||||
fn events(&mut self) -> EventFlags;
|
||||
fn timeout(&self, _count: u64) {
|
||||
// Handled only by PTY control term
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::rc::Rc;
|
||||
use std::str;
|
||||
|
||||
use redox_scheme::scheme::SchemeSync;
|
||||
use redox_scheme::{CallerCtx, OpenResult};
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::{Error, Result, EACCES, EBADF, EINVAL, ENOENT};
|
||||
use syscall::flag::{EventFlags, MODE_CHR};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
|
||||
use crate::controlterm::PtyControlTerm;
|
||||
use crate::pgrp::PtyPgrp;
|
||||
use crate::pty::Pty;
|
||||
use crate::resource::Resource;
|
||||
use crate::subterm::PtySubTerm;
|
||||
use crate::termios::PtyTermios;
|
||||
use crate::winsize::PtyWinsize;
|
||||
|
||||
pub enum Handle {
|
||||
Resource(Box<dyn Resource>),
|
||||
SchemeRoot,
|
||||
}
|
||||
|
||||
pub struct PtyScheme {
|
||||
next_id: usize,
|
||||
pub handles: BTreeMap<usize, Handle>,
|
||||
}
|
||||
|
||||
impl PtyScheme {
|
||||
pub fn new() -> Self {
|
||||
PtyScheme {
|
||||
next_id: 0,
|
||||
handles: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_resource_mut(&mut self, id: usize) -> Result<&mut Box<dyn Resource>> {
|
||||
match self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::Resource(res) => Ok(res),
|
||||
Handle::SchemeRoot => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeSync for PtyScheme {
|
||||
fn scheme_root(&mut self) -> Result<usize> {
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::SchemeRoot);
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn openat(
|
||||
&mut self,
|
||||
dirfd: usize,
|
||||
path: &str,
|
||||
flags: usize,
|
||||
fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<OpenResult> {
|
||||
if !matches!(
|
||||
self.handles.get(&dirfd).ok_or(Error::new(EBADF))?,
|
||||
Handle::SchemeRoot
|
||||
) {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
let path = path.trim_matches('/');
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
|
||||
if path.is_empty() {
|
||||
let pty = Rc::new(RefCell::new(Pty::new(id)));
|
||||
self.handles.insert(
|
||||
id,
|
||||
Handle::Resource(Box::new(PtyControlTerm::new(pty, flags))),
|
||||
);
|
||||
} else {
|
||||
let control_term_id = path.parse::<usize>().or(Err(Error::new(EINVAL)))?;
|
||||
let pty = {
|
||||
let handle = self
|
||||
.handles
|
||||
.get(&control_term_id)
|
||||
.ok_or(Error::new(ENOENT))?;
|
||||
|
||||
match handle {
|
||||
Handle::Resource(res) => res.pty(),
|
||||
Handle::SchemeRoot => return Err(Error::new(ENOENT)),
|
||||
}
|
||||
};
|
||||
|
||||
self.handles.insert(
|
||||
id,
|
||||
Handle::Resource(Box::new(PtySubTerm::new(pty, flags | fcntl_flags as usize))),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
fn dup(&mut self, old_id: usize, buf: &[u8], _ctx: &CallerCtx) -> Result<OpenResult> {
|
||||
let handle: Box<dyn Resource> = {
|
||||
let old_handle = self.handles.get(&old_id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let old_resource = match old_handle {
|
||||
Handle::Resource(res) => res,
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
|
||||
if buf == b"pgrp" {
|
||||
Box::new(PtyPgrp::new(old_resource.pty(), old_resource.flags()))
|
||||
} else if buf == b"termios" {
|
||||
Box::new(PtyTermios::new(old_resource.pty(), old_resource.flags()))
|
||||
} else if buf == b"winsize" {
|
||||
Box::new(PtyWinsize::new(old_resource.pty(), old_resource.flags()))
|
||||
} else {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
};
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Resource(handle));
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
_offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let handle = self.get_resource_mut(id)?;
|
||||
handle.read(buf)
|
||||
}
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &[u8],
|
||||
_offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let handle = self.get_resource_mut(id)?;
|
||||
handle.write(buf)
|
||||
}
|
||||
|
||||
fn fcntl(&mut self, id: usize, cmd: usize, arg: usize, _ctx: &CallerCtx) -> Result<usize> {
|
||||
let handle = self.get_resource_mut(id)?;
|
||||
handle.fcntl(cmd, arg)
|
||||
}
|
||||
|
||||
fn fevent(&mut self, id: usize, _flags: EventFlags, _ctx: &CallerCtx) -> Result<EventFlags> {
|
||||
let handle = self.get_resource_mut(id)?;
|
||||
handle.fevent()
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
let handle = self.get_resource_mut(id)?;
|
||||
handle.path(buf)
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match handle {
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
Handle::Resource(_res) => {
|
||||
*stat = Stat {
|
||||
st_mode: MODE_CHR | 0o666,
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> {
|
||||
let handle = self.get_resource_mut(id)?;
|
||||
handle.sync()
|
||||
}
|
||||
|
||||
fn on_close(&mut self, id: usize) {
|
||||
let _ = self.handles.remove(&id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Weak;
|
||||
|
||||
use syscall::error::{Error, Result, EAGAIN, EINVAL, EPIPE, EWOULDBLOCK};
|
||||
use syscall::flag::{EventFlags, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK};
|
||||
|
||||
use crate::pty::Pty;
|
||||
use crate::resource::Resource;
|
||||
|
||||
/// Read side of a pipe
|
||||
pub struct PtySubTerm {
|
||||
pty: Weak<RefCell<Pty>>,
|
||||
flags: usize,
|
||||
notified_read: bool,
|
||||
notified_write: bool,
|
||||
}
|
||||
|
||||
impl PtySubTerm {
|
||||
pub fn new(pty: Weak<RefCell<Pty>>, flags: usize) -> Self {
|
||||
PtySubTerm {
|
||||
pty,
|
||||
flags,
|
||||
notified_read: false,
|
||||
notified_write: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for PtySubTerm {
|
||||
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> {
|
||||
self.notified_read = false;
|
||||
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
let mut pty = pty_lock.borrow_mut();
|
||||
|
||||
pty.update();
|
||||
|
||||
if let Some(packet) = pty.mosi.pop_front() {
|
||||
let mut i = 0;
|
||||
|
||||
while i < buf.len() && i < packet.len() {
|
||||
buf[i] = packet[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if i < packet.len() {
|
||||
pty.mosi.push_front(packet[i..].to_vec());
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
} else if self.flags & O_NONBLOCK == O_NONBLOCK {
|
||||
Err(Error::new(EAGAIN))
|
||||
} else {
|
||||
Err(Error::new(EWOULDBLOCK))
|
||||
}
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
let mut pty = pty_lock.borrow_mut();
|
||||
|
||||
if pty.miso.len() >= 64 {
|
||||
return Err(Error::new(EWOULDBLOCK));
|
||||
}
|
||||
|
||||
pty.output(buf);
|
||||
|
||||
Ok(buf.len())
|
||||
} else {
|
||||
Err(Error::new(EPIPE))
|
||||
}
|
||||
}
|
||||
|
||||
fn sync(&mut self) -> Result<()> {
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
let mut pty = pty_lock.borrow_mut();
|
||||
|
||||
pty.miso.push_back(vec![1]);
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(EPIPE))
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
self.notified_read = false; // resend
|
||||
self.notified_write = false;
|
||||
Ok(self.events())
|
||||
}
|
||||
|
||||
fn events(&mut self) -> EventFlags {
|
||||
let mut events = EventFlags::empty();
|
||||
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
let pty = pty_lock.borrow();
|
||||
if pty.mosi.front().is_some() {
|
||||
if !self.notified_read {
|
||||
self.notified_read = true;
|
||||
events |= syscall::EVENT_READ;
|
||||
}
|
||||
} else {
|
||||
self.notified_read = false;
|
||||
}
|
||||
}
|
||||
|
||||
if !self.notified_write {
|
||||
self.notified_write = true;
|
||||
events |= syscall::EVENT_WRITE;
|
||||
}
|
||||
|
||||
events
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
use std::cell::RefCell;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Weak;
|
||||
|
||||
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 PtyTermios {
|
||||
pty: Weak<RefCell<Pty>>,
|
||||
flags: usize,
|
||||
}
|
||||
|
||||
impl PtyTermios {
|
||||
pub fn new(pty: Weak<RefCell<Pty>>, flags: usize) -> Self {
|
||||
PtyTermios { pty, flags }
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for PtyTermios {
|
||||
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> {
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
let pty = pty_lock.borrow();
|
||||
let termios: &[u8] = pty.termios.deref();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buf.len() && i < termios.len() {
|
||||
buf[i] = termios[i];
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
let mut pty = pty_lock.borrow_mut();
|
||||
let termios: &mut [u8] = pty.termios.deref_mut();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buf.len() && i < termios.len() {
|
||||
termios[i] = buf[i];
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
} else {
|
||||
Err(Error::new(EPIPE))
|
||||
}
|
||||
}
|
||||
|
||||
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,94 @@
|
||||
use std::cell::RefCell;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Weak;
|
||||
|
||||
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 PtyWinsize {
|
||||
pty: Weak<RefCell<Pty>>,
|
||||
flags: usize,
|
||||
}
|
||||
|
||||
impl PtyWinsize {
|
||||
pub fn new(pty: Weak<RefCell<Pty>>, flags: usize) -> Self {
|
||||
PtyWinsize { pty, flags }
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for PtyWinsize {
|
||||
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> {
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
let pty = pty_lock.borrow();
|
||||
let winsize: &[u8] = pty.winsize.deref();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buf.len() && i < winsize.len() {
|
||||
buf[i] = winsize[i];
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
if let Some(pty_lock) = self.pty.upgrade() {
|
||||
let mut pty = pty_lock.borrow_mut();
|
||||
let winsize: &mut [u8] = pty.winsize.deref_mut();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buf.len() && i < winsize.len() {
|
||||
winsize[i] = buf[i];
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
} else {
|
||||
Err(Error::new(EPIPE))
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user