From 6bb9d807f6dd6574ee7da5e7d4843b3388506b1e Mon Sep 17 00:00:00 2001 From: Jeron Aldaron Lau Date: Sat, 2 Mar 2024 19:18:00 -0600 Subject: [PATCH 1/5] Support Redox --- .gitignore | 1 + Cargo.toml | 13 ++-- TESTING.md | 43 +++++++++++++ src/os/redox.rs | 156 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 TESTING.md create mode 100644 src/os/redox.rs diff --git a/.gitignore b/.gitignore index 7ed80a4..6ec0057 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ Cargo.lock /ignore/ /a.out /**/*.swp +/build/ diff --git a/Cargo.toml b/Cargo.toml index b54095c..462cdc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,15 @@ include = [ ] rust-version = "1.40" -# Target-specific dependency required to work with wasm-bindgen +# Target specific dependencies for redox +[target.'cfg(all(target_os = "redox", not(target_arch = "wasm32")))'.dependencies.redox_syscall] +version = "0.4" + +# Target specific dependencies for wasite +[target.'cfg(all(target_arch = "wasm32", target_os = "wasi"))'.dependencies.wasite] +version = "0.1" + +# Target-specific dependency for wasm-bindgen [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi"), not(target_os = "daku")))'.dependencies.web-sys] version = "0.3" features = ["Navigator", "Document", "Window", "Location"] @@ -34,9 +42,6 @@ optional = true version = "0.2" optional = true -[target.'cfg(all(target_arch = "wasm32", target_os = "wasi"))'.dependencies.wasite] -version = "0.1" - [features] default = ["web"] # Enabling this feature indicates that the wasm32-unknown-unknown target should diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..7db9086 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,43 @@ +# Testing + +This file outlines the regression testing plan for all platforms. + +## Redox + + + +Tested on Fedora Silverblue 39 + +### Update Rust Nightly and Stable + +```shell +rustup update nightly stable +``` + +### Install pre-requisites + +```shell +sudo dnf install git file autoconf vim bison flex genisoimage gperf glibc-devel.i686 expat expat-devel fuse-devel fuse3-devel gmp-devel perl-HTML-Parser libpng-devel libtool libjpeg-turbo-devel libvorbis-devel SDL2_ttf-devel mesa-libOSMesa-devel m4 nasm po4a syslinux texinfo sdl12-compat-devel ninja-build meson python3-mako make gcc gcc-c++ openssl patch automake perl-Pod-Html perl-FindBin gperf curl gettext-devel perl-Pod-Xhtml pkgconf-pkg-config cmake cbindgen just qemu doxygen 'perl(ExtUtils::MakeMaker)' + +cargo install --locked --force --version 0.1.1 cargo-config +``` + +### Get redox source + +```shell +mkdir -p build/ +cd build/ +git clone https://gitlab.redox-os.org/redox-os/redox.git --origin upstream --recursive +``` + +### Build Redox + +```shell +make all +``` + +### Run Redox + +```shell +make qemu +``` diff --git a/src/os/redox.rs b/src/os/redox.rs new file mode 100644 index 0000000..c804f16 --- /dev/null +++ b/src/os/redox.rs @@ -0,0 +1,156 @@ +#![forbid(unsafe_code)] + +use std::{io::Error, ffi::OsString, fs, borrow::Cow}; + +use redox_syscall::{call, error}; + +use crate::{ + os::{Os, Target}, + Arch, DesktopEnv, Language, Platform, Result +}; + +/// Row in the Redox /etc/passwd file +struct Passwd<'a>(Cow<'a, str>); + +impl Passwd<'_> { + fn column(&self, number: usize) -> Option<&str> { + self.split(';').skip(number).next() + } + + fn username(&self) -> Option { + self.column(0).map(ToString::to_string) + } + + fn uid(&self) -> Option { + self.column(1)?.parse().ok() + } + + fn gid(&self) -> Option { + self.column(2)?.parse().ok() + } + + fn fullname(&self) -> Option { + self.column(3).map(ToString::to_string) + } +} + +struct Uname<'a>(Cow<'a, str>); + +impl Uname { + fn row(&self, number: usize) -> Option<&str> { + self.lines().skip(number).next() + } + + fn kernel_name(&self) -> Option { + self.row(0).map(ToString::to_string) + } + + fn kernel_release(&self) -> Option { + self.row(2).map(ToString::to_string) + } + + fn machine_arch(&self) -> Option { + // FIXME: Don't hardcode unknown arch + Some(Arc::Unknown(self.row(4)?)) + } +} + +fn to_io_error(error: error::Error) -> Error { + Error::from_raw_os_error(error.errno) +} + +fn euid() -> Result { + call::geteuid().map_err(to_io_error) +} + +fn egid() -> Result { + call::getegid().map_err(to_io_error) +} + +fn passwd() -> Result> { + let (euid, egid) = (euid()?, egid()?); + let passwd_file = fs::read_to_string("/etc/passwd")?; + + for user in passwd_file.lines() { + let passwd = Passwd(user.into()); + + if passwd.uid() == Some(euid) && passwd.gid() == Some(egid) { + return Ok(Passwd(passwd.0.to_owned())); + } + } + + Err(Error::new(ErrorKind::NotFound, "Missing record")) +} + +fn uname() -> Result> { + let uname_file = fs::read_to_string("sys:uname")?; + + Ok(Uname(uname_file.into())) +} + +fn redox_version() -> Result { + let release_file = fs::read_to_string("/etc/redox-release")?; + + Ok(release_file.lines().next().unwrap_or_default().to_string()) +} + +fn hostname() -> Result { + let hostname_file = fs::read_to_string("/etc/hostname")?; + + Ok(hostname_file.lines().next().unwrap_or_default().to_string()) +} + +#[inline(always)] +pub(crate) fn lang() -> impl Iterator { + std::iter::once("en-US".to_string()) +} + +impl Target for Os { + fn langs(self) -> Vec { + todo!() + } + + #[inline(always)] + fn realname(self) -> Result { + Ok(passwd()?.fullname().unwrap_or_default().into()) + } + + #[inline(always)] + fn username(self) -> Result { + Ok(passwd()?.username().unwrap_or_default().into()) + } + + #[inline(always)] + fn devicename(self) -> Result { + hostname().map(OsString::from) + } + + #[inline(always)] + fn hostname(self) -> Result { + hostname() + } + + #[inline(always)] + fn distro(self) -> Result { + let version = redox_version(); + let mut distro_name = "Redox ".to_string(); + + distro_name.push_str(&version); + distro_name + } + + #[inline(always)] + fn desktop_env(self) -> DesktopEnv { + DesktopEnv::Orbital + } + + #[inline(always)] + fn platform(self) -> Platform { + Platform::Redox + } + + #[inline(always)] + fn arch(self) -> Result { + uname().arch() + } +} From 3ce423d3aad61d33484e25410af6aea6343a2f47 Mon Sep 17 00:00:00 2001 From: Jeron Aldaron Lau Date: Sat, 2 Mar 2024 19:33:28 -0600 Subject: [PATCH 2/5] Make Redox MSRV 1.65 in CI --- .github/workflows/ci.yml | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f5f91a..626cb89 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,7 +125,6 @@ jobs: - i686-unknown-linux-gnu - wasm32-wasi - x86_64-apple-darwin - - x86_64-unknown-redox steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 @@ -198,8 +197,23 @@ jobs: matrix: os: [ubuntu-latest] tc: [1.65.0, stable, beta, nightly] - cc: - - x86_64-unknown-illumos + cc: [x86_64-unknown-illumos] + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.tc }} + target: ${{ matrix.cc }} + override: true + - run: cargo build --all-features --target=${{ matrix.cc }} + cross-compile-redox: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + tc: [1.65.0, stable, beta, nightly] + cc: [x86_64-unknown-redox] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 From c769677299294c926bca528e5669f08909d8038e Mon Sep 17 00:00:00 2001 From: Jeron Aldaron Lau Date: Sat, 2 Mar 2024 20:29:43 -0600 Subject: [PATCH 3/5] Fix issues --- CHANGELOG.md | 1 + README.md | 11 ++++++--- TESTING.md | 1 + src/os.rs | 31 +++++++++++++++++++---- src/os/daku.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ src/os/fake.rs | 2 +- src/os/redox.rs | 53 ++++++++++++++++++--------------------- src/os/unix.rs | 18 +++++++------- 8 files changed, 135 insertions(+), 48 deletions(-) create mode 100644 src/os/daku.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index a11c093..b30c1c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog], and this project adheres to ### Added - WASI support + - Redox support - Fallible functions - `whoami::fallible::devicename()` - `whoami::fallible::devicename_os()` diff --git a/README.md b/README.md index 5340904..13e0bed 100644 --- a/README.md +++ b/README.md @@ -32,15 +32,15 @@ WhoAmI targets all platforms that can run Rust, including: - Windows - Mac OS - BSD variants (FreeBSD, others) + - Illumos variants (SmartOS, OmniOS, others) **Target-Specific MSRV 1.65** + - Redox **Target-Specific MSRV 1.65** - [Web Assembly](https://github.com/ardaku/whoami/blob/stable/WASM.md) - Mock implementation - Web Browser - DOM - WASI (Wasite, others) **may partially or fully work - but untested** - Daku (Ardaku/Quantii, others) **mock implementation, full implementation planned later** - - Illumos variants (SmartOS, OmniOS, others) **may partially or fully work - but untested** - Android **may partially or fully work - but untested, planned later** - iOS **planned later** - - Redox **planned later** - Fuchsia **planned later** - Various game consoles **planned later** - Others? (make a PR or open an issue) @@ -49,8 +49,11 @@ WhoAmI targets all platforms that can run Rust, including: WhoAmI 1.x.y targets Rust 1.40.0 stable and later, and the 1.x.y track will be maintained at least until the release of the Rust 2024 edition. -The MSRV will only be updated on major version bumps, and version 2.0.0 will -target Rust 1.65.0 and later to make use of the `let else` syntax. +The MSRV will not be updated until version 2.0.0, after which breaking changes +may happen on minor releases, and version 2.0.0 will target Rust 1.65.0 and +later to make use of the `let else` syntax. The current plan is for all 2.x +releases to be supported and receive bugfixes at least until sometime in 2027, +bumping MSRV only as needed. ## Binary [whome](https://crates.io/crates/whome): `whoami` command RiR (Re-written in diff --git a/TESTING.md b/TESTING.md index 7db9086..1c531f2 100644 --- a/TESTING.md +++ b/TESTING.md @@ -12,6 +12,7 @@ Tested on Fedora Silverblue 39 ```shell rustup update nightly stable +rustup target add --toolchain stable x86_64-unknown-redox ``` ### Install pre-requisites diff --git a/src/os.rs b/src/os.rs index ccd9c89..0c6c45e 100644 --- a/src/os.rs +++ b/src/os.rs @@ -1,9 +1,9 @@ #![allow(unsafe_code)] -// Redox - FIXME: Currently routes to fake.rs +// Redox #[cfg_attr( all(target_os = "redox", not(target_arch = "wasm32")), - path = "os/fake.rs" + path = "os/redox.rs" )] // Unix #[cfg_attr( @@ -14,10 +14,10 @@ )), path = "os/unix.rs" )] -// Wasm32 (Daku) - FIXME: Currently routes to fake.rs +// Wasm32 (Daku) #[cfg_attr( all(target_arch = "wasm32", target_os = "daku"), - path = "os/fake.rs" + path = "os/daku.rs" )] // Wasm32 (Wasi) #[cfg_attr( @@ -51,7 +51,10 @@ )] mod target; -use std::ffi::OsString; +use std::{ + ffi::OsString, + io::{Error, ErrorKind}, +}; pub(crate) use self::target::*; use crate::{Arch, DesktopEnv, Language, Platform, Result}; @@ -81,3 +84,21 @@ pub(crate) trait Target { /// Return the computer's CPU architecture. fn arch(self) -> Result; } + +// This is only used on some platforms +#[allow(dead_code)] +fn err_missing_record() -> Error { + Error::new(ErrorKind::NotFound, "Missing record") +} + +// This is only used on some platforms +#[allow(dead_code)] +fn err_null_record() -> Error { + Error::new(ErrorKind::NotFound, "Null record") +} + +// This is only used on some platforms +#[allow(dead_code)] +fn err_empty_record() -> Error { + Error::new(ErrorKind::NotFound, "Empty record") +} diff --git a/src/os/daku.rs b/src/os/daku.rs new file mode 100644 index 0000000..a4cf16f --- /dev/null +++ b/src/os/daku.rs @@ -0,0 +1,66 @@ +//! This is mostly the same as fake.rs for now + +#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] +compile_error!("Unexpected pointer width for target platform"); + +use std::ffi::OsString; + +use crate::{ + os::{Os, Target}, + Arch, DesktopEnv, Language, Platform, Result, +}; + +#[inline(always)] +pub(crate) fn lang() -> impl Iterator { + std::iter::once("en/US".to_string()) +} + +impl Target for Os { + fn langs(self) -> Vec { + todo!() + } + + #[inline(always)] + fn realname(self) -> Result { + Ok("Anonymous".to_string().into()) + } + + #[inline(always)] + fn username(self) -> Result { + Ok("anonymous".to_string().into()) + } + + #[inline(always)] + fn devicename(self) -> Result { + Ok("Unknown".to_string().into()) + } + + #[inline(always)] + fn hostname(self) -> Result { + Ok("localhost".to_string()) + } + + #[inline(always)] + fn distro(self) -> Result { + Ok("Emulated".to_string()) + } + + #[inline(always)] + fn desktop_env(self) -> DesktopEnv { + DesktopEnv::Unknown("Unknown Daku".to_string()) + } + + #[inline(always)] + fn platform(self) -> Platform { + Platform::Unknown("Daku".to_string()) + } + + #[inline(always)] + fn arch(self) -> Result { + Ok(if cfg!(target_pointer_width = "64") { + Arch::Wasm64 + } else { + Arch::Wasm32 + }) + } +} diff --git a/src/os/fake.rs b/src/os/fake.rs index ae43aab..152bc88 100644 --- a/src/os/fake.rs +++ b/src/os/fake.rs @@ -12,7 +12,7 @@ use crate::{ #[inline(always)] pub(crate) fn lang() -> impl Iterator { - std::iter::once("en-US".to_string()) + std::iter::once("en/US".to_string()) } impl Target for Os { diff --git a/src/os/redox.rs b/src/os/redox.rs index c804f16..f399cd3 100644 --- a/src/os/redox.rs +++ b/src/os/redox.rs @@ -1,12 +1,12 @@ #![forbid(unsafe_code)] -use std::{io::Error, ffi::OsString, fs, borrow::Cow}; +use std::{borrow::Cow, ffi::OsString, fs, io::Error}; -use redox_syscall::{call, error}; +use syscall::{call, error}; use crate::{ os::{Os, Target}, - Arch, DesktopEnv, Language, Platform, Result + Arch, DesktopEnv, Language, Platform, Result, }; /// Row in the Redox /etc/passwd file @@ -14,7 +14,7 @@ struct Passwd<'a>(Cow<'a, str>); impl Passwd<'_> { fn column(&self, number: usize) -> Option<&str> { - self.split(';').skip(number).next() + self.0.split(';').nth(number) } fn username(&self) -> Option { @@ -36,22 +36,14 @@ impl Passwd<'_> { struct Uname<'a>(Cow<'a, str>); -impl Uname { +impl Uname<'_> { fn row(&self, number: usize) -> Option<&str> { - self.lines().skip(number).next() - } - - fn kernel_name(&self) -> Option { - self.row(0).map(ToString::to_string) - } - - fn kernel_release(&self) -> Option { - self.row(2).map(ToString::to_string) + self.0.lines().nth(number) } fn machine_arch(&self) -> Option { // FIXME: Don't hardcode unknown arch - Some(Arc::Unknown(self.row(4)?)) + Some(Arch::Unknown(self.row(4)?.to_string())) } } @@ -75,11 +67,11 @@ fn passwd() -> Result> { let passwd = Passwd(user.into()); if passwd.uid() == Some(euid) && passwd.gid() == Some(egid) { - return Ok(Passwd(passwd.0.to_owned())); + return Ok(Passwd(passwd.0.into_owned().into())); } } - Err(Error::new(ErrorKind::NotFound, "Missing record")) + Err(super::err_missing_record()) } fn uname() -> Result> { @@ -88,12 +80,6 @@ fn uname() -> Result> { Ok(Uname(uname_file.into())) } -fn redox_version() -> Result { - let release_file = fs::read_to_string("/etc/redox-release")?; - - Ok(release_file.lines().next().unwrap_or_default().to_string()) -} - fn hostname() -> Result { let hostname_file = fs::read_to_string("/etc/hostname")?; @@ -102,7 +88,8 @@ fn hostname() -> Result { #[inline(always)] pub(crate) fn lang() -> impl Iterator { - std::iter::once("en-US".to_string()) + // FIXME: Look for unix-like language env vars + std::iter::once("en/US".to_string()) } impl Target for Os { @@ -132,11 +119,17 @@ impl Target for Os { #[inline(always)] fn distro(self) -> Result { - let version = redox_version(); - let mut distro_name = "Redox ".to_string(); + let release_file = fs::read_to_string("/etc/os-release")?; + + for kv in release_file.lines() { + if let Some(kv) = kv.strip_prefix("PRETTY_NAME=\"") { + if let Some(kv) = kv.strip_suffix('\"') { + return Ok(kv.to_string()); + } + } + } - distro_name.push_str(&version); - distro_name + Err(super::err_missing_record()) } #[inline(always)] @@ -151,6 +144,8 @@ impl Target for Os { #[inline(always)] fn arch(self) -> Result { - uname().arch() + uname()? + .machine_arch() + .ok_or_else(super::err_missing_record) } } diff --git a/src/os/unix.rs b/src/os/unix.rs index 17dfb34..bde8d7b 100644 --- a/src/os/unix.rs +++ b/src/os/unix.rs @@ -126,7 +126,7 @@ unsafe fn strlen_gecos(cs: *const c_void) -> usize { fn os_from_cstring_gecos(string: *const c_void) -> Result { if string.is_null() { - return Err(Error::new(ErrorKind::NotFound, "Null record")); + return Err(super::err_null_record()); } // Get a byte slice of the c string. @@ -134,7 +134,7 @@ fn os_from_cstring_gecos(string: *const c_void) -> Result { let length = strlen_gecos(string); if length == 0 { - return Err(Error::new(ErrorKind::InvalidData, "Empty record")); + return Err(super::err_empty_record()); } std::slice::from_raw_parts(string.cast(), length) @@ -146,7 +146,7 @@ fn os_from_cstring_gecos(string: *const c_void) -> Result { fn os_from_cstring(string: *const c_void) -> Result { if string.is_null() { - return Err(Error::new(ErrorKind::NotFound, "Null record")); + return Err(super::err_null_record()); } // Get a byte slice of the c string. @@ -154,7 +154,7 @@ fn os_from_cstring(string: *const c_void) -> Result { let length = strlen(string); if length == 0 { - return Err(Error::new(ErrorKind::InvalidData, "Empty record")); + return Err(super::err_empty_record()); } std::slice::from_raw_parts(string.cast(), length) @@ -219,7 +219,7 @@ fn getpwuid(name: Name) -> Result { let _passwd = _passwd.assume_init(); if _passwd.is_null() { - return Err(Error::new(ErrorKind::NotFound, "Null record")); + return Err(super::err_null_record()); } passwd.assume_init() @@ -424,7 +424,7 @@ impl Target for Os { }); if out.as_bytes().is_empty() { - return Err(Error::new(ErrorKind::InvalidData, "Empty record")); + return Err(super::err_empty_record()); } Ok(out) @@ -440,7 +440,7 @@ impl Target for Os { } if nodename.is_empty() { - return Err(Error::new(ErrorKind::InvalidData, "Empty record")); + return Err(super::err_empty_record()); } Ok(OsString::from_vec(nodename)) @@ -461,7 +461,7 @@ impl Target for Os { } } - Err(Error::new(ErrorKind::NotFound, "Missing record")) + Err(super::err_missing_record()) } } @@ -494,7 +494,7 @@ impl Target for Os { ) { distro_xml(data) } else { - Err(Error::new(ErrorKind::NotFound, "Missing record")) + Err(super::err_missing_record()) } } From ef35b598d538169be3cbcfba4e0dbb91a9e11808 Mon Sep 17 00:00:00 2001 From: Jeron Aldaron Lau Date: Sat, 2 Mar 2024 20:33:13 -0600 Subject: [PATCH 4/5] Nightly lint fix --- src/os/redox.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/os/redox.rs b/src/os/redox.rs index f399cd3..ab3c3a7 100644 --- a/src/os/redox.rs +++ b/src/os/redox.rs @@ -1,4 +1,7 @@ +// We don't need unsafe, yay! #![forbid(unsafe_code)] +// Redox support has its own MSRV of 1.65, is nightly lint +#![allow(unknown_lints, clippy::incompatible_msrv)] use std::{borrow::Cow, ffi::OsString, fs, io::Error}; From 074e53262bb64434ed0ab589bfdb1c2700d634f7 Mon Sep 17 00:00:00 2001 From: Jeron Aldaron Lau Date: Sat, 2 Mar 2024 21:34:45 -0600 Subject: [PATCH 5/5] Tested Redox support, and it's working --- TESTING.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ recipe.toml | 7 +++++++ 2 files changed, 52 insertions(+) create mode 100644 recipe.toml diff --git a/TESTING.md b/TESTING.md index 1c531f2..7dc6b61 100644 --- a/TESTING.md +++ b/TESTING.md @@ -31,14 +31,59 @@ cd build/ git clone https://gitlab.redox-os.org/redox-os/redox.git --origin upstream --recursive ``` +### Create our demo recipe + +Make sure whome is updated to the whoami testing branch. + +```shell +mkdir -p build/redox/cookbook/recipes/demos/whome/ +cp recipe.toml build/redox/cookbook/recipes/demos/whome/ +cp build/redox/config/desktop.toml config/x86_64/ardaku.toml +``` + +In `config/x86_64/ardaku.toml`, under `[packages]`: + +```toml +whome = {} +``` + ### Build Redox +This takes a while + ```shell make all ``` +or + +```shell +make rebuild +``` + ### Run Redox ```shell make qemu ``` + +### Test it + +Verify you are on the new version + +```shell +whome --version +``` + +Default settings should output: + +```console +realname: user +username: user +devicename: redox +hostname: redox +distro: Redox OS 0.8.0 +desktop_env: Orbital +platform: Redox +arch: Unknown: x86_64 +``` diff --git a/recipe.toml b/recipe.toml new file mode 100644 index 0000000..10ad316 --- /dev/null +++ b/recipe.toml @@ -0,0 +1,7 @@ +# Redox recipe for testing whoami + +[source] +git = "https://github.com/AldaronLau/whome.git" + +[build] +template = "cargo"