diff --git a/src/header/_aio/mod.rs b/src/header/_aio/mod.rs index 904ef8ce52..99e6859b1f 100644 --- a/src/header/_aio/mod.rs +++ b/src/header/_aio/mod.rs @@ -1,5 +1,5 @@ use crate::{ - header::time::{sigevent, timespec}, + header::{signal::sigevent, time::timespec}, platform::types::*, }; diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index e2606c60a0..4dcde99ea1 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -37,6 +37,10 @@ pub const SIG_SETMASK: c_int = 2; pub const SI_QUEUE: c_int = -1; pub const SI_USER: c_int = 0; +pub const SIGEV_SIGNAL: c_int = 0; +pub const SIGEV_NONE: c_int = 1; +pub const SIGEV_THREAD: c_int = 2; + #[repr(C)] #[derive(Clone, Debug)] /// cbindgen:ignore @@ -55,6 +59,16 @@ pub struct sigaltstack { pub ss_size: size_t, } +#[repr(C)] +#[derive(Clone)] +pub struct sigevent { + pub sigev_value: sigval, + pub sigev_signo: c_int, + pub sigev_notify: c_int, + pub sigev_notify_function: Option, + pub sigev_notify_attributes: *mut pthread_attr_t, +} + // FIXME: This struct is wrong on Linux #[repr(C)] #[derive(Clone, Copy)] @@ -525,3 +539,6 @@ pub unsafe extern "C" fn psiginfo(info: *const siginfo_t, prefix: *const c_char) string.as_bytes().len(), ); } + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbindgen_stupid_struct_sigevent_for_timer(a: sigevent) {} diff --git a/src/header/time/cbindgen.toml b/src/header/time/cbindgen.toml index 0ca04e77a9..c3fdddfb1f 100644 --- a/src/header/time/cbindgen.toml +++ b/src/header/time/cbindgen.toml @@ -1,4 +1,4 @@ -sys_includes = ["sys/types.h", "stdint.h", "stddef.h", "features.h"] +sys_includes = ["sys/types.h", "signal.h", "stdint.h", "stddef.h", "features.h"] include_guard = "_RELIBC_TIME_H" language = "C" style = "Tag" @@ -7,3 +7,6 @@ cpp_compat = true [enum] prefix_with_name = true + +[export.rename] +"sigevent" = "struct sigevent" diff --git a/src/header/time/mod.rs b/src/header/time/mod.rs index 6b81533a73..93dd9aaa96 100644 --- a/src/header/time/mod.rs +++ b/src/header/time/mod.rs @@ -6,7 +6,9 @@ use crate::{ c_str::{CStr, CString}, error::ResultExt, fs::File, - header::{errno::EOVERFLOW, fcntl::O_RDONLY, stdlib::getenv, unistd::readlink}, + header::{ + errno::EOVERFLOW, fcntl::O_RDONLY, signal::sigevent, stdlib::getenv, unistd::readlink, + }, io::Read, out::Out, platform::{self, Pal, Sys, types::*}, @@ -46,13 +48,29 @@ pub struct timespec { } impl timespec { + // TODO: Write test + pub fn add(base: timespec, interval: timespec) -> Option { + let base_nsec = c_ulong::try_from(base.tv_nsec).ok()?; + let interval_nsec = c_ulong::try_from(interval.tv_nsec).ok()?; + + Some(if base_nsec.checked_add(interval_nsec)? < 1_000_000_000 { + timespec { + tv_sec: base.tv_sec.checked_add(interval.tv_sec)?, + tv_nsec: (base_nsec + interval_nsec) as _, + } + } else { + timespec { + tv_sec: base.tv_sec.checked_add(interval.tv_sec)?.checked_add(1)?, + tv_nsec: ((interval_nsec + base_nsec) - 1_000_000_000) as c_long, + } + }) + } // TODO: Write test pub fn subtract(later: timespec, earlier: timespec) -> Option { - // TODO: Can tv_nsec be negative? let later_nsec = c_ulong::try_from(later.tv_nsec).ok()?; let earlier_nsec = c_ulong::try_from(earlier.tv_nsec).ok()?; - Some(if later_nsec > earlier_nsec { + let time = if later_nsec > earlier_nsec { timespec { tv_sec: later.tv_sec.checked_sub(earlier.tv_sec)?, tv_nsec: (later_nsec - earlier_nsec) as _, @@ -62,7 +80,18 @@ impl timespec { tv_sec: later.tv_sec.checked_sub(earlier.tv_sec)?.checked_sub(1)?, tv_nsec: 1_000_000_000 - (earlier_nsec - later_nsec) as c_long, } - }) + }; + + if time.tv_sec < 0 { + // https://man7.org/linux/man-pages/man2/settimeofday.2.html + // caller should return EINVAL + return None; + } + + Some(time) + } + pub fn is_default(&self) -> bool { + return self.tv_nsec == 0 && self.tv_sec == 0; } } @@ -76,6 +105,20 @@ impl<'a> From<&'a timespec> for syscall::TimeSpec { } } +/// timer_t internal data, ABI unstable +#[repr(C)] +#[derive(Clone)] +#[cfg(target_os = "redox")] +pub(crate) struct timer_internal_t { + pub clockid: clockid_t, + pub timerfd: usize, + pub eventfd: usize, + pub evp: sigevent, + pub thread: pthread_t, + // relibc handles it_interval, not the kernel + pub next_wake_time: itimerspec, +} + /// See . #[repr(C)] pub struct tm { @@ -132,14 +175,12 @@ pub static mut getdate_err: c_int = 0; /// See . #[repr(C)] +#[derive(Clone, Default)] pub struct itimerspec { pub it_interval: timespec, pub it_value: timespec, } -/// See . -pub struct sigevent; - /// See . /// /// # Deprecation @@ -493,19 +534,21 @@ pub unsafe extern "C" fn timelocal(tm: *mut tm) -> time_t { } /// See . -// #[unsafe(no_mangle)] +#[unsafe(no_mangle)] pub extern "C" fn timer_create( clock_id: clockid_t, evp: *mut sigevent, timerid: *mut timer_t, ) -> c_int { - unimplemented!(); + Sys::timer_create(clock_id, evp, timerid) + .map(|()| 0) + .or_minus_one_errno() } /// See . -// #[unsafe(no_mangle)] +#[unsafe(no_mangle)] pub extern "C" fn timer_delete(timerid: timer_t) -> c_int { - unimplemented!(); + Sys::timer_delete(timerid).map(|()| 0).or_minus_one_errno() } /// See . @@ -515,20 +558,24 @@ pub extern "C" fn timer_getoverrun(timerid: timer_t) -> c_int { } /// See . -// #[unsafe(no_mangle)] +#[unsafe(no_mangle)] pub extern "C" fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> c_int { - unimplemented!(); + Sys::timer_gettime(timerid, value) + .map(|()| 0) + .or_minus_one_errno() } /// See . -// #[unsafe(no_mangle)] +#[unsafe(no_mangle)] pub extern "C" fn timer_settime( timerid: timer_t, flags: c_int, value: *const itimerspec, ovalue: *mut itimerspec, ) -> c_int { - unimplemented!(); + Sys::timer_settime(timerid, flags, value, ovalue) + .map(|()| 0) + .or_minus_one_errno() } /// See . diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 53a51b8668..bc53294ae8 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -7,11 +7,12 @@ use crate::{ dirent::dirent, errno::{EINVAL, EIO, EOPNOTSUPP}, fcntl::{AT_EMPTY_PATH, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_NOFOLLOW}, - signal::SIGCHLD, + signal::{SIGCHLD, sigevent}, sys_resource::{rlimit, rusage}, sys_stat::{S_IFIFO, stat}, sys_statvfs::statvfs, sys_time::{timeval, timezone}, + time::itimerspec, unistd::{SEEK_CUR, SEEK_SET}, }, io::Write, @@ -725,6 +726,27 @@ impl Pal for Sys { e_raw(unsafe { syscall!(SYNC) }).map(|_| ()) } + fn timer_create(clock_id: clockid_t, evp: *mut sigevent, timerid: *mut timer_t) -> Result<()> { + e_raw(unsafe { syscall!(TIMER_CREATE, clock_id, evp, timerid) }).map(|_| ()) + } + + fn timer_delete(timerid: timer_t) -> Result<()> { + e_raw(unsafe { syscall!(TIMER_DELETE, timerid) }).map(|_| ()) + } + + fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> Result<()> { + e_raw(unsafe { syscall!(TIMER_GETTIME, timerid, value) }).map(|_| ()) + } + + fn timer_settime( + timerid: timer_t, + flags: c_int, + value: *const itimerspec, + ovalue: *mut itimerspec, + ) -> Result<()> { + e_raw(unsafe { syscall!(TIMER_SETTIME, timerid, flags, value, ovalue) }).map(|_| ()) + } + fn umask(mask: mode_t) -> mode_t { unsafe { syscall!(UMASK, mask) as mode_t } } diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index d497d1fcea..38c114e355 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -3,12 +3,13 @@ use crate::{ c_str::CStr, error::{Errno, Result}, header::{ + signal::sigevent, sys_resource::{rlimit, rusage}, sys_stat::stat, sys_statvfs::statvfs, sys_time::{timeval, timezone}, sys_utsname::utsname, - time::timespec, + time::{itimerspec, timespec}, }, out::Out, pthread, @@ -252,6 +253,19 @@ pub trait Pal { fn sync() -> Result<()>; + fn timer_create(clock_id: clockid_t, evp: *mut sigevent, timerid: *mut timer_t) -> Result<()>; + + fn timer_delete(timerid: timer_t) -> Result<()>; + + fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> Result<()>; + + fn timer_settime( + timerid: timer_t, + flags: c_int, + value: *const itimerspec, + ovalue: *mut itimerspec, + ) -> Result<()>; + // Always successful fn umask(mask: mode_t) -> mode_t; diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 4d0a4a80ad..60c75b26ab 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -21,11 +21,13 @@ use crate::{ header::{ dirent::dirent, errno::{ - EBADF, EBADFD, EBADR, EINTR, EINVAL, EIO, ENAMETOOLONG, ENOENT, ENOMEM, ENOSYS, + EBADF, EBADFD, EBADR, EFAULT, EINTR, EINVAL, EIO, ENAMETOOLONG, ENOENT, ENOMEM, ENOSYS, EOPNOTSUPP, EPERM, ERANGE, }, - fcntl::{self, AT_FDCWD, O_RDONLY}, + fcntl::{self, AT_FDCWD, O_CREAT, O_RDONLY, O_RDWR}, limits, + pthread::{pthread_cancel, pthread_create}, + signal::{SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD, sigevent}, sys_mman::{MAP_ANONYMOUS, MAP_FAILED, PROT_READ, PROT_WRITE}, sys_random, sys_resource::{RLIM_INFINITY, rlimit, rusage}, @@ -34,11 +36,15 @@ use crate::{ sys_time::{timeval, timezone}, sys_utsname::{UTSLENGTH, utsname}, sys_wait, - time::timespec, + time::{itimerspec, timer_internal_t, timespec}, unistd::{F_OK, R_OK, SEEK_CUR, SEEK_SET, W_OK, X_OK}, }, io::{self, BufReader, prelude::*}, out::Out, + platform::sys::{ + libredox::RawResult, + timer::{timer_routine, timer_update_wake_time}, + }, sync::rwlock::RwLock, }; @@ -73,6 +79,7 @@ pub(crate) mod path; mod ptrace; pub(crate) mod signal; mod socket; +mod timer; macro_rules! path_from_c_str { ($c_str:expr) => {{ @@ -863,6 +870,7 @@ impl Pal for Sys { let fd = usize::try_from(fd).map_err(|_| Errno(EBADF))?; Ok(redox_rt::sys::posix_read(fd, buf)?) } + fn pread(fd: c_int, buf: &mut [u8], offset: off_t) -> Result { unsafe { Ok(syscall::syscall5( @@ -1008,6 +1016,178 @@ impl Pal for Sys { Ok(()) } + fn timer_create(clock_id: clockid_t, evp: *mut sigevent, timerid: *mut timer_t) -> Result<()> { + if timerid.is_null() || evp.is_null() { + return Err(Errno(EFAULT)); + } + let ev = unsafe { &*evp }; + if ev.sigev_notify == SIGEV_THREAD && ev.sigev_notify_function.is_none() { + return Err(Errno(EINVAL)); + } + + let path = format!("/scheme/time/{clock_id}"); + let timerfd = libredox::open(&path, O_RDWR, 0)?; + + unsafe { + let eventfd = Error::demux(event::redox_event_queue_create_v1(0)).map_err(|e| { + let _ = syscall::close(timerfd); + e + })?; + + let timer_buf = Self::mmap( + ptr::null_mut(), + size_of::(), + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS, + 0, + 0, + ) + .map_err(|e| { + let _ = syscall::close(timerfd); + let _ = syscall::close(eventfd); + e + })?; + + *timerid = timer_buf; + + let timer_ptr = timer_buf as *mut timer_internal_t; + let timer_st = (&mut *timer_ptr); + + timer_st.clockid = clock_id; + timer_st.timerfd = timerfd; + timer_st.eventfd = eventfd; + timer_st.evp = (*evp).clone(); + timer_st.next_wake_time = itimerspec::default(); + timer_st.thread = ptr::null_mut(); + } + Ok(()) + } + + fn timer_delete(timerid: timer_t) -> Result<()> { + if timerid.is_null() { + return Err(Errno(EFAULT)); + } + unsafe { + let timer_ptr = timerid as *mut timer_internal_t; + let timer_st = (&mut *timer_ptr); + let _ = syscall::close(timer_st.timerfd); + let _ = syscall::close(timer_st.eventfd); + if !timer_st.thread.is_null() { + let _ = pthread_cancel(timer_st.thread); + } + Self::munmap(timer_ptr as *mut c_void, size_of::())?; + } + + Ok(()) + } + + fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> Result<()> { + if value.is_null() { + return Err(Errno(EFAULT)); + } + + let timer_st = unsafe { &mut *(timerid as *mut timer_internal_t) }; + let mut now = timespec::default(); + Self::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?; + + if timer_st.evp.sigev_notify == SIGEV_NONE { + if timespec::subtract(timer_st.next_wake_time.it_value, now).is_none() { + // error here means the timer is disarmed + let _ = timer_update_wake_time(timer_st); + } + } + + unsafe { + *value = if timer_st.next_wake_time.it_value.is_default() { + // disarmed + itimerspec::default() + } else { + itimerspec { + it_interval: timer_st.next_wake_time.it_interval, + it_value: timespec::subtract(timer_st.next_wake_time.it_value, now) + .unwrap_or_default(), + } + }; + } + + Ok(()) + } + + fn timer_settime( + timerid: timer_t, + _flags: c_int, + value: *const itimerspec, + ovalue: *mut itimerspec, + ) -> Result<()> { + if timerid.is_null() || value.is_null() { + return Err(Errno(EFAULT)); + } + + let timer_st = unsafe { &mut *(timerid as *mut timer_internal_t) }; + + if !ovalue.is_null() { + Self::timer_gettime(timerid, ovalue)?; + } + + let mut now = timespec::default(); + Self::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?; + + //FIXME: make these atomic? + timer_st.next_wake_time = unsafe { + let mut val = (*value).clone(); + val.it_value = timespec::add(now, val.it_value).ok_or((Errno(EINVAL)))?; + val + }; + + Error::demux(unsafe { + event::redox_event_queue_ctl_v1(timer_st.eventfd, timer_st.timerfd, 1, 0) + })?; + + let buf_to_write = unsafe { + slice::from_raw_parts( + &timer_st.next_wake_time.it_value as *const _ as *const u8, + mem::size_of::(), + ) + }; + + let bytes_written = redox_rt::sys::posix_write(timer_st.timerfd, buf_to_write)?; + + if bytes_written < mem::size_of::() { + return Err(Errno(EIO)); + } + + if timer_st.thread.is_null() { + timer_st.thread = match timer_st.evp.sigev_notify { + SIGEV_THREAD => { + let mut tid = pthread_t::default(); + + let result = unsafe { + pthread_create( + &mut tid as *mut _, + ptr::null(), + timer_routine, + timerid as *mut c_void, + ) + }; + if result != 0 { + return Err(Errno(result)); + } + tid + } + //TODO + SIGEV_SIGNAL => { + return Err(Errno(ENOSYS)); + } + SIGEV_NONE => ptr::null_mut(), + _ => { + return Err(Errno(EINVAL)); + } + }; + } + + Ok(()) + } + fn umask(mask: mode_t) -> mode_t { let new_effective_mask = mask & mode_t::from(MODE_PERM) & !S_ISVTX; (redox_rt::sys::swap_umask(new_effective_mask as u32) as mode_t) & !S_ISVTX diff --git a/src/platform/redox/timer.rs b/src/platform/redox/timer.rs new file mode 100644 index 0000000000..59cdb578b1 --- /dev/null +++ b/src/platform/redox/timer.rs @@ -0,0 +1,100 @@ +use ::event::raw::RawEventV1; +use syscall::Error; + +use crate::{ + error::{Errno, Result}, + header::{ + errno::EIO, + signal::{sigevent, sigval}, + time::{timer_internal_t, timespec}, + }, + out::Out, + platform::{ + Pal, Sys, + sys::{event, libredox}, + types::c_void, + }, +}; +use core::{ + mem::{MaybeUninit, size_of, transmute}, + ops::ControlFlow, + ptr, slice, +}; + +pub extern "C" fn timer_routine(arg: *mut c_void) -> *mut c_void { + unsafe { + let timer_st = &mut *(arg as *mut timer_internal_t); + + loop { + let mut buf = MaybeUninit::uninit(); + + unsafe { + let res = Error::demux(event::redox_event_queue_get_events_v1( + timer_st.eventfd, + buf.as_mut_ptr(), + 1, + 0, + core::ptr::null(), + core::ptr::null(), + )); + if let Ok(res) = res { + assert_eq!(res, 1, "EOF is not yet well defined for event queues"); + } else { + timer_st.thread = ptr::null_mut(); + break; + } + + if let Some(fun) = timer_st.evp.sigev_notify_function { + fun(timer_st.evp.sigev_value); + } + + if timer_next_event(timer_st).is_err() { + timer_st.thread = ptr::null_mut(); + break; + } + } + } + } + + ptr::null_mut() +} + +fn timer_next_event(timer_st: &mut timer_internal_t) -> Result<()> { + timer_update_wake_time(timer_st)?; + + Error::demux(unsafe { + event::redox_event_queue_ctl_v1(timer_st.eventfd, timer_st.timerfd, 1, 0) + })?; + + let buf_to_write = unsafe { + slice::from_raw_parts( + &timer_st.next_wake_time.it_value as *const _ as *const u8, + size_of::(), + ) + }; + + let bytes_written = redox_rt::sys::posix_write(timer_st.timerfd, buf_to_write)?; + if bytes_written < size_of::() { + return Err(Errno(EIO)); + } + Ok(()) +} + +pub(crate) fn timer_update_wake_time(timer_st: &mut timer_internal_t) -> Result<()> { + timer_st.next_wake_time.it_value = if timer_st.next_wake_time.it_interval.is_default() { + timespec::default() + } else { + let mut now = timespec::default(); + Sys::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?; + let next_time = match timespec::add(now, timer_st.next_wake_time.it_interval) { + Some(a) => a, + None => timespec::default(), + }; + + next_time + }; + if timer_st.next_wake_time.it_value.is_default() { + return Err(Errno(0)); + } + Ok(()) +}