diff --git a/brush-core/Cargo.toml b/brush-core/Cargo.toml index d3f59d289..3b955fb43 100644 --- a/brush-core/Cargo.toml +++ b/brush-core/Cargo.toml @@ -77,6 +77,8 @@ nix = { version = "0.31.2", features = [ "user", ] } terminfo = "0.9.0" + +[target.'cfg(all(unix, not(target_os = "ios")))'.dependencies] uzers = "0.12.2" [target.wasm32-unknown-unknown.dependencies] diff --git a/brush-core/src/interp.rs b/brush-core/src/interp.rs index ddf637f11..6fe519cd7 100644 --- a/brush-core/src/interp.rs +++ b/brush-core/src/interp.rs @@ -138,6 +138,15 @@ impl ExecutionParameters { } } + /// Returns whether the given file descriptor was explicitly specified for + /// this execution (e.g. via a redirection or pipeline), as opposed to being + /// inherited from the shell's persistent open files. Useful for telling a + /// redirected/piped stdin from an interactive one. + #[must_use] + pub fn is_fd_specified(&self, fd: ShellFd) -> bool { + matches!(self.open_files.fd_entry(fd), openfiles::OpenFileEntry::Open(_)) + } + /// Sets the given file descriptor to the provided open file. /// /// # Arguments diff --git a/brush-core/src/sys/unix.rs b/brush-core/src/sys/unix.rs index 77732c968..b022bd827 100644 --- a/brush-core/src/sys/unix.rs +++ b/brush-core/src/sys/unix.rs @@ -11,6 +11,10 @@ pub use crate::sys::tokio_process as process; pub mod resource; pub mod signal; pub mod terminal; +#[cfg(not(target_os = "ios"))] +pub(crate) mod users; +#[cfg(target_os = "ios")] +#[path = "unix/users_ios.rs"] pub(crate) mod users; /// Platform-specific errors. diff --git a/brush-core/src/sys/unix/commands.rs b/brush-core/src/sys/unix/commands.rs index b5554399a..bf2aa0a3b 100644 --- a/brush-core/src/sys/unix/commands.rs +++ b/brush-core/src/sys/unix/commands.rs @@ -88,9 +88,9 @@ fn pre_exec_lead_session() -> Result<(), std::io::Error> { ))); } - #[cfg(not(target_os = "macos"))] + #[cfg(not(target_vendor = "apple"))] let control = libc::TIOCSCTTY; - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] let control: u64 = libc::TIOCSCTTY.into(); // SAFETY: diff --git a/brush-core/src/sys/unix/signal.rs b/brush-core/src/sys/unix/signal.rs index 36830d6a4..5611c9445 100644 --- a/brush-core/src/sys/unix/signal.rs +++ b/brush-core/src/sys/unix/signal.rs @@ -92,16 +92,21 @@ pub(crate) fn poll_for_stopped_children() -> Result { Ok(found_stopped) } -#[cfg(not(any(target_os = "macos", target_os = "netbsd", target_os = "openbsd")))] +#[cfg(not(any( + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "ios" +)))] fn waitid_all( flags: nix::sys::wait::WaitPidFlag, ) -> Result { nix::sys::wait::waitid(nix::sys::wait::Id::All, flags) } -// nix does not expose `waitid` on NetBSD/OpenBSD; `waitpid` for any child is +// nix does not expose `waitid` on NetBSD/OpenBSD/iOS; `waitpid` for any child is // equivalent for the flags used here. -#[cfg(any(target_os = "netbsd", target_os = "openbsd"))] +#[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "ios"))] fn waitid_all( flags: nix::sys::wait::WaitPidFlag, ) -> Result { diff --git a/brush-core/src/sys/unix/users_ios.rs b/brush-core/src/sys/unix/users_ios.rs new file mode 100644 index 000000000..5f031263c --- /dev/null +++ b/brush-core/src/sys/unix/users_ios.rs @@ -0,0 +1,70 @@ +//! iOS: no user database is accessible from within the app sandbox; identity +//! comes from uid/gid syscalls and the process environment. + +use crate::error; +use std::path::PathBuf; + +pub(crate) const fn is_root() -> bool { + false +} + +pub(crate) fn get_user_home_dir(username: &str) -> Option { + // Only the current (sandboxed) user is resolvable. + if get_current_username().is_ok_and(|current| current == username) { + get_current_user_home_dir() + } else { + None + } +} + +pub(crate) fn get_current_user_home_dir() -> Option { + std::env::var_os("HOME").map(PathBuf::from) +} + +pub(crate) const fn get_current_user_default_shell() -> Option { + None +} + +#[expect(clippy::unnecessary_wraps)] +pub(crate) fn get_current_uid() -> Result { + // SAFETY: getuid is always successful and has no preconditions. + Ok(unsafe { libc::getuid() }) +} + +#[expect(clippy::unnecessary_wraps)] +pub(crate) fn get_current_gid() -> Result { + // SAFETY: getgid is always successful and has no preconditions. + Ok(unsafe { libc::getgid() }) +} + +#[expect(clippy::unnecessary_wraps)] +pub(crate) fn get_effective_uid() -> Result { + // SAFETY: geteuid is always successful and has no preconditions. + Ok(unsafe { libc::geteuid() }) +} + +#[expect(clippy::unnecessary_wraps)] +pub(crate) fn get_effective_gid() -> Result { + // SAFETY: getegid is always successful and has no preconditions. + Ok(unsafe { libc::getegid() }) +} + +#[expect(clippy::unnecessary_wraps)] +pub(crate) fn get_current_username() -> Result { + Ok(std::env::var("USER").unwrap_or_else(|_| "mobile".to_owned())) +} + +#[expect(clippy::unnecessary_wraps)] +pub(crate) fn get_user_group_ids() -> Result, error::Error> { + // SAFETY: getgid is always successful and has no preconditions. + Ok(vec![unsafe { libc::getgid() }]) +} + +pub(crate) fn get_all_users() -> Result, error::Error> { + Ok(vec![get_current_username()?]) +} + +#[expect(clippy::unnecessary_wraps)] +pub(crate) fn get_all_groups() -> Result, error::Error> { + Ok(vec!["mobile".to_owned()]) +}