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}