Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions brush-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
9 changes: 9 additions & 0 deletions brush-core/src/interp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions brush-core/src/sys/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions brush-core/src/sys/unix/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 8 additions & 3 deletions brush-core/src/sys/unix/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,21 @@ pub(crate) fn poll_for_stopped_children() -> Result<bool, error::Error> {
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::WaitStatus, nix::errno::Errno> {
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<nix::sys::wait::WaitStatus, nix::errno::Errno> {
Expand Down
70 changes: 70 additions & 0 deletions brush-core/src/sys/unix/users_ios.rs
Original file line number Diff line number Diff line change
@@ -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<PathBuf> {
// 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<PathBuf> {
std::env::var_os("HOME").map(PathBuf::from)
}

pub(crate) const fn get_current_user_default_shell() -> Option<PathBuf> {
None
}

#[expect(clippy::unnecessary_wraps)]
pub(crate) fn get_current_uid() -> Result<u32, error::Error> {
// SAFETY: getuid is always successful and has no preconditions.
Ok(unsafe { libc::getuid() })
}

#[expect(clippy::unnecessary_wraps)]
pub(crate) fn get_current_gid() -> Result<u32, error::Error> {
// SAFETY: getgid is always successful and has no preconditions.
Ok(unsafe { libc::getgid() })
}

#[expect(clippy::unnecessary_wraps)]
pub(crate) fn get_effective_uid() -> Result<u32, error::Error> {
// SAFETY: geteuid is always successful and has no preconditions.
Ok(unsafe { libc::geteuid() })
}

#[expect(clippy::unnecessary_wraps)]
pub(crate) fn get_effective_gid() -> Result<u32, error::Error> {
// SAFETY: getegid is always successful and has no preconditions.
Ok(unsafe { libc::getegid() })
}

#[expect(clippy::unnecessary_wraps)]
pub(crate) fn get_current_username() -> Result<String, error::Error> {
Ok(std::env::var("USER").unwrap_or_else(|_| "mobile".to_owned()))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is USER typically set in the iOS targets you've tested with? If so, I'm tempted to avoid a hard-coded fake fallback.

}

#[expect(clippy::unnecessary_wraps)]
pub(crate) fn get_user_group_ids() -> Result<Vec<u32>, error::Error> {
// SAFETY: getgid is always successful and has no preconditions.
Ok(vec![unsafe { libc::getgid() }])

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While these are straightforward, we prefer using the nix crate instead of direct unsafe libc calls. Could you please update the libc calls in this file to go through it instead?

}

pub(crate) fn get_all_users() -> Result<Vec<String>, error::Error> {
Ok(vec![get_current_username()?])
}

#[expect(clippy::unnecessary_wraps)]
pub(crate) fn get_all_groups() -> Result<Vec<String>, error::Error> {
Ok(vec!["mobile".to_owned()])

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you find a case where it's important to return at least one group instead of just returning an empty vector?

}
Loading