From 85eb9a7c74cf8f10eefb13c0b3036b6d91d57498 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Fri, 12 Jan 2024 18:51:07 +0200 Subject: [PATCH] time: most of --- Cargo.toml | 1 + src/header/mod.rs | 2 +- src/header/sys_types/mod.rs | 9 ++- src/header/time/cbindgen.toml | 3 +- src/header/time/convert.rs | 64 ++++++++++++++++++ src/header/time/mod.rs | 120 +++++++++++++++++++++++++++++++++- src/header/time/string.rs | 109 ++++++++++++++++++++++++++++++ src/header/time/timer.rs | 42 ++++++++++++ src/header/time/utility.rs | 47 +++++++++++++ src/util.rs | 60 ++++++++++++++++- 10 files changed, 451 insertions(+), 6 deletions(-) create mode 100644 src/header/time/convert.rs create mode 100644 src/header/time/string.rs create mode 100644 src/header/time/timer.rs create mode 100644 src/header/time/utility.rs diff --git a/Cargo.toml b/Cargo.toml index f903127..3f4e4b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ crate-type = ["staticlib"] yggdrasil-rt = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-rt.git" } libyalloc = { git = "https://git.alnyan.me/yggdrasil/libyalloc.git" } bitflags = { version = "2.4.1" } +chrono = { version = "0.4.31", default-features = false } [build-dependencies] cbindgen = { git = "https://git.alnyan.me/yggdrasil/cbindgen.git", branch = "master" } diff --git a/src/header/mod.rs b/src/header/mod.rs index 5847217..f1bdfc5 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -62,7 +62,7 @@ MAYBE pthread.h - threads ----- sched.h - execution scheduling ----- search.h - search tables ----- semaphore.h - semaphores ------ setjmp.h - stack environment declarations +MOSTLY setjmp.h - stack environment declarations TODO signal? ----- signal.h - signals MAYBE spawn.h - spawn (ADVANCED REALTIME) +++++ stdarg.h - handle variable argument list diff --git a/src/header/sys_types/mod.rs b/src/header/sys_types/mod.rs index 02888b9..0f7c001 100644 --- a/src/header/sys_types/mod.rs +++ b/src/header/sys_types/mod.rs @@ -24,8 +24,15 @@ pub type fsfilcnt_t = u64; pub type ino_t = u64; pub type clock_t = u64; -pub type time_t = u64; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[repr(transparent)] +pub struct time_t(pub u64); pub type off_t = i64; pub type suseconds_t = c_ulong; + +impl time_t { + pub const INVALID: Self = Self(u64::MAX); +} diff --git a/src/header/time/cbindgen.toml b/src/header/time/cbindgen.toml index 48d1068..afaf310 100644 --- a/src/header/time/cbindgen.toml +++ b/src/header/time/cbindgen.toml @@ -4,6 +4,7 @@ style = "Tag" sys_includes = [ "stddef.h", "stdint.h", + "locale.h", "sys/types.h", "bits/time.h" ] @@ -15,4 +16,4 @@ usize_type = "size_t" isize_type = "ssize_t" [export] -include = ["timespec"] +include = ["timespec", "tm", "itimerspec"] diff --git a/src/header/time/convert.rs b/src/header/time/convert.rs new file mode 100644 index 0000000..69750f3 --- /dev/null +++ b/src/header/time/convert.rs @@ -0,0 +1,64 @@ +use core::{cmp::Ordering, mem::MaybeUninit, ptr::null_mut}; + +use chrono::Utc; + +use crate::header::{sys_types::time_t, time::tm}; + +use super::{get_timezone, CDateTime}; + +#[no_mangle] +unsafe extern "C" fn gmtime(tp: *const time_t) -> *mut tm { + static mut BUFFER: MaybeUninit = MaybeUninit::uninit(); + gmtime_r(tp, BUFFER.as_mut_ptr()) +} + +#[no_mangle] +unsafe extern "C" fn gmtime_r(tp: *const time_t, timep: *mut tm) -> *mut tm { + let tp = tp.as_ref().unwrap(); + let timep = timep.as_mut().unwrap(); + + match tp.to_datetime(Utc) { + Some(t) => { + *timep = t.into(); + timep + } + None => null_mut(), + } +} + +#[no_mangle] +unsafe extern "C" fn localtime(tp: *const time_t) -> *mut tm { + static mut BUFFER: MaybeUninit = MaybeUninit::uninit(); + localtime_r(tp, BUFFER.as_mut_ptr()) +} + +#[no_mangle] +unsafe extern "C" fn localtime_r(tp: *const time_t, timep: *mut tm) -> *mut tm { + let tp = tp.as_ref().unwrap(); + let timep = timep.as_mut().unwrap(); + + let tz = get_timezone(); + match tp.to_datetime(tz) { + Some(t) => { + *timep = t.into(); + timep + } + None => null_mut(), + } +} + +#[no_mangle] +unsafe extern "C" fn mktime(timep: *const tm) -> time_t { + let timep = timep.as_ref().unwrap(); + let is_dst = match timep.tm_isdst.cmp(&0) { + Ordering::Less => todo!(), + Ordering::Equal => false, + Ordering::Greater => true, + }; + + let tz = get_timezone(); + match timep.to_datetime(tz) { + Some(t) => time_t(t.timestamp() as _), + None => time_t::INVALID, + } +} diff --git a/src/header/time/mod.rs b/src/header/time/mod.rs index 8a9acc9..e380122 100644 --- a/src/header/time/mod.rs +++ b/src/header/time/mod.rs @@ -1,7 +1,29 @@ -use core::ffi::c_long; +use core::{ + ffi::{c_char, c_int, c_long}, + ptr::null_mut, + time::Duration, +}; + +use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc}; use super::sys_types::time_t; +mod convert; +mod string; +mod timer; +mod utility; + +/* +TODO +int timer_create(clockid_t, struct sigevent *restrict, + timer_t *restrict); +int timer_delete(timer_t); +int timer_getoverrun(timer_t); +int timer_gettime(timer_t, struct itimerspec *); +int timer_settime(timer_t, int, const struct itimerspec *restrict, + struct itimerspec *restrict); +*/ + #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] #[repr(C)] pub struct timespec { @@ -9,4 +31,100 @@ pub struct timespec { pub tv_nsec: c_long, } +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[repr(C)] +pub struct itimerspec { + pub it_interval: timespec, + pub it_value: timespec, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[repr(C)] +pub struct tm { + pub tm_sec: c_int, + pub tm_min: c_int, + pub tm_hour: c_int, + pub tm_mday: c_int, + pub tm_mon: c_int, + pub tm_year: c_int, + pub tm_wday: c_int, + pub tm_yday: c_int, + pub tm_isdst: c_int, +} + pub type __ygg_timespec_t = timespec; + +#[no_mangle] +pub static mut daylight: c_int = 0; +#[no_mangle] +pub static mut timezone: c_long = 0; +#[no_mangle] +pub static mut tzname: *mut *mut c_char = null_mut(); + +pub trait CDateTime { + fn to_naive_datetime(&self) -> Option; + + fn to_datetime(&self, tz: Tz) -> Option> { + self.to_naive_datetime()?.and_local_timezone(tz).latest() + } +} + +impl tm { + pub fn naive_date(&self) -> Option { + Some(NaiveDate::from_ymd( + self.tm_year + 1900, + u32::try_from(self.tm_mon).ok()? + 1, + u32::try_from(self.tm_mday).ok()?, + )) + } + + pub fn naive_time(&self) -> Option { + NaiveTime::from_hms_opt( + u32::try_from(self.tm_hour).ok()?, + u32::try_from(self.tm_min).ok()?, + u32::try_from(self.tm_sec).ok()?, + ) + } +} + +impl CDateTime for tm { + fn to_naive_datetime(&self) -> Option { + let date = self.naive_date()?; + let time = self.naive_time()?; + + Some(NaiveDateTime::new(date, time)) + } +} + +impl CDateTime for time_t { + fn to_naive_datetime(&self) -> Option { + NaiveDateTime::from_timestamp_opt(self.0.try_into().ok()?, 0) + } +} + +impl From for tm { + fn from(value: T) -> Self { + Self { + tm_sec: value.second() as _, + tm_min: value.minute() as _, + tm_hour: value.hour() as _, + tm_mday: value.day() as _, + tm_mon: value.month0() as _, + tm_year: value.year() - 1900, + tm_wday: value.weekday().num_days_from_sunday() as _, + tm_yday: value.ordinal0() as _, + tm_isdst: 0, + } + } +} + +impl From for Duration { + fn from(value: timespec) -> Self { + Self::new(value.tv_sec.0, value.tv_nsec.try_into().unwrap()) + } +} + +pub unsafe fn get_timezone() -> impl TimeZone { + // TODO get it from C + Utc +} diff --git a/src/header/time/string.rs b/src/header/time/string.rs new file mode 100644 index 0000000..0131042 --- /dev/null +++ b/src/header/time/string.rs @@ -0,0 +1,109 @@ +use core::{ffi::c_char, ptr::null_mut}; + +use chrono::{Datelike, TimeZone, Timelike, Utc}; + +use crate::{ + header::{locale::locale_t, sys_types::time_t}, + util::CStringWriter, +}; + +use super::{get_timezone, tm, CDateTime}; + +unsafe fn fmt_time( + t: &T, + tz: Tz, + buffer: *mut c_char, +) -> Option<*mut c_char> { + const MONTHS: &[&str] = &[ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + ]; + const WDAYS: &[&str] = &["Sun", "Mon", "Wed", "Thu", "Fri", "Sat"]; + + use core::fmt::Write; + + let dt = t.to_datetime(tz)?; + let mut writer = CStringWriter::from_ptr(buffer, usize::MAX)?; + + writeln!( + writer, + "{} {} {:02} {:02}:{:02}:{:02} {:04}", + WDAYS[dt.weekday().num_days_from_sunday() as usize], + MONTHS[dt.month0() as usize], + dt.day(), + dt.hour(), + dt.minute(), + dt.second(), + dt.year() + ) + .ok()?; + + writer.finish() +} + +#[no_mangle] +unsafe extern "C" fn asctime(timep: *const tm) -> *mut c_char { + static mut BUFFER: [c_char; 32] = [0; 32]; + asctime_r(timep, BUFFER.as_mut_ptr()) +} + +#[no_mangle] +unsafe extern "C" fn asctime_r(timep: *const tm, buffer: *mut c_char) -> *mut c_char { + let timep = timep.as_ref().unwrap(); + + // Utc, already accounts for timezone + fmt_time(timep, Utc, buffer).unwrap_or(null_mut()) +} + +#[no_mangle] +unsafe extern "C" fn ctime(tp: *const time_t) -> *mut c_char { + static mut BUFFER: [c_char; 32] = [0; 32]; + ctime_r(tp, BUFFER.as_mut_ptr()) +} + +#[no_mangle] +unsafe extern "C" fn ctime_r(tp: *const time_t, buffer: *mut c_char) -> *mut c_char { + let tp = tp.as_ref().unwrap(); + let tz = get_timezone(); + + fmt_time(tp, tz, buffer).unwrap_or(null_mut()) +} + +#[no_mangle] +unsafe extern "C" fn getdate(string: *const c_char) -> *mut tm { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn getdate_r(string: *const c_char, timep: *mut tm) -> *mut tm { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strftime( + dst: *mut c_char, + len: usize, + format: *const c_char, + timep: *const tm, +) -> usize { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strftime_l( + dst: *mut c_char, + len: usize, + format: *const c_char, + timep: *const tm, + locale: locale_t, +) -> usize { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strptime( + src: *const c_char, + format: *const c_char, + timep: *mut tm, +) -> *mut c_char { + todo!() +} diff --git a/src/header/time/timer.rs b/src/header/time/timer.rs new file mode 100644 index 0000000..a155ec8 --- /dev/null +++ b/src/header/time/timer.rs @@ -0,0 +1,42 @@ +use core::ffi::c_int; + +use crate::header::sys_types::{clock_t, clockid_t, pid_t}; + +use super::timespec; + +#[no_mangle] +unsafe extern "C" fn clock() -> clock_t { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn clock_getcpuclockid(pid: pid_t, clock_id_ptr: *mut clockid_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn clock_getres(clock_id: clockid_t, ts: *mut timespec) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn clock_gettime(clock_id: clockid_t, ts: *mut timespec) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn clock_settime(clock_id: clockid_t, ts: *const timespec) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn clock_nanosleep( + clock_id: clockid_t, + flags: c_int, + rqtp: *const timespec, + rmtp: *mut timespec, +) -> c_int { + todo!() +} + +// TODO Timer something diff --git a/src/header/time/utility.rs b/src/header/time/utility.rs new file mode 100644 index 0000000..9e98052 --- /dev/null +++ b/src/header/time/utility.rs @@ -0,0 +1,47 @@ +use core::{ + ffi::{c_double, c_int}, + time::Duration, +}; + +use crate::{header::sys_types::time_t, util}; + +use super::timespec; + +#[no_mangle] +unsafe extern "C" fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int { + let rqtp = rqtp.as_ref().unwrap(); + + let amount = Duration::from(*rqtp); + + match util::nanosleep(amount, None) { + Ok(()) => { + if let Some(rmtp) = rmtp.as_mut() { + // Zero + *rmtp = timespec::default(); + } + + 0 + } + Err(_) => { + if let Some(rmtp) = rmtp.as_mut() { + todo!(); + } + -1 + } + } +} + +#[no_mangle] +unsafe extern "C" fn time(tp: *mut time_t) -> time_t { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn tzset() { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn difftime(a: time_t, b: time_t) -> c_double { + todo!() +} diff --git a/src/util.rs b/src/util.rs index fb017ec..030d4de 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,13 +1,26 @@ -use core::marker::PhantomData; +use core::{ + ffi::c_char, + fmt, + marker::PhantomData, + ptr::{null_mut, NonNull}, + time::Duration, +}; -use yggdrasil_rt::path::Path; +use yggdrasil_rt::{path::Path, sys as syscall}; use crate::{ error::{EResult, OptionExt}, + header::errno::Errno, path::{PathBuf, PathExt}, process::getenv, }; +pub struct CStringWriter { + ptr: NonNull, + pos: usize, + limit: usize, +} + pub trait Nullable { fn is_null(&self) -> bool; fn ensure(&self); @@ -64,6 +77,40 @@ impl<'a, T: Nullable + 'a> Iterator for NullTerminated<'a, T> { } } +impl CStringWriter { + pub unsafe fn from_ptr(ptr: *mut c_char, limit: usize) -> Option { + let ptr = NonNull::new(ptr)?; + Some(Self { ptr, pos: 0, limit }) + } + + pub fn finish(self) -> Option<*mut c_char> { + if self.pos != self.limit { + unsafe { + self.ptr.as_ptr().add(self.pos).write(0); + } + Some(self.ptr.as_ptr()) + } else { + None + } + } +} + +impl fmt::Write for CStringWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + if self.pos + s.len() < self.limit { + let dst = unsafe { + core::slice::from_raw_parts_mut(self.ptr.as_ptr().add(self.pos) as *mut u8, s.len()) + }; + dst.copy_from_slice(s.as_bytes()); + self.pos += s.len(); + + Ok(()) + } else { + Err(fmt::Error) + } + } +} + pub fn resolve_binary>(name: P) -> EResult { let name = name.as_ref(); if name.is_absolute() { @@ -80,3 +127,12 @@ pub fn resolve_binary>(name: P) -> EResult { EResult::Err(yggdrasil_rt::Error::DoesNotExist) } + +// TODO EINTR for interrupted sleeps +pub fn nanosleep(amount: Duration, remaining: Option<&mut Duration>) -> Result<(), Errno> { + if remaining.is_some() { + todo!(); + } + unsafe { syscall::nanosleep(amount) }; + Ok(()) +}