//! Redox-specific system library. #![no_std] pub use syscall::error; use syscall::error::{Error, Result}; pub mod flag { pub use libc::{ O_ACCMODE, O_CLOEXEC, O_CREAT, O_DIRECTORY, O_NONBLOCK, O_RDONLY, O_RDWR, O_WRONLY, }; pub use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; pub use libc::{SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK}; pub use libc::{SIGUSR1, SIGUSR2}; #[cfg(target_os = "redox")] pub use libc::{O_EXLOCK, O_SHLOCK}; #[cfg(target_os = "redox")] pub const O_STAT: i32 = syscall::flag::O_STAT as i32; pub const MAP_SHARED: u32 = libc::MAP_SHARED as u32; pub const MAP_PRIVATE: u32 = libc::MAP_PRIVATE as u32; pub const PROT_NONE: u32 = libc::PROT_NONE as u32; pub const PROT_READ: u32 = libc::PROT_READ as u32; pub const PROT_WRITE: u32 = libc::PROT_WRITE as u32; pub const PROT_EXEC: u32 = libc::PROT_EXEC as u32; } pub mod errno { pub use libc::{ EACCES, EAGAIN, EBADF, EBADFD, EBUSY, EINTR, ENODEV, EPERM, EPIPE, ESPIPE, ESRCH, EWOULDBLOCK, }; } pub mod data { pub use libc::sigaction as SigAction; pub use libc::timespec as TimeSpec; #[cfg(target_os = "redox")] pub use libc::sigset_t as SigSet; // TODO: Should libredox compile on non-Redox platforms? #[cfg(not(target_os = "redox"))] pub type SigSet = u64; } type RawResult = usize; extern "C" { // NOTE: Although there are version suffixes, there'd have to be strong reasons for adding new // version. fn redox_open_v1(path_base: *const u8, path_len: usize, flags: u32, mode: u16) -> RawResult; fn redox_dup_v1(fd: usize, buf: *const u8, len: usize) -> RawResult; fn redox_dup2_v1(old_fd: usize, new_fd: usize, buf: *const u8, len: usize) -> RawResult; fn redox_read_v1(fd: usize, dst_base: *mut u8, dst_len: usize) -> RawResult; fn redox_write_v1(fd: usize, src_base: *const u8, src_len: usize) -> RawResult; fn redox_fsync_v1(fd: usize) -> RawResult; fn redox_fdatasync_v1(fd: usize) -> RawResult; fn redox_fchmod_v1(fd: usize, new_mode: u16) -> RawResult; fn redox_fchown_v1(fd: usize, new_uid: u32, new_gid: u32) -> RawResult; fn redox_fpath_v1(fd: usize, dst_base: *mut u8, dst_len: usize) -> RawResult; fn redox_close_v1(fd: usize) -> RawResult; // NOTE: While the Redox kernel currently doesn't distinguish between threads and processes, // the return value of this function is expected to be treated as a process ID and not a thread // ID. fn redox_get_pid_v1() -> RawResult; fn redox_get_euid_v1() -> RawResult; fn redox_get_ruid_v1() -> RawResult; fn redox_get_egid_v1() -> RawResult; fn redox_get_rgid_v1() -> RawResult; fn redox_setrens_v1(rns: usize, ens: usize) -> RawResult; fn redox_kill_v1(pid: usize, signal: u32) -> RawResult; fn redox_waitpid_v1(pid: usize, status: *mut i32, options: u32) -> RawResult; fn redox_sigprocmask_v1(how: u32, new: *const u64, old: *mut u64) -> RawResult; fn redox_sigaction_v1( signal: u32, new: *const data::SigAction, old: *mut data::SigAction, ) -> RawResult; fn redox_clock_gettime_v1(clock: usize, ts: *mut data::TimeSpec) -> RawResult; fn redox_mmap_v1( addr: *mut (), unaligned_len: usize, prot: u32, flags: u32, fd: usize, offset: u64, ) -> RawResult; fn redox_munmap_v1(addr: *mut (), unaligned_len: usize) -> RawResult; } #[cfg(feature = "call")] pub struct Fd(usize); #[cfg(feature = "call")] impl Fd { #[inline] pub fn open(path: &str, flags: i32, mode: u16) -> Result { Ok(Self(call::open(path, flags, mode)?)) } #[inline] pub fn dup(&self, buf: &[u8]) -> Result { call::dup(self.raw(), buf) } #[inline] pub fn dup2(&self, new_fd: usize, buf: &[u8]) -> Result { call::dup2(self.raw(), new_fd, buf) } #[inline] pub const fn raw(&self) -> usize { self.0 } #[inline] pub fn into_raw(self) -> usize { let raw = self.raw(); core::mem::forget(self); raw } #[inline] pub fn read(&self, buf: &mut [u8]) -> Result { call::read(self.raw(), buf) } #[inline] pub fn write(&self, buf: &[u8]) -> Result { call::write(self.raw(), buf) } #[inline] pub fn fpath(&self, path: &mut [u8]) -> Result { call::fpath(self.raw(), path) } #[inline] pub fn fsync(&self) -> Result<()> { call::fsync(self.raw()) } #[inline] pub fn fdatasync(&self) -> Result<()> { call::fdatasync(self.raw()) } #[inline] pub fn chmod(&self, new_mode: u16) -> Result<()> { call::fchmod(self.raw(), new_mode) } #[inline] pub fn chown(&self, new_uid: u32, new_gid: u32) -> Result<()> { call::fchown(self.raw(), new_uid, new_gid) } #[inline] pub fn close(self) -> Result<()> { call::close(self.into_raw()) } } #[cfg(feature = "call")] impl Drop for Fd { fn drop(&mut self) { let _ = unsafe { redox_close_v1(self.0) }; } } #[cfg(feature = "call")] pub mod call { use super::*; /// flags and mode are binary compatible with libc #[inline] pub fn open(path: impl AsRef, flags: i32, mode: u16) -> Result { let path = path.as_ref(); Ok(Error::demux(unsafe { redox_open_v1(path.as_ptr(), path.len(), flags as u32, mode) })?) } #[inline] pub fn dup(fd: usize, buf: impl AsRef<[u8]>) -> Result { let buf = buf.as_ref(); Ok(Error::demux(unsafe { redox_dup_v1(fd, buf.as_ptr(), buf.len()) })?) } #[inline] pub fn dup2(old_fd: usize, new_fd: usize, buf: impl AsRef<[u8]>) -> Result { let buf = buf.as_ref(); Ok(Error::demux(unsafe { redox_dup2_v1(old_fd, new_fd, buf.as_ptr(), buf.len()) })?) } #[inline] pub fn read(raw_fd: usize, buf: &mut [u8]) -> Result { Ok(Error::demux(unsafe { redox_read_v1(raw_fd, buf.as_mut_ptr(), buf.len()) })?) } #[inline] pub fn write(raw_fd: usize, buf: &[u8]) -> Result { Ok(Error::demux(unsafe { redox_write_v1(raw_fd, buf.as_ptr(), buf.len()) })?) } #[inline] pub fn fsync(raw_fd: usize) -> Result<()> { Error::demux(unsafe { redox_fsync_v1(raw_fd) }).map(|_| ()) } #[inline] pub fn fdatasync(raw_fd: usize) -> Result<()> { Ok(Error::demux(unsafe { redox_fdatasync_v1(raw_fd) })?).map(|_| ()) } #[inline] pub fn fchmod(raw_fd: usize, new_mode: u16) -> Result<()> { Error::demux(unsafe { redox_fchmod_v1(raw_fd, new_mode) })?; Ok(()) } #[inline] pub fn fchown(raw_fd: usize, new_uid: u32, new_gid: u32) -> Result<()> { Error::demux(unsafe { redox_fchown_v1(raw_fd, new_uid, new_gid) })?; Ok(()) } #[inline] pub fn fpath(raw_fd: usize, buf: &mut [u8]) -> Result { Ok(Error::demux(unsafe { redox_fpath_v1(raw_fd, buf.as_mut_ptr(), buf.len()) })?) } #[inline] pub fn close(raw_fd: usize) -> Result<()> { Error::demux(unsafe { redox_close_v1(raw_fd) })?; Ok(()) } #[inline] pub fn geteuid() -> Result { Error::demux(unsafe { redox_get_euid_v1() }) } #[inline] pub fn getruid() -> Result { Error::demux(unsafe { redox_get_ruid_v1() }) } #[inline] pub fn getegid() -> Result { Error::demux(unsafe { redox_get_egid_v1() }) } #[inline] pub fn getrgid() -> Result { Error::demux(unsafe { redox_get_rgid_v1() }) } #[inline] pub fn getpid() -> Result { Error::demux(unsafe { redox_get_pid_v1() }) } #[inline] pub fn setrens(rns: usize, ens: usize) -> Result { Error::demux(unsafe { redox_setrens_v1(rns, ens) }) } #[inline] pub fn waitpid(pid: usize, status: &mut i32, options: i32) -> Result { Error::demux(unsafe { redox_waitpid_v1(pid, status as *mut i32, options as u32) }) } #[inline] pub fn kill(pid: usize, signal: u32) -> Result<()> { Error::demux(unsafe { redox_kill_v1(pid, signal) }).map(|_| ()) } #[inline] pub fn clock_gettime(clock: i32, ts: &mut data::TimeSpec) -> Result<()> { Error::demux(unsafe { redox_clock_gettime_v1(clock as usize, ts) }).map(|_| ()) } #[inline] pub fn sigprocmask( how: i32, newmask: Option<&data::SigSet>, oldmask: Option<&mut data::SigSet>, ) -> Result<()> { Error::demux(unsafe { redox_sigprocmask_v1( how as u32, newmask.map_or(core::ptr::null(), |m| m), oldmask.map_or(core::ptr::null_mut(), |m| m), ) }) .map(|_| ()) } #[inline] pub fn sigaction( signal: i32, newact: Option<&data::SigAction>, oldact: Option<&mut data::SigAction>, ) -> Result<()> { Error::demux(unsafe { redox_sigaction_v1( signal as u32, newact.map_or(core::ptr::null(), |m| m), oldact.map_or(core::ptr::null_mut(), |m| m), ) }) .map(|_| ()) } #[derive(Clone, Copy, Debug)] pub struct MmapArgs { pub addr: *mut (), pub length: usize, pub prot: u32, pub flags: u32, pub fd: usize, pub offset: u64, } #[inline] pub unsafe fn mmap(args: MmapArgs) -> Result<*mut ()> { Error::demux(redox_mmap_v1( args.addr, args.length, args.prot, args.flags, args.fd, args.offset, )) .map(|addr| addr as *mut ()) } #[inline] pub unsafe fn munmap(addr: *mut (), length: usize) -> Result<()> { Error::demux(redox_munmap_v1(addr, length)).map(|_| ()) } } #[cfg(feature = "scheme")] pub mod scheme { use super::*; use core::mem::size_of; use syscall::Packet; pub use syscall::{Scheme, SchemeBlock, SchemeBlockMut, SchemeMut}; #[repr(transparent)] #[derive(Clone, Copy, Default)] pub struct Request(Packet); impl Request { pub fn handle_scheme(mut self, scheme: &impl Scheme) -> Response { scheme.handle(&mut self.0); Response(self.0) } pub fn handle_scheme_mut(mut self, scheme: &mut impl SchemeMut) -> Response { scheme.handle(&mut self.0); Response(self.0) } pub fn handle_scheme_block( mut self, scheme: &mut impl SchemeBlock, ) -> Result { match scheme.handle(&mut self.0) { Some(code) => Ok(Response(Packet { a: code, ..self.0 })), None => Err(self), } } pub fn handle_scheme_block_mut( mut self, scheme: &mut impl SchemeBlockMut, ) -> Result { match scheme.handle(&mut self.0) { Some(code) => Ok(Response(Packet { a: code, ..self.0 })), None => Err(self), } } } #[repr(transparent)] #[derive(Clone, Copy, Default)] pub struct Response(Packet); impl Response { pub fn new(req: &Request, status: Result) -> Self { Self(Packet { a: Error::mux(status), ..req.0 }) } } pub enum SignalBehavior { Interrupt, Restart, } // TODO: Support uninitialized memory #[inline] pub fn read_requests( socket: usize, buf: &mut [Request], behavior: SignalBehavior, ) -> Result { let len = buf.len().checked_mul(size_of::()).unwrap(); let bytes_read = loop { match call::read(socket, unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), len) }) { Ok(n) => break n, error @ Err(Error { errno: errno::EINTR, }) => match behavior { SignalBehavior::Restart => continue, SignalBehavior::Interrupt => return error, }, Err(err) => return Err(err), } }; debug_assert_eq!(bytes_read % size_of::(), 0); Ok(bytes_read / size_of::()) } #[inline] pub fn write_responses( socket: usize, buf: &[Response], behavior: SignalBehavior, ) -> Result { let bytes = unsafe { core::slice::from_raw_parts( buf.as_ptr().cast(), buf.len().checked_mul(size_of::()).unwrap(), ) }; let bytes_written = loop { match call::write(socket, bytes) { Ok(n) => break n, error @ Err(Error { errno: errno::EINTR, }) => match behavior { SignalBehavior::Restart => continue, SignalBehavior::Interrupt => return error, }, Err(err) => return Err(err), } }; debug_assert_eq!(bytes_written % size_of::(), 0); Ok(bytes_written / size_of::()) } }