1 //! Implementation of `std::os` functionality for unix systems
3 #![allow(unused_imports)] // lots of cfg code here
8 use crate::os
::unix
::prelude
::*;
10 use crate::error
::Error
as StdError
;
11 use crate::ffi
::{CStr, CString, OsStr, OsString}
;
16 use crate::path
::{self, PathBuf}
;
22 use crate::sys
::memchr
;
23 use crate::sys_common
::rwlock
::{StaticRwLock, StaticRwLockReadGuard}
;
26 #[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
27 use crate::sys
::weak
::weak
;
29 use libc
::{c_char, c_int, c_void}
;
31 const TMPBUF_SZ
: usize = 128;
34 if #[cfg(target_os = "redox")] {
35 const PATH_SEPARATOR
: u8 = b'
;'
;
37 const PATH_SEPARATOR
: u8 = b'
:'
;
42 #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))]
46 target_os
= "emscripten",
47 target_os
= "fuchsia",
50 link_name
= "__errno_location"
55 target_os
= "openbsd",
56 target_os
= "android",
62 #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")]
64 any(target_os
= "macos", target_os
= "ios", target_os
= "freebsd"),
67 #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
68 fn errno_location() -> *mut c_int
;
71 /// Returns the platform-specific value of errno
72 #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))]
73 pub fn errno() -> i32 {
74 unsafe { (*errno_location()) as i32 }
77 /// Sets the platform-specific value of errno
78 #[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall!
79 #[allow(dead_code)] // but not all target cfgs actually end up using it
80 pub fn set_errno(e
: i32) {
81 unsafe { *errno_location() = e as c_int }
84 #[cfg(target_os = "vxworks")]
85 pub fn errno() -> i32 {
86 unsafe { libc::errnoGet() }
89 #[cfg(target_os = "dragonfly")]
90 pub fn errno() -> i32 {
96 unsafe { errno as i32 }
99 #[cfg(target_os = "dragonfly")]
101 pub fn set_errno(e
: i32) {
104 static mut errno
: c_int
;
112 /// Gets a detailed string description for the given error number.
113 pub fn error_string(errno
: i32) -> String
{
115 #[cfg_attr(any(target_os = "linux", target_env = "newlib"), link_name = "__xpg_strerror_r")]
116 fn strerror_r(errnum
: c_int
, buf
: *mut c_char
, buflen
: libc
::size_t
) -> c_int
;
119 let mut buf
= [0 as c_char
; TMPBUF_SZ
];
121 let p
= buf
.as_mut_ptr();
123 if strerror_r(errno
as c_int
, p
, buf
.len()) < 0 {
124 panic
!("strerror_r failure");
127 let p
= p
as *const _
;
128 str::from_utf8(CStr
::from_ptr(p
).to_bytes()).unwrap().to_owned()
132 #[cfg(target_os = "espidf")]
133 pub fn getcwd() -> io
::Result
<PathBuf
> {
134 Ok(PathBuf
::from("/"))
137 #[cfg(not(target_os = "espidf"))]
138 pub fn getcwd() -> io
::Result
<PathBuf
> {
139 let mut buf
= Vec
::with_capacity(512);
142 let ptr
= buf
.as_mut_ptr() as *mut libc
::c_char
;
143 if !libc
::getcwd(ptr
, buf
.capacity()).is_null() {
144 let len
= CStr
::from_ptr(buf
.as_ptr() as *const libc
::c_char
).to_bytes().len();
147 return Ok(PathBuf
::from(OsString
::from_vec(buf
)));
149 let error
= io
::Error
::last_os_error();
150 if error
.raw_os_error() != Some(libc
::ERANGE
) {
155 // Trigger the internal buffer resizing logic of `Vec` by requiring
156 // more space than the current capacity.
157 let cap
= buf
.capacity();
164 #[cfg(target_os = "espidf")]
165 pub fn chdir(p
: &path
::Path
) -> io
::Result
<()> {
166 super::unsupported
::unsupported()
169 #[cfg(not(target_os = "espidf"))]
170 pub fn chdir(p
: &path
::Path
) -> io
::Result
<()> {
171 let p
: &OsStr
= p
.as_ref();
172 let p
= CString
::new(p
.as_bytes())?
;
173 if unsafe { libc::chdir(p.as_ptr()) }
!= 0 {
174 return Err(io
::Error
::last_os_error());
179 pub struct SplitPaths
<'a
> {
180 iter
: iter
::Map
<slice
::Split
<'a
, u8, fn(&u8) -> bool
>, fn(&'a
[u8]) -> PathBuf
>,
183 pub fn split_paths(unparsed
: &OsStr
) -> SplitPaths
<'_
> {
184 fn bytes_to_path(b
: &[u8]) -> PathBuf
{
185 PathBuf
::from(<OsStr
as OsStrExt
>::from_bytes(b
))
187 fn is_separator(b
: &u8) -> bool
{
190 let unparsed
= unparsed
.as_bytes();
193 .split(is_separator
as fn(&u8) -> bool
)
194 .map(bytes_to_path
as fn(&[u8]) -> PathBuf
),
198 impl<'a
> Iterator
for SplitPaths
<'a
> {
200 fn next(&mut self) -> Option
<PathBuf
> {
203 fn size_hint(&self) -> (usize, Option
<usize>) {
204 self.iter
.size_hint()
209 pub struct JoinPathsError
;
211 pub fn join_paths
<I
, T
>(paths
: I
) -> Result
<OsString
, JoinPathsError
>
213 I
: Iterator
<Item
= T
>,
216 let mut joined
= Vec
::new();
218 for (i
, path
) in paths
.enumerate() {
219 let path
= path
.as_ref().as_bytes();
221 joined
.push(PATH_SEPARATOR
)
223 if path
.contains(&PATH_SEPARATOR
) {
224 return Err(JoinPathsError
);
226 joined
.extend_from_slice(path
);
228 Ok(OsStringExt
::from_vec(joined
))
231 impl fmt
::Display
for JoinPathsError
{
232 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
233 write
!(f
, "path segment contains separator `{}`", char::from(PATH_SEPARATOR
))
237 impl StdError
for JoinPathsError
{
239 fn description(&self) -> &str {
240 "failed to join paths"
244 #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
245 pub fn current_exe() -> io
::Result
<PathBuf
> {
248 libc
::CTL_KERN
as c_int
,
249 libc
::KERN_PROC
as c_int
,
250 libc
::KERN_PROC_PATHNAME
as c_int
,
256 mib
.len() as libc
::c_uint
,
263 return Err(io
::Error
::last_os_error());
265 let mut v
: Vec
<u8> = Vec
::with_capacity(sz
);
268 mib
.len() as libc
::c_uint
,
269 v
.as_mut_ptr() as *mut libc
::c_void
,
275 return Err(io
::Error
::last_os_error());
277 v
.set_len(sz
- 1); // chop off trailing NUL
278 Ok(PathBuf
::from(OsString
::from_vec(v
)))
282 #[cfg(target_os = "netbsd")]
283 pub fn current_exe() -> io
::Result
<PathBuf
> {
284 fn sysctl() -> io
::Result
<PathBuf
> {
286 let mib
= [libc
::CTL_KERN
, libc
::KERN_PROC_ARGS
, -1, libc
::KERN_PROC_PATHNAME
];
287 let mut path_len
: usize = 0;
290 mib
.len() as libc
::c_uint
,
297 return Err(io
::const_io_error
!(
298 io
::ErrorKind
::Uncategorized
,
299 "KERN_PROC_PATHNAME sysctl returned zero-length string",
302 let mut path
: Vec
<u8> = Vec
::with_capacity(path_len
);
305 mib
.len() as libc
::c_uint
,
306 path
.as_ptr() as *mut libc
::c_void
,
311 path
.set_len(path_len
- 1); // chop off NUL
312 Ok(PathBuf
::from(OsString
::from_vec(path
)))
315 fn procfs() -> io
::Result
<PathBuf
> {
316 let curproc_exe
= path
::Path
::new("/proc/curproc/exe");
317 if curproc_exe
.is_file() {
318 return crate::fs
::read_link(curproc_exe
);
320 Err(io
::const_io_error
!(
321 io
::ErrorKind
::Uncategorized
,
322 "/proc/curproc/exe doesn't point to regular file.",
325 sysctl().or_else(|_
| procfs())
328 #[cfg(target_os = "openbsd")]
329 pub fn current_exe() -> io
::Result
<PathBuf
> {
331 let mut mib
= [libc
::CTL_KERN
, libc
::KERN_PROC_ARGS
, libc
::getpid(), libc
::KERN_PROC_ARGV
];
332 let mib
= mib
.as_mut_ptr();
333 let mut argv_len
= 0;
334 cvt(libc
::sysctl(mib
, 4, ptr
::null_mut(), &mut argv_len
, ptr
::null_mut(), 0))?
;
335 let mut argv
= Vec
::<*const libc
::c_char
>::with_capacity(argv_len
as usize);
336 cvt(libc
::sysctl(mib
, 4, argv
.as_mut_ptr() as *mut _
, &mut argv_len
, ptr
::null_mut(), 0))?
;
337 argv
.set_len(argv_len
as usize);
338 if argv
[0].is_null() {
339 return Err(io
::const_io_error
!(
340 io
::ErrorKind
::Uncategorized
,
341 "no current exe available",
344 let argv0
= CStr
::from_ptr(argv
[0]).to_bytes();
345 if argv0
[0] == b'
.'
|| argv0
.iter().any(|b
| *b
== b'
/'
) {
346 crate::fs
::canonicalize(OsStr
::from_bytes(argv0
))
348 Ok(PathBuf
::from(OsStr
::from_bytes(argv0
)))
353 #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))]
354 pub fn current_exe() -> io
::Result
<PathBuf
> {
355 match crate::fs
::read_link("/proc/self/exe") {
356 Err(ref e
) if e
.kind() == io
::ErrorKind
::NotFound
=> Err(io
::const_io_error
!(
357 io
::ErrorKind
::Uncategorized
,
358 "no /proc/self/exe available. Is /proc mounted?",
364 #[cfg(any(target_os = "macos", target_os = "ios"))]
365 pub fn current_exe() -> io
::Result
<PathBuf
> {
368 libc
::_NSGetExecutablePath(ptr
::null_mut(), &mut sz
);
370 return Err(io
::Error
::last_os_error());
372 let mut v
: Vec
<u8> = Vec
::with_capacity(sz
as usize);
373 let err
= libc
::_NSGetExecutablePath(v
.as_mut_ptr() as *mut i8, &mut sz
);
375 return Err(io
::Error
::last_os_error());
377 v
.set_len(sz
as usize - 1); // chop off trailing NUL
378 Ok(PathBuf
::from(OsString
::from_vec(v
)))
382 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
383 pub fn current_exe() -> io
::Result
<PathBuf
> {
384 if let Ok(path
) = crate::fs
::read_link("/proc/self/path/a.out") {
388 let path
= libc
::getexecname();
390 Err(io
::Error
::last_os_error())
392 let filename
= CStr
::from_ptr(path
).to_bytes();
393 let path
= PathBuf
::from(<OsStr
as OsStrExt
>::from_bytes(filename
));
395 // Prepend a current working directory to the path if
396 // it doesn't contain an absolute pathname.
397 if filename
[0] == b'
/' { Ok(path) }
else { getcwd().map(|cwd| cwd.join(path)) }
403 #[cfg(target_os = "haiku")]
404 pub fn current_exe() -> io
::Result
<PathBuf
> {
406 let mut info
: mem
::MaybeUninit
<libc
::image_info
> = mem
::MaybeUninit
::uninit();
407 let mut cookie
: i32 = 0;
408 // the executable can be found at team id 0
409 let result
= libc
::_get_next_image_info(
413 mem
::size_of
::<libc
::image_info
>(),
416 use crate::io
::ErrorKind
;
417 Err(io
::const_io_error
!(ErrorKind
::Uncategorized
, "Error getting executable path"))
419 let name
= CStr
::from_ptr((*info
.as_ptr()).name
.as_ptr()).to_bytes();
420 Ok(PathBuf
::from(OsStr
::from_bytes(name
)))
425 #[cfg(target_os = "redox")]
426 pub fn current_exe() -> io
::Result
<PathBuf
> {
427 crate::fs
::read_to_string("sys:exe").map(PathBuf
::from
)
430 #[cfg(target_os = "l4re")]
431 pub fn current_exe() -> io
::Result
<PathBuf
> {
432 use crate::io
::ErrorKind
;
433 Err(io
::const_io_error
!(ErrorKind
::Unsupported
, "Not yet implemented!"))
436 #[cfg(target_os = "vxworks")]
437 pub fn current_exe() -> io
::Result
<PathBuf
> {
444 let exe_path
= env
::args().next().unwrap();
445 let path
= path
::Path
::new(&exe_path
);
449 #[cfg(target_os = "espidf")]
450 pub fn current_exe() -> io
::Result
<PathBuf
> {
451 super::unsupported
::unsupported()
454 #[cfg(target_os = "fuchsia")]
455 pub fn current_exe() -> io
::Result
<PathBuf
> {
456 use crate::io
::ErrorKind
;
464 let exe_path
= env
::args().next().ok_or(io
::const_io_error
!(
465 ErrorKind
::Uncategorized
,
466 "an executable path was not found because no arguments were provided through argv"
468 let path
= PathBuf
::from(exe_path
);
470 // Prepend the current working directory to the path if it's not absolute.
471 if !path
.is_absolute() { getcwd().map(|cwd| cwd.join(path)) }
else { Ok(path) }
475 iter
: vec
::IntoIter
<(OsString
, OsString
)>,
478 impl !Send
for Env {}
479 impl !Sync
for Env {}
481 impl Iterator
for Env
{
482 type Item
= (OsString
, OsString
);
483 fn next(&mut self) -> Option
<(OsString
, OsString
)> {
486 fn size_hint(&self) -> (usize, Option
<usize>) {
487 self.iter
.size_hint()
491 #[cfg(target_os = "macos")]
492 pub unsafe fn environ() -> *mut *const *const c_char
{
493 libc
::_NSGetEnviron() as *mut *const *const c_char
496 #[cfg(not(target_os = "macos"))]
497 pub unsafe fn environ() -> *mut *const *const c_char
{
499 static mut environ
: *const *const c_char
;
501 ptr
::addr_of_mut
!(environ
)
504 static ENV_LOCK
: StaticRwLock
= StaticRwLock
::new();
506 pub fn env_read_lock() -> StaticRwLockReadGuard
{
510 /// Returns a vector of (variable, value) byte-vector pairs for all the
511 /// environment variables of the current process.
512 pub fn env() -> Env
{
514 let _guard
= env_read_lock();
515 let mut environ
= *environ();
516 let mut result
= Vec
::new();
517 if !environ
.is_null() {
518 while !(*environ
).is_null() {
519 if let Some(key_value
) = parse(CStr
::from_ptr(*environ
).to_bytes()) {
520 result
.push(key_value
);
522 environ
= environ
.add(1);
525 return Env { iter: result.into_iter() }
;
528 fn parse(input
: &[u8]) -> Option
<(OsString
, OsString
)> {
529 // Strategy (copied from glibc): Variable name and value are separated
530 // by an ASCII equals sign '='. Since a variable name must not be
531 // empty, allow variable names starting with an equals sign. Skip all
533 if input
.is_empty() {
536 let pos
= memchr
::memchr(b'
='
, &input
[1..]).map(|p
| p
+ 1);
539 OsStringExt
::from_vec(input
[..p
].to_vec()),
540 OsStringExt
::from_vec(input
[p
+ 1..].to_vec()),
546 pub fn getenv(k
: &OsStr
) -> Option
<OsString
> {
547 // environment variables with a nul byte can't be set, so their value is
548 // always None as well
549 let k
= CString
::new(k
.as_bytes()).ok()?
;
551 let _guard
= env_read_lock();
552 let s
= libc
::getenv(k
.as_ptr()) as *const libc
::c_char
;
556 Some(OsStringExt
::from_vec(CStr
::from_ptr(s
).to_bytes().to_vec()))
561 pub fn setenv(k
: &OsStr
, v
: &OsStr
) -> io
::Result
<()> {
562 let k
= CString
::new(k
.as_bytes())?
;
563 let v
= CString
::new(v
.as_bytes())?
;
566 let _guard
= ENV_LOCK
.write();
567 cvt(libc
::setenv(k
.as_ptr(), v
.as_ptr(), 1)).map(drop
)
571 pub fn unsetenv(n
: &OsStr
) -> io
::Result
<()> {
572 let nbuf
= CString
::new(n
.as_bytes())?
;
575 let _guard
= ENV_LOCK
.write();
576 cvt(libc
::unsetenv(nbuf
.as_ptr())).map(drop
)
580 #[cfg(not(target_os = "espidf"))]
581 pub fn page_size() -> usize {
582 unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
585 pub fn temp_dir() -> PathBuf
{
586 crate::env
::var_os("TMPDIR").map(PathBuf
::from
).unwrap_or_else(|| {
587 if cfg
!(target_os
= "android") {
588 PathBuf
::from("/data/local/tmp")
590 PathBuf
::from("/tmp")
595 pub fn home_dir() -> Option
<PathBuf
> {
596 return crate::env
::var_os("HOME").or_else(|| unsafe { fallback() }
).map(PathBuf
::from
);
599 target_os
= "android",
601 target_os
= "emscripten",
603 target_os
= "vxworks",
606 unsafe fn fallback() -> Option
<OsString
> {
610 target_os
= "android",
612 target_os
= "emscripten",
614 target_os
= "vxworks",
617 unsafe fn fallback() -> Option
<OsString
> {
618 let amt
= match libc
::sysconf(libc
::_SC_GETPW_R_SIZE_MAX
) {
619 n
if n
< 0 => 512 as usize,
622 let mut buf
= Vec
::with_capacity(amt
);
623 let mut passwd
: libc
::passwd
= mem
::zeroed();
624 let mut result
= ptr
::null_mut();
625 match libc
::getpwuid_r(
632 0 if !result
.is_null() => {
633 let ptr
= passwd
.pw_dir
as *const _
;
634 let bytes
= CStr
::from_ptr(ptr
).to_bytes().to_vec();
635 Some(OsStringExt
::from_vec(bytes
))
642 pub fn exit(code
: i32) -> ! {
643 unsafe { libc::exit(code as c_int) }
646 pub fn getpid() -> u32 {
647 unsafe { libc::getpid() as u32 }
650 pub fn getppid() -> u32 {
651 unsafe { libc::getppid() as u32 }
654 #[cfg(all(target_os = "linux", target_env = "gnu"))]
655 pub fn glibc_version() -> Option
<(usize, usize)> {
657 fn gnu_get_libc_version() -> *const libc
::c_char
;
659 let version_cstr
= unsafe { CStr::from_ptr(gnu_get_libc_version()) }
;
660 if let Ok(version_str
) = version_cstr
.to_str() {
661 parse_glibc_version(version_str
)
667 // Returns Some((major, minor)) if the string is a valid "x.y" version,
668 // ignoring any extra dot-separated parts. Otherwise return None.
669 #[cfg(all(target_os = "linux", target_env = "gnu"))]
670 fn parse_glibc_version(version
: &str) -> Option
<(usize, usize)> {
671 let mut parsed_ints
= version
.split('
.'
).map(str::parse
::<usize>).fuse();
672 match (parsed_ints
.next(), parsed_ints
.next()) {
673 (Some(Ok(major
)), Some(Ok(minor
))) => Some((major
, minor
)),