feature: Stdin/Stdout/Stderr structs

This commit is contained in:
Mark Poliakov 2021-11-16 14:34:14 +02:00
parent 1f204e1d4c
commit 6bb4f38edc
13 changed files with 376 additions and 156 deletions

16
Cargo.lock generated
View File

@ -97,6 +97,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
dependencies = [
"spin",
]
[[package]] [[package]]
name = "libsys" name = "libsys"
version = "0.1.0" version = "0.1.0"
@ -108,6 +117,7 @@ dependencies = [
name = "libusr" name = "libusr"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"lazy_static",
"libsys", "libsys",
] ]
@ -195,6 +205,12 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]] [[package]]
name = "static_assertions" name = "static_assertions"
version = "1.1.0" version = "1.1.0"

View File

@ -7,3 +7,4 @@ edition = "2021"
[dependencies] [dependencies]
libsys = { path = "../libsys", features = ["user"] } libsys = { path = "../libsys", features = ["user"] }
lazy_static = { version = "^1.4.0", features = ["spin_no_std"] }

View File

@ -1,12 +1,26 @@
use crate::io::{AsRawFd, Error};
use crate::os;
use crate::trace;
use libsys::stat::FileDescriptor; use libsys::stat::FileDescriptor;
use crate::io;
pub struct File { pub struct File {
fd: FileDescriptor fd: FileDescriptor,
} }
impl File { impl File {
pub fn open(path: &str) -> Result<File, io::Error> { pub fn open(path: &str) -> Result<File, Error> {
todo!() todo!()
} }
} }
impl AsRawFd for File {
fn as_raw_fd(&self) -> FileDescriptor {
self.fd
}
}
impl Drop for File {
fn drop(&mut self) {
todo!();
}
}

View File

@ -1,65 +0,0 @@
use core::fmt;
use libsys::{
calls::{sys_fstatat, sys_write},
stat::{Stat, FileDescriptor},
};
// TODO populate this type
pub struct Error;
pub fn stat(pathname: &str) -> Result<Stat, Error> {
let mut buf = Stat::default();
// TODO error handling
let res = unsafe { sys_fstatat(None, pathname, &mut buf, 0).unwrap() };
Ok(buf)
}
// print!/println! group
#[macro_export]
macro_rules! print {
($($args:tt)+) => ($crate::io::_print($crate::sys::FileDescriptor::STDOUT, format_args!($($args)+)))
}
#[macro_export]
macro_rules! println {
($($args:tt)+) => (print!("{}\n", format_args!($($args)+)))
}
#[macro_export]
macro_rules! eprint {
($($args:tt)+) => ($crate::io::_print($crate::sys::FileDescriptor::STDERR, format_args!($($args)+)))
}
#[macro_export]
macro_rules! eprintln {
($($args:tt)+) => (eprint!("{}\n", format_args!($($args)+)))
}
struct BufferWriter<'a> {
buf: &'a mut [u8],
pos: usize,
}
impl fmt::Write for BufferWriter<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for byte in s.bytes() {
self.buf[self.pos] = byte;
self.pos += 1;
}
Ok(())
}
}
pub fn _print(fd: FileDescriptor, args: fmt::Arguments) {
use core::fmt::Write;
static mut BUFFER: [u8; 4096] = [0; 4096];
let mut writer = BufferWriter {
buf: unsafe { &mut BUFFER },
pos: 0,
};
writer.write_fmt(args).ok();
unsafe {
sys_write(fd, &BUFFER[..writer.pos]);
}
}

35
libusr/src/io/error.rs Normal file
View File

@ -0,0 +1,35 @@
use libsys::error::Errno;
#[derive(Debug)]
pub struct Error {
repr: Repr,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum ErrorKind {
NotFound,
PermissionDenied,
InvalidData,
}
#[derive(Debug)]
enum Repr {
Os(Errno),
Simple(ErrorKind),
}
impl Error {
pub const fn new(kind: ErrorKind) -> Self {
Self {
repr: Repr::Simple(kind),
}
}
}
impl From<Errno> for Error {
fn from(e: Errno) -> Self {
Self {
repr: Repr::Os(e)
}
}
}

32
libusr/src/io/mod.rs Normal file
View File

@ -0,0 +1,32 @@
use libsys::{
calls::sys_fstatat,
stat::{FileDescriptor, Stat},
};
use core::fmt;
mod error;
pub use error::{Error, ErrorKind};
mod writer;
pub use writer::{_print};
mod stdio;
pub use stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout};
pub trait Read {
fn read(&mut self, bytes: &mut [u8]) -> Result<usize, Error>;
}
pub trait Write {
fn write(&mut self, bytes: &[u8]) -> Result<usize, Error>;
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Error>;
}
pub trait AsRawFd {
fn as_raw_fd(&self) -> FileDescriptor;
}
pub fn stat(pathname: &str) -> Result<Stat, Error> {
let mut buf = Stat::default();
// TODO error handling
let res = sys_fstatat(None, pathname, &mut buf, 0).unwrap();
Ok(buf)
}

130
libusr/src/io/stdio.rs Normal file
View File

@ -0,0 +1,130 @@
use libsys::{
stat::FileDescriptor,
calls::{sys_read, sys_write}
};
use crate::io::{Read, Write, Error};
use crate::sync::{Mutex, MutexGuard};
use core::fmt;
struct InputInner {
fd: FileDescriptor
}
struct OutputInner {
fd: FileDescriptor
}
pub struct StdinLock<'a> {
lock: MutexGuard<'a, InputInner>
}
pub struct StdoutLock<'a> {
lock: MutexGuard<'a, OutputInner>
}
pub struct StderrLock<'a> {
lock: MutexGuard<'a, OutputInner>
}
pub struct Stdin {
inner: &'static Mutex<InputInner>,
}
pub struct Stdout {
inner: &'static Mutex<OutputInner>
}
pub struct Stderr {
inner: &'static Mutex<OutputInner>
}
// STDIN
impl Read for InputInner {
fn read(&mut self, bytes: &mut [u8]) -> Result<usize, Error> {
sys_read(self.fd, bytes).map_err(Error::from)
}
}
impl Read for Stdin {
fn read(&mut self, bytes: &mut [u8]) -> Result<usize, Error> {
self.inner.lock().read(bytes)
}
}
// STDOUT/STDERR
impl fmt::Write for OutputInner {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write(s.as_bytes()).map(|_| ()).map_err(|_| todo!())
}
}
impl Write for OutputInner {
fn write(&mut self, bytes: &[u8]) -> Result<usize, Error> {
sys_write(self.fd, bytes).map_err(Error::from)
}
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Error> {
fmt::Write::write_fmt(self, args).map_err(|_| todo!())
}
}
impl Write for Stdout {
fn write(&mut self, bytes: &[u8]) -> Result<usize, Error> {
self.inner.lock().write(bytes)
}
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Error> {
self.inner.lock().write_fmt(args)
}
}
impl Write for Stderr {
fn write(&mut self, bytes: &[u8]) -> Result<usize, Error> {
self.inner.lock().write(bytes)
}
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Error> {
self.inner.lock().write_fmt(args)
}
}
impl Stdout {
pub fn lock(&self) -> StdoutLock {
StdoutLock {
lock: self.inner.lock()
}
}
}
impl Stderr {
pub fn lock(&self) -> StderrLock {
StderrLock {
lock: self.inner.lock()
}
}
}
lazy_static! {
static ref STDIN: Mutex<InputInner> = Mutex::new(InputInner {
fd: FileDescriptor::STDIN
});
static ref STDOUT: Mutex<OutputInner> = Mutex::new(OutputInner {
fd: FileDescriptor::STDOUT
});
static ref STDERR: Mutex<OutputInner> = Mutex::new(OutputInner {
fd: FileDescriptor::STDOUT
});
}
pub fn stdin() -> Stdin {
Stdin { inner: &STDIN }
}
pub fn stdout() -> Stdout {
Stdout { inner: &STDOUT }
}
pub fn stderr() -> Stderr {
Stderr { inner: &STDERR }
}

26
libusr/src/io/writer.rs Normal file
View File

@ -0,0 +1,26 @@
use core::fmt;
use crate::io::{self, Write};
#[macro_export]
macro_rules! print {
($($args:tt)+) => ($crate::io::_print($crate::io::stdout, format_args!($($args)+)))
}
#[macro_export]
macro_rules! println {
($($args:tt)+) => (print!("{}\n", format_args!($($args)+)))
}
#[macro_export]
macro_rules! eprint {
($($args:tt)+) => ($crate::io::_print($crate::io::stderr, format_args!($($args)+)))
}
#[macro_export]
macro_rules! eprintln {
($($args:tt)+) => (eprint!("{}\n", format_args!($($args)+)))
}
pub fn _print<T: Write>(out: fn() -> T, args: fmt::Arguments) {
out().write_fmt(args).expect("stdout/stderr write failed");
}

View File

@ -4,24 +4,20 @@
use core::panic::PanicInfo; use core::panic::PanicInfo;
use libsys::proc::ExitCode; use libsys::proc::ExitCode;
#[macro_use]
extern crate lazy_static;
pub mod file;
pub mod io; pub mod io;
pub mod os; pub mod os;
pub mod file; pub mod sys;
pub mod sync;
pub mod sys {
pub use libsys::signal::{Signal, SignalDestination};
pub use libsys::termios;
pub use libsys::calls::*;
pub use libsys::stat::{self, FileDescriptor};
}
#[inline(never)] #[inline(never)]
extern "C" fn _signal_handler(arg: sys::Signal) -> ! { extern "C" fn _signal_handler(arg: sys::Signal) -> ! {
trace!("Entered signal handler: arg={:?}", arg); trace!("Entered signal handler: arg={:?}", arg);
unsafe {
sys::sys_ex_sigreturn(); sys::sys_ex_sigreturn();
} }
}
static mut SIGNAL_STACK: [u8; 4096] = [0; 4096]; static mut SIGNAL_STACK: [u8; 4096] = [0; 4096];
@ -31,17 +27,22 @@ extern "C" fn _start(_arg: usize) -> ! {
extern "Rust" { extern "Rust" {
fn main() -> i32; fn main() -> i32;
} }
unsafe {
SIGNAL_STACK[0] = 1;
sys::sys_ex_signal(_signal_handler as usize, SIGNAL_STACK.as_ptr() as usize + 4096);
sys::sys_exit(ExitCode::from(main())); unsafe {
sys::sys_ex_signal(
_signal_handler as usize,
SIGNAL_STACK.as_ptr() as usize + 4096,
)
.unwrap();
} }
let res = unsafe { main() };
sys::sys_exit(ExitCode::from(res));
} }
#[panic_handler] #[panic_handler]
fn panic_handler(pi: &PanicInfo) -> ! { fn panic_handler(pi: &PanicInfo) -> ! {
// TODO formatted messages // TODO print to stdout/stderr (if available)
trace!("Panic ocurred: {}", pi); trace!("Panic ocurred: {}", pi);
sys::sys_exit(ExitCode::from(-1)); sys::sys_exit(ExitCode::from(-1));
} }

View File

@ -3,35 +3,6 @@ use core::fmt;
use core::mem::{size_of, MaybeUninit}; use core::mem::{size_of, MaybeUninit};
use libsys::{ioctl::IoctlCmd, stat::FileDescriptor, termios::Termios}; use libsys::{ioctl::IoctlCmd, stat::FileDescriptor, termios::Termios};
pub fn get_tty_attrs(fd: FileDescriptor) -> Result<Termios, &'static str> {
let mut termios = MaybeUninit::<Termios>::uninit();
let res = sys::sys_ioctl(
fd,
IoctlCmd::TtyGetAttributes,
termios.as_mut_ptr() as usize,
size_of::<Termios>(),
)
.unwrap();
if res != size_of::<Termios>() {
return Err("Failed");
}
Ok(unsafe { termios.assume_init() })
}
pub fn set_tty_attrs(fd: FileDescriptor, attrs: &Termios) -> Result<(), &'static str> {
let res = sys::sys_ioctl(
fd,
IoctlCmd::TtySetAttributes,
attrs as *const _ as usize,
size_of::<Termios>(),
)
.unwrap();
if res != size_of::<Termios>() {
return Err("Failed");
}
Ok(())
}
#[macro_export] #[macro_export]
macro_rules! trace { macro_rules! trace {
($($args:tt)+) => ($crate::os::_trace(format_args!($($args)+))) ($($args:tt)+) => ($crate::os::_trace(format_args!($($args)+)))
@ -60,7 +31,5 @@ pub fn _trace(args: fmt::Arguments) {
pos: 0, pos: 0,
}; };
writer.write_fmt(args).ok(); writer.write_fmt(args).ok();
unsafe { sys::sys_ex_debug_trace(unsafe { &BUFFER[..writer.pos] }).ok();
sys::sys_ex_debug_trace(&BUFFER[..writer.pos]);
}
} }

54
libusr/src/sync.rs Normal file
View File

@ -0,0 +1,54 @@
use core::cell::UnsafeCell;
use core::ops::{Deref, DerefMut};
use crate::sys::RawMutex;
pub struct Mutex<T> {
inner: RawMutex,
data: UnsafeCell<T>
}
pub struct MutexGuard<'a, T> {
data: &'a mut T,
lock: &'a RawMutex,
}
impl<T> Mutex<T> {
pub fn new(t: T) -> Self {
Self {
inner: RawMutex::new(),
data: UnsafeCell::new(t)
}
}
pub fn lock(&self) -> MutexGuard<'_, T> {
unsafe {
self.inner.lock();
MutexGuard {
data: (&mut *self.data.get()),
lock: &self.inner
}
}
}
}
impl<'a, T> Drop for MutexGuard<'a, T> {
fn drop(&mut self) {
unsafe { self.lock.release(); }
}
}
impl<'a, T> Deref for MutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.data
}
}
impl<'a, T> DerefMut for MutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
self.data
}
}
unsafe impl<T> Sync for Mutex<T> {}

39
libusr/src/sys/mod.rs Normal file
View File

@ -0,0 +1,39 @@
pub use libsys::signal::{Signal, SignalDestination};
pub use libsys::termios;
pub use libsys::calls::*;
pub use libsys::stat::{self, FileDescriptor};
use core::sync::atomic::{Ordering, AtomicBool};
// TODO replace with a proper mutex impl
pub(crate) struct RawMutex {
inner: AtomicBool
}
impl RawMutex {
pub const fn new() -> Self {
Self { inner: AtomicBool::new(false) }
}
#[inline]
unsafe fn try_lock(&self) -> bool {
self.inner.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_ok()
}
#[inline]
unsafe fn is_locked(&self) -> bool {
self.inner.load(Ordering::Acquire)
}
#[inline]
pub unsafe fn lock(&self) {
while !self.try_lock() {
asm!("nop");
}
}
#[inline]
pub unsafe fn release(&self) {
self.inner.store(false, Ordering::Release);
}
}

View File

@ -4,54 +4,22 @@
#[macro_use] #[macro_use]
extern crate libusr; extern crate libusr;
use libusr::sys::stat::{FdSet, FileDescriptor}; use libusr::io::{self, Read};
use libusr::sys::{Signal, SignalDestination};
fn readline(fd: FileDescriptor, buf: &mut [u8]) -> Result<&str, ()> {
// select() just for test
loop {
let mut rfds = FdSet::empty();
rfds.set(fd);
let res = unsafe {
libusr::sys::sys_select(Some(&mut rfds), None, 1_000_000_000).unwrap()
};
if res == 0 {
continue;
}
if !rfds.is_set(fd) {
panic!();
}
let count = unsafe { libusr::sys::sys_read(fd, buf).unwrap() };
return core::str::from_utf8(&buf[..count as usize]).map_err(|_| ());
}
}
#[no_mangle] #[no_mangle]
fn main() -> i32 { fn main() -> i32 {
let mut buf = [0; 512]; let mut buf = [0; 512];
let mut stdin = io::stdin();
eprintln!("stderr test");
loop { loop {
print!("> "); let count = stdin.read(&mut buf).unwrap();
let line = readline(FileDescriptor::STDIN, &mut buf).unwrap(); if count == 0 {
if line.is_empty() {
break;
}
let line = line.trim_end_matches('\n');
println!(":: {:?}", line);
if line == "test" {
unsafe {
libusr::sys::sys_ex_kill(SignalDestination::This, Signal::Interrupt);
}
trace!("Returned from signal");
continue;
}
if line == "quit" || line == "exit" {
break; break;
} }
let line = core::str::from_utf8(&buf[..count]).unwrap();
println!("{:?}", line);
} }
0 0