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