diff --git a/Cargo.toml b/Cargo.toml index ba572fcb6d..1a42d4668c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,11 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["scheme", "call"] +scheme = ["call"] +call = [] + [dependencies] bitflags = "2" libc = "0.2" diff --git a/src/lib.rs b/src/lib.rs index 9677731e9c..9de6165b0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,10 @@ pub mod flag { pub use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; + pub use libc::{SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK}; + + pub use libc::{SIGUSR1, SIGUSR2}; + #[cfg(target_os = "redox")] pub use libc::{O_SHLOCK, O_EXLOCK}; @@ -21,6 +25,8 @@ pub mod errno { } pub mod data { pub use libc::timespec as TimeSpec; + pub use libc::sigaction as SigAction; + pub use libc::sigset_t as SigSet; } type RawResult = usize; @@ -40,18 +46,30 @@ extern "C" { 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; } +#[cfg(feature = "call")] pub struct Fd(usize); +#[cfg(feature = "call")] impl Fd { #[inline] pub fn open(path: &str, flags: i32, mode: u16) -> Result { @@ -89,11 +107,11 @@ impl Fd { call::fpath(self.raw(), path) } #[inline] - pub fn fsync(&self) -> Result { + pub fn fsync(&self) -> Result<()> { call::fsync(self.raw()) } #[inline] - pub fn fdatasync(&self) -> Result { + pub fn fdatasync(&self) -> Result<()> { call::fdatasync(self.raw()) } @@ -110,12 +128,14 @@ impl Fd { 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::*; @@ -144,12 +164,12 @@ pub mod call { Ok(Error::demux(unsafe { redox_write_v1(raw_fd, buf.as_ptr(), buf.len()) })?) } #[inline] - pub fn fsync(raw_fd: usize) -> Result { - Ok(Error::demux(unsafe { redox_fsync_v1(raw_fd) })?) + 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) })?) + 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<()> { @@ -190,6 +210,10 @@ pub mod call { 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) }) } @@ -198,7 +222,113 @@ pub mod call { Error::demux(unsafe { redox_waitpid_v1(pid, status as *mut i32, options as u32) }) } #[inline] - pub fn clock_gettime(clock: i32, ts: &mut data::TimeSpec) -> Result { - Error::demux(unsafe { redox_clock_gettime_v1(clock as usize, ts) }) + 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(|_| ()) + } +} + +#[cfg(feature = "scheme")] +pub mod scheme { + use super::*; + + use core::mem::size_of; + + pub use syscall::{Scheme, SchemeMut, SchemeBlock, SchemeBlockMut}; + use syscall::Packet; + + #[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::()) } }