Skip to main content

std/sys/pal/unix/
conf.rs

1#[cfg(test)]
2mod tests;
3
4#[cfg(not(target_os = "espidf"))]
5pub fn page_size() -> usize {
6    unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
7}
8
9/// Returns the value for [`confstr(key, ...)`][posix_confstr]. Currently only
10/// used on Darwin, but should work on any unix (in case we need to get
11/// `_CS_PATH` or `_CS_V[67]_ENV` in the future).
12///
13/// [posix_confstr]:
14///     https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html
15#[cfg(target_vendor = "apple")]
16pub fn confstr(
17    key: crate::ffi::c_int,
18    size_hint: Option<usize>,
19) -> crate::io::Result<crate::ffi::OsString> {
20    use crate::ffi::OsString;
21    use crate::io;
22    use crate::os::unix::ffi::OsStringExt;
23
24    let mut buf: Vec<u8> = Vec::with_capacity(0);
25    let mut bytes_needed_including_nul = size_hint
26        .unwrap_or_else(|| {
27            // Treat "None" as "do an extra call to get the length". In theory
28            // we could move this into the loop below, but it's hard to do given
29            // that it isn't 100% clear if it's legal to pass 0 for `len` when
30            // the buffer isn't null.
31            unsafe { libc::confstr(key, core::ptr::null_mut(), 0) }
32        })
33        .max(1);
34    // If the value returned by `confstr` is greater than the len passed into
35    // it, then the value was truncated, meaning we need to retry. Note that
36    // while `confstr` results don't seem to change for a process, it's unclear
37    // if this is guaranteed anywhere, so looping does seem required.
38    while bytes_needed_including_nul > buf.capacity() {
39        // We write into the spare capacity of `buf`. This lets us avoid
40        // changing buf's `len`, which both simplifies `reserve` computation,
41        // allows working with `Vec<u8>` instead of `Vec<MaybeUninit<u8>>`, and
42        // may avoid a copy, since the Vec knows that none of the bytes are needed
43        // when reallocating (well, in theory anyway).
44        buf.reserve(bytes_needed_including_nul);
45        // `confstr` returns
46        // - 0 in the case of errors: we break and return an error.
47        // - The number of bytes written, iff the provided buffer is enough to
48        //   hold the entire value: we break and return the data in `buf`.
49        // - Otherwise, the number of bytes needed (including nul): we go
50        //   through the loop again.
51        bytes_needed_including_nul =
52            unsafe { libc::confstr(key, buf.as_mut_ptr().cast(), buf.capacity()) };
53    }
54    // `confstr` returns 0 in the case of an error.
55    if bytes_needed_including_nul == 0 {
56        return Err(io::Error::last_os_error());
57    }
58    // Safety: `confstr(..., buf.as_mut_ptr(), buf.capacity())` returned a
59    // non-zero value, meaning `bytes_needed_including_nul` bytes were
60    // initialized.
61    unsafe {
62        buf.set_len(bytes_needed_including_nul);
63        // Remove the NUL-terminator.
64        let last_byte = buf.pop();
65        // ... and smoke-check that it *was* a NUL-terminator.
66        assert_eq!(last_byte, Some(0), "`confstr` provided a string which wasn't nul-terminated");
67    };
68    Ok(OsString::from_vec(buf))
69}
70
71#[cfg(all(target_os = "linux", target_env = "gnu"))]
72pub fn glibc_version() -> Option<(usize, usize)> {
73    use crate::ffi::CStr;
74
75    unsafe extern "C" {
76        fn gnu_get_libc_version() -> *const libc::c_char;
77    }
78    let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) };
79    if let Ok(version_str) = version_cstr.to_str() {
80        parse_glibc_version(version_str)
81    } else {
82        None
83    }
84}
85
86/// Returns Some((major, minor)) if the string is a valid "x.y" version,
87/// ignoring any extra dot-separated parts. Otherwise return None.
88#[cfg(all(target_os = "linux", target_env = "gnu"))]
89fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
90    let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse();
91    match (parsed_ints.next(), parsed_ints.next()) {
92        (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)),
93        _ => None,
94    }
95}