1#![allow(unused_imports)] #[cfg(test)]
6mod tests;
7
8use libc::{c_char, c_int, c_void};
9
10use crate::error::Error as StdError;
11use crate::ffi::{CStr, OsStr, OsString};
12use crate::os::unix::prelude::*;
13use crate::path::{self, PathBuf};
14use crate::sys::common::small_c_string::run_path_with_cstr;
15use crate::sys::cvt;
16use crate::{fmt, io, iter, mem, ptr, slice, str};
17
18const TMPBUF_SZ: usize = 128;
19
20const PATH_SEPARATOR: u8 = cfg_select! {
21 target_os = "redox" => b';',
22 _ => b':',
23};
24
25unsafe extern "C" {
26 #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))]
27 #[cfg_attr(
28 any(
29 target_os = "linux",
30 target_os = "emscripten",
31 target_os = "fuchsia",
32 target_os = "l4re",
33 target_os = "hurd",
34 ),
35 link_name = "__errno_location"
36 )]
37 #[cfg_attr(
38 any(
39 target_os = "netbsd",
40 target_os = "openbsd",
41 target_os = "cygwin",
42 target_os = "android",
43 target_os = "redox",
44 target_os = "nuttx",
45 target_env = "newlib"
46 ),
47 link_name = "__errno"
48 )]
49 #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")]
50 #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")]
51 #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")]
52 #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
53 #[cfg_attr(target_os = "aix", link_name = "_Errno")]
54 #[unsafe(ffi_const)]
56 pub safe fn errno_location() -> *mut c_int;
57}
58
59#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))]
61#[inline]
62pub fn errno() -> i32 {
63 unsafe { (*errno_location()) as i32 }
64}
65
66#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))]
69#[allow(dead_code)] #[inline]
71pub fn set_errno(e: i32) {
72 unsafe { *errno_location() = e as c_int }
73}
74
75#[cfg(target_os = "vxworks")]
76#[inline]
77pub fn errno() -> i32 {
78 unsafe { libc::errnoGet() }
79}
80
81#[cfg(target_os = "rtems")]
82#[inline]
83pub fn errno() -> i32 {
84 unsafe extern "C" {
85 #[thread_local]
86 static _tls_errno: c_int;
87 }
88
89 unsafe { _tls_errno as i32 }
90}
91
92#[cfg(target_os = "dragonfly")]
93#[inline]
94pub fn errno() -> i32 {
95 unsafe extern "C" {
96 #[thread_local]
97 static errno: c_int;
98 }
99
100 unsafe { errno as i32 }
101}
102
103#[cfg(target_os = "dragonfly")]
104#[allow(dead_code)]
105#[inline]
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
117pub 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 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 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 = run_path_with_cstr(p, &|p| unsafe { Ok(libc::chdir(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(b))
200 }
201 fn is_separator(b: &u8) -> bool {
202 *b == PATH_SEPARATOR
203 }
204 let unparsed = unparsed.as_bytes();
205 SplitPaths {
206 iter: unparsed
207 .split(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::new();
231
232 for (i, path) in paths.enumerate() {
233 let path = 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 if let Some(pstr) = path.to_str()
277 && pstr.contains("/")
278 {
279 return getcwd().map(|cwd| cwd.join(path))?.canonicalize();
280 }
281 if let Some(p) = env::var_os(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); 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); 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("/proc/self/exe") {
412 Err(ref e) 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 => 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 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); 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 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 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 if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) }
545}
546
547#[cfg(not(target_os = "espidf"))]
548pub fn page_size() -> usize {
549 unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
550}
551
552#[cfg(all(target_vendor = "apple", not(miri)))]
561fn confstr(key: c_int, size_hint: Option<usize>) -> io::Result<OsString> {
562 let mut buf: Vec<u8> = Vec::with_capacity(0);
563 let mut bytes_needed_including_nul = size_hint
564 .unwrap_or_else(|| {
565 unsafe { libc::confstr(key, core::ptr::null_mut(), 0) }
570 })
571 .max(1);
572 while bytes_needed_including_nul > buf.capacity() {
577 buf.reserve(bytes_needed_including_nul);
583 bytes_needed_including_nul =
590 unsafe { libc::confstr(key, buf.as_mut_ptr().cast::<c_char>(), buf.capacity()) };
591 }
592 if bytes_needed_including_nul == 0 {
594 return Err(io::Error::last_os_error());
595 }
596 unsafe {
600 buf.set_len(bytes_needed_including_nul);
601 let last_byte = buf.pop();
603 assert_eq!(last_byte, Some(0), "`confstr` provided a string which wasn't nul-terminated");
605 };
606 Ok(OsString::from_vec(buf))
607}
608
609#[cfg(all(target_vendor = "apple", not(miri)))]
610fn darwin_temp_dir() -> PathBuf {
611 confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64)).map(PathBuf::from).unwrap_or_else(|_| {
612 PathBuf::from("/tmp")
615 })
616}
617
618pub fn temp_dir() -> PathBuf {
619 crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
620 cfg_select! {
621 all(target_vendor = "apple", not(miri)) => darwin_temp_dir(),
622 target_os = "android" => PathBuf::from("/data/local/tmp"),
623 _ => PathBuf::from("/tmp"),
624 }
625 })
626}
627
628pub fn home_dir() -> Option<PathBuf> {
629 return crate::env::var_os("HOME")
630 .filter(|s| !s.is_empty())
631 .or_else(|| unsafe { fallback() })
632 .map(PathBuf::from);
633
634 #[cfg(any(
635 target_os = "android",
636 target_os = "emscripten",
637 target_os = "redox",
638 target_os = "vxworks",
639 target_os = "espidf",
640 target_os = "horizon",
641 target_os = "vita",
642 target_os = "nuttx",
643 all(target_vendor = "apple", not(target_os = "macos")),
644 ))]
645 unsafe fn fallback() -> Option<OsString> {
646 None
647 }
648 #[cfg(not(any(
649 target_os = "android",
650 target_os = "emscripten",
651 target_os = "redox",
652 target_os = "vxworks",
653 target_os = "espidf",
654 target_os = "horizon",
655 target_os = "vita",
656 target_os = "nuttx",
657 all(target_vendor = "apple", not(target_os = "macos")),
658 )))]
659 unsafe fn fallback() -> Option<OsString> {
660 let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
661 n if n < 0 => 512 as usize,
662 n => n as usize,
663 };
664 let mut buf = Vec::with_capacity(amt);
665 let mut p = mem::MaybeUninit::<libc::passwd>::uninit();
666 let mut result = ptr::null_mut();
667 match libc::getpwuid_r(
668 libc::getuid(),
669 p.as_mut_ptr(),
670 buf.as_mut_ptr(),
671 buf.capacity(),
672 &mut result,
673 ) {
674 0 if !result.is_null() => {
675 let ptr = (*result).pw_dir as *const _;
676 let bytes = CStr::from_ptr(ptr).to_bytes().to_vec();
677 Some(OsStringExt::from_vec(bytes))
678 }
679 _ => None,
680 }
681 }
682}
683
684pub fn exit(code: i32) -> ! {
685 crate::sys::exit_guard::unique_thread_exit();
686 unsafe { libc::exit(code as c_int) }
687}
688
689pub fn getpid() -> u32 {
690 unsafe { libc::getpid() as u32 }
691}
692
693pub fn getppid() -> u32 {
694 unsafe { libc::getppid() as u32 }
695}
696
697#[cfg(all(target_os = "linux", target_env = "gnu"))]
698pub fn glibc_version() -> Option<(usize, usize)> {
699 unsafe extern "C" {
700 fn gnu_get_libc_version() -> *const libc::c_char;
701 }
702 let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) };
703 if let Ok(version_str) = version_cstr.to_str() {
704 parse_glibc_version(version_str)
705 } else {
706 None
707 }
708}
709
710#[cfg(all(target_os = "linux", target_env = "gnu"))]
713fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
714 let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse();
715 match (parsed_ints.next(), parsed_ints.next()) {
716 (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)),
717 _ => None,
718 }
719}