1//! Implementation of `std::os` functionality for unix systems
2
3#![allow(unused_imports)] // lots of cfg code here
4
5#[cfg(test)]
6mod tests;
7
8use core::slice::memchr;
9
10use libc::{c_char, c_int, c_void};
11
12use crate::error::Error as StdError;
13use crate::ffi::{CStr, CString, OsStr, OsString};
14use crate::os::unix::prelude::*;
15use crate::path::{self, PathBuf};
16use crate::sync::{PoisonError, RwLock};
17use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr};
18#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
19use crate::sys::weak::weak;
20use crate::sys::{cvt, fd};
21use crate::{fmt, io, iter, mem, ptr, slice, str, vec};
22
23const TMPBUF_SZ: usize = 128;
24
25cfg_if::cfg_if! {
26 if #[cfg(target_os = "redox")] {
27 const PATH_SEPARATOR: u8 = b';';
28 } else {
29 const PATH_SEPARATOR: u8 = b':';
30 }
31}
32
33unsafe extern "C" {
34 #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))]
35 #[cfg_attr(
36 any(
37 target_os = "linux",
38 target_os = "emscripten",
39 target_os = "fuchsia",
40 target_os = "l4re",
41 target_os = "hurd",
42 ),
43 link_name = "__errno_location"
44 )]
45 #[cfg_attr(
46 any(
47 target_os = "netbsd",
48 target_os = "openbsd",
49 target_os = "cygwin",
50 target_os = "android",
51 target_os = "redox",
52 target_os = "nuttx",
53 target_env = "newlib"
54 ),
55 link_name = "__errno"
56 )]
57 #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")]
58 #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")]
59 #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")]
60 #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
61 #[cfg_attr(target_os = "aix", link_name = "_Errno")]
62 unsafefn errno_location() -> *mut c_int;
63}
64
65/// Returns the platform-specific value of errno
66#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))]
67pub fn errno() -> i32 {
68 unsafe { (*errno_location()) as i32 }
69}
70
71/// Sets the platform-specific value of errno
72// needed for readdir and syscall!
73#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))]
74#[allow(dead_code)] // but not all target cfgs actually end up using it
75pub fn set_errno(e: i32) {
76 unsafe { *errno_location() = e as c_int }
77}
78
79#[cfg(target_os = "vxworks")]
80pub fn errno() -> i32 {
81 unsafe { libc::errnoGet() }
82}
83
84#[cfg(target_os = "rtems")]
85pub fn errno() -> i32 {
86 unsafe extern "C" {
87 #[thread_local]
88 static _tls_errno: c_int;
89 }
90
91 unsafe { _tls_errno as i32 }
92}
93
94#[cfg(target_os = "dragonfly")]
95pub fn errno() -> i32 {
96 unsafe extern "C" {
97 #[thread_local]
98 static errno: c_int;
99 }
100
101 unsafe { errno as i32 }
102}
103
104#[cfg(target_os = "dragonfly")]
105#[allow(dead_code)]
106pub fn set_errno(e: i32) {
107 unsafe extern "C" {
108 #[thread_local]
109 static mut errno: c_int;
110 }
111
112 unsafe {
113 errno = e;
114 }
115}
116
117/// Gets a detailed string description for the given error number.
118pub fn error_string(errno: i32) -> String {
119 unsafe extern "C" {
120 #[cfg_attr(
121 all(
122 any(
123 target_os = "linux",
124 target_os = "hurd",
125 target_env = "newlib",
126 target_os = "cygwin"
127 ),
128 not(target_env = "ohos")
129 ),
130 link_name = "__xpg_strerror_r"
131 )]
132 fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int;
133 }
134
135 let mut buf = [0 as c_char; TMPBUF_SZ];
136
137 let p = buf.as_mut_ptr();
138 unsafe {
139 if strerror_r(errno as c_int, p, buf.len()) < 0 {
140 panic!("strerror_r failure");
141 }
142
143 let p = p as *const _;
144 // We can't always expect a UTF-8 environment. When we don't get that luxury,
145 // it's better to give a low-quality error message than none at all.
146 String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into()
147 }
148}
149
150#[cfg(target_os = "espidf")]
151pub fn getcwd() -> io::Result<PathBuf> {
152 Ok(PathBuf::from("/"))
153}
154
155#[cfg(not(target_os = "espidf"))]
156pub fn getcwd() -> io::Result<PathBuf> {
157 let mut buf = Vec::with_capacity(512);
158 loop {
159 unsafe {
160 let ptr = buf.as_mut_ptr() as *mut libc::c_char;
161 if !libc::getcwd(ptr, buf.capacity()).is_null() {
162 let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len();
163 buf.set_len(len);
164 buf.shrink_to_fit();
165 return Ok(PathBuf::from(OsString::from_vec(buf)));
166 } else {
167 let error = io::Error::last_os_error();
168 if error.raw_os_error() != Some(libc::ERANGE) {
169 return Err(error);
170 }
171 }
172
173 // Trigger the internal buffer resizing logic of `Vec` by requiring
174 // more space than the current capacity.
175 let cap = buf.capacity();
176 buf.set_len(cap);
177 buf.reserve(1);
178 }
179 }
180}
181
182#[cfg(target_os = "espidf")]
183pub fn chdir(_p: &path::Path) -> io::Result<()> {
184 super::unsupported::unsupported()
185}
186
187#[cfg(not(target_os = "espidf"))]
188pub fn chdir(p: &path::Path) -> io::Result<()> {
189 let result: i32 = run_path_with_cstr(path:p, &|p: &CStr| unsafe { Ok(libc::chdir(dir:p.as_ptr())) })?;
190 if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) }
191}
192
193pub struct SplitPaths<'a> {
194 iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>, fn(&'a [u8]) -> PathBuf>,
195}
196
197pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
198 fn bytes_to_path(b: &[u8]) -> PathBuf {
199 PathBuf::from(<OsStr as OsStrExt>::from_bytes(slice:b))
200 }
201 fn is_separator(b: &u8) -> bool {
202 *b == PATH_SEPARATOR
203 }
204 let unparsed: &[u8] = unparsed.as_bytes();
205 SplitPaths {
206 iter: unparsedSplit<'_, u8, fn(&u8) -> bool>
207 .split(pred:is_separator as fn(&u8) -> bool)
208 .map(bytes_to_path as fn(&[u8]) -> PathBuf),
209 }
210}
211
212impl<'a> Iterator for SplitPaths<'a> {
213 type Item = PathBuf;
214 fn next(&mut self) -> Option<PathBuf> {
215 self.iter.next()
216 }
217 fn size_hint(&self) -> (usize, Option<usize>) {
218 self.iter.size_hint()
219 }
220}
221
222#[derive(Debug)]
223pub struct JoinPathsError;
224
225pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
226where
227 I: Iterator<Item = T>,
228 T: AsRef<OsStr>,
229{
230 let mut joined: Vec = Vec::new();
231
232 for (i: usize, path: T) in paths.enumerate() {
233 let path: &[u8] = path.as_ref().as_bytes();
234 if i > 0 {
235 joined.push(PATH_SEPARATOR)
236 }
237 if path.contains(&PATH_SEPARATOR) {
238 return Err(JoinPathsError);
239 }
240 joined.extend_from_slice(path);
241 }
242 Ok(OsStringExt::from_vec(joined))
243}
244
245impl fmt::Display for JoinPathsError {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 write!(f, "path segment contains separator `{}`", char::from(PATH_SEPARATOR))
248 }
249}
250
251impl StdError for JoinPathsError {
252 #[allow(deprecated)]
253 fn description(&self) -> &str {
254 "failed to join paths"
255 }
256}
257
258#[cfg(target_os = "aix")]
259pub fn current_exe() -> io::Result<PathBuf> {
260 #[cfg(test)]
261 use realstd::env;
262
263 #[cfg(not(test))]
264 use crate::env;
265 use crate::io::ErrorKind;
266
267 let exe_path = env::args().next().ok_or(io::const_error!(
268 ErrorKind::NotFound,
269 "an executable path was not found because no arguments were provided through argv",
270 ))?;
271 let path = PathBuf::from(exe_path);
272 if path.is_absolute() {
273 return path.canonicalize();
274 }
275 // Search PWD to infer current_exe.
276 if let Some(pstr) = path.to_str()
277 && pstr.contains("/")
278 {
279 return getcwd().map(|cwd| cwd.join(path))?.canonicalize();
280 }
281 // Search PATH to infer current_exe.
282 if let Some(p) = getenv(OsStr::from_bytes("PATH".as_bytes())) {
283 for search_path in split_paths(&p) {
284 let pb = search_path.join(&path);
285 if pb.is_file()
286 && let Ok(metadata) = crate::fs::metadata(&pb)
287 && metadata.permissions().mode() & 0o111 != 0
288 {
289 return pb.canonicalize();
290 }
291 }
292 }
293 Err(io::const_error!(ErrorKind::NotFound, "an executable path was not found"))
294}
295
296#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
297pub fn current_exe() -> io::Result<PathBuf> {
298 unsafe {
299 let mut mib = [
300 libc::CTL_KERN as c_int,
301 libc::KERN_PROC as c_int,
302 libc::KERN_PROC_PATHNAME as c_int,
303 -1 as c_int,
304 ];
305 let mut sz = 0;
306 cvt(libc::sysctl(
307 mib.as_mut_ptr(),
308 mib.len() as libc::c_uint,
309 ptr::null_mut(),
310 &mut sz,
311 ptr::null_mut(),
312 0,
313 ))?;
314 if sz == 0 {
315 return Err(io::Error::last_os_error());
316 }
317 let mut v: Vec<u8> = Vec::with_capacity(sz);
318 cvt(libc::sysctl(
319 mib.as_mut_ptr(),
320 mib.len() as libc::c_uint,
321 v.as_mut_ptr() as *mut libc::c_void,
322 &mut sz,
323 ptr::null_mut(),
324 0,
325 ))?;
326 if sz == 0 {
327 return Err(io::Error::last_os_error());
328 }
329 v.set_len(sz - 1); // chop off trailing NUL
330 Ok(PathBuf::from(OsString::from_vec(v)))
331 }
332}
333
334#[cfg(target_os = "netbsd")]
335pub fn current_exe() -> io::Result<PathBuf> {
336 fn sysctl() -> io::Result<PathBuf> {
337 unsafe {
338 let mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, -1, libc::KERN_PROC_PATHNAME];
339 let mut path_len: usize = 0;
340 cvt(libc::sysctl(
341 mib.as_ptr(),
342 mib.len() as libc::c_uint,
343 ptr::null_mut(),
344 &mut path_len,
345 ptr::null(),
346 0,
347 ))?;
348 if path_len <= 1 {
349 return Err(io::const_error!(
350 io::ErrorKind::Uncategorized,
351 "KERN_PROC_PATHNAME sysctl returned zero-length string",
352 ));
353 }
354 let mut path: Vec<u8> = Vec::with_capacity(path_len);
355 cvt(libc::sysctl(
356 mib.as_ptr(),
357 mib.len() as libc::c_uint,
358 path.as_ptr() as *mut libc::c_void,
359 &mut path_len,
360 ptr::null(),
361 0,
362 ))?;
363 path.set_len(path_len - 1); // chop off NUL
364 Ok(PathBuf::from(OsString::from_vec(path)))
365 }
366 }
367 fn procfs() -> io::Result<PathBuf> {
368 let curproc_exe = path::Path::new("/proc/curproc/exe");
369 if curproc_exe.is_file() {
370 return crate::fs::read_link(curproc_exe);
371 }
372 Err(io::const_error!(
373 io::ErrorKind::Uncategorized,
374 "/proc/curproc/exe doesn't point to regular file.",
375 ))
376 }
377 sysctl().or_else(|_| procfs())
378}
379
380#[cfg(target_os = "openbsd")]
381pub fn current_exe() -> io::Result<PathBuf> {
382 unsafe {
383 let mut mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, libc::getpid(), libc::KERN_PROC_ARGV];
384 let mib = mib.as_mut_ptr();
385 let mut argv_len = 0;
386 cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_len, ptr::null_mut(), 0))?;
387 let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize);
388 cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?;
389 argv.set_len(argv_len as usize);
390 if argv[0].is_null() {
391 return Err(io::const_error!(io::ErrorKind::Uncategorized, "no current exe available"));
392 }
393 let argv0 = CStr::from_ptr(argv[0]).to_bytes();
394 if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') {
395 crate::fs::canonicalize(OsStr::from_bytes(argv0))
396 } else {
397 Ok(PathBuf::from(OsStr::from_bytes(argv0)))
398 }
399 }
400}
401
402#[cfg(any(
403 target_os = "linux",
404 target_os = "cygwin",
405 target_os = "hurd",
406 target_os = "android",
407 target_os = "nuttx",
408 target_os = "emscripten"
409))]
410pub fn current_exe() -> io::Result<PathBuf> {
411 match crate::fs::read_link(path:"/proc/self/exe") {
412 Err(ref e: &Error) if e.kind() == io::ErrorKind::NotFound => Err(io::const_error!(
413 io::ErrorKind::Uncategorized,
414 "no /proc/self/exe available. Is /proc mounted?",
415 )),
416 other: Result => other,
417 }
418}
419
420#[cfg(target_os = "nto")]
421pub fn current_exe() -> io::Result<PathBuf> {
422 let mut e = crate::fs::read("/proc/self/exefile")?;
423 // Current versions of QNX Neutrino provide a null-terminated path.
424 // Ensure the trailing null byte is not returned here.
425 if let Some(0) = e.last() {
426 e.pop();
427 }
428 Ok(PathBuf::from(OsString::from_vec(e)))
429}
430
431#[cfg(target_vendor = "apple")]
432pub fn current_exe() -> io::Result<PathBuf> {
433 unsafe {
434 let mut sz: u32 = 0;
435 #[expect(deprecated)]
436 libc::_NSGetExecutablePath(ptr::null_mut(), &mut sz);
437 if sz == 0 {
438 return Err(io::Error::last_os_error());
439 }
440 let mut v: Vec<u8> = Vec::with_capacity(sz as usize);
441 #[expect(deprecated)]
442 let err = libc::_NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
443 if err != 0 {
444 return Err(io::Error::last_os_error());
445 }
446 v.set_len(sz as usize - 1); // chop off trailing NUL
447 Ok(PathBuf::from(OsString::from_vec(v)))
448 }
449}
450
451#[cfg(any(target_os = "solaris", target_os = "illumos"))]
452pub fn current_exe() -> io::Result<PathBuf> {
453 if let Ok(path) = crate::fs::read_link("/proc/self/path/a.out") {
454 Ok(path)
455 } else {
456 unsafe {
457 let path = libc::getexecname();
458 if path.is_null() {
459 Err(io::Error::last_os_error())
460 } else {
461 let filename = CStr::from_ptr(path).to_bytes();
462 let path = PathBuf::from(<OsStr as OsStrExt>::from_bytes(filename));
463
464 // Prepend a current working directory to the path if
465 // it doesn't contain an absolute pathname.
466 if filename[0] == b'/' { Ok(path) } else { getcwd().map(|cwd| cwd.join(path)) }
467 }
468 }
469 }
470}
471
472#[cfg(target_os = "haiku")]
473pub fn current_exe() -> io::Result<PathBuf> {
474 let mut name = vec![0; libc::PATH_MAX as usize];
475 unsafe {
476 let result = libc::find_path(
477 crate::ptr::null_mut(),
478 libc::path_base_directory::B_FIND_PATH_IMAGE_PATH,
479 crate::ptr::null_mut(),
480 name.as_mut_ptr(),
481 name.len(),
482 );
483 if result != libc::B_OK {
484 use crate::io::ErrorKind;
485 Err(io::const_error!(ErrorKind::Uncategorized, "error getting executable path"))
486 } else {
487 // find_path adds the null terminator.
488 let name = CStr::from_ptr(name.as_ptr()).to_bytes();
489 Ok(PathBuf::from(OsStr::from_bytes(name)))
490 }
491 }
492}
493
494#[cfg(target_os = "redox")]
495pub fn current_exe() -> io::Result<PathBuf> {
496 crate::fs::read_to_string("/scheme/sys/exe").map(PathBuf::from)
497}
498
499#[cfg(target_os = "rtems")]
500pub fn current_exe() -> io::Result<PathBuf> {
501 crate::fs::read_to_string("sys:exe").map(PathBuf::from)
502}
503
504#[cfg(target_os = "l4re")]
505pub fn current_exe() -> io::Result<PathBuf> {
506 use crate::io::ErrorKind;
507 Err(io::const_error!(ErrorKind::Unsupported, "not yet implemented!"))
508}
509
510#[cfg(target_os = "vxworks")]
511pub fn current_exe() -> io::Result<PathBuf> {
512 #[cfg(test)]
513 use realstd::env;
514
515 #[cfg(not(test))]
516 use crate::env;
517
518 let exe_path = env::args().next().unwrap();
519 let path = path::Path::new(&exe_path);
520 path.canonicalize()
521}
522
523#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
524pub fn current_exe() -> io::Result<PathBuf> {
525 super::unsupported::unsupported()
526}
527
528#[cfg(target_os = "fuchsia")]
529pub fn current_exe() -> io::Result<PathBuf> {
530 #[cfg(test)]
531 use realstd::env;
532
533 #[cfg(not(test))]
534 use crate::env;
535 use crate::io::ErrorKind;
536
537 let exe_path = env::args().next().ok_or(io::const_error!(
538 ErrorKind::Uncategorized,
539 "an executable path was not found because no arguments were provided through argv",
540 ))?;
541 let path = PathBuf::from(exe_path);
542
543 // Prepend the current working directory to the path if it's not absolute.
544 if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) }
545}
546
547pub struct Env {
548 iter: vec::IntoIter<(OsString, OsString)>,
549}
550
551// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
552pub struct EnvStrDebug<'a> {
553 slice: &'a [(OsString, OsString)],
554}
555
556impl fmt::Debug for EnvStrDebug<'_> {
557 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
558 let Self { slice: &&[(OsString, OsString)] } = self;
559 f&mut DebugList<'_, '_>.debug_list()
560 .entries(slice.iter().map(|(a: &OsString, b: &OsString)| (a.to_str().unwrap(), b.to_str().unwrap())))
561 .finish()
562 }
563}
564
565impl Env {
566 pub fn str_debug(&self) -> impl fmt::Debug + '_ {
567 let Self { iter: &IntoIter<(OsString, OsString)> } = self;
568 EnvStrDebug { slice: iter.as_slice() }
569 }
570}
571
572impl fmt::Debug for Env {
573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574 let Self { iter: &IntoIter<(OsString, OsString)> } = self;
575 f.debug_list().entries(iter.as_slice()).finish()
576 }
577}
578
579impl !Send for Env {}
580impl !Sync for Env {}
581
582impl Iterator for Env {
583 type Item = (OsString, OsString);
584 fn next(&mut self) -> Option<(OsString, OsString)> {
585 self.iter.next()
586 }
587 fn size_hint(&self) -> (usize, Option<usize>) {
588 self.iter.size_hint()
589 }
590}
591
592// Use `_NSGetEnviron` on Apple platforms.
593//
594// `_NSGetEnviron` is the documented alternative (see `man environ`), and has
595// been available since the first versions of both macOS and iOS.
596//
597// Nowadays, specifically since macOS 10.8, `environ` has been exposed through
598// `libdyld.dylib`, which is linked via. `libSystem.dylib`:
599// <https://github.com/apple-oss-distributions/dyld/blob/dyld-1160.6/libdyld/libdyldGlue.cpp#L913>
600//
601// So in the end, it likely doesn't really matter which option we use, but the
602// performance cost of using `_NSGetEnviron` is extremely miniscule, and it
603// might be ever so slightly more supported, so let's just use that.
604//
605// NOTE: The header where this is defined (`crt_externs.h`) was added to the
606// iOS 13.0 SDK, which has been the source of a great deal of confusion in the
607// past about the availability of this API.
608//
609// NOTE(madsmtm): Neither this nor using `environ` has been verified to not
610// cause App Store rejections; if this is found to be the case, an alternative
611// implementation of this is possible using `[NSProcessInfo environment]`
612// - which internally uses `_NSGetEnviron` and a system-wide lock on the
613// environment variables to protect against `setenv`, so using that might be
614// desirable anyhow? Though it also means that we have to link to Foundation.
615#[cfg(target_vendor = "apple")]
616pub unsafe fn environ() -> *mut *const *const c_char {
617 libc::_NSGetEnviron() as *mut *const *const c_char
618}
619
620// Use the `environ` static which is part of POSIX.
621#[cfg(not(target_vendor = "apple"))]
622pub unsafe fn environ() -> *mut *const *const c_char {
623 unsafe extern "C" {
624 unsafestatic mut environ: *const *const c_char;
625 }
626 &raw mut environ
627}
628
629static ENV_LOCK: RwLock<()> = RwLock::new(());
630
631pub fn env_read_lock() -> impl Drop {
632 ENV_LOCK.read().unwrap_or_else(op:PoisonError::into_inner)
633}
634
635/// Returns a vector of (variable, value) byte-vector pairs for all the
636/// environment variables of the current process.
637pub fn env() -> Env {
638 unsafe {
639 let _guard = env_read_lock();
640 let mut environ = *environ();
641 let mut result = Vec::new();
642 if !environ.is_null() {
643 while !(*environ).is_null() {
644 if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
645 result.push(key_value);
646 }
647 environ = environ.add(1);
648 }
649 }
650 return Env { iter: result.into_iter() };
651 }
652
653 fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
654 // Strategy (copied from glibc): Variable name and value are separated
655 // by an ASCII equals sign '='. Since a variable name must not be
656 // empty, allow variable names starting with an equals sign. Skip all
657 // malformed lines.
658 if input.is_empty() {
659 return None;
660 }
661 let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
662 pos.map(|p| {
663 (
664 OsStringExt::from_vec(input[..p].to_vec()),
665 OsStringExt::from_vec(input[p + 1..].to_vec()),
666 )
667 })
668 }
669}
670
671pub fn getenv(k: &OsStr) -> Option<OsString> {
672 // environment variables with a nul byte can't be set, so their value is
673 // always None as well
674 run_with_cstrOption>(k.as_bytes(), &|k: &CStr| {
675 let _guard: impl Drop = env_read_lock();
676 let v: *const i8 = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
677
678 if v.is_null() {
679 Ok(None)
680 } else {
681 // SAFETY: `v` cannot be mutated while executing this line since we've a read lock
682 let bytes: Vec = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
683
684 Ok(Some(OsStringExt::from_vec(bytes)))
685 }
686 })
687 .ok()
688 .flatten()
689}
690
691pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
692 run_with_cstr(k.as_bytes(), &|k: &CStr| {
693 run_with_cstr(v.as_bytes(), &|v: &CStr| {
694 let _guard: Result, …> = ENV_LOCK.write();
695 cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(op:drop)
696 })
697 })
698}
699
700pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
701 run_with_cstr(n.as_bytes(), &|nbuf: &CStr| {
702 let _guard: Result, …> = ENV_LOCK.write();
703 cvt(libc::unsetenv(nbuf.as_ptr())).map(op:drop)
704 })
705}
706
707#[cfg(not(target_os = "espidf"))]
708pub fn page_size() -> usize {
709 unsafe { libc::sysconf(name:libc::_SC_PAGESIZE) as usize }
710}
711
712// Returns the value for [`confstr(key, ...)`][posix_confstr]. Currently only
713// used on Darwin, but should work on any unix (in case we need to get
714// `_CS_PATH` or `_CS_V[67]_ENV` in the future).
715//
716// [posix_confstr]:
717// https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html
718//
719// FIXME: Support `confstr` in Miri.
720#[cfg(all(target_vendor = "apple", not(miri)))]
721fn confstr(key: c_int, size_hint: Option<usize>) -> io::Result<OsString> {
722 let mut buf: Vec<u8> = Vec::with_capacity(0);
723 let mut bytes_needed_including_nul = size_hint
724 .unwrap_or_else(|| {
725 // Treat "None" as "do an extra call to get the length". In theory
726 // we could move this into the loop below, but it's hard to do given
727 // that it isn't 100% clear if it's legal to pass 0 for `len` when
728 // the buffer isn't null.
729 unsafe { libc::confstr(key, core::ptr::null_mut(), 0) }
730 })
731 .max(1);
732 // If the value returned by `confstr` is greater than the len passed into
733 // it, then the value was truncated, meaning we need to retry. Note that
734 // while `confstr` results don't seem to change for a process, it's unclear
735 // if this is guaranteed anywhere, so looping does seem required.
736 while bytes_needed_including_nul > buf.capacity() {
737 // We write into the spare capacity of `buf`. This lets us avoid
738 // changing buf's `len`, which both simplifies `reserve` computation,
739 // allows working with `Vec<u8>` instead of `Vec<MaybeUninit<u8>>`, and
740 // may avoid a copy, since the Vec knows that none of the bytes are needed
741 // when reallocating (well, in theory anyway).
742 buf.reserve(bytes_needed_including_nul);
743 // `confstr` returns
744 // - 0 in the case of errors: we break and return an error.
745 // - The number of bytes written, iff the provided buffer is enough to
746 // hold the entire value: we break and return the data in `buf`.
747 // - Otherwise, the number of bytes needed (including nul): we go
748 // through the loop again.
749 bytes_needed_including_nul =
750 unsafe { libc::confstr(key, buf.as_mut_ptr().cast::<c_char>(), buf.capacity()) };
751 }
752 // `confstr` returns 0 in the case of an error.
753 if bytes_needed_including_nul == 0 {
754 return Err(io::Error::last_os_error());
755 }
756 // Safety: `confstr(..., buf.as_mut_ptr(), buf.capacity())` returned a
757 // non-zero value, meaning `bytes_needed_including_nul` bytes were
758 // initialized.
759 unsafe {
760 buf.set_len(bytes_needed_including_nul);
761 // Remove the NUL-terminator.
762 let last_byte = buf.pop();
763 // ... and smoke-check that it *was* a NUL-terminator.
764 assert_eq!(last_byte, Some(0), "`confstr` provided a string which wasn't nul-terminated");
765 };
766 Ok(OsString::from_vec(buf))
767}
768
769#[cfg(all(target_vendor = "apple", not(miri)))]
770fn darwin_temp_dir() -> PathBuf {
771 confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64)).map(PathBuf::from).unwrap_or_else(|_| {
772 // It failed for whatever reason (there are several possible reasons),
773 // so return the global one.
774 PathBuf::from("/tmp")
775 })
776}
777
778pub fn temp_dir() -> PathBuf {
779 crate::env::var_os(key:"TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
780 cfg_if::cfg_if! {
781 if #[cfg(all(target_vendor = "apple", not(miri)))] {
782 darwin_temp_dir()
783 } else if #[cfg(target_os = "android")] {
784 PathBuf::from("/data/local/tmp")
785 } else {
786 PathBuf::from("/tmp")
787 }
788 }
789 })
790}
791
792pub fn home_dir() -> Option<PathBuf> {
793 return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from);
794
795 #[cfg(any(
796 target_os = "android",
797 target_os = "emscripten",
798 target_os = "redox",
799 target_os = "vxworks",
800 target_os = "espidf",
801 target_os = "horizon",
802 target_os = "vita",
803 target_os = "nuttx",
804 all(target_vendor = "apple", not(target_os = "macos")),
805 ))]
806 unsafe fn fallback() -> Option<OsString> {
807 None
808 }
809 #[cfg(not(any(
810 target_os = "android",
811 target_os = "emscripten",
812 target_os = "redox",
813 target_os = "vxworks",
814 target_os = "espidf",
815 target_os = "horizon",
816 target_os = "vita",
817 target_os = "nuttx",
818 all(target_vendor = "apple", not(target_os = "macos")),
819 )))]
820 unsafe fn fallback() -> Option<OsString> {
821 let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
822 n if n < 0 => 512 as usize,
823 n => n as usize,
824 };
825 let mut buf = Vec::with_capacity(amt);
826 let mut p = mem::MaybeUninit::<libc::passwd>::uninit();
827 let mut result = ptr::null_mut();
828 match libc::getpwuid_r(
829 libc::getuid(),
830 p.as_mut_ptr(),
831 buf.as_mut_ptr(),
832 buf.capacity(),
833 &mut result,
834 ) {
835 0 if !result.is_null() => {
836 let ptr = (*result).pw_dir as *const _;
837 let bytes = CStr::from_ptr(ptr).to_bytes().to_vec();
838 Some(OsStringExt::from_vec(bytes))
839 }
840 _ => None,
841 }
842 }
843}
844
845pub fn exit(code: i32) -> ! {
846 crate::sys::exit_guard::unique_thread_exit();
847 unsafe { libc::exit(status:code as c_int) }
848}
849
850pub fn getpid() -> u32 {
851 unsafe { libc::getpid() as u32 }
852}
853
854pub fn getppid() -> u32 {
855 unsafe { libc::getppid() as u32 }
856}
857
858#[cfg(all(target_os = "linux", target_env = "gnu"))]
859pub fn glibc_version() -> Option<(usize, usize)> {
860 unsafe extern "C" {
861 unsafefn gnu_get_libc_version() -> *const libc::c_char;
862 }
863 let version_cstr: &CStr = unsafe { CStr::from_ptr(gnu_get_libc_version()) };
864 if let Ok(version_str: &str) = version_cstr.to_str() {
865 parse_glibc_version(version_str)
866 } else {
867 None
868 }
869}
870
871// Returns Some((major, minor)) if the string is a valid "x.y" version,
872// ignoring any extra dot-separated parts. Otherwise return None.
873#[cfg(all(target_os = "linux", target_env = "gnu"))]
874fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
875 let mut parsed_ints: impl Iterator> = version.split('.').map(str::parse::<usize>).fuse();
876 match (parsed_ints.next(), parsed_ints.next()) {
877 (Some(Ok(major: usize)), Some(Ok(minor: usize))) => Some((major, minor)),
878 _ => None,
879 }
880}
881

Provided by KDAB

Privacy Policy