mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-04-24 09:09:24 -04:00
Merge pull request #227 from andrewdavidmackenzie/master
Rework uname to avoid hardcoded datetime and add unit tests Restructure `uname` so that: - there is an internal function that does the main logic and can be tested by unit tests - Generate an env var in build.rs that is the actual build datetime timestamp - Avoid hardcoded date in the version string - Use the timestamp in the building of `uname` response, so the data is the actual build time - add a simple unit test for the sysname field - add more complex test for the version field that checks the date formatting (not exhaustively) NOTE: The build timestamp can be used by any other function, syscall or part of the code if required.
This commit is contained in:
79
Cargo.lock
generated
79
Cargo.lock
generated
@@ -88,6 +88,15 @@ version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
@@ -223,6 +232,12 @@ dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.181"
|
||||
@@ -306,9 +321,16 @@ dependencies = [
|
||||
"rand",
|
||||
"ringbuf",
|
||||
"rustc-hash",
|
||||
"time",
|
||||
"tock-registers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.38.1"
|
||||
@@ -375,6 +397,12 @@ dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@@ -489,6 +517,26 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.8"
|
||||
@@ -546,6 +594,37 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde_core",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tock-registers"
|
||||
version = "0.10.1"
|
||||
|
||||
@@ -32,6 +32,9 @@ futures = { version = "0.3.31", default-features = false, features = ["alloc", "
|
||||
rand = { version = "0.9.2", default-features = false, features = ["small_rng"] }
|
||||
rustc-hash = { version = "2.1", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
time = { version = "0.3.47", features = ["formatting", "macros"] } # For build timestamping via build.rs
|
||||
|
||||
[features]
|
||||
default = ["smp"]
|
||||
# Support for Symmetric Multiprocessing
|
||||
@@ -44,3 +47,4 @@ codegen-units = 1
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
|
||||
13
build.rs
13
build.rs
@@ -1,4 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
use time::OffsetDateTime;
|
||||
use time::macros::format_description;
|
||||
|
||||
fn main() {
|
||||
let linker_script = match std::env::var("CARGO_CFG_TARGET_ARCH") {
|
||||
@@ -12,4 +14,15 @@ fn main() {
|
||||
|
||||
println!("cargo::rerun-if-changed={}", linker_script.display());
|
||||
println!("cargo::rustc-link-arg=-T{}", linker_script.display());
|
||||
|
||||
// Set an environment variable with the date and time of the build
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let format = format_description!(
|
||||
"[weekday repr:short] [month repr:short] [day] [hour]:[minute]:[second] UTC [year]"
|
||||
);
|
||||
let timestamp = now.format(&format).unwrap();
|
||||
#[cfg(feature = "smp")]
|
||||
println!("cargo:rustc-env=MOSS_VERSION=#1 Moss SMP {timestamp}");
|
||||
#[cfg(not(feature = "smp"))]
|
||||
println!("cargo:rustc-env=MOSS_VERSION=#1 Moss {timestamp}");
|
||||
}
|
||||
|
||||
@@ -4,10 +4,23 @@ use crate::{
|
||||
memory::uaccess::{UserCopyable, copy_to_user},
|
||||
};
|
||||
use alloc::ffi::CString;
|
||||
use core::ffi::CStr;
|
||||
use core::ffi::c_char;
|
||||
use core::str::FromStr;
|
||||
use core::{ffi::c_char, mem};
|
||||
use libkernel::{error::Result, memory::address::TUA};
|
||||
|
||||
const SYSNAME: &CStr = c"Moss";
|
||||
|
||||
/// Systemd uses the release field to determine compatibility.
|
||||
/// It's also necessary for libc programs; otherwise they exit with an error Kernel too old.
|
||||
const RELEASE: &CStr = c"4.2.3";
|
||||
|
||||
/// POSIX specifies the order when using -a (equivalent to -snrvm):
|
||||
/// 1. sysname (-s) - OS name
|
||||
/// 2. nodename (-n) - hostname
|
||||
/// 3. release (-r) - OS release
|
||||
/// 4. version (-v) - OS version
|
||||
/// 5. machine (-m) - hardware type
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct OldUtsname {
|
||||
@@ -18,6 +31,18 @@ pub struct OldUtsname {
|
||||
machine: [c_char; 65],
|
||||
}
|
||||
|
||||
impl Default for OldUtsname {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sysname: [0; 65],
|
||||
nodename: [0; 65],
|
||||
release: [0; 65],
|
||||
version: [0; 65],
|
||||
machine: [0; 65],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl UserCopyable for OldUtsname {}
|
||||
|
||||
fn copy_str_to_c_char_arr(dest: &mut [c_char], src: &[u8]) {
|
||||
@@ -33,29 +58,103 @@ fn copy_str_to_c_char_arr(dest: &mut [c_char], src: &[u8]) {
|
||||
// The rest of `dest` will remain zeroed from the initial `mem::zeroed`.
|
||||
}
|
||||
|
||||
pub async fn sys_uname(uts_ptr: TUA<OldUtsname>) -> Result<usize> {
|
||||
let mut uts = unsafe { mem::zeroed::<OldUtsname>() };
|
||||
/// Build an `OldUtsname` struct with the current system information, without involving the
|
||||
/// kernel. This makes it easier to test.
|
||||
fn build_utsname() -> OldUtsname {
|
||||
let mut uts = OldUtsname::default();
|
||||
|
||||
let sysname = c"Moss".to_bytes_with_nul();
|
||||
copy_str_to_c_char_arr(&mut uts.sysname, sysname);
|
||||
copy_str_to_c_char_arr(&mut uts.sysname, SYSNAME.to_bytes_with_nul());
|
||||
|
||||
let nodename = CString::from_str(&hostname().lock_save_irq()).unwrap();
|
||||
copy_str_to_c_char_arr(&mut uts.nodename, nodename.as_c_str().to_bytes_with_nul());
|
||||
|
||||
let release = c"4.2.3".to_bytes_with_nul();
|
||||
copy_str_to_c_char_arr(&mut uts.release, release);
|
||||
copy_str_to_c_char_arr(&mut uts.release, RELEASE.to_bytes_with_nul());
|
||||
|
||||
#[cfg(feature = "smp")]
|
||||
let version = c"#1 Moss SMP Tue Feb 20 12:34:56 UTC 2024".to_bytes_with_nul();
|
||||
#[cfg(not(feature = "smp"))]
|
||||
let version = c"#1 Moss Tue Feb 20 12:34:56 UTC 2024".to_bytes_with_nul();
|
||||
copy_str_to_c_char_arr(&mut uts.version, version);
|
||||
let version = CString::from_str(env!("MOSS_VERSION")).unwrap();
|
||||
copy_str_to_c_char_arr(&mut uts.version, version.as_c_str().to_bytes_with_nul());
|
||||
|
||||
let machine = CString::new(ArchImpl::name()).unwrap();
|
||||
let machine = machine.to_bytes_with_nul();
|
||||
copy_str_to_c_char_arr(&mut uts.machine, machine);
|
||||
|
||||
copy_to_user(uts_ptr, uts).await?;
|
||||
uts
|
||||
}
|
||||
|
||||
/// Implement the uname syscall, returning 0 for success
|
||||
pub async fn sys_uname(uts_ptr: TUA<OldUtsname>) -> Result<usize> {
|
||||
let uts = build_utsname();
|
||||
copy_to_user(uts_ptr, uts).await?;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::kernel::uname::{SYSNAME, build_utsname};
|
||||
use crate::ktest;
|
||||
use core::ffi::CStr;
|
||||
|
||||
ktest! {
|
||||
fn sysname_correct() {
|
||||
let uts = build_utsname();
|
||||
let sysname_cstr = unsafe { CStr::from_ptr(uts.sysname.as_ptr()) };
|
||||
assert_eq!(sysname_cstr, SYSNAME);
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_datetime(datetime: &str) {
|
||||
let mut parts = datetime.splitn(6, ' ');
|
||||
let day_of_week = parts.next().expect("Day of week"); // "Tue"
|
||||
let month = parts.next().expect("Month"); // "Feb"
|
||||
let day = parts.next().expect("Day"); // "20"
|
||||
let time = parts.next().expect("Time"); // "12:34:56"
|
||||
let timezone = parts.next().expect("TimeZone"); // "UTC"
|
||||
let year = parts.next().expect("Year"); // "2024"
|
||||
|
||||
assert_eq!(timezone, "UTC");
|
||||
assert!(year.parse::<u16>().is_ok());
|
||||
assert!(year.starts_with("20"));
|
||||
assert!(time.split(':').all(|s| s.len() == 2));
|
||||
assert_eq!(month.len(), 3);
|
||||
assert_eq!(day_of_week.len(), 3);
|
||||
assert!(day.parse::<u8>().is_ok());
|
||||
}
|
||||
|
||||
fn validate_version(version: &str, smp: bool) {
|
||||
let mut parts = if smp {
|
||||
version.splitn(4, ' ')
|
||||
} else {
|
||||
version.splitn(3, ' ')
|
||||
};
|
||||
|
||||
let build_num = parts.next().unwrap(); // "#1"
|
||||
assert!(
|
||||
build_num.starts_with('#'),
|
||||
"Build number should start with '#'"
|
||||
);
|
||||
|
||||
let sysname = parts.next().unwrap(); // "Moss"
|
||||
assert_eq!(sysname, "Moss");
|
||||
|
||||
if smp {
|
||||
let smp = parts.next().unwrap(); // "SMP"
|
||||
assert_eq!(smp, "SMP");
|
||||
}
|
||||
|
||||
let datetime = parts.next().unwrap(); // "Tue Feb 20 12:34:56 UTC 2024"
|
||||
validate_datetime(datetime)
|
||||
}
|
||||
|
||||
ktest! {
|
||||
// Test that the version string is of the format "#1 Moss SMP Tue Feb 20 12:34:56 UTC 2024"
|
||||
fn version_format_smp() {
|
||||
let uts = build_utsname();
|
||||
let version_cstr = unsafe { CStr::from_ptr(uts.version.as_ptr()) };
|
||||
let version = version_cstr.to_str().unwrap();
|
||||
|
||||
#[cfg(feature = "smp")]
|
||||
validate_version(version, true);
|
||||
#[cfg(not(feature = "smp"))]
|
||||
validate_version(version, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user