diff --git a/.gitignore b/.gitignore index 364b8ecf03..84483ac12e 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,4 @@ sources/x86_64-unknown-redox/ sources/*.tar.gz Packages/*.pkgar .slim/deepwork/ +local/recipes/shells/brush/source diff --git a/config/redbear-mini.toml b/config/redbear-mini.toml index abc70059ef..11afb7eb96 100644 --- a/config/redbear-mini.toml +++ b/config/redbear-mini.toml @@ -49,6 +49,10 @@ tlc = {} #mc = {} redbear-info = {} +# brush: Rust shell, candidate default login shell (validated in-image before +# switching the [users.*] shell over from zsh). +brush = {} + # Keep package builder utility in live environment. cub = {} cpufreqd = {} diff --git a/local/recipes/shells/brush/patches/brush-umask-redox.patch b/local/recipes/shells/brush/patches/brush-umask-redox.patch new file mode 100644 index 0000000000..bc7d019f0e --- /dev/null +++ b/local/recipes/shells/brush/patches/brush-umask-redox.patch @@ -0,0 +1,14 @@ +--- a/brush-builtins/src/umask.rs ++++ b/brush-builtins/src/umask.rs +@@ -68,7 +68,10 @@ + fn get_umask() -> Result { + let u = nix::sys::stat::umask(Mode::empty()); + nix::sys::stat::umask(u); +- Ok(u32::from(u.bits())) ++ // mode_t is signed (c_int) on Redox and some other targets, so ++ // `u32::from` (widening only) does not apply; a value-preserving cast ++ // covers signed and unsigned mode_t alike. ++ Ok(u.bits() as u32) + } + } + } diff --git a/local/recipes/shells/brush/patches/libc-0.2-redox.patch b/local/recipes/shells/brush/patches/libc-0.2-redox.patch new file mode 100644 index 0000000000..c7f572344e --- /dev/null +++ b/local/recipes/shells/brush/patches/libc-0.2-redox.patch @@ -0,0 +1,89 @@ +--- a/src/unix/redox/mod.rs ++++ b/src/unix/redox/mod.rs +@@ -1289,6 +1289,12 @@ + termp: *const termios, + winp: *const crate::winsize, + ) -> c_int; ++ pub fn forkpty( ++ amaster: *mut c_int, ++ name: *mut c_char, ++ termp: *const termios, ++ winp: *const crate::winsize, ++ ) -> c_int; + + // pwd.h + pub fn getpwent() -> *mut passwd; +@@ -1385,6 +1391,13 @@ + pub fn setpriority(which: c_int, who: crate::id_t, prio: c_int) -> c_int; + pub fn getrlimit(resource: c_int, rlim: *mut crate::rlimit) -> c_int; + pub fn setrlimit(resource: c_int, rlim: *const crate::rlimit) -> c_int; ++ pub fn getrusage(resource: c_int, usage: *mut crate::rusage) -> c_int; ++ pub fn waitid( ++ idtype: crate::idtype_t, ++ id: crate::id_t, ++ infop: *mut siginfo_t, ++ options: c_int, ++ ) -> c_int; + + // sys/socket.h + pub fn CMSG_DATA(cmsg: *const cmsghdr) -> *mut c_uchar; +@@ -1431,3 +1444,59 @@ + // utmp.h + pub fn login_tty(fd: c_int) -> c_int; + } ++ ++// REDBEAR_REDOX_WAITID: symbols relibc provides but the libc crate omits. ++pub type idtype_t = c_uint; ++ ++// waitid idtype_t values. ++pub const P_ALL: idtype_t = 0; ++pub const P_PID: idtype_t = 1; ++pub const P_PGID: idtype_t = 2; ++ ++// siginfo_t.si_code values (child status change). ++pub const CLD_EXITED: c_int = 1; ++pub const CLD_KILLED: c_int = 2; ++pub const CLD_DUMPED: c_int = 3; ++pub const CLD_TRAPPED: c_int = 4; ++pub const CLD_STOPPED: c_int = 5; ++pub const CLD_CONTINUED: c_int = 6; ++ ++s! { ++ pub struct rusage { ++ pub ru_utime: crate::timeval, ++ pub ru_stime: crate::timeval, ++ pub ru_maxrss: c_long, ++ pub ru_ixrss: c_long, ++ pub ru_idrss: c_long, ++ pub ru_isrss: c_long, ++ pub ru_minflt: c_long, ++ pub ru_majflt: c_long, ++ pub ru_nswap: c_long, ++ pub ru_inblock: c_long, ++ pub ru_oublock: c_long, ++ pub ru_msgsnd: c_long, ++ pub ru_msgrcv: c_long, ++ pub ru_nsignals: c_long, ++ pub ru_nvcsw: c_long, ++ pub ru_nivcsw: c_long, ++ } ++} ++ ++impl siginfo_t { ++ // relibc's siginfo layout places these fields after the leading ++ // si_signo/si_errno/si_code triple: si_pid at c_int index 3 (byte 12), ++ // si_uid at index 4 (byte 16), si_status at index 8 (byte 32). Read them ++ // out of the padding region to match. ++ pub unsafe fn si_pid(&self) -> crate::pid_t { ++ let p = self as *const siginfo_t as *const c_int; ++ unsafe { *p.offset(3) } ++ } ++ pub unsafe fn si_uid(&self) -> crate::uid_t { ++ let p = self as *const siginfo_t as *const c_int; ++ unsafe { *p.offset(4) as crate::uid_t } ++ } ++ pub unsafe fn si_status(&self) -> c_int { ++ let p = self as *const siginfo_t as *const c_int; ++ unsafe { *p.offset(8) } ++ } ++} diff --git a/local/recipes/shells/brush/patches/nix-0.31-redox.patch b/local/recipes/shells/brush/patches/nix-0.31-redox.patch new file mode 100644 index 0000000000..66dcb13e69 --- /dev/null +++ b/local/recipes/shells/brush/patches/nix-0.31-redox.patch @@ -0,0 +1,89 @@ +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -164,7 +164,7 @@ + #![feature = "poll"] + pub mod poll; + } +-#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] ++#[cfg(not(target_os = "fuchsia"))] + feature! { + #![feature = "term"] + #[deny(missing_docs)] +--- a/src/sys/mod.rs ++++ b/src/sys/mod.rs +@@ -95,7 +95,6 @@ + } + + #[cfg(not(any( +- target_os = "redox", + target_os = "fuchsia", + target_os = "solaris", + target_os = "haiku" +--- a/src/sys/signal.rs ++++ b/src/sys/signal.rs +@@ -856,7 +856,7 @@ + (*p).sa_flags = match handler { + #[cfg(not(target_os = "redox"))] + SigHandler::SigAction(_) => (flags | SaFlags::SA_SIGINFO).bits(), +- _ => (flags - SaFlags::SA_SIGINFO).bits(), ++ _ => (flags - SaFlags::SA_SIGINFO).bits() as _, + }; + (*p).sa_mask = mask.sigset; + +@@ -866,7 +866,7 @@ + + /// Returns the flags set on the action. + pub fn flags(&self) -> SaFlags { +- SaFlags::from_bits_truncate(self.sigaction.sa_flags) ++ SaFlags::from_bits_truncate(self.sigaction.sa_flags as _) + } + + /// Returns the set of signals that are blocked during execution of the action's +--- a/src/sys/resource.rs ++++ b/src/sys/resource.rs +@@ -21,7 +21,8 @@ + target_os = "aix", + target_os = "illumos", + all(target_os = "linux", not(target_env = "gnu")), +- target_os = "cygwin" ++ target_os = "cygwin", ++ target_os = "redox" + ))]{ + use libc::rlimit; + } +@@ -53,7 +54,8 @@ + target_os = "aix", + target_os = "illumos", + all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc"))), +- target_os = "cygwin" ++ target_os = "cygwin", ++ target_os = "redox" + ), repr(i32))] + #[non_exhaustive] + pub enum Resource { +--- a/src/sys/wait.rs ++++ b/src/sys/wait.rs +@@ -242,6 +242,7 @@ + target_os = "android", + target_os = "freebsd", + target_os = "haiku", ++ target_os = "redox", + all(target_os = "linux", not(target_env = "uclibc")), + ))] + unsafe fn from_siginfo(siginfo: &libc::siginfo_t) -> Result { +@@ -327,6 +328,7 @@ + target_os = "android", + target_os = "freebsd", + target_os = "haiku", ++ target_os = "redox", + all(target_os = "linux", not(target_env = "uclibc")), + ))] + #[derive(Debug)] +@@ -355,6 +357,7 @@ + target_os = "android", + target_os = "freebsd", + target_os = "haiku", ++ target_os = "redox", + all(target_os = "linux", not(target_env = "uclibc")), + ))] + pub fn waitid(id: Id, flags: WaitPidFlag) -> Result { diff --git a/local/recipes/shells/brush/recipe.toml b/local/recipes/shells/brush/recipe.toml new file mode 100644 index 0000000000..71ad7bf9d9 --- /dev/null +++ b/local/recipes/shells/brush/recipe.toml @@ -0,0 +1,78 @@ +[package] +name = "brush" +version = "0.1.0" + +[source] +git = "https://github.com/reubeno/brush" + +[build] +template = "custom" +script = """ +# --------------------------------------------------------------------------- +# RedBear Redox port for brush. +# +# brush pulls `nix` 0.31.x and `libc` 0.2.x, neither of which builds for Redox +# as-is: upstream nix only partially cfg-enables Redox, and the `libc` crate's +# Redox module omits several POSIX symbols that relibc actually provides. The +# fixes live as reviewable unified diffs under patches/: +# +# patches/nix-0.31-redox.patch cfg-enable resource/Id/waitid/from_siginfo, +# rlimit import + repr(i32), SaFlags width +# casts, and pty for Redox. +# patches/libc-0.2-redox.patch add idtype_t, P_*/CLD_*, rusage, getrusage +# + waitid externs, siginfo child accessors. +# patches/brush-umask-redox.patch mode_t is signed on Redox; use a cast +# instead of u32::from (widening-only). +# +# Registry dependency sources are only unpacked during the build, so we run +# `cargo fetch` first to materialise them, then patch. apply_patch is +# idempotent (skips if already applied) but fails loudly if a patch does not +# apply cleanly -- the intended signal that a version bump moved the code. +# --------------------------------------------------------------------------- + +PATCHES="${COOKBOOK_RECIPE}/patches" + +# Materialise all dependency sources into the cargo registry before patching. +cargo fetch --manifest-path "${COOKBOOK_SOURCE}/Cargo.toml" + +# Resolve the extracted registry directory for a crate whose Cargo.lock version +# begins with the given prefix (brush may depend on several majors of the same +# crate, e.g. nix 0.26 and 0.31; the patch targets one specific series). +crate_dir() { + local name="$1" prefix="$2" ver + ver="$(awk -v n="name = \\"$1\\"" -v p="$2" ' + $0 == n { getline; gsub(/[^0-9.]/, "", $0); + if (index($0, p) == 1) { print; exit } } + ' "${COOKBOOK_SOURCE}/Cargo.lock")" + [ -n "$ver" ] || { echo "crate_dir: ${name} ${prefix}* not found in Cargo.lock" >&2; return 1; } + local d + for d in "${HOME}/.cargo/registry/src/"*/"${name}-${ver}"; do + [ -d "$d" ] && { echo "$d"; return 0; } + done + echo "crate_dir: ${name}-${ver} not extracted" >&2 + return 1 +} + +# apply_patch : apply forward, or skip if the patch is +# already applied (detected via a clean reverse dry-run). Any other failure is +# fatal. +apply_patch() { + local dir="$1" patch="$2" + if patch -p1 -R --dry-run -f -d "$dir" < "$patch" >/dev/null 2>&1; then + echo "apply_patch: $(basename "$patch") already applied in $dir, skipping" + return 0 + fi + echo "apply_patch: applying $(basename "$patch") in $dir" + patch -p1 -f -d "$dir" < "$patch" +} + +apply_patch "$(crate_dir nix 0.31)" "${PATCHES}/nix-0.31-redox.patch" +apply_patch "$(crate_dir libc 0.2)" "${PATCHES}/libc-0.2-redox.patch" +apply_patch "${COOKBOOK_SOURCE}" "${PATCHES}/brush-umask-redox.patch" + +# The shell lives in the `brush-shell` package but its binary target is named +# `brush` (see brush-shell/Cargo.toml `[[bin]] name = "brush"`), so +# cookbook_cargo_packages (which copies a binary matching the package name) +# cannot find it. Build the package explicitly and install the `brush` binary. +bin_name="brush" bin_flags="--package brush-shell --bin brush" bin_final_name="brush" cookbook_cargo_build +""" diff --git a/recipes/shells/brush b/recipes/shells/brush new file mode 120000 index 0000000000..051c905b67 --- /dev/null +++ b/recipes/shells/brush @@ -0,0 +1 @@ +../../local/recipes/shells/brush \ No newline at end of file diff --git a/recipes/wip/shells/brush/recipe.toml b/recipes/wip/shells/brush/recipe.toml deleted file mode 100644 index 233bb472e3..0000000000 --- a/recipes/wip/shells/brush/recipe.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "brush" -version = "0.1.0" - -#TODO redox is not supported by the procfs crate -[source] -git = "https://github.com/reubeno/brush" -[build] -template = "custom" -script = """ -cookbook_cargo_packages brush-shell -"""