Add Motor OS std library port

Motor OS was added as a no-std Tier-3 target in
https://github.com/rust-lang/rust/pull/146848
as x86_64-unknown-motor.

This patch/PR adds the std library for Motor OS.

While the patch may seem large, all it does is proxy
std pal calls to moto-rt. When there is some non-trivial
code (e.g. thread::spawn), it is quite similar, and often
identical, to what other platforms do.
This commit is contained in:
U. Lasiotus
2025-10-07 18:56:25 -07:00
parent 8fad3a17ca
commit a828ffcf5f
46 changed files with 2296 additions and 8 deletions

View File

@@ -166,6 +166,16 @@ dependencies = [
"rustc-std-workspace-core", "rustc-std-workspace-core",
] ]
[[package]]
name = "moto-rt"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058a2807a30527bee4c30df7ababe971cdde94372d4dbd1ff145bb403381436c"
dependencies = [
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]] [[package]]
name = "object" name = "object"
version = "0.37.3" version = "0.37.3"
@@ -316,6 +326,7 @@ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
"miniz_oxide", "miniz_oxide",
"moto-rt",
"object", "object",
"panic_abort", "panic_abort",
"panic_unwind", "panic_unwind",

View File

@@ -70,6 +70,9 @@ fortanix-sgx-abi = { version = "0.6.1", features = [
'rustc-dep-of-std', 'rustc-dep-of-std',
], public = true } ], public = true }
[target.'cfg(target_os = "motor")'.dependencies]
moto-rt = { version = "0.15", features = ['rustc-dep-of-std'], public = true }
[target.'cfg(target_os = "hermit")'.dependencies] [target.'cfg(target_os = "hermit")'.dependencies]
hermit-abi = { version = "0.5.0", features = [ hermit-abi = { version = "0.5.0", features = [
'rustc-dep-of-std', 'rustc-dep-of-std',

View File

@@ -30,6 +30,7 @@ fn main() {
|| target_os == "windows" || target_os == "windows"
|| target_os == "fuchsia" || target_os == "fuchsia"
|| (target_vendor == "fortanix" && target_env == "sgx") || (target_vendor == "fortanix" && target_env == "sgx")
|| target_os == "motor"
|| target_os == "hermit" || target_os == "hermit"
|| target_os == "trusty" || target_os == "trusty"
|| target_os == "l4re" || target_os == "l4re"

View File

@@ -3,6 +3,9 @@
#![stable(feature = "io_safety", since = "1.63.0")] #![stable(feature = "io_safety", since = "1.63.0")]
#![deny(unsafe_op_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)]
#[cfg(target_os = "motor")]
use moto_rt::libc;
use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(not(target_os = "trusty"))] #[cfg(not(target_os = "trusty"))]
use crate::fs; use crate::fs;
@@ -12,7 +15,8 @@ use crate::mem::ManuallyDrop;
target_arch = "wasm32", target_arch = "wasm32",
target_env = "sgx", target_env = "sgx",
target_os = "hermit", target_os = "hermit",
target_os = "trusty" target_os = "trusty",
target_os = "motor"
)))] )))]
use crate::sys::cvt; use crate::sys::cvt;
#[cfg(not(target_os = "trusty"))] #[cfg(not(target_os = "trusty"))]
@@ -95,7 +99,12 @@ impl OwnedFd {
impl BorrowedFd<'_> { impl BorrowedFd<'_> {
/// Creates a new `OwnedFd` instance that shares the same underlying file /// Creates a new `OwnedFd` instance that shares the same underlying file
/// description as the existing `BorrowedFd` instance. /// description as the existing `BorrowedFd` instance.
#[cfg(not(any(target_arch = "wasm32", target_os = "hermit", target_os = "trusty")))] #[cfg(not(any(
target_arch = "wasm32",
target_os = "hermit",
target_os = "trusty",
target_os = "motor"
)))]
#[stable(feature = "io_safety", since = "1.63.0")] #[stable(feature = "io_safety", since = "1.63.0")]
pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> { pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> {
// We want to atomically duplicate this file descriptor and set the // We want to atomically duplicate this file descriptor and set the
@@ -123,6 +132,15 @@ impl BorrowedFd<'_> {
pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> { pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> {
Err(crate::io::Error::UNSUPPORTED_PLATFORM) Err(crate::io::Error::UNSUPPORTED_PLATFORM)
} }
/// Creates a new `OwnedFd` instance that shares the same underlying file
/// description as the existing `BorrowedFd` instance.
#[cfg(target_os = "motor")]
#[stable(feature = "io_safety", since = "1.63.0")]
pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> {
let fd = moto_rt::fs::duplicate(self.as_raw_fd()).map_err(crate::sys::map_motor_error)?;
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}
} }
#[stable(feature = "io_safety", since = "1.63.0")] #[stable(feature = "io_safety", since = "1.63.0")]

View File

@@ -4,13 +4,17 @@
#[cfg(target_os = "hermit")] #[cfg(target_os = "hermit")]
use hermit_abi as libc; use hermit_abi as libc;
#[cfg(target_os = "motor")]
use moto_rt::libc;
#[cfg(target_os = "motor")]
use super::owned::OwnedFd;
#[cfg(not(target_os = "trusty"))] #[cfg(not(target_os = "trusty"))]
use crate::fs; use crate::fs;
use crate::io; use crate::io;
#[cfg(target_os = "hermit")] #[cfg(target_os = "hermit")]
use crate::os::hermit::io::OwnedFd; use crate::os::hermit::io::OwnedFd;
#[cfg(not(target_os = "hermit"))] #[cfg(all(not(target_os = "hermit"), not(target_os = "motor")))]
use crate::os::raw; use crate::os::raw;
#[cfg(all(doc, not(target_arch = "wasm32")))] #[cfg(all(doc, not(target_arch = "wasm32")))]
use crate::os::unix::io::AsFd; use crate::os::unix::io::AsFd;
@@ -23,10 +27,10 @@ use crate::sys_common::{AsInner, FromInner, IntoInner};
/// Raw file descriptors. /// Raw file descriptors.
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[cfg(not(target_os = "hermit"))] #[cfg(all(not(target_os = "hermit"), not(target_os = "motor")))]
pub type RawFd = raw::c_int; pub type RawFd = raw::c_int;
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[cfg(target_os = "hermit")] #[cfg(any(target_os = "hermit", target_os = "motor"))]
pub type RawFd = i32; pub type RawFd = i32;
/// A trait to extract the raw file descriptor from an underlying object. /// A trait to extract the raw file descriptor from an underlying object.

View File

@@ -155,6 +155,8 @@ pub mod ios;
pub mod l4re; pub mod l4re;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub mod macos; pub mod macos;
#[cfg(target_os = "motor")]
pub mod motor;
#[cfg(target_os = "netbsd")] #[cfg(target_os = "netbsd")]
pub mod netbsd; pub mod netbsd;
#[cfg(target_os = "nto")] #[cfg(target_os = "nto")]
@@ -182,7 +184,14 @@ pub mod vxworks;
#[cfg(target_os = "xous")] #[cfg(target_os = "xous")]
pub mod xous; pub mod xous;
#[cfg(any(unix, target_os = "hermit", target_os = "trusty", target_os = "wasi", doc))] #[cfg(any(
unix,
target_os = "hermit",
target_os = "trusty",
target_os = "wasi",
target_os = "motor",
doc
))]
pub mod fd; pub mod fd;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin", doc))] #[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin", doc))]

View File

@@ -0,0 +1,37 @@
//! Motor OS-specific extensions to primitives in the [`std::ffi`] module.
#![unstable(feature = "motor_ext", issue = "147456")]
use crate::ffi::{OsStr, OsString};
use crate::sealed::Sealed;
/// Motor OS-specific extensions to [`OsString`].
///
/// This trait is sealed: it cannot be implemented outside the standard library.
/// This is so that future additional methods are not breaking changes.
pub trait OsStringExt: Sealed {
/// Motor OS strings are utf-8, and thus just strings.
fn as_str(&self) -> &str;
}
impl OsStringExt for OsString {
#[inline]
fn as_str(&self) -> &str {
self.to_str().unwrap()
}
}
/// Motor OS-specific extensions to [`OsString`].
///
/// This trait is sealed: it cannot be implemented outside the standard library.
/// This is so that future additional methods are not breaking changes.
pub trait OsStrExt: Sealed {
/// Motor OS strings are utf-8, and thus just strings.
fn as_str(&self) -> &str;
}
impl OsStrExt for OsStr {
#[inline]
fn as_str(&self) -> &str {
self.to_str().unwrap()
}
}

View File

@@ -0,0 +1,4 @@
#![unstable(feature = "motor_ext", issue = "147456")]
pub mod ffi;
pub mod process;

View File

@@ -0,0 +1,15 @@
#![unstable(feature = "motor_ext", issue = "147456")]
use crate::sealed::Sealed;
use crate::sys_common::AsInner;
pub trait ChildExt: Sealed {
/// Extracts the main thread raw handle, without taking ownership
fn sys_handle(&self) -> u64;
}
impl ChildExt for crate::process::Child {
fn sys_handle(&self) -> u64 {
self.as_inner().handle()
}
}

View File

@@ -83,6 +83,9 @@ cfg_select! {
target_os = "hermit" => { target_os = "hermit" => {
mod hermit; mod hermit;
} }
target_os = "motor" => {
mod motor;
}
all(target_vendor = "fortanix", target_env = "sgx") => { all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx; mod sgx;
} }

View File

@@ -0,0 +1,28 @@
use crate::alloc::{GlobalAlloc, Layout, System};
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// SAFETY: same requirements as in GlobalAlloc::alloc.
moto_rt::alloc::alloc(layout)
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
// SAFETY: same requirements as in GlobalAlloc::alloc_zeroed.
moto_rt::alloc::alloc_zeroed(layout)
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
// SAFETY: same requirements as in GlobalAlloc::dealloc.
unsafe { moto_rt::alloc::dealloc(ptr, layout) }
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
// SAFETY: same requirements as in GlobalAlloc::realloc.
unsafe { moto_rt::alloc::realloc(ptr, layout, new_size) }
}
}

View File

@@ -9,6 +9,10 @@ cfg_select! {
mod windows; mod windows;
pub use windows::{AnonPipe, pipe}; pub use windows::{AnonPipe, pipe};
} }
target_os = "motor" => {
mod motor;
pub use motor::{AnonPipe, pipe};
}
_ => { _ => {
mod unsupported; mod unsupported;
pub use unsupported::{AnonPipe, pipe}; pub use unsupported::{AnonPipe, pipe};

View File

@@ -0,0 +1,11 @@
use crate::io;
use crate::sys::fd::FileDesc;
use crate::sys::pipe::anon_pipe;
use crate::sys_common::IntoInner;
pub type AnonPipe = FileDesc;
#[inline]
pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> {
anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner()))
}

View File

@@ -6,6 +6,7 @@
all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))),
target_family = "windows", target_family = "windows",
target_os = "hermit", target_os = "hermit",
target_os = "motor",
target_os = "uefi", target_os = "uefi",
target_os = "wasi", target_os = "wasi",
target_os = "xous", target_os = "xous",
@@ -28,6 +29,10 @@ cfg_select! {
mod sgx; mod sgx;
pub use sgx::*; pub use sgx::*;
} }
target_os = "motor" => {
mod motor;
pub use motor::*;
}
target_os = "uefi" => { target_os = "uefi" => {
mod uefi; mod uefi;
pub use uefi::*; pub use uefi::*;

View File

@@ -0,0 +1,13 @@
pub use super::common::Args;
use crate::ffi::OsString;
pub fn args() -> Args {
let motor_args: Vec<String> = moto_rt::process::args();
let mut rust_args = Vec::new();
for arg in motor_args {
rust_args.push(OsString::from(arg));
}
Args::new(rust_args)
}

View File

@@ -5,6 +5,7 @@
#[cfg(any( #[cfg(any(
target_family = "unix", target_family = "unix",
target_os = "hermit", target_os = "hermit",
target_os = "motor",
all(target_vendor = "fortanix", target_env = "sgx"), all(target_vendor = "fortanix", target_env = "sgx"),
target_os = "solid_asp3", target_os = "solid_asp3",
target_os = "uefi", target_os = "uefi",
@@ -26,6 +27,10 @@ cfg_select! {
mod hermit; mod hermit;
pub use hermit::*; pub use hermit::*;
} }
target_os = "motor" => {
mod motor;
pub use motor::*;
}
all(target_vendor = "fortanix", target_env = "sgx") => { all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx; mod sgx;
pub use sgx::*; pub use sgx::*;

27
library/std/src/sys/env/motor.rs vendored Normal file
View File

@@ -0,0 +1,27 @@
pub use super::common::Env;
use crate::ffi::{OsStr, OsString};
use crate::io;
use crate::os::motor::ffi::OsStrExt;
pub fn env() -> Env {
let motor_env: Vec<(String, String)> = moto_rt::process::env();
let mut rust_env = vec![];
for (k, v) in motor_env {
rust_env.push((OsString::from(k), OsString::from(v)));
}
Env::new(rust_env)
}
pub fn getenv(key: &OsStr) -> Option<OsString> {
moto_rt::process::getenv(key.as_str()).map(|s| OsString::from(s))
}
pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> {
Ok(moto_rt::process::setenv(key.as_str(), val.as_str()))
}
pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> {
Ok(moto_rt::process::unsetenv(key.as_str()))
}

View File

@@ -11,6 +11,10 @@ cfg_select! {
mod hermit; mod hermit;
pub use hermit::*; pub use hermit::*;
} }
target_os = "motor" => {
mod motor;
pub use motor::*;
}
all(target_vendor = "fortanix", target_env = "sgx") => { all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx; mod sgx;
pub use sgx::*; pub use sgx::*;

View File

@@ -0,0 +1,124 @@
#![unstable(reason = "not public", issue = "none", feature = "fd")]
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use crate::sys::map_motor_error;
use crate::sys_common::{AsInner, FromInner, IntoInner};
#[derive(Debug)]
pub struct FileDesc(OwnedFd);
impl FileDesc {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
crate::io::default_read_buf(|buf| self.read(buf), cursor)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
io::default_read_vectored(|b| self.read(b), bufs)
}
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
let mut me = self;
(&mut me).read_to_end(buf)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
crate::io::default_write_vectored(|b| self.write(b), bufs)
}
pub fn is_write_vectored(&self) -> bool {
false
}
#[inline]
pub fn is_read_vectored(&self) -> bool {
false
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
moto_rt::net::set_nonblocking(self.as_raw_fd(), nonblocking).map_err(map_motor_error)
}
#[inline]
pub fn duplicate(&self) -> io::Result<FileDesc> {
let fd = moto_rt::fs::duplicate(self.as_raw_fd()).map_err(map_motor_error)?;
// SAFETY: safe because we just got it from the OS runtime.
unsafe { Ok(Self::from_raw_fd(fd)) }
}
#[inline]
pub fn try_clone(&self) -> io::Result<Self> {
self.duplicate()
}
}
impl<'a> Read for &'a FileDesc {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(**self).read(buf)
}
fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
(**self).read_buf(cursor)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
(**self).read_vectored(bufs)
}
#[inline]
fn is_read_vectored(&self) -> bool {
(**self).is_read_vectored()
}
}
impl AsInner<OwnedFd> for FileDesc {
#[inline]
fn as_inner(&self) -> &OwnedFd {
&self.0
}
}
impl IntoInner<OwnedFd> for FileDesc {
fn into_inner(self) -> OwnedFd {
self.0
}
}
impl FromInner<OwnedFd> for FileDesc {
fn from_inner(owned_fd: OwnedFd) -> Self {
Self(owned_fd)
}
}
impl AsFd for FileDesc {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl AsRawFd for FileDesc {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl IntoRawFd for FileDesc {
fn into_raw_fd(self) -> RawFd {
self.0.into_raw_fd()
}
}
impl FromRawFd for FileDesc {
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) }
}
}

View File

@@ -27,6 +27,10 @@ cfg_select! {
mod hermit; mod hermit;
use hermit as imp; use hermit as imp;
} }
target_os = "motor" => {
mod motor;
use motor as imp;
}
target_os = "solid_asp3" => { target_os = "solid_asp3" => {
mod solid; mod solid;
use solid as imp; use solid as imp;

View File

@@ -0,0 +1,478 @@
use crate::ffi::OsString;
use crate::hash::Hash;
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::path::{Path, PathBuf};
use crate::sys::fd::FileDesc;
pub use crate::sys::fs::common::exists;
use crate::sys::time::SystemTime;
use crate::sys::{map_motor_error, unsupported};
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct FileType {
rt_filetype: u8,
}
impl FileType {
pub fn is_dir(&self) -> bool {
self.rt_filetype == moto_rt::fs::FILETYPE_DIRECTORY
}
pub fn is_file(&self) -> bool {
self.rt_filetype == moto_rt::fs::FILETYPE_FILE
}
pub fn is_symlink(&self) -> bool {
false
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct FilePermissions {
rt_perm: u64,
}
impl FilePermissions {
pub fn readonly(&self) -> bool {
(self.rt_perm & moto_rt::fs::PERM_WRITE == 0)
&& (self.rt_perm & moto_rt::fs::PERM_READ != 0)
}
pub fn set_readonly(&mut self, readonly: bool) {
if readonly {
self.rt_perm = moto_rt::fs::PERM_READ;
} else {
self.rt_perm = moto_rt::fs::PERM_READ | moto_rt::fs::PERM_WRITE;
}
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct FileTimes {
modified: u128,
accessed: u128,
}
impl FileTimes {
pub fn set_accessed(&mut self, t: SystemTime) {
self.accessed = t.as_u128();
}
pub fn set_modified(&mut self, t: SystemTime) {
self.modified = t.as_u128();
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct FileAttr {
inner: moto_rt::fs::FileAttr,
}
impl FileAttr {
pub fn size(&self) -> u64 {
self.inner.size
}
pub fn perm(&self) -> FilePermissions {
FilePermissions { rt_perm: self.inner.perm }
}
pub fn file_type(&self) -> FileType {
FileType { rt_filetype: self.inner.file_type }
}
pub fn modified(&self) -> io::Result<SystemTime> {
match self.inner.modified {
0 => Err(crate::io::Error::from(crate::io::ErrorKind::Other)),
x => Ok(SystemTime::from_u128(x)),
}
}
pub fn accessed(&self) -> io::Result<SystemTime> {
match self.inner.accessed {
0 => Err(crate::io::Error::from(crate::io::ErrorKind::Other)),
x => Ok(SystemTime::from_u128(x)),
}
}
pub fn created(&self) -> io::Result<SystemTime> {
match self.inner.created {
0 => Err(crate::io::Error::from(crate::io::ErrorKind::Other)),
x => Ok(SystemTime::from_u128(x)),
}
}
}
#[derive(Clone, Debug)]
pub struct OpenOptions {
rt_open_options: u32,
}
impl OpenOptions {
pub fn new() -> OpenOptions {
OpenOptions { rt_open_options: 0 }
}
pub fn read(&mut self, read: bool) {
if read {
self.rt_open_options |= moto_rt::fs::O_READ;
} else {
self.rt_open_options &= !moto_rt::fs::O_READ;
}
}
pub fn write(&mut self, write: bool) {
if write {
self.rt_open_options |= moto_rt::fs::O_WRITE;
} else {
self.rt_open_options &= !moto_rt::fs::O_WRITE;
}
}
pub fn append(&mut self, append: bool) {
if append {
self.rt_open_options |= moto_rt::fs::O_APPEND;
} else {
self.rt_open_options &= !moto_rt::fs::O_APPEND;
}
}
pub fn truncate(&mut self, truncate: bool) {
if truncate {
self.rt_open_options |= moto_rt::fs::O_TRUNCATE;
} else {
self.rt_open_options &= !moto_rt::fs::O_TRUNCATE;
}
}
pub fn create(&mut self, create: bool) {
if create {
self.rt_open_options |= moto_rt::fs::O_CREATE;
} else {
self.rt_open_options &= !moto_rt::fs::O_CREATE;
}
}
pub fn create_new(&mut self, create_new: bool) {
if create_new {
self.rt_open_options |= moto_rt::fs::O_CREATE_NEW;
} else {
self.rt_open_options &= !moto_rt::fs::O_CREATE_NEW;
}
}
}
#[derive(Debug)]
pub struct File(FileDesc);
impl File {
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
moto_rt::fs::open(path, opts.rt_open_options)
.map(|fd| unsafe { Self::from_raw_fd(fd) })
.map_err(map_motor_error)
}
pub fn file_attr(&self) -> io::Result<FileAttr> {
moto_rt::fs::get_file_attr(self.as_raw_fd())
.map(|inner| -> FileAttr { FileAttr { inner } })
.map_err(map_motor_error)
}
pub fn fsync(&self) -> io::Result<()> {
moto_rt::fs::fsync(self.as_raw_fd()).map_err(map_motor_error)
}
pub fn datasync(&self) -> io::Result<()> {
moto_rt::fs::datasync(self.as_raw_fd()).map_err(map_motor_error)
}
pub fn truncate(&self, size: u64) -> io::Result<()> {
moto_rt::fs::truncate(self.as_raw_fd(), size).map_err(map_motor_error)
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
crate::io::default_read_vectored(|b| self.read(b), bufs)
}
pub fn is_read_vectored(&self) -> bool {
false
}
pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
crate::io::default_read_buf(|buf| self.read(buf), cursor)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
crate::io::default_write_vectored(|b| self.write(b), bufs)
}
pub fn is_write_vectored(&self) -> bool {
false
}
pub fn flush(&self) -> io::Result<()> {
moto_rt::fs::flush(self.as_raw_fd()).map_err(map_motor_error)
}
pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
match pos {
SeekFrom::Start(offset) => {
moto_rt::fs::seek(self.as_raw_fd(), offset as i64, moto_rt::fs::SEEK_SET)
.map_err(map_motor_error)
}
SeekFrom::End(offset) => {
moto_rt::fs::seek(self.as_raw_fd(), offset, moto_rt::fs::SEEK_END)
.map_err(map_motor_error)
}
SeekFrom::Current(offset) => {
moto_rt::fs::seek(self.as_raw_fd(), offset, moto_rt::fs::SEEK_CUR)
.map_err(map_motor_error)
}
}
}
pub fn tell(&self) -> io::Result<u64> {
self.seek(SeekFrom::Current(0))
}
pub fn duplicate(&self) -> io::Result<File> {
moto_rt::fs::duplicate(self.as_raw_fd())
.map(|fd| unsafe { Self::from_raw_fd(fd) })
.map_err(map_motor_error)
}
pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
moto_rt::fs::set_file_perm(self.as_raw_fd(), perm.rt_perm).map_err(map_motor_error)
}
pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
unsupported() // Let's not do that.
}
pub fn lock(&self) -> io::Result<()> {
unsupported()
}
pub fn lock_shared(&self) -> io::Result<()> {
unsupported()
}
pub fn try_lock(&self) -> Result<(), crate::fs::TryLockError> {
Err(crate::fs::TryLockError::Error(io::Error::from(io::ErrorKind::Unsupported)))
}
pub fn try_lock_shared(&self) -> Result<(), crate::fs::TryLockError> {
Err(crate::fs::TryLockError::Error(io::Error::from(io::ErrorKind::Unsupported)))
}
pub fn unlock(&self) -> io::Result<()> {
unsupported()
}
pub fn size(&self) -> Option<io::Result<u64>> {
None
}
}
#[derive(Debug)]
pub struct DirBuilder {}
impl DirBuilder {
pub fn new() -> DirBuilder {
DirBuilder {}
}
pub fn mkdir(&self, path: &Path) -> io::Result<()> {
let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
moto_rt::fs::mkdir(path).map_err(map_motor_error)
}
}
pub fn unlink(path: &Path) -> io::Result<()> {
let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
moto_rt::fs::unlink(path).map_err(map_motor_error)
}
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
let old = old.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
let new = new.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
moto_rt::fs::rename(old, new).map_err(map_motor_error)
}
pub fn rmdir(path: &Path) -> io::Result<()> {
let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
moto_rt::fs::rmdir(path).map_err(map_motor_error)
}
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
moto_rt::fs::rmdir_all(path).map_err(map_motor_error)
}
pub fn set_perm(path: &Path, perm: FilePermissions) -> io::Result<()> {
let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
moto_rt::fs::set_perm(path, perm.rt_perm).map_err(map_motor_error)
}
pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
unsupported()
}
pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
unsupported()
}
pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
unsupported()
}
pub fn stat(path: &Path) -> io::Result<FileAttr> {
let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
let inner = moto_rt::fs::stat(path).map_err(map_motor_error)?;
Ok(FileAttr { inner })
}
pub fn lstat(path: &Path) -> io::Result<FileAttr> {
stat(path)
}
pub fn canonicalize(path: &Path) -> io::Result<PathBuf> {
let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
let path = moto_rt::fs::canonicalize(path).map_err(map_motor_error)?;
Ok(path.into())
}
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
let from = from.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
let to = to.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
moto_rt::fs::copy(from, to).map_err(map_motor_error)
}
#[derive(Debug)]
pub struct ReadDir {
rt_fd: moto_rt::RtFd,
path: String,
}
impl Drop for ReadDir {
fn drop(&mut self) {
moto_rt::fs::closedir(self.rt_fd).unwrap();
}
}
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
Ok(ReadDir {
rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?,
path: path.to_owned(),
})
}
impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
match moto_rt::fs::readdir(self.rt_fd).map_err(map_motor_error) {
Ok(maybe_item) => match maybe_item {
Some(inner) => Some(Ok(DirEntry { inner, parent_path: self.path.clone() })),
None => None,
},
Err(err) => Some(Err(err)),
}
}
}
pub struct DirEntry {
parent_path: String,
inner: moto_rt::fs::DirEntry,
}
impl DirEntry {
fn filename(&self) -> &str {
core::str::from_utf8(unsafe {
core::slice::from_raw_parts(self.inner.fname.as_ptr(), self.inner.fname_size as usize)
})
.unwrap()
}
pub fn path(&self) -> PathBuf {
let mut path = self.parent_path.clone();
path.push_str("/");
path.push_str(self.filename());
path.into()
}
pub fn file_name(&self) -> OsString {
self.filename().to_owned().into()
}
pub fn metadata(&self) -> io::Result<FileAttr> {
Ok(FileAttr { inner: self.inner.attr })
}
pub fn file_type(&self) -> io::Result<FileType> {
Ok(FileType { rt_filetype: self.inner.attr.file_type })
}
}
impl AsInner<FileDesc> for File {
#[inline]
fn as_inner(&self) -> &FileDesc {
&self.0
}
}
impl AsInnerMut<FileDesc> for File {
#[inline]
fn as_inner_mut(&mut self) -> &mut FileDesc {
&mut self.0
}
}
impl IntoInner<FileDesc> for File {
fn into_inner(self) -> FileDesc {
self.0
}
}
impl FromInner<FileDesc> for File {
fn from_inner(file_desc: FileDesc) -> Self {
Self(file_desc)
}
}
impl AsFd for File {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl AsRawFd for File {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl IntoRawFd for File {
fn into_raw_fd(self) -> RawFd {
self.0.into_raw_fd()
}
}
impl FromRawFd for File {
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) }
}
}

View File

@@ -0,0 +1,6 @@
use crate::os::fd::{AsFd, AsRawFd};
pub fn is_terminal(fd: &impl AsFd) -> bool {
let fd = fd.as_fd();
moto_rt::fs::is_terminal(fd.as_raw_fd())
}

View File

@@ -39,6 +39,10 @@ mod is_terminal {
mod hermit; mod hermit;
pub use hermit::*; pub use hermit::*;
} }
target_os = "motor" => {
mod motor;
pub use motor::*;
}
_ => { _ => {
mod unsupported; mod unsupported;
pub use unsupported::*; pub use unsupported::*;

View File

@@ -17,6 +17,10 @@ cfg_select! {
mod wasip1; mod wasip1;
pub use wasip1::*; pub use wasip1::*;
} }
target_os = "motor" => {
mod motor;
pub use motor::*;
}
target_os = "xous" => { target_os = "xous" => {
mod xous; mod xous;
pub use xous::*; pub use xous::*;

View File

@@ -0,0 +1,521 @@
pub use moto_rt::netc;
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::net::SocketAddr::{V4, V6};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys::fd::FileDesc;
use crate::sys::map_motor_error;
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::Duration;
// We want to re-use as much of Rust's stdlib code as possible,
// and most of it is unixy, but with a lot of nesting.
#[derive(Debug)]
pub struct Socket(FileDesc);
#[derive(Debug)]
pub struct TcpStream {
inner: Socket,
}
impl TcpStream {
pub fn socket(&self) -> &Socket {
&self.inner
}
pub fn into_socket(self) -> Socket {
self.inner
}
pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap());
moto_rt::net::tcp_connect(&addr, Duration::MAX, false)
.map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } })
.map_err(map_motor_error)
}
pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result<TcpStream> {
let addr = into_netc(addr);
moto_rt::net::tcp_connect(&addr, timeout, false)
.map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } })
.map_err(map_motor_error)
}
pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
moto_rt::net::set_read_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error)
}
pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
moto_rt::net::set_write_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error)
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
moto_rt::net::read_timeout(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
moto_rt::net::write_timeout(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
moto_rt::net::peek(self.inner.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
moto_rt::fs::read(self.inner.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
crate::io::default_read_buf(|buf| self.read(buf), cursor)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
let bufs: &mut [&mut [u8]] = unsafe { core::mem::transmute(bufs) };
moto_rt::fs::read_vectored(self.inner.as_raw_fd(), bufs).map_err(map_motor_error)
}
pub fn is_read_vectored(&self) -> bool {
true
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
moto_rt::fs::write(self.inner.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let bufs: &[&[u8]] = unsafe { core::mem::transmute(bufs) };
moto_rt::fs::write_vectored(self.inner.as_raw_fd(), bufs).map_err(map_motor_error)
}
pub fn is_write_vectored(&self) -> bool {
true
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
moto_rt::net::peer_addr(self.inner.as_raw_fd())
.map(|addr| from_netc(&addr))
.map_err(map_motor_error)
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
moto_rt::net::socket_addr(self.inner.as_raw_fd())
.map(|addr| from_netc(&addr))
.map_err(map_motor_error)
}
pub fn shutdown(&self, shutdown: Shutdown) -> io::Result<()> {
let shutdown = match shutdown {
Shutdown::Read => moto_rt::net::SHUTDOWN_READ,
Shutdown::Write => moto_rt::net::SHUTDOWN_WRITE,
Shutdown::Both => moto_rt::net::SHUTDOWN_READ | moto_rt::net::SHUTDOWN_WRITE,
};
moto_rt::net::shutdown(self.inner.as_raw_fd(), shutdown).map_err(map_motor_error)
}
pub fn duplicate(&self) -> io::Result<TcpStream> {
moto_rt::fs::duplicate(self.inner.as_raw_fd())
.map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } })
.map_err(map_motor_error)
}
pub fn set_linger(&self, timeout: Option<Duration>) -> io::Result<()> {
moto_rt::net::set_linger(self.inner.as_raw_fd(), timeout).map_err(map_motor_error)
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
moto_rt::net::linger(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
moto_rt::net::set_nodelay(self.inner.as_raw_fd(), nodelay).map_err(map_motor_error)
}
pub fn nodelay(&self) -> io::Result<bool> {
moto_rt::net::nodelay(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
moto_rt::net::set_ttl(self.inner.as_raw_fd(), ttl).map_err(map_motor_error)
}
pub fn ttl(&self) -> io::Result<u32> {
moto_rt::net::ttl(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
let e = moto_rt::net::take_error(self.inner.as_raw_fd()).map_err(map_motor_error)?;
if e == moto_rt::E_OK { Ok(None) } else { Ok(Some(map_motor_error(e))) }
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
moto_rt::net::set_nonblocking(self.inner.as_raw_fd(), nonblocking).map_err(map_motor_error)
}
}
#[derive(Debug)]
pub struct TcpListener {
inner: Socket,
}
impl TcpListener {
#[inline]
pub fn socket(&self) -> &Socket {
&self.inner
}
pub fn into_socket(self) -> Socket {
self.inner
}
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap());
moto_rt::net::bind(moto_rt::net::PROTO_TCP, &addr)
.map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } })
.map_err(map_motor_error)
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
moto_rt::net::socket_addr(self.inner.as_raw_fd())
.map(|addr| from_netc(&addr))
.map_err(map_motor_error)
}
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
moto_rt::net::accept(self.inner.as_raw_fd())
.map(|(fd, addr)| {
(TcpStream { inner: unsafe { Socket::from_raw_fd(fd) } }, from_netc(&addr))
})
.map_err(map_motor_error)
}
pub fn duplicate(&self) -> io::Result<TcpListener> {
moto_rt::fs::duplicate(self.inner.as_raw_fd())
.map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } })
.map_err(map_motor_error)
}
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
moto_rt::net::set_ttl(self.inner.as_raw_fd(), ttl).map_err(map_motor_error)
}
pub fn ttl(&self) -> io::Result<u32> {
moto_rt::net::ttl(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
moto_rt::net::set_only_v6(self.inner.as_raw_fd(), only_v6).map_err(map_motor_error)
}
pub fn only_v6(&self) -> io::Result<bool> {
moto_rt::net::only_v6(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
let e = moto_rt::net::take_error(self.inner.as_raw_fd()).map_err(map_motor_error)?;
if e == moto_rt::E_OK { Ok(None) } else { Ok(Some(map_motor_error(e))) }
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
moto_rt::net::set_nonblocking(self.inner.as_raw_fd(), nonblocking).map_err(map_motor_error)
}
}
#[derive(Debug)]
pub struct UdpSocket {
inner: Socket,
}
impl UdpSocket {
pub fn socket(&self) -> &Socket {
&self.inner
}
pub fn into_socket(self) -> Socket {
self.inner
}
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> {
let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap());
moto_rt::net::bind(moto_rt::net::PROTO_UDP, &addr)
.map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } })
.map_err(map_motor_error)
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
moto_rt::net::peer_addr(self.inner.as_raw_fd())
.map(|addr| from_netc(&addr))
.map_err(map_motor_error)
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
moto_rt::net::socket_addr(self.inner.as_raw_fd())
.map(|addr| from_netc(&addr))
.map_err(map_motor_error)
}
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
moto_rt::net::udp_recv_from(self.inner.as_raw_fd(), buf)
.map(|(sz, addr)| (sz, from_netc(&addr)))
.map_err(map_motor_error)
}
pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
moto_rt::net::udp_peek_from(self.inner.as_raw_fd(), buf)
.map(|(sz, addr)| (sz, from_netc(&addr)))
.map_err(map_motor_error)
}
pub fn send_to(&self, buf: &[u8], addr: &SocketAddr) -> io::Result<usize> {
let addr = into_netc(addr);
moto_rt::net::udp_send_to(self.inner.as_raw_fd(), buf, &addr).map_err(map_motor_error)
}
pub fn duplicate(&self) -> io::Result<UdpSocket> {
moto_rt::fs::duplicate(self.inner.as_raw_fd())
.map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } })
.map_err(map_motor_error)
}
pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
moto_rt::net::set_read_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error)
}
pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
moto_rt::net::set_write_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error)
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
moto_rt::net::read_timeout(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
moto_rt::net::write_timeout(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> {
moto_rt::net::set_udp_broadcast(self.inner.as_raw_fd(), broadcast).map_err(map_motor_error)
}
pub fn broadcast(&self) -> io::Result<bool> {
moto_rt::net::udp_broadcast(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn set_multicast_loop_v4(&self, val: bool) -> io::Result<()> {
moto_rt::net::set_udp_multicast_loop_v4(self.inner.as_raw_fd(), val)
.map_err(map_motor_error)
}
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
moto_rt::net::udp_multicast_loop_v4(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn set_multicast_ttl_v4(&self, val: u32) -> io::Result<()> {
moto_rt::net::set_udp_multicast_ttl_v4(self.inner.as_raw_fd(), val).map_err(map_motor_error)
}
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
moto_rt::net::udp_multicast_ttl_v4(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn set_multicast_loop_v6(&self, val: bool) -> io::Result<()> {
moto_rt::net::set_udp_multicast_loop_v6(self.inner.as_raw_fd(), val)
.map_err(map_motor_error)
}
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
moto_rt::net::udp_multicast_loop_v6(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn join_multicast_v4(&self, addr: &Ipv4Addr, iface: &Ipv4Addr) -> io::Result<()> {
let addr = (*addr).into();
let iface = (*iface).into();
moto_rt::net::join_udp_multicast_v4(self.inner.as_raw_fd(), &addr, &iface)
.map_err(map_motor_error)
}
pub fn join_multicast_v6(&self, addr: &Ipv6Addr, iface: u32) -> io::Result<()> {
let addr = (*addr).into();
moto_rt::net::join_udp_multicast_v6(self.inner.as_raw_fd(), &addr, iface)
.map_err(map_motor_error)
}
pub fn leave_multicast_v4(&self, addr: &Ipv4Addr, iface: &Ipv4Addr) -> io::Result<()> {
let addr = (*addr).into();
let iface = (*iface).into();
moto_rt::net::leave_udp_multicast_v4(self.inner.as_raw_fd(), &addr, &iface)
.map_err(map_motor_error)
}
pub fn leave_multicast_v6(&self, addr: &Ipv6Addr, iface: u32) -> io::Result<()> {
let addr = (*addr).into();
moto_rt::net::leave_udp_multicast_v6(self.inner.as_raw_fd(), &addr, iface)
.map_err(map_motor_error)
}
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
moto_rt::net::set_ttl(self.inner.as_raw_fd(), ttl).map_err(map_motor_error)
}
pub fn ttl(&self) -> io::Result<u32> {
moto_rt::net::ttl(self.inner.as_raw_fd()).map_err(map_motor_error)
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
moto_rt::net::take_error(self.inner.as_raw_fd())
.map(|e| match e {
moto_rt::E_OK => None,
e => Some(map_motor_error(e)),
})
.map_err(map_motor_error)
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
moto_rt::net::set_nonblocking(self.inner.as_raw_fd(), nonblocking).map_err(map_motor_error)
}
pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
moto_rt::fs::read(self.inner.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
moto_rt::net::peek(self.inner.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
moto_rt::fs::write(self.inner.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> {
let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap());
moto_rt::net::udp_connect(self.inner.as_raw_fd(), &addr).map_err(map_motor_error)
}
}
pub struct LookupHost {
addresses: alloc::collections::VecDeque<netc::sockaddr>,
}
pub fn lookup_host(host: &str, port: u16) -> io::Result<LookupHost> {
let (_port, addresses) = moto_rt::net::lookup_host(host, port).map_err(map_motor_error)?;
Ok(LookupHost { addresses })
}
impl Iterator for LookupHost {
type Item = SocketAddr;
fn next(&mut self) -> Option<SocketAddr> {
self.addresses.pop_front().map(|addr| from_netc(&addr))
}
}
impl TryFrom<&str> for LookupHost {
type Error = io::Error;
fn try_from(host_port: &str) -> io::Result<LookupHost> {
let (host, port_str) = host_port
.rsplit_once(':')
.ok_or(moto_rt::E_INVALID_ARGUMENT)
.map_err(map_motor_error)?;
let port: u16 =
port_str.parse().map_err(|_| moto_rt::E_INVALID_ARGUMENT).map_err(map_motor_error)?;
(host, port).try_into()
}
}
impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
type Error = io::Error;
fn try_from(host_port: (&'a str, u16)) -> io::Result<LookupHost> {
let (host, port) = host_port;
let (_port, addresses) = moto_rt::net::lookup_host(host, port).map_err(map_motor_error)?;
Ok(LookupHost { addresses })
}
}
fn into_netc(addr: &SocketAddr) -> netc::sockaddr {
match addr {
V4(addr4) => netc::sockaddr { v4: (*addr4).into() },
V6(addr6) => netc::sockaddr { v6: (*addr6).into() },
}
}
fn from_netc(addr: &netc::sockaddr) -> SocketAddr {
// SAFETY: all variants of union netc::sockaddr have `sin_family` at the same offset.
let family = unsafe { addr.v4.sin_family };
match family {
netc::AF_INET => SocketAddr::V4(crate::net::SocketAddrV4::from(unsafe { addr.v4 })),
netc::AF_INET6 => SocketAddr::V6(crate::net::SocketAddrV6::from(unsafe { addr.v6 })),
_ => panic!("bad sin_family {family}"),
}
}
impl AsInner<FileDesc> for Socket {
#[inline]
fn as_inner(&self) -> &FileDesc {
&self.0
}
}
impl IntoInner<FileDesc> for Socket {
fn into_inner(self) -> FileDesc {
self.0
}
}
impl FromInner<FileDesc> for Socket {
fn from_inner(file_desc: FileDesc) -> Self {
Self(file_desc)
}
}
impl AsFd for Socket {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl AsRawFd for Socket {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl IntoRawFd for Socket {
fn into_raw_fd(self) -> RawFd {
self.0.into_raw_fd()
}
}
impl FromRawFd for Socket {
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
Self(FromRawFd::from_raw_fd(raw_fd))
}
}
impl AsInner<Socket> for TcpStream {
#[inline]
fn as_inner(&self) -> &Socket {
&self.inner
}
}
impl FromInner<Socket> for TcpStream {
fn from_inner(socket: Socket) -> TcpStream {
TcpStream { inner: socket }
}
}
impl FromInner<Socket> for TcpListener {
fn from_inner(socket: Socket) -> TcpListener {
TcpListener { inner: socket }
}
}
impl FromInner<Socket> for UdpSocket {
fn from_inner(socket: Socket) -> UdpSocket {
UdpSocket { inner: socket }
}
}

View File

@@ -41,6 +41,10 @@ cfg_select! {
mod hermit; mod hermit;
pub use self::hermit::*; pub use self::hermit::*;
} }
target_os = "motor" => {
mod motor;
pub use self::motor::*;
}
target_os = "trusty" => { target_os = "trusty" => {
mod trusty; mod trusty;
pub use self::trusty::*; pub use self::trusty::*;

View File

@@ -0,0 +1,77 @@
#![allow(unsafe_op_in_unsafe_fn)]
pub mod os;
pub mod pipe;
pub mod time;
pub use moto_rt::futex;
use crate::io as std_io;
use crate::sys::RawOsError;
pub(crate) fn map_motor_error(err: moto_rt::ErrorCode) -> crate::io::Error {
crate::io::Error::from_raw_os_error(err.into())
}
#[cfg(not(test))]
#[unsafe(no_mangle)]
pub extern "C" fn motor_start() -> ! {
// Initialize the runtime.
moto_rt::start();
// Call main.
unsafe extern "C" {
fn main(_: isize, _: *const *const u8, _: u8) -> i32;
}
let result = unsafe { main(0, core::ptr::null(), 0) };
// Terminate the process.
moto_rt::process::exit(result)
}
// SAFETY: must be called only once during runtime initialization.
// NOTE: Motor OS uses moto_rt::start() to initialize runtime (see above).
pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
// SAFETY: must be called only once during runtime cleanup.
// NOTE: this is not guaranteed to run, for example when the program aborts.
pub unsafe fn cleanup() {}
pub fn unsupported<T>() -> std_io::Result<T> {
Err(unsupported_err())
}
pub fn unsupported_err() -> std_io::Error {
std_io::Error::UNSUPPORTED_PLATFORM
}
pub fn is_interrupted(_code: RawOsError) -> bool {
false // Motor OS doesn't have signals.
}
pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind {
use moto_rt::error::*;
use std_io::ErrorKind;
if code < 0 || code > u16::MAX.into() {
return std_io::ErrorKind::Uncategorized;
}
match code as moto_rt::ErrorCode /* u16 */ {
E_ALREADY_IN_USE => ErrorKind::AlreadyExists,
E_INVALID_FILENAME => ErrorKind::InvalidFilename,
E_NOT_FOUND => ErrorKind::NotFound,
E_TIMED_OUT => ErrorKind::TimedOut,
E_NOT_IMPLEMENTED => ErrorKind::Unsupported,
E_FILE_TOO_LARGE => ErrorKind::FileTooLarge,
E_UNEXPECTED_EOF => ErrorKind::UnexpectedEof,
E_INVALID_ARGUMENT => ErrorKind::InvalidInput,
E_NOT_READY => ErrorKind::WouldBlock,
E_NOT_CONNECTED => ErrorKind::NotConnected,
_ => crate::io::ErrorKind::Uncategorized,
}
}
pub fn abort_internal() -> ! {
core::intrinsics::abort();
}

View File

@@ -0,0 +1,100 @@
use super::map_motor_error;
use crate::error::Error as StdError;
use crate::ffi::{OsStr, OsString};
use crate::marker::PhantomData;
use crate::os::motor::ffi::OsStrExt;
use crate::path::{self, PathBuf};
use crate::sys::RawOsError;
use crate::{fmt, io};
pub fn errno() -> RawOsError {
// Not used in Motor OS because it is ambiguous: Motor OS
// is micro-kernel-based, and I/O happens via a shared-memory
// ring buffer, so an I/O operation that on a unix is a syscall
// may involve no sycalls on Motor OS at all, or a syscall
// that e.g. waits for a notification from the I/O driver
// (sys-io); and the wait syscall may succeed, but the
// driver may report an I/O error; or a bunch of results
// for several I/O operations, some successful and some
// not.
//
// Also I/O operations in a Motor OS process are handled by a
// separate runtime background/I/O thread, so it is really hard
// to define what "last system error in the current thread"
// actually means.
moto_rt::E_UNKNOWN.into()
}
pub fn error_string(errno: RawOsError) -> String {
let error_code: moto_rt::ErrorCode = match errno {
x if x < 0 => moto_rt::E_UNKNOWN,
x if x > u16::MAX.into() => moto_rt::E_UNKNOWN,
x => x as moto_rt::ErrorCode, /* u16 */
};
format!("{}", moto_rt::Error::from(error_code))
}
pub fn getcwd() -> io::Result<PathBuf> {
moto_rt::fs::getcwd().map(PathBuf::from).map_err(map_motor_error)
}
pub fn chdir(path: &path::Path) -> io::Result<()> {
moto_rt::fs::chdir(path.as_os_str().as_str()).map_err(map_motor_error)
}
pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
panic!("unsupported")
}
impl<'a> Iterator for SplitPaths<'a> {
type Item = PathBuf;
fn next(&mut self) -> Option<PathBuf> {
self.0
}
}
#[derive(Debug)]
pub struct JoinPathsError;
pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
where
I: Iterator<Item = T>,
T: AsRef<OsStr>,
{
Err(JoinPathsError)
}
impl fmt::Display for JoinPathsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"not supported on this platform yet".fmt(f)
}
}
impl StdError for JoinPathsError {
#[allow(deprecated)]
fn description(&self) -> &str {
"not supported on this platform yet"
}
}
pub fn current_exe() -> io::Result<PathBuf> {
moto_rt::process::current_exe().map(PathBuf::from).map_err(map_motor_error)
}
pub fn temp_dir() -> PathBuf {
PathBuf::from(moto_rt::fs::TEMP_DIR)
}
pub fn home_dir() -> Option<PathBuf> {
None
}
pub fn exit(code: i32) -> ! {
moto_rt::process::exit(code)
}
pub fn getpid() -> u32 {
panic!("Pids on Motor OS are u64.")
}

View File

@@ -0,0 +1,121 @@
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use crate::sys::fd::FileDesc;
use crate::sys::map_motor_error;
use crate::sys_common::{FromInner, IntoInner};
#[derive(Debug)]
pub struct AnonPipe(FileDesc);
impl From<moto_rt::RtFd> for AnonPipe {
fn from(rt_fd: moto_rt::RtFd) -> AnonPipe {
unsafe { AnonPipe::from_raw_fd(rt_fd) }
}
}
impl AnonPipe {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
crate::io::default_read_buf(|buf| self.read(buf), cursor)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
crate::io::default_read_vectored(|b| self.read(b), bufs)
}
pub fn is_read_vectored(&self) -> bool {
false
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error)
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
crate::io::default_write_vectored(|b| self.write(b), bufs)
}
pub fn is_write_vectored(&self) -> bool {
false
}
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
let mut temp_vec = Vec::new();
let mut size = 0_usize;
loop {
temp_vec.resize(256, 0_u8);
match self.read(&mut temp_vec[..]) {
Ok(sz) => {
if sz == 0 {
return Ok(size);
}
size += sz;
temp_vec.truncate(sz);
buf.append(&mut temp_vec);
}
Err(err) => {
if size != 0 {
return Ok(size);
} else {
return Err(err);
}
}
}
}
}
}
impl AsRawFd for AnonPipe {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl FromRawFd for AnonPipe {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
let desc = FileDesc::from_raw_fd(fd);
Self(desc)
}
}
impl IntoRawFd for AnonPipe {
fn into_raw_fd(self) -> RawFd {
self.0.into_raw_fd()
}
}
impl AsFd for AnonPipe {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl IntoInner<OwnedFd> for AnonPipe {
fn into_inner(self) -> OwnedFd {
self.0.into_inner()
}
}
impl IntoInner<FileDesc> for AnonPipe {
fn into_inner(self) -> FileDesc {
self.0
}
}
impl FromInner<OwnedFd> for AnonPipe {
fn from_inner(owned_fd: OwnedFd) -> Self {
Self(FileDesc::from_inner(owned_fd))
}
}
pub fn read2(_p1: AnonPipe, _v1: &mut Vec<u8>, _p2: AnonPipe, _v2: &mut Vec<u8>) -> io::Result<()> {
Err(io::Error::from_raw_os_error(moto_rt::E_NOT_IMPLEMENTED.into()))
}
#[inline]
pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
Err(io::Error::UNSUPPORTED_PLATFORM)
}

View File

@@ -0,0 +1 @@
pub use moto_rt::time::{Instant, SystemTime, UNIX_EPOCH};

View File

@@ -62,7 +62,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
} }
pub(crate) fn is_absolute(path: &Path) -> bool { pub(crate) fn is_absolute(path: &Path) -> bool {
if cfg!(any(unix, target_os = "hermit", target_os = "wasi")) { if cfg!(any(unix, target_os = "hermit", target_os = "wasi", target_os = "motor")) {
path.has_root() path.has_root()
} else { } else {
path.has_root() && path.prefix().is_some() path.has_root() && path.prefix().is_some()

View File

@@ -17,7 +17,7 @@ cfg_select! {
target_os = "emscripten" => { target_os = "emscripten" => {
mod emcc; mod emcc;
} }
any(target_env = "msvc", target_family = "wasm") => { any(target_env = "msvc", target_family = "wasm", target_os = "motor") => {
// This is required by the compiler to exist (e.g., it's a lang item), // This is required by the compiler to exist (e.g., it's a lang item),
// but it's never actually called by the compiler because // but it's never actually called by the compiler because
// __CxxFrameHandler3 (msvc) / __gxx_wasm_personality_v0 (wasm) is the // __CxxFrameHandler3 (msvc) / __gxx_wasm_personality_v0 (wasm) is the

View File

@@ -11,6 +11,10 @@ cfg_select! {
mod uefi; mod uefi;
use uefi as imp; use uefi as imp;
} }
target_os = "motor" => {
mod motor;
use motor as imp;
}
_ => { _ => {
mod unsupported; mod unsupported;
use unsupported as imp; use unsupported as imp;
@@ -38,6 +42,7 @@ pub use imp::{
)) ))
), ),
target_os = "windows", target_os = "windows",
target_os = "motor"
))] ))]
pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> { pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
use crate::sys::pipe::read2; use crate::sys::pipe::read2;
@@ -77,5 +82,6 @@ pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec<u8>, Vec<
)) ))
), ),
target_os = "windows", target_os = "windows",
target_os = "motor"
)))] )))]
pub use imp::output; pub use imp::output;

View File

@@ -0,0 +1,313 @@
use super::CommandEnvs;
use super::env::CommandEnv;
use crate::ffi::OsStr;
pub use crate::ffi::OsString as EnvKey;
use crate::num::NonZeroI32;
use crate::os::fd::{FromRawFd, IntoRawFd};
use crate::os::motor::ffi::OsStrExt;
use crate::path::Path;
use crate::process::StdioPipes;
use crate::sys::fs::File;
use crate::sys::map_motor_error;
use crate::sys::pipe::AnonPipe;
use crate::sys_common::{AsInner, FromInner};
use crate::{fmt, io};
pub enum Stdio {
Inherit,
Null,
MakePipe,
Fd(crate::sys::fd::FileDesc),
}
impl Stdio {
fn into_rt(self) -> moto_rt::RtFd {
match self {
Stdio::Inherit => moto_rt::process::STDIO_INHERIT,
Stdio::Null => moto_rt::process::STDIO_NULL,
Stdio::MakePipe => moto_rt::process::STDIO_MAKE_PIPE,
Stdio::Fd(fd) => fd.into_raw_fd(),
}
}
fn try_clone(&self) -> io::Result<Self> {
match self {
Self::Fd(fd) => {
Ok(Self::Fd(crate::sys::fd::FileDesc::from_inner(fd.as_inner().try_clone()?)))
}
Self::Inherit => Ok(Self::Inherit),
Self::Null => Ok(Self::Null),
Self::MakePipe => Ok(Self::MakePipe),
}
}
}
#[derive(Default)]
pub struct Command {
program: String,
args: Vec<String>,
cwd: Option<String>,
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
env: CommandEnv,
}
impl Command {
pub fn new(program: &OsStr) -> Command {
let mut env = CommandEnv::default();
env.remove(OsStr::new(moto_rt::process::STDIO_IS_TERMINAL_ENV_KEY));
Command { program: program.as_str().to_owned(), env, ..Default::default() }
}
pub fn arg(&mut self, arg: &OsStr) {
self.args.push(arg.as_str().to_owned())
}
pub fn env_mut(&mut self) -> &mut CommandEnv {
&mut self.env
}
pub fn cwd(&mut self, dir: &OsStr) {
self.cwd = Some(dir.as_str().to_owned())
}
pub fn stdin(&mut self, stdin: Stdio) {
self.stdin = Some(stdin);
}
pub fn stdout(&mut self, stdout: Stdio) {
self.stdout = Some(stdout);
}
pub fn stderr(&mut self, stderr: Stdio) {
self.stderr = Some(stderr);
}
pub fn get_program(&self) -> &OsStr {
OsStr::new(self.program.as_str())
}
pub fn get_args(&self) -> CommandArgs<'_> {
let iter = self.args.iter();
CommandArgs { iter }
}
pub fn get_envs(&self) -> CommandEnvs<'_> {
self.env.iter()
}
pub fn get_current_dir(&self) -> Option<&Path> {
self.cwd.as_ref().map(Path::new)
}
pub fn spawn(
&mut self,
default: Stdio,
needs_stdin: bool,
) -> io::Result<(Process, StdioPipes)> {
let stdin = if let Some(stdin) = self.stdin.as_ref() {
stdin.try_clone()?.into_rt()
} else if needs_stdin {
default.try_clone()?.into_rt()
} else {
Stdio::Null.into_rt()
};
let stdout = if let Some(stdout) = self.stdout.as_ref() {
stdout.try_clone()?.into_rt()
} else {
default.try_clone()?.into_rt()
};
let stderr = if let Some(stderr) = self.stdout.as_ref() {
stderr.try_clone()?.into_rt()
} else {
default.try_clone()?.into_rt()
};
let mut env = Vec::<(String, String)>::new();
for (k, v) in self.env.capture() {
env.push((k.as_str().to_owned(), v.as_str().to_owned()));
}
let args = moto_rt::process::SpawnArgs {
program: self.program.clone(),
args: self.args.clone(),
env,
cwd: self.cwd.clone(),
stdin,
stdout,
stderr,
};
let (handle, stdin, stdout, stderr) =
moto_rt::process::spawn(args).map_err(map_motor_error)?;
Ok((
Process { handle },
StdioPipes {
stdin: if stdin >= 0 { Some(stdin.into()) } else { None },
stdout: if stdout >= 0 { Some(stdout.into()) } else { None },
stderr: if stderr >= 0 { Some(stderr.into()) } else { None },
},
))
}
}
impl From<AnonPipe> for Stdio {
fn from(pipe: AnonPipe) -> Stdio {
unsafe { Stdio::Fd(crate::sys::fd::FileDesc::from_raw_fd(pipe.into_raw_fd())) }
}
}
impl From<crate::sys::fd::FileDesc> for Stdio {
fn from(fd: crate::sys::fd::FileDesc) -> Stdio {
Stdio::Fd(fd)
}
}
impl From<File> for Stdio {
fn from(_file: File) -> Stdio {
panic!("Not implemented")
}
}
impl From<io::Stdout> for Stdio {
fn from(_: io::Stdout) -> Stdio {
panic!("Not implemented")
}
}
impl From<io::Stderr> for Stdio {
fn from(_: io::Stderr) -> Stdio {
panic!("Not implemented")
}
}
impl fmt::Debug for Command {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
Ok(())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct ExitStatus(i32);
impl ExitStatus {
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
if self.0 == 0 { Ok(()) } else { Err(ExitStatusError(*self)) }
}
pub fn code(&self) -> Option<i32> {
Some(self.0)
}
}
impl fmt::Display for ExitStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "exit code: {}", self.0)
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ExitStatusError(ExitStatus);
impl Into<ExitStatus> for ExitStatusError {
fn into(self) -> ExitStatus {
self.0
}
}
impl ExitStatusError {
pub fn code(self) -> Option<NonZeroI32> {
NonZeroI32::new(self.0.0)
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ExitCode(i32);
impl ExitCode {
pub const SUCCESS: ExitCode = ExitCode(0);
pub const FAILURE: ExitCode = ExitCode(1);
pub fn as_i32(&self) -> i32 {
self.0
}
}
impl From<u8> for ExitCode {
fn from(code: u8) -> Self {
Self(code as i32)
}
}
pub struct Process {
handle: u64,
}
impl Drop for Process {
fn drop(&mut self) {
moto_rt::alloc::release_handle(self.handle).unwrap();
}
}
impl Process {
pub fn id(&self) -> u32 {
0
}
pub fn kill(&mut self) -> io::Result<()> {
match moto_rt::process::kill(self.handle) {
moto_rt::E_OK => Ok(()),
err => Err(map_motor_error(err)),
}
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
moto_rt::process::wait(self.handle).map(|c| ExitStatus(c)).map_err(map_motor_error)
}
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
match moto_rt::process::try_wait(self.handle) {
Ok(s) => Ok(Some(ExitStatus(s))),
Err(err) => match err {
moto_rt::E_NOT_READY => Ok(None),
err => Err(map_motor_error(err)),
},
}
}
#[allow(unused)]
pub fn handle(&self) -> u64 {
self.handle
}
}
pub struct CommandArgs<'a> {
iter: crate::slice::Iter<'a, String>,
}
impl<'a> Iterator for CommandArgs<'a> {
type Item = &'a OsStr;
fn next(&mut self) -> Option<&'a OsStr> {
self.iter.next().map(|arg| OsStr::new(arg))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a> ExactSizeIterator for CommandArgs<'a> {
fn len(&self) -> usize {
self.iter.len()
}
fn is_empty(&self) -> bool {
self.iter.is_empty()
}
}
impl<'a> fmt::Debug for CommandArgs<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter.clone()).finish()
}
}

View File

@@ -62,6 +62,10 @@ cfg_select! {
mod redox; mod redox;
pub use redox::fill_bytes; pub use redox::fill_bytes;
} }
target_os = "motor" => {
mod motor;
pub use motor::fill_bytes;
}
all(target_vendor = "fortanix", target_env = "sgx") => { all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx; mod sgx;
pub use sgx::fill_bytes; pub use sgx::fill_bytes;

View File

@@ -0,0 +1,3 @@
pub fn fill_bytes(bytes: &mut [u8]) {
moto_rt::fill_random_bytes(bytes)
}

View File

@@ -13,6 +13,10 @@ cfg_select! {
mod sgx; mod sgx;
pub use sgx::*; pub use sgx::*;
} }
target_os = "motor" => {
mod motor;
pub use motor::*;
}
target_os = "solid_asp3" => { target_os = "solid_asp3" => {
mod solid; mod solid;
pub use solid::*; pub use solid::*;

View File

@@ -0,0 +1,232 @@
use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use crate::sys::map_motor_error;
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::{io, process, sys};
pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
pub struct Stdin {}
impl Stdin {
pub const fn new() -> Self {
Self {}
}
}
pub struct Stdout {}
impl Stdout {
pub const fn new() -> Self {
Self {}
}
}
pub struct Stderr {}
impl Stderr {
pub const fn new() -> Self {
Self {}
}
}
impl crate::sealed::Sealed for Stdin {}
impl crate::io::IsTerminal for Stdin {
fn is_terminal(&self) -> bool {
moto_rt::fs::is_terminal(moto_rt::FD_STDIN)
}
}
impl io::Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
moto_rt::fs::read(moto_rt::FD_STDIN, buf).map_err(map_motor_error)
}
}
impl io::Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
moto_rt::fs::write(moto_rt::FD_STDOUT, buf).map_err(map_motor_error)
}
fn flush(&mut self) -> io::Result<()> {
moto_rt::fs::flush(moto_rt::FD_STDOUT).map_err(map_motor_error)
}
}
impl io::Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
moto_rt::fs::write(moto_rt::FD_STDERR, buf).map_err(map_motor_error)
}
fn flush(&mut self) -> io::Result<()> {
moto_rt::fs::flush(moto_rt::FD_STDERR).map_err(map_motor_error)
}
}
pub fn panic_output() -> Option<impl io::Write> {
Some(Stderr::new())
}
pub fn is_ebadf(_err: &io::Error) -> bool {
true
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl FromRawFd for process::Stdio {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
let fd = unsafe { sys::fd::FileDesc::from_raw_fd(fd) };
let io = sys::process::Stdio::Fd(fd);
process::Stdio::from_inner(io)
}
}
#[stable(feature = "io_safety", since = "1.63.0")]
impl From<OwnedFd> for process::Stdio {
/// Takes ownership of a file descriptor and returns a [`Stdio`](process::Stdio)
/// that can attach a stream to it.
#[inline]
fn from(fd: OwnedFd) -> process::Stdio {
let fd = sys::fd::FileDesc::from_inner(fd);
let io = sys::process::Stdio::Fd(fd);
process::Stdio::from_inner(io)
}
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdin {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_inner().as_raw_fd()
}
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdout {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_inner().as_raw_fd()
}
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStderr {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_inner().as_raw_fd()
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdin {
#[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_raw_fd()
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdout {
#[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_raw_fd()
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStderr {
#[inline]
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_raw_fd()
}
}
#[stable(feature = "io_safety", since = "1.63.0")]
impl AsFd for crate::process::ChildStdin {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.as_inner().as_fd()
}
}
#[stable(feature = "io_safety", since = "1.63.0")]
impl From<crate::process::ChildStdin> for OwnedFd {
/// Takes ownership of a [`ChildStdin`](crate::process::ChildStdin)'s file descriptor.
#[inline]
fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd {
child_stdin.into_inner().into_inner()
}
}
/// Creates a `ChildStdin` from the provided `OwnedFd`.
///
/// The provided file descriptor must point to a pipe
/// with the `CLOEXEC` flag set.
#[stable(feature = "child_stream_from_fd", since = "1.74.0")]
impl From<OwnedFd> for process::ChildStdin {
#[inline]
fn from(fd: OwnedFd) -> process::ChildStdin {
let pipe = sys::pipe::AnonPipe::from_inner(fd);
process::ChildStdin::from_inner(pipe)
}
}
#[stable(feature = "io_safety", since = "1.63.0")]
impl AsFd for crate::process::ChildStdout {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.as_inner().as_fd()
}
}
#[stable(feature = "io_safety", since = "1.63.0")]
impl From<crate::process::ChildStdout> for OwnedFd {
/// Takes ownership of a [`ChildStdout`](crate::process::ChildStdout)'s file descriptor.
#[inline]
fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd {
child_stdout.into_inner().into_inner()
}
}
/// Creates a `ChildStdout` from the provided `OwnedFd`.
///
/// The provided file descriptor must point to a pipe
/// with the `CLOEXEC` flag set.
#[stable(feature = "child_stream_from_fd", since = "1.74.0")]
impl From<OwnedFd> for process::ChildStdout {
#[inline]
fn from(fd: OwnedFd) -> process::ChildStdout {
let pipe = sys::pipe::AnonPipe::from_inner(fd);
process::ChildStdout::from_inner(pipe)
}
}
#[stable(feature = "io_safety", since = "1.63.0")]
impl AsFd for crate::process::ChildStderr {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.as_inner().as_fd()
}
}
#[stable(feature = "io_safety", since = "1.63.0")]
impl From<crate::process::ChildStderr> for OwnedFd {
/// Takes ownership of a [`ChildStderr`](crate::process::ChildStderr)'s file descriptor.
#[inline]
fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd {
child_stderr.into_inner().into_inner()
}
}
/// Creates a `ChildStderr` from the provided `OwnedFd`.
///
/// The provided file descriptor must point to a pipe
/// with the `CLOEXEC` flag set.
#[stable(feature = "child_stream_from_fd", since = "1.74.0")]
impl From<OwnedFd> for process::ChildStderr {
#[inline]
fn from(fd: OwnedFd) -> process::ChildStderr {
let pipe = sys::pipe::AnonPipe::from_inner(fd);
process::ChildStderr::from_inner(pipe)
}
}

View File

@@ -6,6 +6,7 @@ cfg_select! {
target_os = "freebsd", target_os = "freebsd",
target_os = "openbsd", target_os = "openbsd",
target_os = "dragonfly", target_os = "dragonfly",
target_os = "motor",
target_os = "fuchsia", target_os = "fuchsia",
all(target_family = "wasm", target_feature = "atomics"), all(target_family = "wasm", target_feature = "atomics"),
target_os = "hermit", target_os = "hermit",

View File

@@ -5,6 +5,7 @@ cfg_select! {
target_os = "android", target_os = "android",
target_os = "freebsd", target_os = "freebsd",
target_os = "openbsd", target_os = "openbsd",
target_os = "motor",
target_os = "dragonfly", target_os = "dragonfly",
all(target_family = "wasm", target_feature = "atomics"), all(target_family = "wasm", target_feature = "atomics"),
target_os = "hermit", target_os = "hermit",

View File

@@ -14,6 +14,7 @@ cfg_select! {
target_os = "android", target_os = "android",
all(target_arch = "wasm32", target_feature = "atomics"), all(target_arch = "wasm32", target_feature = "atomics"),
target_os = "freebsd", target_os = "freebsd",
target_os = "motor",
target_os = "openbsd", target_os = "openbsd",
target_os = "dragonfly", target_os = "dragonfly",
target_os = "fuchsia", target_os = "fuchsia",

View File

@@ -9,6 +9,7 @@ cfg_select! {
target_os = "fuchsia", target_os = "fuchsia",
all(target_family = "wasm", target_feature = "atomics"), all(target_family = "wasm", target_feature = "atomics"),
target_os = "hermit", target_os = "hermit",
target_os = "motor",
) => { ) => {
mod futex; mod futex;
pub use futex::RwLock; pub use futex::RwLock;

View File

@@ -8,6 +8,7 @@ cfg_select! {
target_os = "openbsd", target_os = "openbsd",
target_os = "dragonfly", target_os = "dragonfly",
target_os = "fuchsia", target_os = "fuchsia",
target_os = "motor",
target_os = "hermit", target_os = "hermit",
) => { ) => {
mod futex; mod futex;

View File

@@ -6,6 +6,10 @@ cfg_select! {
mod unsupported; mod unsupported;
pub use unsupported::{current_os_id, set_name}; pub use unsupported::{current_os_id, set_name};
} }
target_os = "motor" => {
mod motor;
pub use motor::*;
}
all(target_vendor = "fortanix", target_env = "sgx") => { all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx; mod sgx;
pub use sgx::{Thread, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; pub use sgx::{Thread, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};

View File

@@ -0,0 +1,63 @@
use crate::ffi::CStr;
use crate::io;
use crate::num::NonZeroUsize;
use crate::sys::map_motor_error;
use crate::time::Duration;
pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 256;
pub struct Thread {
sys_thread: moto_rt::thread::ThreadHandle,
}
unsafe impl Send for Thread {}
unsafe impl Sync for Thread {}
impl Thread {
pub unsafe fn new(
stack: usize,
_name: Option<&str>,
p: Box<dyn FnOnce()>,
) -> io::Result<Thread> {
extern "C" fn __moto_rt_thread_fn(thread_arg: u64) {
unsafe {
Box::from_raw(
core::ptr::with_exposed_provenance::<Box<dyn FnOnce()>>(thread_arg as usize)
.cast_mut(),
)();
}
}
let thread_arg = Box::into_raw(Box::new(p)).expose_provenance() as u64;
let sys_thread = moto_rt::thread::spawn(__moto_rt_thread_fn, stack, thread_arg)
.map_err(map_motor_error)?;
Ok(Self { sys_thread })
}
pub fn join(self) {
assert!(moto_rt::thread::join(self.sys_thread) == moto_rt::E_OK)
}
}
pub fn set_name(name: &CStr) {
let bytes = name.to_bytes();
if let Ok(s) = core::str::from_utf8(bytes) {
let _ = moto_rt::thread::set_name(s);
}
}
pub fn current_os_id() -> Option<u64> {
None
}
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
Ok(unsafe { NonZeroUsize::new_unchecked(moto_rt::num_cpus()) })
}
pub fn yield_now() {
moto_rt::thread::yield_now()
}
pub fn sleep(dur: Duration) {
moto_rt::thread::sleep_until(moto_rt::time::Instant::now() + dur)
}

View File

@@ -187,6 +187,14 @@ pub(crate) mod key {
pub(super) use xous::{Key, get, set}; pub(super) use xous::{Key, get, set};
use xous::{create, destroy}; use xous::{create, destroy};
} }
target_os = "motor" => {
mod racy;
#[cfg(test)]
mod tests;
pub(super) use racy::LazyKey;
pub(super) use moto_rt::tls::{Key, get, set};
use moto_rt::tls::{create, destroy};
}
_ => {} _ => {}
} }
} }