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:
2026-04-29 09:54:06 +01:00
parent b23714f542
commit 8acc73d774
508 changed files with 76526 additions and 396 deletions
+1
View File
@@ -0,0 +1 @@
/target/
+26
View File
@@ -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
+17
View File
@@ -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
+129
View File
@@ -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);
}
}
+139
View File
@@ -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(())
}
+96
View File
@@ -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()
}
}
+317
View File
@@ -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();
}
}
}
+23
View File
@@ -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
}
}
+200
View File
@@ -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);
}
}
+144
View File
@@ -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
}
}
+94
View File
@@ -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()
}
}
+94
View File
@@ -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()
}
}