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