1#![allow(unused_imports)] #[cfg(test)]
6mod tests;
7
8use libc::{c_char, c_int, c_void};
9
10use crate::ffi::{CStr, OsStr, OsString};
11use crate::os::unix::prelude::*;
12use crate::path::{self, PathBuf};
13use crate::sys::common::small_c_string::run_path_with_cstr;
14use crate::sys::cvt;
15use crate::{fmt, io, iter, mem, ptr, slice, str};
16
17const TMPBUF_SZ: usize = 128;
18
19const PATH_SEPARATOR: u8 = if cfg!(target_os = "redox") { b';' } else { b':' };
20
21unsafe extern "C" {
22 #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))]
23 #[cfg_attr(
24 any(
25 target_os = "linux",
26 target_os = "emscripten",
27 target_os = "fuchsia",
28 target_os = "l4re",
29 target_os = "hurd",
30 ),
31 link_name = "__errno_location"
32 )]
33 #[cfg_attr(
34 any(
35 target_os = "netbsd",
36 target_os = "openbsd",
37 target_os = "cygwin",
38 target_os = "android",
39 target_os = "redox",
40 target_os = "nuttx",
41 target_env = "newlib"
42 ),
43 link_name = "__errno"
44 )]
45 #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")]
46 #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")]
47 #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")]
48 #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
49 #[cfg_attr(target_os = "aix", link_name = "_Errno")]
50 #[unsafe(ffi_const)]
52 pub safe fn errno_location() -> *mut c_int;
53}
54
55#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))]
57#[inline]
58pub fn errno() -> i32 {
59 unsafe { (*errno_location()) as i32 }
60}
61
62#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))]
65#[allow(dead_code)] #[inline]
67pub fn set_errno(e: i32) {
68 unsafe { *errno_location() = e as c_int }
69}
70
71#[cfg(target_os = "vxworks")]
72#[inline]
73pub fn errno() -> i32 {
74 unsafe { libc::errnoGet() }
75}
76
77#[cfg(target_os = "rtems")]
78#[inline]
79pub fn errno() -> i32 {
80 unsafe extern "C" {
81 #[thread_local]
82 static _tls_errno: c_int;
83 }
84
85 unsafe { _tls_errno as i32 }
86}
87
88#[cfg(target_os = "dragonfly")]
89#[inline]
90pub fn errno() -> i32 {
91 unsafe extern "C" {
92 #[thread_local]
93 static errno: c_int;
94 }
95
96 unsafe { errno as i32 }
97}
98
99#[cfg(target_os = "dragonfly")]
100#[allow(dead_code)]
101#[inline]
102pub fn set_errno(e: i32) {
103 unsafe extern "C" {
104 #[thread_local]
105 static mut errno: c_int;
106 }
107
108 unsafe {
109 errno = e;
110 }
111}
112
113pub fn error_string(errno: i32) -> String {
115 unsafe extern "C" {
116 #[cfg_attr(
117 all(
118 any(
119 target_os = "linux",
120 target_os = "hurd",
121 target_env = "newlib",
122 target_os = "cygwin"
123 ),
124 not(target_env = "ohos")
125 ),
126 link_name = "__xpg_strerror_r"
127 )]
128 fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int;
129 }
130
131 let mut buf = [0 as c_char; TMPBUF_SZ];
132
133 let p = buf.as_mut_ptr();
134 unsafe {
135 if strerror_r(errno as c_int, p, buf.len()) < 0 {
136 panic!("strerror_r failure");
137 }
138
139 let p = p as *const _;
140 String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into()
143 }
144}
145
146#[cfg(target_os = "espidf")]
147pub fn getcwd() -> io::Result<PathBuf> {
148 Ok(PathBuf::from("/"))
149}
150
151#[cfg(not(target_os = "espidf"))]
152pub fn getcwd() -> io::Result<PathBuf> {
153 let mut buf = Vec::with_capacity(512);
154 loop {
155 unsafe {
156 let ptr = buf.as_mut_ptr() as *mut libc::c_char;
157 if !libc::getcwd(ptr, buf.capacity()).is_null() {
158 let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len();
159 buf.set_len(len);
160 buf.shrink_to_fit();
161 return Ok(PathBuf::from(OsString::from_vec(buf)));
162 } else {
163 let error = io::Error::last_os_error();
164 if error.raw_os_error() != Some(libc::ERANGE) {
165 return Err(error);
166 }
167 }
168
169 let cap = buf.capacity();
172 buf.set_len(cap);
173 buf.reserve(1);
174 }
175 }
176}
177
178#[cfg(target_os = "espidf")]
179pub fn chdir(_p: &path::Path) -> io::Result<()> {
180 super::unsupported::unsupported()
181}
182
183#[cfg(not(target_os = "espidf"))]
184pub fn chdir(p: &path::Path) -> io::Result<()> {
185 let result = run_path_with_cstr(p, &|p| unsafe { Ok(libc::chdir(p.as_ptr())) })?;
186 if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) }
187}
188
189pub type SplitPaths<'a> = impl Iterator<Item = PathBuf>;
190
191#[define_opaque(SplitPaths)]
192pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
193 unparsed
194 .as_bytes()
195 .split(|&b| b == PATH_SEPARATOR)
196 .map(|part| PathBuf::from(OsStr::from_bytes(part)))
197}
198
199#[derive(Debug)]
200pub struct JoinPathsError;
201
202pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
203where
204 I: Iterator<Item = T>,
205 T: AsRef<OsStr>,
206{
207 let mut joined = Vec::new();
208
209 for (i, path) in paths.enumerate() {
210 let path = path.as_ref().as_bytes();
211 if i > 0 {
212 joined.push(PATH_SEPARATOR)
213 }
214 if path.contains(&PATH_SEPARATOR) {
215 return Err(JoinPathsError);
216 }
217 joined.extend_from_slice(path);
218 }
219 Ok(OsStringExt::from_vec(joined))
220}
221
222impl fmt::Display for JoinPathsError {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 write!(f, "path segment contains separator `{}`", char::from(PATH_SEPARATOR))
225 }
226}
227
228impl crate::error::Error for JoinPathsError {}
229
230#[cfg(target_os = "aix")]
231pub fn current_exe() -> io::Result<PathBuf> {
232 #[cfg(test)]
233 use realstd::env;
234
235 #[cfg(not(test))]
236 use crate::env;
237 use crate::io::ErrorKind;
238
239 let exe_path = env::args().next().ok_or(io::const_error!(
240 ErrorKind::NotFound,
241 "an executable path was not found because no arguments were provided through argv",
242 ))?;
243 let path = PathBuf::from(exe_path);
244 if path.is_absolute() {
245 return path.canonicalize();
246 }
247 if let Some(pstr) = path.to_str()
249 && pstr.contains("/")
250 {
251 return getcwd().map(|cwd| cwd.join(path))?.canonicalize();
252 }
253 if let Some(p) = env::var_os(OsStr::from_bytes("PATH".as_bytes())) {
255 for search_path in split_paths(&p) {
256 let pb = search_path.join(&path);
257 if pb.is_file()
258 && let Ok(metadata) = crate::fs::metadata(&pb)
259 && metadata.permissions().mode() & 0o111 != 0
260 {
261 return pb.canonicalize();
262 }
263 }
264 }
265 Err(io::const_error!(ErrorKind::NotFound, "an executable path was not found"))
266}
267
268#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
269pub fn current_exe() -> io::Result<PathBuf> {
270 unsafe {
271 let mut mib = [
272 libc::CTL_KERN as c_int,
273 libc::KERN_PROC as c_int,
274 libc::KERN_PROC_PATHNAME as c_int,
275 -1 as c_int,
276 ];
277 let mut sz = 0;
278 cvt(libc::sysctl(
279 mib.as_mut_ptr(),
280 mib.len() as libc::c_uint,
281 ptr::null_mut(),
282 &mut sz,
283 ptr::null_mut(),
284 0,
285 ))?;
286 if sz == 0 {
287 return Err(io::Error::last_os_error());
288 }
289 let mut v: Vec<u8> = Vec::with_capacity(sz);
290 cvt(libc::sysctl(
291 mib.as_mut_ptr(),
292 mib.len() as libc::c_uint,
293 v.as_mut_ptr() as *mut libc::c_void,
294 &mut sz,
295 ptr::null_mut(),
296 0,
297 ))?;
298 if sz == 0 {
299 return Err(io::Error::last_os_error());
300 }
301 v.set_len(sz - 1); Ok(PathBuf::from(OsString::from_vec(v)))
303 }
304}
305
306#[cfg(target_os = "netbsd")]
307pub fn current_exe() -> io::Result<PathBuf> {
308 fn sysctl() -> io::Result<PathBuf> {
309 unsafe {
310 let mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, -1, libc::KERN_PROC_PATHNAME];
311 let mut path_len: usize = 0;
312 cvt(libc::sysctl(
313 mib.as_ptr(),
314 mib.len() as libc::c_uint,
315 ptr::null_mut(),
316 &mut path_len,
317 ptr::null(),
318 0,
319 ))?;
320 if path_len <= 1 {
321 return Err(io::const_error!(
322 io::ErrorKind::Uncategorized,
323 "KERN_PROC_PATHNAME sysctl returned zero-length string",
324 ));
325 }
326 let mut path: Vec<u8> = Vec::with_capacity(path_len);
327 cvt(libc::sysctl(
328 mib.as_ptr(),
329 mib.len() as libc::c_uint,
330 path.as_ptr() as *mut libc::c_void,
331 &mut path_len,
332 ptr::null(),
333 0,
334 ))?;
335 path.set_len(path_len - 1); Ok(PathBuf::from(OsString::from_vec(path)))
337 }
338 }
339 fn procfs() -> io::Result<PathBuf> {
340 let curproc_exe = path::Path::new("/proc/curproc/exe");
341 if curproc_exe.is_file() {
342 return crate::fs::read_link(curproc_exe);
343 }
344 Err(io::const_error!(
345 io::ErrorKind::Uncategorized,
346 "/proc/curproc/exe doesn't point to regular file.",
347 ))
348 }
349 sysctl().or_else(|_| procfs())
350}
351
352#[cfg(target_os = "openbsd")]
353pub fn current_exe() -> io::Result<PathBuf> {
354 unsafe {
355 let mut mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, libc::getpid(), libc::KERN_PROC_ARGV];
356 let mib = mib.as_mut_ptr();
357 let mut argv_len = 0;
358 cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_len, ptr::null_mut(), 0))?;
359 let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize);
360 cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?;
361 argv.set_len(argv_len as usize);
362 if argv[0].is_null() {
363 return Err(io::const_error!(io::ErrorKind::Uncategorized, "no current exe available"));
364 }
365 let argv0 = CStr::from_ptr(argv[0]).to_bytes();
366 if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') {
367 crate::fs::canonicalize(OsStr::from_bytes(argv0))
368 } else {
369 Ok(PathBuf::from(OsStr::from_bytes(argv0)))
370 }
371 }
372}
373
374#[cfg(any(
375 target_os = "linux",
376 target_os = "cygwin",
377 target_os = "hurd",
378 target_os = "android",
379 target_os = "nuttx",
380 target_os = "emscripten"
381))]
382pub fn current_exe() -> io::Result<PathBuf> {
383 match crate::fs::read_link("/proc/self/exe") {
384 Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::const_error!(
385 io::ErrorKind::Uncategorized,
386 "no /proc/self/exe available. Is /proc mounted?",
387 )),
388 other => other,
389 }
390}
391
392#[cfg(target_os = "nto")]
393pub fn current_exe() -> io::Result<PathBuf> {
394 let mut e = crate::fs::read("/proc/self/exefile")?;
395 if let Some(0) = e.last() {
398 e.pop();
399 }
400 Ok(PathBuf::from(OsString::from_vec(e)))
401}
402
403#[cfg(target_vendor = "apple")]
404pub fn current_exe() -> io::Result<PathBuf> {
405 unsafe {
406 let mut sz: u32 = 0;
407 #[expect(deprecated)]
408 libc::_NSGetExecutablePath(ptr::null_mut(), &mut sz);
409 if sz == 0 {
410 return Err(io::Error::last_os_error());
411 }
412 let mut v: Vec<u8> = Vec::with_capacity(sz as usize);
413 #[expect(deprecated)]
414 let err = libc::_NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
415 if err != 0 {
416 return Err(io::Error::last_os_error());
417 }
418 v.set_len(sz as usize - 1); Ok(PathBuf::from(OsString::from_vec(v)))
420 }
421}
422
423#[cfg(any(target_os = "solaris", target_os = "illumos"))]
424pub fn current_exe() -> io::Result<PathBuf> {
425 if let Ok(path) = crate::fs::read_link("/proc/self/path/a.out") {
426 Ok(path)
427 } else {
428 unsafe {
429 let path = libc::getexecname();
430 if path.is_null() {
431 Err(io::Error::last_os_error())
432 } else {
433 let filename = CStr::from_ptr(path).to_bytes();
434 let path = PathBuf::from(<OsStr as OsStrExt>::from_bytes(filename));
435
436 if filename[0] == b'/' { Ok(path) } else { getcwd().map(|cwd| cwd.join(path)) }
439 }
440 }
441 }
442}
443
444#[cfg(target_os = "haiku")]
445pub fn current_exe() -> io::Result<PathBuf> {
446 let mut name = vec![0; libc::PATH_MAX as usize];
447 unsafe {
448 let result = libc::find_path(
449 crate::ptr::null_mut(),
450 libc::B_FIND_PATH_IMAGE_PATH,
451 crate::ptr::null_mut(),
452 name.as_mut_ptr(),
453 name.len(),
454 );
455 if result != libc::B_OK {
456 use crate::io::ErrorKind;
457 Err(io::const_error!(ErrorKind::Uncategorized, "error getting executable path"))
458 } else {
459 let name = CStr::from_ptr(name.as_ptr()).to_bytes();
461 Ok(PathBuf::from(OsStr::from_bytes(name)))
462 }
463 }
464}
465
466#[cfg(target_os = "redox")]
467pub fn current_exe() -> io::Result<PathBuf> {
468 crate::fs::read_to_string("/scheme/sys/exe").map(PathBuf::from)
469}
470
471#[cfg(target_os = "rtems")]
472pub fn current_exe() -> io::Result<PathBuf> {
473 crate::fs::read_to_string("sys:exe").map(PathBuf::from)
474}
475
476#[cfg(target_os = "l4re")]
477pub fn current_exe() -> io::Result<PathBuf> {
478 use crate::io::ErrorKind;
479 Err(io::const_error!(ErrorKind::Unsupported, "not yet implemented!"))
480}
481
482#[cfg(target_os = "vxworks")]
483pub fn current_exe() -> io::Result<PathBuf> {
484 #[cfg(test)]
485 use realstd::env;
486
487 #[cfg(not(test))]
488 use crate::env;
489
490 let exe_path = env::args().next().unwrap();
491 let path = path::Path::new(&exe_path);
492 path.canonicalize()
493}
494
495#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
496pub fn current_exe() -> io::Result<PathBuf> {
497 super::unsupported::unsupported()
498}
499
500#[cfg(target_os = "fuchsia")]
501pub fn current_exe() -> io::Result<PathBuf> {
502 #[cfg(test)]
503 use realstd::env;
504
505 #[cfg(not(test))]
506 use crate::env;
507 use crate::io::ErrorKind;
508
509 let exe_path = env::args().next().ok_or(io::const_error!(
510 ErrorKind::Uncategorized,
511 "an executable path was not found because no arguments were provided through argv",
512 ))?;
513 let path = PathBuf::from(exe_path);
514
515 if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) }
517}
518
519#[cfg(not(target_os = "espidf"))]
520pub fn page_size() -> usize {
521 unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
522}
523
524#[cfg(all(target_vendor = "apple", not(miri)))]
533fn confstr(key: c_int, size_hint: Option<usize>) -> io::Result<OsString> {
534 let mut buf: Vec<u8> = Vec::with_capacity(0);
535 let mut bytes_needed_including_nul = size_hint
536 .unwrap_or_else(|| {
537 unsafe { libc::confstr(key, core::ptr::null_mut(), 0) }
542 })
543 .max(1);
544 while bytes_needed_including_nul > buf.capacity() {
549 buf.reserve(bytes_needed_including_nul);
555 bytes_needed_including_nul =
562 unsafe { libc::confstr(key, buf.as_mut_ptr().cast::<c_char>(), buf.capacity()) };
563 }
564 if bytes_needed_including_nul == 0 {
566 return Err(io::Error::last_os_error());
567 }
568 unsafe {
572 buf.set_len(bytes_needed_including_nul);
573 let last_byte = buf.pop();
575 assert_eq!(last_byte, Some(0), "`confstr` provided a string which wasn't nul-terminated");
577 };
578 Ok(OsString::from_vec(buf))
579}
580
581#[cfg(all(target_vendor = "apple", not(miri)))]
582fn darwin_temp_dir() -> PathBuf {
583 confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64)).map(PathBuf::from).unwrap_or_else(|_| {
584 PathBuf::from("/tmp")
587 })
588}
589
590pub fn temp_dir() -> PathBuf {
591 crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
592 cfg_select! {
593 all(target_vendor = "apple", not(miri)) => darwin_temp_dir(),
594 target_os = "android" => PathBuf::from("/data/local/tmp"),
595 _ => PathBuf::from("/tmp"),
596 }
597 })
598}
599
600pub fn home_dir() -> Option<PathBuf> {
601 return crate::env::var_os("HOME")
602 .filter(|s| !s.is_empty())
603 .or_else(|| unsafe { fallback() })
604 .map(PathBuf::from);
605
606 #[cfg(any(
607 target_os = "android",
608 target_os = "emscripten",
609 target_os = "redox",
610 target_os = "vxworks",
611 target_os = "espidf",
612 target_os = "horizon",
613 target_os = "vita",
614 target_os = "nuttx",
615 all(target_vendor = "apple", not(target_os = "macos")),
616 ))]
617 unsafe fn fallback() -> Option<OsString> {
618 None
619 }
620 #[cfg(not(any(
621 target_os = "android",
622 target_os = "emscripten",
623 target_os = "redox",
624 target_os = "vxworks",
625 target_os = "espidf",
626 target_os = "horizon",
627 target_os = "vita",
628 target_os = "nuttx",
629 all(target_vendor = "apple", not(target_os = "macos")),
630 )))]
631 unsafe fn fallback() -> Option<OsString> {
632 let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
633 n if n < 0 => 512 as usize,
634 n => n as usize,
635 };
636 let mut buf = Vec::with_capacity(amt);
637 let mut p = mem::MaybeUninit::<libc::passwd>::uninit();
638 let mut result = ptr::null_mut();
639 match libc::getpwuid_r(
640 libc::getuid(),
641 p.as_mut_ptr(),
642 buf.as_mut_ptr(),
643 buf.capacity(),
644 &mut result,
645 ) {
646 0 if !result.is_null() => {
647 let ptr = (*result).pw_dir as *const _;
648 let bytes = CStr::from_ptr(ptr).to_bytes().to_vec();
649 Some(OsStringExt::from_vec(bytes))
650 }
651 _ => None,
652 }
653 }
654}
655
656pub fn exit(code: i32) -> ! {
657 crate::sys::exit_guard::unique_thread_exit();
658 unsafe { libc::exit(code as c_int) }
659}
660
661pub fn getpid() -> u32 {
662 unsafe { libc::getpid() as u32 }
663}
664
665pub fn getppid() -> u32 {
666 unsafe { libc::getppid() as u32 }
667}
668
669#[cfg(all(target_os = "linux", target_env = "gnu"))]
670pub fn glibc_version() -> Option<(usize, usize)> {
671 unsafe extern "C" {
672 fn gnu_get_libc_version() -> *const libc::c_char;
673 }
674 let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) };
675 if let Ok(version_str) = version_cstr.to_str() {
676 parse_glibc_version(version_str)
677 } else {
678 None
679 }
680}
681
682#[cfg(all(target_os = "linux", target_env = "gnu"))]
685fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
686 let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse();
687 match (parsed_ints.next(), parsed_ints.next()) {
688 (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)),
689 _ => None,
690 }
691}