1 //! Implementation of `std::os` functionality for unix systems
3 #![allow(unused_imports)] // lots of cfg code here
5 #[cfg(all(test, target_env = "gnu"))]
8 use crate::os
::unix
::prelude
::*;
10 use crate::error
::Error
as StdError
;
11 use crate::ffi
::{CStr, CString, OsStr, OsString}
;
15 use crate::marker
::PhantomData
;
18 use crate::path
::{self, PathBuf}
;
24 use crate::sys_common
::mutex
::{StaticMutex, StaticMutexGuard}
;
25 use crate::sys_common
::rwlock
::{RWLockReadGuard, StaticRWLock}
;
28 use libc
::{c_char, c_int, c_void}
;
30 const TMPBUF_SZ
: usize = 128;
33 if #[cfg(target_os = "redox")] {
34 const PATH_SEPARATOR
: u8 = b'
;'
;
36 const PATH_SEPARATOR
: u8 = b'
:'
;
41 #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))]
45 target_os
= "emscripten",
46 target_os
= "fuchsia",
49 link_name
= "__errno_location"
54 target_os
= "openbsd",
55 target_os
= "android",
61 #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")]
63 any(target_os
= "macos", target_os
= "ios", target_os
= "freebsd"),
66 #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
67 fn errno_location() -> *mut c_int
;
70 /// Returns the platform-specific value of errno
71 #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))]
72 pub fn errno() -> i32 {
73 unsafe { (*errno_location()) as i32 }
76 /// Sets the platform-specific value of errno
77 #[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall!
78 #[allow(dead_code)] // but not all target cfgs actually end up using it
79 pub fn set_errno(e
: i32) {
80 unsafe { *errno_location() = e as c_int }
83 #[cfg(target_os = "vxworks")]
84 pub fn errno() -> i32 {
85 unsafe { libc::errnoGet() }
88 #[cfg(target_os = "vxworks")]
89 pub fn set_errno(e
: i32) {
90 unsafe { libc::errnoSet(e as c_int) }
;
93 #[cfg(target_os = "dragonfly")]
94 pub fn errno() -> i32 {
100 unsafe { errno as i32 }
103 #[cfg(target_os = "dragonfly")]
104 pub fn set_errno(e
: i32) {
107 static mut errno
: c_int
;
115 /// Gets a detailed string description for the given error number.
116 pub fn error_string(errno
: i32) -> String
{
118 #[cfg_attr(any(target_os = "linux", target_env = "newlib"), link_name = "__xpg_strerror_r")]
119 fn strerror_r(errnum
: c_int
, buf
: *mut c_char
, buflen
: libc
::size_t
) -> c_int
;
122 let mut buf
= [0 as c_char
; TMPBUF_SZ
];
124 let p
= buf
.as_mut_ptr();
126 if strerror_r(errno
as c_int
, p
, buf
.len()) < 0 {
127 panic
!("strerror_r failure");
130 let p
= p
as *const _
;
131 str::from_utf8(CStr
::from_ptr(p
).to_bytes()).unwrap().to_owned()
135 pub fn getcwd() -> io
::Result
<PathBuf
> {
136 let mut buf
= Vec
::with_capacity(512);
139 let ptr
= buf
.as_mut_ptr() as *mut libc
::c_char
;
140 if !libc
::getcwd(ptr
, buf
.capacity()).is_null() {
141 let len
= CStr
::from_ptr(buf
.as_ptr() as *const libc
::c_char
).to_bytes().len();
144 return Ok(PathBuf
::from(OsString
::from_vec(buf
)));
146 let error
= io
::Error
::last_os_error();
147 if error
.raw_os_error() != Some(libc
::ERANGE
) {
152 // Trigger the internal buffer resizing logic of `Vec` by requiring
153 // more space than the current capacity.
154 let cap
= buf
.capacity();
161 pub fn chdir(p
: &path
::Path
) -> io
::Result
<()> {
162 let p
: &OsStr
= p
.as_ref();
163 let p
= CString
::new(p
.as_bytes())?
;
165 match libc
::chdir(p
.as_ptr()) == (0 as c_int
) {
167 false => Err(io
::Error
::last_os_error()),
172 pub struct SplitPaths
<'a
> {
173 iter
: iter
::Map
<slice
::Split
<'a
, u8, fn(&u8) -> bool
>, fn(&'a
[u8]) -> PathBuf
>,
176 pub fn split_paths(unparsed
: &OsStr
) -> SplitPaths
<'_
> {
177 fn bytes_to_path(b
: &[u8]) -> PathBuf
{
178 PathBuf
::from(<OsStr
as OsStrExt
>::from_bytes(b
))
180 fn is_separator(b
: &u8) -> bool
{
183 let unparsed
= unparsed
.as_bytes();
186 .split(is_separator
as fn(&u8) -> bool
)
187 .map(bytes_to_path
as fn(&[u8]) -> PathBuf
),
191 impl<'a
> Iterator
for SplitPaths
<'a
> {
193 fn next(&mut self) -> Option
<PathBuf
> {
196 fn size_hint(&self) -> (usize, Option
<usize>) {
197 self.iter
.size_hint()
202 pub struct JoinPathsError
;
204 pub fn join_paths
<I
, T
>(paths
: I
) -> Result
<OsString
, JoinPathsError
>
206 I
: Iterator
<Item
= T
>,
209 let mut joined
= Vec
::new();
211 for (i
, path
) in paths
.enumerate() {
212 let path
= path
.as_ref().as_bytes();
214 joined
.push(PATH_SEPARATOR
)
216 if path
.contains(&PATH_SEPARATOR
) {
217 return Err(JoinPathsError
);
219 joined
.extend_from_slice(path
);
221 Ok(OsStringExt
::from_vec(joined
))
224 impl fmt
::Display
for JoinPathsError
{
225 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
226 write
!(f
, "path segment contains separator `{}`", PATH_SEPARATOR
)
230 impl StdError
for JoinPathsError
{
232 fn description(&self) -> &str {
233 "failed to join paths"
237 #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
238 pub fn current_exe() -> io
::Result
<PathBuf
> {
241 libc
::CTL_KERN
as c_int
,
242 libc
::KERN_PROC
as c_int
,
243 libc
::KERN_PROC_PATHNAME
as c_int
,
249 mib
.len() as libc
::c_uint
,
256 return Err(io
::Error
::last_os_error());
258 let mut v
: Vec
<u8> = Vec
::with_capacity(sz
);
261 mib
.len() as libc
::c_uint
,
262 v
.as_mut_ptr() as *mut libc
::c_void
,
268 return Err(io
::Error
::last_os_error());
270 v
.set_len(sz
- 1); // chop off trailing NUL
271 Ok(PathBuf
::from(OsString
::from_vec(v
)))
275 #[cfg(target_os = "netbsd")]
276 pub fn current_exe() -> io
::Result
<PathBuf
> {
277 fn sysctl() -> io
::Result
<PathBuf
> {
279 let mib
= [libc
::CTL_KERN
, libc
::KERN_PROC_ARGS
, -1, libc
::KERN_PROC_PATHNAME
];
280 let mut path_len
: usize = 0;
283 mib
.len() as libc
::c_uint
,
290 return Err(io
::Error
::new(
291 io
::ErrorKind
::Other
,
292 "KERN_PROC_PATHNAME sysctl returned zero-length string",
295 let mut path
: Vec
<u8> = Vec
::with_capacity(path_len
);
298 mib
.len() as libc
::c_uint
,
299 path
.as_ptr() as *mut libc
::c_void
,
304 path
.set_len(path_len
- 1); // chop off NUL
305 Ok(PathBuf
::from(OsString
::from_vec(path
)))
308 fn procfs() -> io
::Result
<PathBuf
> {
309 let curproc_exe
= path
::Path
::new("/proc/curproc/exe");
310 if curproc_exe
.is_file() {
311 return crate::fs
::read_link(curproc_exe
);
314 io
::ErrorKind
::Other
,
315 "/proc/curproc/exe doesn't point to regular file.",
318 sysctl().or_else(|_
| procfs())
321 #[cfg(target_os = "openbsd")]
322 pub fn current_exe() -> io
::Result
<PathBuf
> {
324 let mut mib
= [libc
::CTL_KERN
, libc
::KERN_PROC_ARGS
, libc
::getpid(), libc
::KERN_PROC_ARGV
];
325 let mib
= mib
.as_mut_ptr();
326 let mut argv_len
= 0;
327 cvt(libc
::sysctl(mib
, 4, ptr
::null_mut(), &mut argv_len
, ptr
::null_mut(), 0))?
;
328 let mut argv
= Vec
::<*const libc
::c_char
>::with_capacity(argv_len
as usize);
329 cvt(libc
::sysctl(mib
, 4, argv
.as_mut_ptr() as *mut _
, &mut argv_len
, ptr
::null_mut(), 0))?
;
330 argv
.set_len(argv_len
as usize);
331 if argv
[0].is_null() {
332 return Err(io
::Error
::new(io
::ErrorKind
::Other
, "no current exe available"));
334 let argv0
= CStr
::from_ptr(argv
[0]).to_bytes();
335 if argv0
[0] == b'
.'
|| argv0
.iter().any(|b
| *b
== b'
/'
) {
336 crate::fs
::canonicalize(OsStr
::from_bytes(argv0
))
338 Ok(PathBuf
::from(OsStr
::from_bytes(argv0
)))
343 #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))]
344 pub fn current_exe() -> io
::Result
<PathBuf
> {
345 match crate::fs
::read_link("/proc/self/exe") {
346 Err(ref e
) if e
.kind() == io
::ErrorKind
::NotFound
=> Err(io
::Error
::new(
347 io
::ErrorKind
::Other
,
348 "no /proc/self/exe available. Is /proc mounted?",
354 #[cfg(any(target_os = "macos", target_os = "ios"))]
355 pub fn current_exe() -> io
::Result
<PathBuf
> {
357 fn _NSGetExecutablePath(buf
: *mut libc
::c_char
, bufsize
: *mut u32) -> libc
::c_int
;
361 _NSGetExecutablePath(ptr
::null_mut(), &mut sz
);
363 return Err(io
::Error
::last_os_error());
365 let mut v
: Vec
<u8> = Vec
::with_capacity(sz
as usize);
366 let err
= _NSGetExecutablePath(v
.as_mut_ptr() as *mut i8, &mut sz
);
368 return Err(io
::Error
::last_os_error());
370 v
.set_len(sz
as usize - 1); // chop off trailing NUL
371 Ok(PathBuf
::from(OsString
::from_vec(v
)))
375 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
376 pub fn current_exe() -> io
::Result
<PathBuf
> {
378 fn getexecname() -> *const c_char
;
381 let path
= getexecname();
383 Err(io
::Error
::last_os_error())
385 let filename
= CStr
::from_ptr(path
).to_bytes();
386 let path
= PathBuf
::from(<OsStr
as OsStrExt
>::from_bytes(filename
));
388 // Prepend a current working directory to the path if
389 // it doesn't contain an absolute pathname.
390 if filename
[0] == b'
/' { Ok(path) }
else { getcwd().map(|cwd| cwd.join(path)) }
395 #[cfg(target_os = "haiku")]
396 pub fn current_exe() -> io
::Result
<PathBuf
> {
397 // Use Haiku's image info functions
404 init_routine
: *mut libc
::c_void
, // function pointer
405 term_routine
: *mut libc
::c_void
, // function pointer
408 name
: [libc
::c_char
; 1024], // MAXPATHLEN
409 text
: *mut libc
::c_void
,
410 data
: *mut libc
::c_void
,
419 fn _get_next_image_info(
422 info
: *mut image_info
,
427 let mut info
: image_info
= mem
::zeroed();
428 let mut cookie
: i32 = 0;
429 // the executable can be found at team id 0
431 _get_next_image_info(0, &mut cookie
, &mut info
, mem
::size_of
::<image_info
>() as i32);
433 use crate::io
::ErrorKind
;
434 Err(io
::Error
::new(ErrorKind
::Other
, "Error getting executable path"))
436 let name
= CStr
::from_ptr(info
.name
.as_ptr()).to_bytes();
437 Ok(PathBuf
::from(OsStr
::from_bytes(name
)))
442 #[cfg(target_os = "redox")]
443 pub fn current_exe() -> io
::Result
<PathBuf
> {
444 crate::fs
::read_to_string("sys:exe").map(PathBuf
::from
)
447 #[cfg(any(target_os = "fuchsia", target_os = "l4re"))]
448 pub fn current_exe() -> io
::Result
<PathBuf
> {
449 use crate::io
::ErrorKind
;
450 Err(io
::Error
::new(ErrorKind
::Other
, "Not yet implemented!"))
453 #[cfg(target_os = "vxworks")]
454 pub fn current_exe() -> io
::Result
<PathBuf
> {
461 let exe_path
= env
::args().next().unwrap();
462 let path
= path
::Path
::new(&exe_path
);
467 iter
: vec
::IntoIter
<(OsString
, OsString
)>,
468 _dont_send_or_sync_me
: PhantomData
<*mut ()>,
471 impl Iterator
for Env
{
472 type Item
= (OsString
, OsString
);
473 fn next(&mut self) -> Option
<(OsString
, OsString
)> {
476 fn size_hint(&self) -> (usize, Option
<usize>) {
477 self.iter
.size_hint()
481 #[cfg(target_os = "macos")]
482 pub unsafe fn environ() -> *mut *const *const c_char
{
484 fn _NSGetEnviron() -> *mut *const *const c_char
;
489 #[cfg(not(target_os = "macos"))]
490 pub unsafe fn environ() -> *mut *const *const c_char
{
492 static mut environ
: *const *const c_char
;
494 ptr
::addr_of_mut
!(environ
)
497 static ENV_LOCK
: StaticRWLock
= StaticRWLock
::new();
499 pub fn env_read_lock() -> RWLockReadGuard
{
500 ENV_LOCK
.read_with_guard()
503 /// Returns a vector of (variable, value) byte-vector pairs for all the
504 /// environment variables of the current process.
505 pub fn env() -> Env
{
507 let _guard
= env_read_lock();
508 let mut environ
= *environ();
509 let mut result
= Vec
::new();
510 if !environ
.is_null() {
511 while !(*environ
).is_null() {
512 if let Some(key_value
) = parse(CStr
::from_ptr(*environ
).to_bytes()) {
513 result
.push(key_value
);
515 environ
= environ
.add(1);
518 return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData }
;
521 fn parse(input
: &[u8]) -> Option
<(OsString
, OsString
)> {
522 // Strategy (copied from glibc): Variable name and value are separated
523 // by an ASCII equals sign '='. Since a variable name must not be
524 // empty, allow variable names starting with an equals sign. Skip all
526 if input
.is_empty() {
529 let pos
= memchr
::memchr(b'
='
, &input
[1..]).map(|p
| p
+ 1);
532 OsStringExt
::from_vec(input
[..p
].to_vec()),
533 OsStringExt
::from_vec(input
[p
+ 1..].to_vec()),
539 pub fn getenv(k
: &OsStr
) -> io
::Result
<Option
<OsString
>> {
540 // environment variables with a nul byte can't be set, so their value is
541 // always None as well
542 let k
= CString
::new(k
.as_bytes())?
;
544 let _guard
= env_read_lock();
545 let s
= libc
::getenv(k
.as_ptr()) as *const libc
::c_char
;
546 let ret
= if s
.is_null() {
549 Some(OsStringExt
::from_vec(CStr
::from_ptr(s
).to_bytes().to_vec()))
555 pub fn setenv(k
: &OsStr
, v
: &OsStr
) -> io
::Result
<()> {
556 let k
= CString
::new(k
.as_bytes())?
;
557 let v
= CString
::new(v
.as_bytes())?
;
560 let _guard
= ENV_LOCK
.write_with_guard();
561 cvt(libc
::setenv(k
.as_ptr(), v
.as_ptr(), 1)).map(drop
)
565 pub fn unsetenv(n
: &OsStr
) -> io
::Result
<()> {
566 let nbuf
= CString
::new(n
.as_bytes())?
;
569 let _guard
= ENV_LOCK
.write_with_guard();
570 cvt(libc
::unsetenv(nbuf
.as_ptr())).map(drop
)
574 pub fn page_size() -> usize {
575 unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
578 pub fn temp_dir() -> PathBuf
{
579 crate::env
::var_os("TMPDIR").map(PathBuf
::from
).unwrap_or_else(|| {
580 if cfg
!(target_os
= "android") {
581 PathBuf
::from("/data/local/tmp")
583 PathBuf
::from("/tmp")
588 pub fn home_dir() -> Option
<PathBuf
> {
589 return crate::env
::var_os("HOME").or_else(|| unsafe { fallback() }
).map(PathBuf
::from
);
592 target_os
= "android",
594 target_os
= "emscripten",
596 target_os
= "vxworks"
598 unsafe fn fallback() -> Option
<OsString
> {
602 target_os
= "android",
604 target_os
= "emscripten",
606 target_os
= "vxworks"
608 unsafe fn fallback() -> Option
<OsString
> {
609 let amt
= match libc
::sysconf(libc
::_SC_GETPW_R_SIZE_MAX
) {
610 n
if n
< 0 => 512 as usize,
613 let mut buf
= Vec
::with_capacity(amt
);
614 let mut passwd
: libc
::passwd
= mem
::zeroed();
615 let mut result
= ptr
::null_mut();
616 match libc
::getpwuid_r(
623 0 if !result
.is_null() => {
624 let ptr
= passwd
.pw_dir
as *const _
;
625 let bytes
= CStr
::from_ptr(ptr
).to_bytes().to_vec();
626 Some(OsStringExt
::from_vec(bytes
))
633 pub fn exit(code
: i32) -> ! {
634 unsafe { libc::exit(code as c_int) }
637 pub fn getpid() -> u32 {
638 unsafe { libc::getpid() as u32 }
641 pub fn getppid() -> u32 {
642 unsafe { libc::getppid() as u32 }
645 #[cfg(target_env = "gnu")]
646 pub fn glibc_version() -> Option
<(usize, usize)> {
647 if let Some(Ok(version_str
)) = glibc_version_cstr().map(CStr
::to_str
) {
648 parse_glibc_version(version_str
)
654 #[cfg(target_env = "gnu")]
655 fn glibc_version_cstr() -> Option
<&'
static CStr
> {
657 fn gnu_get_libc_version() -> *const libc
::c_char
659 if let Some(f
) = gnu_get_libc_version
.get() {
660 unsafe { Some(CStr::from_ptr(f())) }
666 // Returns Some((major, minor)) if the string is a valid "x.y" version,
667 // ignoring any extra dot-separated parts. Otherwise return None.
668 #[cfg(target_env = "gnu")]
669 fn parse_glibc_version(version
: &str) -> Option
<(usize, usize)> {
670 let mut parsed_ints
= version
.split('
.'
).map(str::parse
::<usize>).fuse();
671 match (parsed_ints
.next(), parsed_ints
.next()) {
672 (Some(Ok(major
)), Some(Ok(minor
))) => Some((major
, minor
)),