diff --git a/test.c b/test.c index 7f8bb2a3..59b1b840 100644 --- a/test.c +++ b/test.c @@ -1,14 +1,85 @@ #include +#include +#include #include #include #include +static void fmt_inaddr(char *buffer, const struct sockaddr_in *inaddr) { + uint8_t a = (uint8_t) inaddr->sin_addr.s_addr; + uint8_t b = (uint8_t) (inaddr->sin_addr.s_addr >> 8); + uint8_t c = (uint8_t) (inaddr->sin_addr.s_addr >> 16); + uint8_t d = (uint8_t) (inaddr->sin_addr.s_addr >> 24); + uint16_t port = ntohs(inaddr->sin_port); + sprintf(buffer, "%hhu.%hhu.%hhu.%hhu:%hu", a, b, c, d, port); +} + int main(int argc, const char **argv) { int fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { perror("socket()"); return EXIT_FAILURE; } + struct sockaddr_in sa; + socklen_t slen; + char buffer[256]; + + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(4321); + sa.sin_addr.s_addr = INADDR_ANY; + + if (bind(fd, (const struct sockaddr *) &sa, sizeof(sa)) != 0) { + perror("bind()"); + return EXIT_FAILURE; + } + + if (listen(fd, 64) != 0) { + perror("listen()"); + return EXIT_FAILURE; + } + + while (1) { + int rfd; + + slen = sizeof(sa); + if ((rfd = accept(fd, (struct sockaddr *) &sa, &slen)) < 0) { + perror("accept()"); + return EXIT_FAILURE; + } + + fmt_inaddr(buffer, &sa); + printf("Received connection from %s\n", buffer); + + while (1) { + ssize_t len; + + if ((len = recv(rfd, buffer, sizeof(buffer), 0)) < 0) { + perror("recv()"); + break; + } + + if (len == 0) { + break; + } + + if (len >= 4 && !strncmp(buffer, "quit", 4)) { + break; + } + + fwrite(buffer, 1, len, stdout); + + if ((len = send(rfd, buffer, len, 0)) < 0) { + perror("send()"); + break; + } + } + + printf("Connection closed\n"); + close(rfd); + } + return 0; } diff --git a/userspace/lib/ygglibc/src/headers/netinet_in/mod.rs b/userspace/lib/ygglibc/src/headers/netinet_in/mod.rs index 6ce23d5b..226aab27 100644 --- a/userspace/lib/ygglibc/src/headers/netinet_in/mod.rs +++ b/userspace/lib/ygglibc/src/headers/netinet_in/mod.rs @@ -1,6 +1,12 @@ -use core::ffi::c_int; +use core::{ffi::c_int, net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6}}; -use super::sys_socket::sa_family_t; +use crate::{ + error::EResult, + headers::sys_socket::{AF_INET, AF_INET6}, + util::PointerExt, +}; + +use super::sys_socket::{sa_family_t, socklen_t, SocketAddrExt}; pub type in_port_t = u16; pub type in_addr_t = u32; @@ -8,13 +14,13 @@ pub type in_addr_t = u32; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct in_addr { - pub s_addr: in_addr_t + pub s_addr: in_addr_t, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct in6_addr { - pub s6_addr: [u8; 16] + pub s6_addr: [u8; 16], } #[derive(Debug, Clone, Copy)] @@ -32,7 +38,7 @@ pub struct sockaddr_in6 { pub sin6_port: in_port_t, pub sin6_flowinfo: u32, pub sin6_addr: in6_addr, - pub sin6_scope_id: u32 + pub sin6_scope_id: u32, } // TODO more IPv6 @@ -58,3 +64,65 @@ pub const IPV6_MULTICAST_IF: c_int = 6004; pub const IPV6_MULTICAST_LOOP: c_int = 6005; pub const IPV6_UNICAST_HOPS: c_int = 6006; pub const IPV6_V6ONLY: c_int = 6007; + +impl in_addr { + pub fn to_rust(&self) -> Ipv4Addr { + Ipv4Addr::from(self.s_addr) + } +} + +impl in6_addr { + pub fn to_rust(&self) -> Ipv6Addr { + Ipv6Addr::from(self.s6_addr) + } +} + +impl sockaddr_in { + pub unsafe fn to_rust(self: *const Self) -> EResult { + let this = self.ensure(); + debug_assert_eq!(this.sin_family, AF_INET as u16); + let ip = this.sin_addr.to_rust(); + let port = u16::from_be(this.sin_port); + EResult::Ok(SocketAddrV4::new(ip, port)) + } +} + +impl sockaddr_in6 { + pub unsafe fn to_rust(self: *const Self) -> EResult { + let this = self.ensure(); + debug_assert_eq!(this.sin6_family, AF_INET6 as u16); + let ip = this.sin6_addr.to_rust(); + let port = u16::from_be(this.sin6_port); + let flowinfo = u32::from_be(this.sin6_flowinfo); + let scope_id = u32::from_be(this.sin6_scope_id); + EResult::Ok(SocketAddrV6::new(ip, port, flowinfo, scope_id)) + } +} + +impl SocketAddrExt for SocketAddrV4 { + fn to_c( + &self, + storage: *mut sockaddr_in, + len: socklen_t, + ) -> EResult { + let storage = unsafe { storage.ensure_mut() }; + debug_assert!(len >= size_of::()); + storage.sin_family = AF_INET as u16; + storage.sin_port = self.port().to_be(); + storage.sin_addr.s_addr = self.ip().to_bits().to_be(); + EResult::Ok(size_of::()) + } +} + +impl SocketAddrExt for SocketAddrV6 { + fn to_c(&self, storage: *mut sockaddr_in6, len: socklen_t) -> EResult { + let storage = unsafe { storage.ensure_mut() }; + debug_assert!(len >= size_of::()); + storage.sin6_family = AF_INET6 as u16; + storage.sin6_port = self.port().to_be(); + storage.sin6_addr.s6_addr = self.ip().octets(); + storage.sin6_flowinfo = self.flowinfo().to_be(); + storage.sin6_scope_id = self.scope_id().to_be(); + EResult::Ok(size_of::()) + } +} diff --git a/userspace/lib/ygglibc/src/headers/sys_socket/io.rs b/userspace/lib/ygglibc/src/headers/sys_socket/io.rs index c8ff0d86..354e7f56 100644 --- a/userspace/lib/ygglibc/src/headers/sys_socket/io.rs +++ b/userspace/lib/ygglibc/src/headers/sys_socket/io.rs @@ -1,14 +1,14 @@ -use core::ffi::{c_int, c_void}; +use core::{ffi::{c_int, c_void}, mem::MaybeUninit, ptr::{null, null_mut}}; + +use yggdrasil_rt::{io::RawFd, sys as syscall}; + +use crate::{error::{CIsizeResult, ResultExt, TryFromExt}, headers::{errno::Errno, sys_socket::SocketAddrExt}, util::PointerExt}; use super::{msghdr, sockaddr, socklen_t}; #[no_mangle] -unsafe extern "C" fn recv(fd: c_int, buffer: *mut c_void, len: usize, flags: c_int) -> isize { - let _ = fd; - let _ = buffer; - let _ = len; - let _ = flags; - todo!() +unsafe extern "C" fn recv(fd: c_int, buffer: *mut c_void, len: usize, flags: c_int) -> CIsizeResult { + recvfrom(fd, buffer, len, flags, null_mut(), null_mut()) } #[no_mangle] @@ -19,18 +19,21 @@ unsafe extern "C" fn recvfrom( flags: c_int, remote: *mut sockaddr, remote_len: *mut socklen_t, -) -> isize { - let _ = fd; - let _ = buffer; - let _ = len; +) -> CIsizeResult { let _ = flags; - let _ = remote; - let _ = remote_len; - todo!() + let buffer = buffer.cast::().ensure_slice_mut(len); + let fd = RawFd::e_try_from(fd)?; + let mut peer = MaybeUninit::uninit(); + let len = syscall::receive_from(fd, buffer, &mut peer).e_map_err(Errno::from)?; + if !remote.is_null() && !remote_len.is_null() { + let peer = peer.assume_init(); + *remote_len = peer.to_c(remote, *remote_len)?; + } + CIsizeResult::success(len) } #[no_mangle] -unsafe extern "C" fn recvmsg(fd: c_int, message: *mut msghdr, flags: c_int) -> isize { +unsafe extern "C" fn recvmsg(fd: c_int, message: *mut msghdr, flags: c_int) -> CIsizeResult { let _ = fd; let _ = message; let _ = flags; @@ -38,15 +41,12 @@ unsafe extern "C" fn recvmsg(fd: c_int, message: *mut msghdr, flags: c_int) -> i } #[no_mangle] -unsafe extern "C" fn send(fd: c_int, data: *const c_void, len: usize) -> isize { - let _ = fd; - let _ = data; - let _ = len; - todo!() +unsafe extern "C" fn send(fd: c_int, data: *const c_void, len: usize, flags: c_int) -> CIsizeResult { + sendto(fd, data, len, flags, null(), 0) } #[no_mangle] -unsafe extern "C" fn sendmsg(fd: c_int, message: *const msghdr, flags: c_int) -> isize { +unsafe extern "C" fn sendmsg(fd: c_int, message: *const msghdr, flags: c_int) -> CIsizeResult { let _ = fd; let _ = message; let _ = flags; @@ -61,12 +61,15 @@ unsafe extern "C" fn sendto( flags: c_int, remote: *const sockaddr, remote_len: socklen_t, -) -> isize { - let _ = fd; - let _ = data; - let _ = len; +) -> CIsizeResult { let _ = flags; - let _ = remote; - let _ = remote_len; - todo!() + let fd = RawFd::e_try_from(fd)?; + let data = data.cast::().ensure_slice(len); + let peer = if !remote.is_null() && remote_len != 0 { + Some(remote.to_rust(remote_len)?) + } else { + None + }; + let len = syscall::send_to(fd, data, &peer).e_map_err(Errno::from)?; + CIsizeResult::success(len) } diff --git a/userspace/lib/ygglibc/src/headers/sys_socket/mod.rs b/userspace/lib/ygglibc/src/headers/sys_socket/mod.rs index a7ada45f..d4f83b8a 100644 --- a/userspace/lib/ygglibc/src/headers/sys_socket/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_socket/mod.rs @@ -1,4 +1,8 @@ -use core::ffi::{c_int, c_void}; +use core::{ffi::{c_int, c_void}, net::SocketAddr}; + +use crate::{error::EResult, headers::errno::Errno, util::PointerExt}; + +use super::netinet_in::{sockaddr_in, sockaddr_in6}; mod io; mod option; @@ -28,7 +32,7 @@ pub struct sockaddr { #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct iovec { - __dummy: u32 + __dummy: u32, } #[derive(Clone, Copy, Debug)] @@ -40,7 +44,7 @@ pub struct msghdr { pub msg_iovlen: c_int, pub msg_control: *mut c_void, pub msg_controllen: socklen_t, - pub msg_flags: c_int + pub msg_flags: c_int, } // TODO struct cmsghdr @@ -84,3 +88,31 @@ pub const SHUT_RD: c_int = 1 << 0; pub const SHUT_WR: c_int = 1 << 1; pub const SHUT_RDWR: c_int = SHUT_RD | SHUT_WR; +pub(crate) trait SocketAddrExt { + fn to_c(&self, storage: *mut T, len: socklen_t) -> EResult; +} + +impl sockaddr { + pub unsafe fn to_rust(self: *const Self, len: socklen_t) -> EResult { + let family = self.ensure().sa_family as c_int; + match family { + AF_INET if len == size_of::() => { + self.cast::().to_rust().map(SocketAddr::V4) + } + AF_INET6 if len == size_of::() => { + self.cast::().to_rust().map(SocketAddr::V6) + } + _ => EResult::Err(Errno::ENOTSUPP), + } + } +} + +impl SocketAddrExt for SocketAddr { + fn to_c(&self, storage: *mut sockaddr, len: socklen_t) -> EResult { + match self { + Self::V4(v4) if len >= size_of::() => v4.to_c(storage.cast(), len), + Self::V6(v6) if len >= size_of::() => v6.to_c(storage.cast(), len), + _ => EResult::Err(Errno::EINVAL) + } + } +} diff --git a/userspace/lib/ygglibc/src/headers/sys_socket/option.rs b/userspace/lib/ygglibc/src/headers/sys_socket/option.rs index 571fec6b..fbaa4a21 100644 --- a/userspace/lib/ygglibc/src/headers/sys_socket/option.rs +++ b/userspace/lib/ygglibc/src/headers/sys_socket/option.rs @@ -1,6 +1,21 @@ use core::ffi::{c_int, c_void}; -use super::socklen_t; +use yggdrasil_rt::{ + io::RawFd, + net::{self as rt, SocketOption}, + sys as syscall, +}; + +use crate::{ + error::{self, CIntZeroResult, CResult, OptionExt, ResultExt, TryFromExt}, + headers::{ + errno::Errno, + sys_socket::{SocketAddrExt, SOL_SOCKET, SO_BROADCAST, SO_RCVTIMEO, SO_SNDTIMEO}, + sys_time::timeval, + }, +}; + +use super::{sockaddr, socklen_t}; #[no_mangle] unsafe extern "C" fn getsockopt( @@ -9,13 +24,50 @@ unsafe extern "C" fn getsockopt( name: c_int, value: *mut c_void, size: *mut socklen_t, -) -> c_int { - let _ = fd; - let _ = level; - let _ = name; - let _ = value; - let _ = size; - todo!() +) -> CIntZeroResult { + if value.is_null() || size.is_null() { + error::errno = Errno::EINVAL; + return CIntZeroResult::ERROR; + } + + let space = *size; + let fd = RawFd::e_try_from(fd)?; + + *size = match (level, name) { + (SOL_SOCKET, SO_RCVTIMEO) if space >= size_of::() => { + let timeout = + rt::get_socket_option1!(fd, RecvTimeout: Option).e_map_err(Errno::from)?; + let value = value.cast::(); + match timeout { + Some(timeout) => value.write(timeval::from(timeout)), + None => value.write(timeval::zero()), + } + size_of::() + } + (SOL_SOCKET, SO_SNDTIMEO) => { + let timeout = + rt::get_socket_option1!(fd, SendTimeout: Option).e_map_err(Errno::from)?; + let value = value.cast::(); + match timeout { + Some(timeout) => value.write(timeval::from(timeout)), + None => value.write(timeval::zero()), + } + size_of::() + } + (SOL_SOCKET, SO_BROADCAST) => { + let broadcast = rt::get_socket_option1!(fd, Broadcast: bool).e_map_err(Errno::from)?; + let value = value.cast::(); + value.write(broadcast as c_int); + size_of::() + } + _ => { + yggdrasil_rt::debug_trace!("Unhandled getsockopt({level}, {name}, {space})"); + error::errno = Errno::EINVAL; + return CIntZeroResult::ERROR; + } + }; + + CIntZeroResult::SUCCESS } #[no_mangle] @@ -25,11 +77,76 @@ unsafe extern "C" fn setsockopt( name: c_int, value: *const c_void, size: socklen_t, -) -> c_int { - let _ = fd; - let _ = level; - let _ = name; - let _ = value; - let _ = size; - todo!() +) -> CIntZeroResult { + if value.is_null() { + error::errno = Errno::EINVAL; + return CIntZeroResult::ERROR; + } + + let fd = RawFd::e_try_from(fd)?; + + match (level, name) { + (SOL_SOCKET, SO_RCVTIMEO) if size == size_of::() => { + let timeval = *value.cast::(); + let timeout = timeval.to_duration_opt(); + syscall::set_socket_option(fd, &SocketOption::RecvTimeout(timeout)) + .e_map_err(Errno::from)?; + } + (SOL_SOCKET, SO_SNDTIMEO) if size == size_of::() => { + let timeval = *value.cast::(); + let timeout = timeval.to_duration_opt(); + syscall::set_socket_option(fd, &SocketOption::SendTimeout(timeout)) + .e_map_err(Errno::from)?; + } + (SOL_SOCKET, SO_BROADCAST) if size == size_of::() => { + let value = *value.cast::() != 0; + syscall::set_socket_option(fd, &SocketOption::Broadcast(value)) + .e_map_err(Errno::from)?; + } + _ => { + yggdrasil_rt::debug_trace!("Unhandled setsockopt({level}, {name}, {size})"); + error::errno = Errno::EINVAL; + return CIntZeroResult::ERROR; + } + } + + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn getpeername( + fd: c_int, + remote: *mut sockaddr, + len: *mut socklen_t, +) -> CIntZeroResult { + if remote.is_null() || len.is_null() { + error::errno = Errno::EINVAL; + return CIntZeroResult::ERROR; + } + + let fd = RawFd::e_try_from(fd)?; + let address = rt::get_socket_option1!(fd, PeerAddress: Option).e_map_err(Errno::from)?; + let address = address.e_ok_or(Errno::ENOTCONN)?; + + *len = address.to_c(remote, *len)?; + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn getsockname( + fd: c_int, + local: *mut sockaddr, + len: *mut socklen_t, +) -> CIntZeroResult { + if local.is_null() || len.is_null() { + error::errno = Errno::EINVAL; + return CIntZeroResult::ERROR; + } + + let fd = RawFd::e_try_from(fd)?; + let address = rt::get_socket_option1!(fd, LocalAddress: Option).e_map_err(Errno::from)?; + let address = address.e_ok_or(Errno::ENOTCONN)?; + + *len = address.to_c(local, *len)?; + CIntZeroResult::SUCCESS } diff --git a/userspace/lib/ygglibc/src/headers/sys_socket/socket.rs b/userspace/lib/ygglibc/src/headers/sys_socket/socket.rs index 0e5c6ce8..d45d75c5 100644 --- a/userspace/lib/ygglibc/src/headers/sys_socket/socket.rs +++ b/userspace/lib/ygglibc/src/headers/sys_socket/socket.rs @@ -1,87 +1,94 @@ -use core::ffi::c_int; +use core::{ffi::c_int, mem::MaybeUninit}; + +use yggdrasil_rt::{io::RawFd, net as rt, sys as syscall}; use crate::{ - error::{self, CFdResult, CResult}, + error::{self, CFdResult, CIntCountResult, CIntZeroResult, CResult, ResultExt, TryFromExt}, headers::{ errno::Errno, - sys_socket::{AF_INET, SOCK_DGRAM, SOCK_STREAM}, + sys_socket::{AF_INET, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_STREAM}, }, }; -use super::{sockaddr, socklen_t}; +use super::{sockaddr, socklen_t, SocketAddrExt}; #[no_mangle] -unsafe extern "C" fn accept(fd: c_int, remote: *mut sockaddr, len: *mut socklen_t) -> c_int { - let _ = fd; - let _ = remote; - let _ = len; - todo!() +unsafe extern "C" fn accept(fd: c_int, remote: *mut sockaddr, len: *mut socklen_t) -> CFdResult { + let listener_fd = RawFd::e_try_from(fd)?; + let mut address = MaybeUninit::uninit(); + let stream_fd = syscall::accept(listener_fd, &mut address).e_map_err(Errno::from)?; + + if !remote.is_null() && !len.is_null() { + let address = address.assume_init(); + *len = address.to_c(remote, *len)?; + } + + CFdResult::success(stream_fd) } #[no_mangle] -unsafe extern "C" fn bind(fd: c_int, local: *const sockaddr, len: socklen_t) -> c_int { - let _ = fd; - let _ = local; - let _ = len; - - todo!() +unsafe extern "C" fn bind(fd: c_int, local: *const sockaddr, len: socklen_t) -> CIntZeroResult { + let fd = RawFd::e_try_from(fd)?; + let local = local.to_rust(len)?; + syscall::bind(fd, &local).e_map_err(Errno::from)?; + CIntZeroResult::SUCCESS } #[no_mangle] -unsafe extern "C" fn connect(fd: c_int, remote: *const sockaddr, len: socklen_t) -> c_int { - let _ = fd; - let _ = remote; - let _ = len; - todo!() +unsafe extern "C" fn connect(fd: c_int, remote: *const sockaddr, len: socklen_t) -> CIntZeroResult { + let fd = RawFd::e_try_from(fd)?; + let remote = remote.to_rust(len)?; + syscall::connect(fd, &remote).e_map_err(Errno::from)?; + CIntZeroResult::SUCCESS } #[no_mangle] -unsafe extern "C" fn getpeername(fd: c_int, remote: *mut sockaddr, len: *mut socklen_t) -> c_int { - let _ = fd; - let _ = remote; - let _ = len; - todo!() -} - -#[no_mangle] -unsafe extern "C" fn getsockname(fd: c_int, local: *mut sockaddr, len: *mut socklen_t) -> c_int { - let _ = fd; - let _ = local; - let _ = len; - todo!() -} - -#[no_mangle] -unsafe extern "C" fn listen(fd: c_int, backlog: c_int) -> c_int { - let _ = fd; +unsafe extern "C" fn listen(fd: c_int, backlog: c_int) -> CIntZeroResult { let _ = backlog; - todo!() + let fd = RawFd::e_try_from(fd)?; + syscall::listen(fd).e_map_err(Errno::from)?; + CIntZeroResult::SUCCESS } #[no_mangle] -unsafe extern "C" fn shutdown(fd: c_int, how: c_int) -> c_int { - let _ = fd; - let _ = how; - todo!() +unsafe extern "C" fn shutdown(fd: c_int, how: c_int) -> CIntZeroResult { + let fd = RawFd::e_try_from(fd)?; + let how = match how { + SHUT_RD => rt::SocketShutdown::READ, + SHUT_WR => rt::SocketShutdown::WRITE, + SHUT_RDWR => rt::SocketShutdown::READ | rt::SocketShutdown::WRITE, + _ => { + error::errno = Errno::EINVAL; + return CIntZeroResult::ERROR; + } + }; + + syscall::shutdown(fd, how).e_map_err(Errno::from)?; + + CIntZeroResult::SUCCESS } +// TODO out-of-band data in sockets #[no_mangle] -unsafe extern "C" fn sockatmark(fd: c_int) -> c_int { - let _ = fd; - todo!() +unsafe extern "C" fn sockatmark(_fd: c_int) -> CIntCountResult { + error::errno = Errno::EINVAL; + CIntCountResult::ERROR } #[no_mangle] unsafe extern "C" fn socket(domain: c_int, ty: c_int, proto: c_int) -> CFdResult { - match (domain, ty, proto) { - (AF_INET, SOCK_STREAM, 0) => todo!(), - (AF_INET, SOCK_DGRAM, 0) => todo!(), + let ty = match (domain, ty, proto) { + (AF_INET, SOCK_STREAM, 0) => rt::SocketType::TcpStream, + (AF_INET, SOCK_DGRAM, 0) => rt::SocketType::UdpPacket, (_, _, _) => { yggdrasil_rt::debug_trace!("Unsupported socket({domain}, {ty}, {proto})"); error::errno = Errno::ENOTSUPP; - CFdResult::ERROR + return CFdResult::ERROR; } - } + }; + + let fd = syscall::create_socket(ty).e_map_err(Errno::from)?; + CFdResult::success(fd) } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/sys_time/mod.rs b/userspace/lib/ygglibc/src/headers/sys_time/mod.rs index b0feeda8..120662c4 100644 --- a/userspace/lib/ygglibc/src/headers/sys_time/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_time/mod.rs @@ -1,5 +1,7 @@ use core::{ffi::{c_char, c_int, c_long, c_void}, time::Duration}; +use yggdrasil_abi::time::MICROSECONDS_IN_SECOND; + use super::sys_types::{suseconds_t, time_t}; #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] @@ -59,6 +61,25 @@ impl timespec { } } +impl timeval { + pub const fn zero() -> Self { + Self { + tv_sec: time_t(0), + tv_usec: 0 + } + } + + pub fn to_duration_opt(&self) -> Option { + if self.tv_sec.0 == 0 && self.tv_usec == 0 { + None + } else { + let seconds = (self.tv_sec.0 as u64).saturating_add(self.tv_usec / MICROSECONDS_IN_SECOND); + let nanoseconds = (self.tv_usec % MICROSECONDS_IN_SECOND) * 1000; + Some(Duration::new(seconds, nanoseconds as u32)) + } + } +} + impl From<__ygg_timespec_t> for Duration { fn from(value: __ygg_timespec_t) -> Self { Self::new( @@ -76,3 +97,12 @@ impl From for __ygg_timespec_t { } } } + +impl From for timeval { + fn from(value: Duration) -> Self { + Self { + tv_sec: time_t(value.as_secs().try_into().unwrap()), + tv_usec: value.subsec_micros() as _ + } + } +}