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