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 crate::os::unix::prelude::*;
9
10use crate::error::Error as StdError;
11use crate::ffi::{CStr, CString, OsStr, OsString};
12use crate::fmt;
13use crate::io;
14use crate::iter;
15use crate::mem;
16use crate::path::{self, PathBuf};
17use crate::ptr;
18use crate::slice;
19use crate::str;
20use crate::sync::{PoisonError, RwLock};
21use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr};
22use crate::sys::cvt;
23use crate::sys::fd;
24use crate::vec;
25use core::slice::memchr;
26
27#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
28use crate::sys::weak::weak;
29
30use libc::{c_char, c_int, c_void};
31
32const TMPBUF_SZ: usize = 128;
33
34cfg_if::cfg_if! {
35 if #[cfg(target_os = "redox")] {
36 const PATH_SEPARATOR: u8 = b';';
37 } else {
38 const PATH_SEPARATOR: u8 = b':';
39 }
40}
41
42extern "C" {
43 #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))]
44 #[cfg_attr(
45 any(
46 target_os = "linux",
47 target_os = "emscripten",
48 target_os = "fuchsia",
49 target_os = "l4re",
50 target_os = "hurd",
51 ),
52 link_name = "__errno_location"
53 )]
54 #[cfg_attr(
55 any(
56 target_os = "netbsd",
57 target_os = "openbsd",
58 target_os = "android",
59 target_os = "redox",
60 target_env = "newlib"
61 ),
62 link_name = "__errno"
63 )]
64 #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")]
65 #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")]
66 #[cfg_attr(
67 any(
68 target_os = "macos",
69 target_os = "ios",
70 target_os = "tvos",
71 target_os = "freebsd",
72 target_os = "watchos",
73 target_os = "visionos",
74 ),
75 link_name = "__error"
76 )]
77 #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
78 #[cfg_attr(target_os = "aix", link_name = "_Errno")]
79 fn errno_location() -> *mut c_int;
80}
81
82/// Returns the platform-specific value of errno
83#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))]
84pub fn errno() -> i32 {
85 unsafe { (*errno_location()) as i32 }
86}
87
88/// Sets the platform-specific value of errno
89#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall!
90#[allow(dead_code)] // but not all target cfgs actually end up using it
91pub fn set_errno(e: i32) {
92 unsafe { *errno_location() = e as c_int }
93}
94
95#[cfg(target_os = "vxworks")]
96pub fn errno() -> i32 {
97 unsafe { libc::errnoGet() }
98}
99
100#[cfg(target_os = "dragonfly")]
101pub fn errno() -> i32 {
102 extern "C" {
103 #[thread_local]
104 static errno: c_int;
105 }
106
107 unsafe { errno as i32 }
108}
109
110#[cfg(target_os = "dragonfly")]
111#[allow(dead_code)]
112pub fn set_errno(e: i32) {
113 extern "C" {
114 #[thread_local]
115 static mut errno: c_int;
116 }
117
118 unsafe {
119 errno = e;
120 }
121}
122
123/// Gets a detailed string description for the given error number.
124pub fn error_string(errno: i32) -> String {
125 extern "C" {
126 #[cfg_attr(
127 all(
128 any(target_os = "linux", target_os = "hurd", target_env = "newlib"),
129 not(target_env = "ohos")
130 ),
131 link_name = "__xpg_strerror_r"
132 )]
133 fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int;
134 }
135
136 let mut buf = [0 as c_char; TMPBUF_SZ];
137
138 let p = buf.as_mut_ptr();
139 unsafe {
140 if strerror_r(errno as c_int, p, buf.len()) < 0 {
141 panic!("strerror_r failure");
142 }
143
144 let p = p as *const _;
145 // We can't always expect a UTF-8 environment. When we don't get that luxury,
146 // it's better to give a low-quality error message than none at all.
147 String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into()
148 }
149}
150
151#[cfg(target_os = "espidf")]
152pub fn getcwd() -> io::Result<PathBuf> {
153 Ok(PathBuf::from("/"))
154}
155
156#[cfg(not(target_os = "espidf"))]
157pub fn getcwd() -> io::Result<PathBuf> {
158 let mut buf = Vec::with_capacity(512);
159 loop {
160 unsafe {
161 let ptr = buf.as_mut_ptr() as *mut libc::c_char;
162 if !libc::getcwd(ptr, buf.capacity()).is_null() {
163 let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len();
164 buf.set_len(len);
165 buf.shrink_to_fit();
166 return Ok(PathBuf::from(OsString::from_vec(buf)));
167 } else {
168 let error = io::Error::last_os_error();
169 if error.raw_os_error() != Some(libc::ERANGE) {
170 return Err(error);
171 }
172 }
173
174 // Trigger the internal buffer resizing logic of `Vec` by requiring
175 // more space than the current capacity.
176 let cap = buf.capacity();
177 buf.set_len(cap);
178 buf.reserve(1);
179 }
180 }
181}
182
183#[cfg(target_os = "espidf")]
184pub fn chdir(_p: &path::Path) -> io::Result<()> {
185 super::unsupported::unsupported()
186}
187
188#[cfg(not(target_os = "espidf"))]
189pub fn chdir(p: &path::Path) -> io::Result<()> {
190 let result: ! = run_path_with_cstr(path:p, &|p: &CStr| unsafe { Ok(libc::chdir(p.as_ptr())) })?;
191 if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) }
192}
193
194pub struct SplitPaths<'a> {
195 iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>, fn(&'a [u8]) -> PathBuf>,
196}
197
198pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
199 fn bytes_to_path(b: &[u8]) -> PathBuf {
200 PathBuf::from(<OsStr as OsStrExt>::from_bytes(slice:b))
201 }
202 fn is_separator(b: &u8) -> bool {
203 *b == PATH_SEPARATOR
204 }
205 let unparsed: &[u8] = unparsed.as_bytes();
206 SplitPaths {
207 iter: unparsedSplit<'_, u8, fn(&u8) -> …>
208 .split(pred:is_separator as fn(&u8) -> bool)
209 .map(bytes_to_path as fn(&[u8]) -> PathBuf),
210 }
211}
212
213impl<'a> Iterator for SplitPaths<'a> {
214 type Item = PathBuf;
215 fn next(&mut self) -> Option<PathBuf> {
216 self.iter.next()
217 }
218 fn size_hint(&self) -> (usize, Option<usize>) {
219 self.iter.size_hint()
220 }
221}
222
223#[derive(Debug)]
224pub struct JoinPathsError;
225
226pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
227where
228 I: Iterator<Item = T>,
229 T: AsRef<OsStr>,
230{
231 let mut joined: Vec = Vec::new();
232
233 for (i: usize, path: T) in paths.enumerate() {
234 let path: &[u8] = path.as_ref().as_bytes();
235 if i > 0 {
236 joined.push(PATH_SEPARATOR)
237 }
238 if path.contains(&PATH_SEPARATOR) {
239 return Err(JoinPathsError);
240 }
241 joined.extend_from_slice(path);
242 }
243 Ok(OsStringExt::from_vec(joined))
244}
245
246impl fmt::Display for JoinPathsError {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 write!(f, "path segment contains separator `{}`", char::from(PATH_SEPARATOR))
249 }
250}
251
252impl StdError for JoinPathsError {
253 #[allow(deprecated)]
254 fn description(&self) -> &str {
255 "failed to join paths"
256 }
257}
258
259#[cfg(target_os = "aix")]
260pub fn current_exe() -> io::Result<PathBuf> {
261 use crate::io::ErrorKind;
262
263 #[cfg(test)]
264 use realstd::env;
265
266 #[cfg(not(test))]
267 use crate::env;
268
269 let exe_path = env::args().next().ok_or(io::const_io_error!(
270 ErrorKind::NotFound,
271 "an executable path was not found because no arguments were provided through argv"
272 ))?;
273 let path = PathBuf::from(exe_path);
274 if path.is_absolute() {
275 return path.canonicalize();
276 }
277 // Search PWD to infer current_exe.
278 if let Some(pstr) = path.to_str()
279 && pstr.contains("/")
280 {
281 return getcwd().map(|cwd| cwd.join(path))?.canonicalize();
282 }
283 // Search PATH to infer current_exe.
284 if let Some(p) = getenv(OsStr::from_bytes("PATH".as_bytes())) {
285 for search_path in split_paths(&p) {
286 let pb = search_path.join(&path);
287 if pb.is_file()
288 && let Ok(metadata) = crate::fs::metadata(&pb)
289 && metadata.permissions().mode() & 0o111 != 0
290 {
291 return pb.canonicalize();
292 }
293 }
294 }
295 Err(io::const_io_error!(ErrorKind::NotFound, "an executable path was not found"))
296}
297
298#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
299pub fn current_exe() -> io::Result<PathBuf> {
300 unsafe {
301 let mut mib = [
302 libc::CTL_KERN as c_int,
303 libc::KERN_PROC as c_int,
304 libc::KERN_PROC_PATHNAME as c_int,
305 -1 as c_int,
306 ];
307 let mut sz = 0;
308 cvt(libc::sysctl(
309 mib.as_mut_ptr(),
310 mib.len() as libc::c_uint,
311 ptr::null_mut(),
312 &mut sz,
313 ptr::null_mut(),
314 0,
315 ))?;
316 if sz == 0 {
317 return Err(io::Error::last_os_error());
318 }
319 let mut v: Vec<u8> = Vec::with_capacity(sz);
320 cvt(libc::sysctl(
321 mib.as_mut_ptr(),
322 mib.len() as libc::c_uint,
323 v.as_mut_ptr() as *mut libc::c_void,
324 &mut sz,
325 ptr::null_mut(),
326 0,
327 ))?;
328 if sz == 0 {
329 return Err(io::Error::last_os_error());
330 }
331 v.set_len(sz - 1); // chop off trailing NUL
332 Ok(PathBuf::from(OsString::from_vec(v)))
333 }
334}
335
336#[cfg(target_os = "netbsd")]
337pub fn current_exe() -> io::Result<PathBuf> {
338 fn sysctl() -> io::Result<PathBuf> {
339 unsafe {
340 let mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, -1, libc::KERN_PROC_PATHNAME];
341 let mut path_len: usize = 0;
342 cvt(libc::sysctl(
343 mib.as_ptr(),
344 mib.len() as libc::c_uint,
345 ptr::null_mut(),
346 &mut path_len,
347 ptr::null(),
348 0,
349 ))?;
350 if path_len <= 1 {
351 return Err(io::const_io_error!(
352 io::ErrorKind::Uncategorized,
353 "KERN_PROC_PATHNAME sysctl returned zero-length string",
354 ));
355 }
356 let mut path: Vec<u8> = Vec::with_capacity(path_len);
357 cvt(libc::sysctl(
358 mib.as_ptr(),
359 mib.len() as libc::c_uint,
360 path.as_ptr() as *mut libc::c_void,
361 &mut path_len,
362 ptr::null(),
363 0,
364 ))?;
365 path.set_len(path_len - 1); // chop off NUL
366 Ok(PathBuf::from(OsString::from_vec(path)))
367 }
368 }
369 fn procfs() -> io::Result<PathBuf> {
370 let curproc_exe = path::Path::new("/proc/curproc/exe");
371 if curproc_exe.is_file() {
372 return crate::fs::read_link(curproc_exe);
373 }
374 Err(io::const_io_error!(
375 io::ErrorKind::Uncategorized,
376 "/proc/curproc/exe doesn't point to regular file.",
377 ))
378 }
379 sysctl().or_else(|_| procfs())
380}
381
382#[cfg(target_os = "openbsd")]
383pub fn current_exe() -> io::Result<PathBuf> {
384 unsafe {
385 let mut mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, libc::getpid(), libc::KERN_PROC_ARGV];
386 let mib = mib.as_mut_ptr();
387 let mut argv_len = 0;
388 cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_len, ptr::null_mut(), 0))?;
389 let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize);
390 cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?;
391 argv.set_len(argv_len as usize);
392 if argv[0].is_null() {
393 return Err(io::const_io_error!(
394 io::ErrorKind::Uncategorized,
395 "no current exe available",
396 ));
397 }
398 let argv0 = CStr::from_ptr(argv[0]).to_bytes();
399 if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') {
400 crate::fs::canonicalize(OsStr::from_bytes(argv0))
401 } else {
402 Ok(PathBuf::from(OsStr::from_bytes(argv0)))
403 }
404 }
405}
406
407#[cfg(any(
408 target_os = "linux",
409 target_os = "hurd",
410 target_os = "android",
411 target_os = "emscripten"
412))]
413pub fn current_exe() -> io::Result<PathBuf> {
414 match crate::fs::read_link(path:"/proc/self/exe") {
415 Err(ref e: &Error) if e.kind() == io::ErrorKind::NotFound => Err(io::const_io_error!(
416 io::ErrorKind::Uncategorized,
417 "no /proc/self/exe available. Is /proc mounted?",
418 )),
419 other: Result => other,
420 }
421}
422
423#[cfg(target_os = "nto")]
424pub fn current_exe() -> io::Result<PathBuf> {
425 let mut e = crate::fs::read("/proc/self/exefile")?;
426 // Current versions of QNX Neutrino provide a null-terminated path.
427 // Ensure the trailing null byte is not returned here.
428 if let Some(0) = e.last() {
429 e.pop();
430 }
431 Ok(PathBuf::from(OsString::from_vec(e)))
432}
433
434#[cfg(any(
435 target_os = "macos",
436 target_os = "ios",
437 target_os = "watchos",
438 target_os = "visionos",
439 target_os = "tvos"
440))]
441pub fn current_exe() -> io::Result<PathBuf> {
442 unsafe {
443 let mut sz: u32 = 0;
444 libc::_NSGetExecutablePath(ptr::null_mut(), &mut sz);
445 if sz == 0 {
446 return Err(io::Error::last_os_error());
447 }
448 let mut v: Vec<u8> = Vec::with_capacity(sz as usize);
449 let err = libc::_NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
450 if err != 0 {
451 return Err(io::Error::last_os_error());
452 }
453 v.set_len(sz as usize - 1); // chop off trailing NUL
454 Ok(PathBuf::from(OsString::from_vec(v)))
455 }
456}
457
458#[cfg(any(target_os = "solaris", target_os = "illumos"))]
459pub fn current_exe() -> io::Result<PathBuf> {
460 if let Ok(path) = crate::fs::read_link("/proc/self/path/a.out") {
461 Ok(path)
462 } else {
463 unsafe {
464 let path = libc::getexecname();
465 if path.is_null() {
466 Err(io::Error::last_os_error())
467 } else {
468 let filename = CStr::from_ptr(path).to_bytes();
469 let path = PathBuf::from(<OsStr as OsStrExt>::from_bytes(filename));
470
471 // Prepend a current working directory to the path if
472 // it doesn't contain an absolute pathname.
473 if filename[0] == b'/' { Ok(path) } else { getcwd().map(|cwd| cwd.join(path)) }
474 }
475 }
476 }
477}
478
479#[cfg(target_os = "haiku")]
480pub fn current_exe() -> io::Result<PathBuf> {
481 unsafe {
482 let mut info: mem::MaybeUninit<libc::image_info> = mem::MaybeUninit::uninit();
483 let mut cookie: i32 = 0;
484 // the executable can be found at team id 0
485 let result = libc::_get_next_image_info(
486 0,
487 &mut cookie,
488 info.as_mut_ptr(),
489 mem::size_of::<libc::image_info>(),
490 );
491 if result != 0 {
492 use crate::io::ErrorKind;
493 Err(io::const_io_error!(ErrorKind::Uncategorized, "Error getting executable path"))
494 } else {
495 let name = CStr::from_ptr((*info.as_ptr()).name.as_ptr()).to_bytes();
496 Ok(PathBuf::from(OsStr::from_bytes(name)))
497 }
498 }
499}
500
501#[cfg(target_os = "redox")]
502pub fn current_exe() -> io::Result<PathBuf> {
503 crate::fs::read_to_string("sys:exe").map(PathBuf::from)
504}
505
506#[cfg(target_os = "l4re")]
507pub fn current_exe() -> io::Result<PathBuf> {
508 use crate::io::ErrorKind;
509 Err(io::const_io_error!(ErrorKind::Unsupported, "Not yet implemented!"))
510}
511
512#[cfg(target_os = "vxworks")]
513pub fn current_exe() -> io::Result<PathBuf> {
514 #[cfg(test)]
515 use realstd::env;
516
517 #[cfg(not(test))]
518 use crate::env;
519
520 let exe_path = env::args().next().unwrap();
521 let path = path::Path::new(&exe_path);
522 path.canonicalize()
523}
524
525#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
526pub fn current_exe() -> io::Result<PathBuf> {
527 super::unsupported::unsupported()
528}
529
530#[cfg(target_os = "fuchsia")]
531pub fn current_exe() -> io::Result<PathBuf> {
532 use crate::io::ErrorKind;
533
534 #[cfg(test)]
535 use realstd::env;
536
537 #[cfg(not(test))]
538 use crate::env;
539
540 let exe_path = env::args().next().ok_or(io::const_io_error!(
541 ErrorKind::Uncategorized,
542 "an executable path was not found because no arguments were provided through argv"
543 ))?;
544 let path = PathBuf::from(exe_path);
545
546 // Prepend the current working directory to the path if it's not absolute.
547 if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) }
548}
549
550pub struct Env {
551 iter: vec::IntoIter<(OsString, OsString)>,
552}
553
554// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
555pub struct EnvStrDebug<'a> {
556 slice: &'a [(OsString, OsString)],
557}
558
559impl fmt::Debug for EnvStrDebug<'_> {
560 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
561 let Self { slice: &&[(OsString, OsString)] } = self;
562 f&mut DebugList<'_, '_>.debug_list()
563 .entries(slice.iter().map(|(a: &OsString, b: &OsString)| (a.to_str().unwrap(), b.to_str().unwrap())))
564 .finish()
565 }
566}
567
568impl Env {
569 pub fn str_debug(&self) -> impl fmt::Debug + '_ {
570 let Self { iter: &IntoIter<(OsString, OsString)> } = self;
571 EnvStrDebug { slice: iter.as_slice() }
572 }
573}
574
575impl fmt::Debug for Env {
576 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577 let Self { iter: &IntoIter<(OsString, OsString)> } = self;
578 f.debug_list().entries(iter.as_slice()).finish()
579 }
580}
581
582impl !Send for Env {}
583impl !Sync for Env {}
584
585impl Iterator for Env {
586 type Item = (OsString, OsString);
587 fn next(&mut self) -> Option<(OsString, OsString)> {
588 self.iter.next()
589 }
590 fn size_hint(&self) -> (usize, Option<usize>) {
591 self.iter.size_hint()
592 }
593}
594
595#[cfg(target_os = "macos")]
596pub unsafe fn environ() -> *mut *const *const c_char {
597 libc::_NSGetEnviron() as *mut *const *const c_char
598}
599
600#[cfg(not(target_os = "macos"))]
601pub unsafe fn environ() -> *mut *const *const c_char {
602 extern "C" {
603 static mut environ: *const *const c_char;
604 }
605 ptr::addr_of_mut!(environ)
606}
607
608static ENV_LOCK: RwLock<()> = RwLock::new(());
609
610pub fn env_read_lock() -> impl Drop {
611 ENV_LOCK.read().unwrap_or_else(op:PoisonError::into_inner)
612}
613
614/// Returns a vector of (variable, value) byte-vector pairs for all the
615/// environment variables of the current process.
616pub fn env() -> Env {
617 unsafe {
618 let _guard = env_read_lock();
619 let mut environ = *environ();
620 let mut result = Vec::new();
621 if !environ.is_null() {
622 while !(*environ).is_null() {
623 if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
624 result.push(key_value);
625 }
626 environ = environ.add(1);
627 }
628 }
629 return Env { iter: result.into_iter() };
630 }
631
632 fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
633 // Strategy (copied from glibc): Variable name and value are separated
634 // by an ASCII equals sign '='. Since a variable name must not be
635 // empty, allow variable names starting with an equals sign. Skip all
636 // malformed lines.
637 if input.is_empty() {
638 return None;
639 }
640 let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
641 pos.map(|p| {
642 (
643 OsStringExt::from_vec(input[..p].to_vec()),
644 OsStringExt::from_vec(input[p + 1..].to_vec()),
645 )
646 })
647 }
648}
649
650pub fn getenv(k: &OsStr) -> Option<OsString> {
651 // environment variables with a nul byte can't be set, so their value is
652 // always None as well
653 run_with_cstrOption>(k.as_bytes(), &|k: &CStr| {
654 let _guard: impl Sized = env_read_lock();
655 let v: *const i8 = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
656
657 if v.is_null() {
658 Ok(None)
659 } else {
660 // SAFETY: `v` cannot be mutated while executing this line since we've a read lock
661 let bytes: Vec = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
662
663 Ok(Some(OsStringExt::from_vec(bytes)))
664 }
665 })
666 .ok()
667 .flatten()
668}
669
670pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
671 run_with_cstr(k.as_bytes(), &|k: &CStr| {
672 run_with_cstr(v.as_bytes(), &|v: &CStr| {
673 let _guard: Result, …> = ENV_LOCK.write();
674 cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(op:drop)
675 })
676 })
677}
678
679pub fn unsetenv(n: &OsStr) -> io::Result<()> {
680 run_with_cstr(n.as_bytes(), &|nbuf: &CStr| {
681 let _guard: Result, …> = ENV_LOCK.write();
682 cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(op:drop)
683 })
684}
685
686#[cfg(not(target_os = "espidf"))]
687pub fn page_size() -> usize {
688 unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
689}
690
691pub fn temp_dir() -> PathBuf {
692 crate::env::var_os(key:"TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
693 if cfg!(target_os = "android") {
694 PathBuf::from("/data/local/tmp")
695 } else {
696 PathBuf::from("/tmp")
697 }
698 })
699}
700
701pub fn home_dir() -> Option<PathBuf> {
702 return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from);
703
704 #[cfg(any(
705 target_os = "android",
706 target_os = "ios",
707 target_os = "tvos",
708 target_os = "watchos",
709 target_os = "visionos",
710 target_os = "emscripten",
711 target_os = "redox",
712 target_os = "vxworks",
713 target_os = "espidf",
714 target_os = "horizon",
715 target_os = "vita",
716 ))]
717 unsafe fn fallback() -> Option<OsString> {
718 None
719 }
720 #[cfg(not(any(
721 target_os = "android",
722 target_os = "ios",
723 target_os = "tvos",
724 target_os = "watchos",
725 target_os = "visionos",
726 target_os = "emscripten",
727 target_os = "redox",
728 target_os = "vxworks",
729 target_os = "espidf",
730 target_os = "horizon",
731 target_os = "vita",
732 )))]
733 unsafe fn fallback() -> Option<OsString> {
734 let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
735 n if n < 0 => 512 as usize,
736 n => n as usize,
737 };
738 let mut buf = Vec::with_capacity(amt);
739 let mut passwd: libc::passwd = mem::zeroed();
740 let mut result = ptr::null_mut();
741 match libc::getpwuid_r(
742 libc::getuid(),
743 &mut passwd,
744 buf.as_mut_ptr(),
745 buf.capacity(),
746 &mut result,
747 ) {
748 0 if !result.is_null() => {
749 let ptr = passwd.pw_dir as *const _;
750 let bytes = CStr::from_ptr(ptr).to_bytes().to_vec();
751 Some(OsStringExt::from_vec(bytes))
752 }
753 _ => None,
754 }
755 }
756}
757
758pub fn exit(code: i32) -> ! {
759 unsafe { libc::exit(code as c_int) }
760}
761
762pub fn getpid() -> u32 {
763 unsafe { libc::getpid() as u32 }
764}
765
766pub fn getppid() -> u32 {
767 unsafe { libc::getppid() as u32 }
768}
769
770#[cfg(all(target_os = "linux", target_env = "gnu"))]
771pub fn glibc_version() -> Option<(usize, usize)> {
772 extern "C" {
773 fn gnu_get_libc_version() -> *const libc::c_char;
774 }
775 let version_cstr: &CStr = unsafe { CStr::from_ptr(gnu_get_libc_version()) };
776 if let Ok(version_str: &str) = version_cstr.to_str() {
777 parse_glibc_version(version_str)
778 } else {
779 None
780 }
781}
782
783// Returns Some((major, minor)) if the string is a valid "x.y" version,
784// ignoring any extra dot-separated parts. Otherwise return None.
785#[cfg(all(target_os = "linux", target_env = "gnu"))]
786fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
787 let mut parsed_ints: impl Iterator> = version.split('.').map(str::parse::<usize>).fuse();
788 match (parsed_ints.next(), parsed_ints.next()) {
789 (Some(Ok(major: usize)), Some(Ok(minor: usize))) => Some((major, minor)),
790 _ => None,
791 }
792}
793