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
24 changes: 14 additions & 10 deletions library/std/src/sys/pal/unix/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,18 @@ pub fn page_size() -> usize {
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
}

/// Returns the value for [`confstr(key, ...)`][posix_confstr]. Currently only
/// used on Darwin, but should work on any unix (in case we need to get
/// `_CS_PATH` or `_CS_V[67]_ENV` in the future).
/// Returns the value for [`confstr(key, ...)`][posix_confstr].
///
/// [posix_confstr]:
/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/confstr.html
#[cfg(target_vendor = "apple")]
#[cfg(any(target_vendor = "apple", all(target_os = "linux", target_env = "gnu")))]
pub fn confstr(
key: crate::ffi::c_int,
size_hint: Option<usize>,
) -> crate::io::Result<crate::ffi::OsString> {
use crate::ffi::OsString;
) -> crate::io::Result<crate::ffi::CString> {
use alloc_crate::ffi::CString;

use crate::io;
use crate::os::unix::ffi::OsStringExt;

let mut buf: Vec<u8> = Vec::with_capacity(0);
let mut bytes_needed_including_nul = size_hint
Expand Down Expand Up @@ -61,11 +59,17 @@ pub fn confstr(
unsafe {
buf.set_len(bytes_needed_including_nul);
// Remove the NUL-terminator.
let last_byte = buf.pop();
let last_byte = buf.last();
// ... and smoke-check that it *was* a NUL-terminator.
assert_eq!(last_byte, Some(0), "`confstr` provided a string which wasn't nul-terminated");
assert_eq!(
last_byte.copied(),
Some(0),
"`confstr` provided a string which wasn't nul-terminated"
);
};
Ok(OsString::from_vec(buf))
// SAFETY: From above, we have confirmed that the buffer
// contains a nul terminating byte.
Ok(unsafe { CString::from_vec_with_nul_unchecked(buf) })
}

#[cfg(all(target_os = "linux", target_env = "gnu"))]
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/pal/unix/conf/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn test_confstr() {
let value_nohint = super::confstr(key, None).unwrap_or_else(|e| {
panic!("confstr({key}, None) failed: {e:?}");
});
let end = (value_nohint.len() + 1) * 2;
let end = (value_nohint.count_bytes() + 1) * 2;
for hint in 0..end {
assert_eq!(
super::confstr(key, Some(hint)).as_deref().ok(),
Expand Down
14 changes: 7 additions & 7 deletions library/std/src/sys/paths/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,13 +430,13 @@ pub fn current_exe() -> io::Result<PathBuf> {

#[cfg(target_vendor = "apple")]
fn darwin_temp_dir() -> PathBuf {
crate::sys::pal::conf::confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64))
.map(PathBuf::from)
.unwrap_or_else(|_| {
// It failed for whatever reason (there are several possible reasons),
// so return the global one.
PathBuf::from("/tmp")
})
let darwin_temp_dir = crate::sys::pal::conf::confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64));
match darwin_temp_dir {
Ok(temp_dir) => OsString::from_vec(temp_dir.into_bytes()).into(),
// It failed for whatever reason (there are several possible reasons),
// so return the global one.
Err(_) => PathBuf::from("/tmp"),
}
}

pub fn temp_dir() -> PathBuf {
Expand Down
32 changes: 32 additions & 0 deletions library/std/src/sys/process/unix/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,31 @@ cfg_select! {
}
}

/// Retrieves the fallback path *specifically* used by each platform's `execvp`
/// implementation.
fn get_default_path() -> CString {

@asder8215 asder8215 Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

(Apologies for the repeat ping, previous message is label outdated due to new commits)

@joboet see my comment earlier #157144 (comment)

Unresolved question here is should we just directly use ":/bin:/usr/bin" for uclibc's execvp default fallback path? This is different from its _PATH_DEFPATH value which is "/usr/bin:/bin"

View changes since the review

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we know what we want to do for certain platforms like Solaris/AIX? I think those platforms are closed-source, so I couldn't find info about _PATH_DEFPATH/_PATH_BSHELL. I know Solaris is listed as tier 2 with host tools, so being able to build the standard library is supposed to be guaranteed.

@psumbera for Solaris, @xingxue-ibm for AIX: Do these platforms have constants like _PATH_DEFPATH that are used by execvp when no PATH is available? Or is there a better way to emulate execvp behavior?

Probably doing a libc release in 1-2 weeks, as a heads up if there's something to add there.

#[cfg(all(target_os = "linux", target_env = "gnu"))]
use crate::sys::conf::confstr;

cfg_select! {
all(target_os = "linux", target_env = "gnu") => {
confstr(libc::_CS_PATH, None).unwrap_or(CString::default())
}
any(
all(target_os = "linux", not(target_env = "gnu")),
target_vendor = "apple",
target_os = "android",
target_os = "openbsd",
target_os = "freebsd",
target_os = "dragonfly",
) => {
// SAFETY: libc::_PATH_DEFPATH is a nul-terminated char pointer
unsafe { CString::from(CStr::from_ptr(libc::_PATH_DEFPATH)) }
}
_ => CString::default(),
}
}

////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////
Expand All @@ -91,6 +116,7 @@ pub struct Command {
program: CString,
args: CStringArray,
env: CommandEnv,
default_path: CString,

program_kind: ProgramKind,
cwd: Option<CString>,
Expand Down Expand Up @@ -168,10 +194,12 @@ impl Command {
let program = os2c(program, &mut saw_nul);
let mut args = CStringArray::with_capacity(1);
args.push(program.clone());

Command {
program,
args,
env: Default::default(),
default_path: get_default_path(),
program_kind,
cwd: None,
chroot: None,
Expand Down Expand Up @@ -271,6 +299,10 @@ impl Command {
self.env.does_clear()
}

pub fn get_default_path(&self) -> &CStr {
&self.default_path
}

pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
CommandResolvedEnvs::new(self.env.capture())
}
Expand Down
5 changes: 5 additions & 0 deletions library/std/src/sys/process/unix/common/cstring_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ impl CStringArray {
drop(unsafe { CString::from_raw(old.cast_mut()) });
}

/// Returns the length of the array (null pointer excluded).
pub fn len(&self) -> usize {
self.ptrs.len() - 1
}

/// Push an additional string to the array.
pub fn push(&mut self, item: CString) {
let argc = self.ptrs.len() - 1;
Expand Down
Loading
Loading