]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/os.rs
Rollup merge of #64935 - AnthonyMikh:librustc_errors/emmiter__code-clarity, r=estebank
[rust.git] / src / libstd / sys / unix / os.rs
1 //! Implementation of `std::os` functionality for unix systems
2
3 #![allow(unused_imports)] // lots of cfg code here
4
5 use crate::os::unix::prelude::*;
6
7 use crate::error::Error as StdError;
8 use crate::ffi::{CString, CStr, OsString, OsStr};
9 use crate::fmt;
10 use crate::io;
11 use crate::iter;
12 use crate::marker::PhantomData;
13 use crate::mem;
14 use crate::memchr;
15 use crate::path::{self, PathBuf};
16 use crate::ptr;
17 use crate::slice;
18 use crate::str;
19 use crate::sys_common::mutex::{Mutex, MutexGuard};
20 use crate::sys::cvt;
21 use crate::sys::fd;
22 use crate::vec;
23
24 use libc::{c_int, c_char, c_void};
25
26 const TMPBUF_SZ: usize = 128;
27
28 cfg_if::cfg_if! {
29     if #[cfg(target_os = "redox")] {
30         const PATH_SEPARATOR: u8 = b';';
31     } else {
32         const PATH_SEPARATOR: u8 = b':';
33     }
34 }
35
36 extern {
37     #[cfg(not(target_os = "dragonfly"))]
38     #[cfg_attr(any(target_os = "linux",
39                    target_os = "emscripten",
40                    target_os = "fuchsia",
41                    target_os = "l4re"),
42                link_name = "__errno_location")]
43     #[cfg_attr(any(target_os = "netbsd",
44                    target_os = "openbsd",
45                    target_os = "android",
46                    target_os = "hermit",
47                    target_os = "redox",
48                    target_env = "newlib"),
49                link_name = "__errno")]
50     #[cfg_attr(target_os = "solaris", link_name = "___errno")]
51     #[cfg_attr(any(target_os = "macos",
52                    target_os = "ios",
53                    target_os = "freebsd"),
54                link_name = "__error")]
55     #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
56     fn errno_location() -> *mut c_int;
57 }
58
59 /// Returns the platform-specific value of errno
60 #[cfg(not(target_os = "dragonfly"))]
61 pub fn errno() -> i32 {
62     unsafe {
63         (*errno_location()) as i32
64     }
65 }
66
67 /// Sets the platform-specific value of errno
68 #[cfg(all(not(target_os = "linux"),
69           not(target_os = "dragonfly")))] // needed for readdir and syscall!
70 pub fn set_errno(e: i32) {
71     unsafe {
72         *errno_location() = e as c_int
73     }
74 }
75
76 #[cfg(target_os = "dragonfly")]
77 pub fn errno() -> i32 {
78     extern {
79         #[thread_local]
80         static errno: c_int;
81     }
82
83     unsafe { errno as i32 }
84 }
85
86 #[cfg(target_os = "dragonfly")]
87 pub fn set_errno(e: i32) {
88     extern {
89         #[thread_local]
90         static mut errno: c_int;
91     }
92
93     unsafe {
94         errno = e;
95     }
96 }
97
98 /// Gets a detailed string description for the given error number.
99 pub fn error_string(errno: i32) -> String {
100     extern {
101         #[cfg_attr(any(target_os = "linux", target_env = "newlib"),
102                    link_name = "__xpg_strerror_r")]
103         fn strerror_r(errnum: c_int, buf: *mut c_char,
104                       buflen: libc::size_t) -> c_int;
105     }
106
107     let mut buf = [0 as c_char; TMPBUF_SZ];
108
109     let p = buf.as_mut_ptr();
110     unsafe {
111         if strerror_r(errno as c_int, p, buf.len()) < 0 {
112             panic!("strerror_r failure");
113         }
114
115         let p = p as *const _;
116         str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned()
117     }
118 }
119
120 pub fn getcwd() -> io::Result<PathBuf> {
121     let mut buf = Vec::with_capacity(512);
122     loop {
123         unsafe {
124             let ptr = buf.as_mut_ptr() as *mut libc::c_char;
125             if !libc::getcwd(ptr, buf.capacity()).is_null() {
126                 let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len();
127                 buf.set_len(len);
128                 buf.shrink_to_fit();
129                 return Ok(PathBuf::from(OsString::from_vec(buf)));
130             } else {
131                 let error = io::Error::last_os_error();
132                 if error.raw_os_error() != Some(libc::ERANGE) {
133                     return Err(error);
134                 }
135             }
136
137             // Trigger the internal buffer resizing logic of `Vec` by requiring
138             // more space than the current capacity.
139             let cap = buf.capacity();
140             buf.set_len(cap);
141             buf.reserve(1);
142         }
143     }
144 }
145
146 pub fn chdir(p: &path::Path) -> io::Result<()> {
147     let p: &OsStr = p.as_ref();
148     let p = CString::new(p.as_bytes())?;
149     unsafe {
150         match libc::chdir(p.as_ptr()) == (0 as c_int) {
151             true => Ok(()),
152             false => Err(io::Error::last_os_error()),
153         }
154     }
155 }
156
157 pub struct SplitPaths<'a> {
158     iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>,
159                     fn(&'a [u8]) -> PathBuf>,
160 }
161
162 pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
163     fn bytes_to_path(b: &[u8]) -> PathBuf {
164         PathBuf::from(<OsStr as OsStrExt>::from_bytes(b))
165     }
166     fn is_separator(b: &u8) -> bool { *b == PATH_SEPARATOR }
167     let unparsed = unparsed.as_bytes();
168     SplitPaths {
169         iter: unparsed.split(is_separator as fn(&u8) -> bool)
170                       .map(bytes_to_path as fn(&[u8]) -> PathBuf)
171     }
172 }
173
174 impl<'a> Iterator for SplitPaths<'a> {
175     type Item = PathBuf;
176     fn next(&mut self) -> Option<PathBuf> { self.iter.next() }
177     fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
178 }
179
180 #[derive(Debug)]
181 pub struct JoinPathsError;
182
183 pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
184     where I: Iterator<Item=T>, T: AsRef<OsStr>
185 {
186     let mut joined = Vec::new();
187
188     for (i, path) in paths.enumerate() {
189         let path = path.as_ref().as_bytes();
190         if i > 0 { joined.push(PATH_SEPARATOR) }
191         if path.contains(&PATH_SEPARATOR) {
192             return Err(JoinPathsError)
193         }
194         joined.extend_from_slice(path);
195     }
196     Ok(OsStringExt::from_vec(joined))
197 }
198
199 impl fmt::Display for JoinPathsError {
200     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201         write!(f, "path segment contains separator `{}`", PATH_SEPARATOR)
202     }
203 }
204
205 impl StdError for JoinPathsError {
206     fn description(&self) -> &str { "failed to join paths" }
207 }
208
209 #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
210 pub fn current_exe() -> io::Result<PathBuf> {
211     unsafe {
212         let mut mib = [libc::CTL_KERN as c_int,
213                        libc::KERN_PROC as c_int,
214                        libc::KERN_PROC_PATHNAME as c_int,
215                        -1 as c_int];
216         let mut sz = 0;
217         cvt(libc::sysctl(mib.as_mut_ptr(), mib.len() as libc::c_uint,
218                          ptr::null_mut(), &mut sz, ptr::null_mut(), 0))?;
219         if sz == 0 {
220             return Err(io::Error::last_os_error())
221         }
222         let mut v: Vec<u8> = Vec::with_capacity(sz);
223         cvt(libc::sysctl(mib.as_mut_ptr(), mib.len() as libc::c_uint,
224                          v.as_mut_ptr() as *mut libc::c_void, &mut sz,
225                          ptr::null_mut(), 0))?;
226         if sz == 0 {
227             return Err(io::Error::last_os_error());
228         }
229         v.set_len(sz - 1); // chop off trailing NUL
230         Ok(PathBuf::from(OsString::from_vec(v)))
231     }
232 }
233
234 #[cfg(target_os = "netbsd")]
235 pub fn current_exe() -> io::Result<PathBuf> {
236     fn sysctl() -> io::Result<PathBuf> {
237         unsafe {
238             let mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, -1, libc::KERN_PROC_PATHNAME];
239             let mut path_len: usize = 0;
240             cvt(libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
241                              ptr::null_mut(), &mut path_len,
242                              ptr::null(), 0))?;
243             if path_len <= 1 {
244                 return Err(io::Error::new(io::ErrorKind::Other,
245                            "KERN_PROC_PATHNAME sysctl returned zero-length string"))
246             }
247             let mut path: Vec<u8> = Vec::with_capacity(path_len);
248             cvt(libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
249                              path.as_ptr() as *mut libc::c_void, &mut path_len,
250                              ptr::null(), 0))?;
251             path.set_len(path_len - 1); // chop off NUL
252             Ok(PathBuf::from(OsString::from_vec(path)))
253         }
254     }
255     fn procfs() -> io::Result<PathBuf> {
256         let curproc_exe = path::Path::new("/proc/curproc/exe");
257         if curproc_exe.is_file() {
258             return crate::fs::read_link(curproc_exe);
259         }
260         Err(io::Error::new(io::ErrorKind::Other,
261                            "/proc/curproc/exe doesn't point to regular file."))
262     }
263     sysctl().or_else(|_| procfs())
264 }
265
266 #[cfg(target_os = "openbsd")]
267 pub fn current_exe() -> io::Result<PathBuf> {
268     unsafe {
269         let mut mib = [libc::CTL_KERN,
270                        libc::KERN_PROC_ARGS,
271                        libc::getpid(),
272                        libc::KERN_PROC_ARGV];
273         let mib = mib.as_mut_ptr();
274         let mut argv_len = 0;
275         cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_len,
276                          ptr::null_mut(), 0))?;
277         let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize);
278         cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _,
279                          &mut argv_len, ptr::null_mut(), 0))?;
280         argv.set_len(argv_len as usize);
281         if argv[0].is_null() {
282             return Err(io::Error::new(io::ErrorKind::Other,
283                                       "no current exe available"))
284         }
285         let argv0 = CStr::from_ptr(argv[0]).to_bytes();
286         if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') {
287             crate::fs::canonicalize(OsStr::from_bytes(argv0))
288         } else {
289             Ok(PathBuf::from(OsStr::from_bytes(argv0)))
290         }
291     }
292 }
293
294 #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))]
295 pub fn current_exe() -> io::Result<PathBuf> {
296     match crate::fs::read_link("/proc/self/exe") {
297         Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
298             Err(io::Error::new(
299                 io::ErrorKind::Other,
300                 "no /proc/self/exe available. Is /proc mounted?"
301             ))
302         },
303         other => other,
304     }
305 }
306
307 #[cfg(any(target_os = "macos", target_os = "ios"))]
308 pub fn current_exe() -> io::Result<PathBuf> {
309     extern {
310         fn _NSGetExecutablePath(buf: *mut libc::c_char,
311                                 bufsize: *mut u32) -> libc::c_int;
312     }
313     unsafe {
314         let mut sz: u32 = 0;
315         _NSGetExecutablePath(ptr::null_mut(), &mut sz);
316         if sz == 0 { return Err(io::Error::last_os_error()); }
317         let mut v: Vec<u8> = Vec::with_capacity(sz as usize);
318         let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
319         if err != 0 { return Err(io::Error::last_os_error()); }
320         v.set_len(sz as usize - 1); // chop off trailing NUL
321         Ok(PathBuf::from(OsString::from_vec(v)))
322     }
323 }
324
325 #[cfg(any(target_os = "solaris"))]
326 pub fn current_exe() -> io::Result<PathBuf> {
327     extern {
328         fn getexecname() -> *const c_char;
329     }
330     unsafe {
331         let path = getexecname();
332         if path.is_null() {
333             Err(io::Error::last_os_error())
334         } else {
335             let filename = CStr::from_ptr(path).to_bytes();
336             let path = PathBuf::from(<OsStr as OsStrExt>::from_bytes(filename));
337
338             // Prepend a current working directory to the path if
339             // it doesn't contain an absolute pathname.
340             if filename[0] == b'/' {
341                 Ok(path)
342             } else {
343                 getcwd().map(|cwd| cwd.join(path))
344             }
345         }
346     }
347 }
348
349 #[cfg(target_os = "haiku")]
350 pub fn current_exe() -> io::Result<PathBuf> {
351     // Use Haiku's image info functions
352     #[repr(C)]
353     struct image_info {
354         id: i32,
355         type_: i32,
356         sequence: i32,
357         init_order: i32,
358         init_routine: *mut libc::c_void,    // function pointer
359         term_routine: *mut libc::c_void,    // function pointer
360         device: libc::dev_t,
361         node: libc::ino_t,
362         name: [libc::c_char; 1024],         // MAXPATHLEN
363         text: *mut libc::c_void,
364         data: *mut libc::c_void,
365         text_size: i32,
366         data_size: i32,
367         api_version: i32,
368         abi: i32,
369     }
370
371     unsafe {
372         extern {
373             fn _get_next_image_info(team_id: i32, cookie: *mut i32,
374                 info: *mut image_info, size: i32) -> i32;
375         }
376
377         let mut info: image_info = mem::zeroed();
378         let mut cookie: i32 = 0;
379         // the executable can be found at team id 0
380         let result = _get_next_image_info(0, &mut cookie, &mut info,
381             mem::size_of::<image_info>() as i32);
382         if result != 0 {
383             use crate::io::ErrorKind;
384             Err(io::Error::new(ErrorKind::Other, "Error getting executable path"))
385         } else {
386             let name = CStr::from_ptr(info.name.as_ptr()).to_bytes();
387             Ok(PathBuf::from(OsStr::from_bytes(name)))
388         }
389     }
390 }
391
392 #[cfg(target_os = "redox")]
393 pub fn current_exe() -> io::Result<PathBuf> {
394     crate::fs::read_to_string("sys:exe").map(PathBuf::from)
395 }
396
397 #[cfg(any(target_os = "fuchsia", target_os = "l4re", target_os = "hermit"))]
398 pub fn current_exe() -> io::Result<PathBuf> {
399     use crate::io::ErrorKind;
400     Err(io::Error::new(ErrorKind::Other, "Not yet implemented!"))
401 }
402
403 pub struct Env {
404     iter: vec::IntoIter<(OsString, OsString)>,
405     _dont_send_or_sync_me: PhantomData<*mut ()>,
406 }
407
408 impl Iterator for Env {
409     type Item = (OsString, OsString);
410     fn next(&mut self) -> Option<(OsString, OsString)> { self.iter.next() }
411     fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
412 }
413
414 #[cfg(target_os = "macos")]
415 pub unsafe fn environ() -> *mut *const *const c_char {
416     extern { fn _NSGetEnviron() -> *mut *const *const c_char; }
417     _NSGetEnviron()
418 }
419
420 #[cfg(not(target_os = "macos"))]
421 pub unsafe fn environ() -> *mut *const *const c_char {
422     extern { static mut environ: *const *const c_char; }
423     &mut environ
424 }
425
426 pub unsafe fn env_lock() -> MutexGuard<'static> {
427     // We never call `ENV_LOCK.init()`, so it is UB to attempt to
428     // acquire this mutex reentrantly!
429     static ENV_LOCK: Mutex = Mutex::new();
430     ENV_LOCK.lock()
431 }
432
433 /// Returns a vector of (variable, value) byte-vector pairs for all the
434 /// environment variables of the current process.
435 pub fn env() -> Env {
436     unsafe {
437         let _guard = env_lock();
438         let mut environ = *environ();
439         let mut result = Vec::new();
440         while environ != ptr::null() && *environ != ptr::null() {
441             if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
442                 result.push(key_value);
443             }
444             environ = environ.offset(1);
445         }
446         return Env {
447             iter: result.into_iter(),
448             _dont_send_or_sync_me: PhantomData,
449         }
450     }
451
452     fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
453         // Strategy (copied from glibc): Variable name and value are separated
454         // by an ASCII equals sign '='. Since a variable name must not be
455         // empty, allow variable names starting with an equals sign. Skip all
456         // malformed lines.
457         if input.is_empty() {
458             return None;
459         }
460         let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
461         pos.map(|p| (
462             OsStringExt::from_vec(input[..p].to_vec()),
463             OsStringExt::from_vec(input[p+1..].to_vec()),
464         ))
465     }
466 }
467
468 pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
469     // environment variables with a nul byte can't be set, so their value is
470     // always None as well
471     let k = CString::new(k.as_bytes())?;
472     unsafe {
473         let _guard = env_lock();
474         let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
475         let ret = if s.is_null() {
476             None
477         } else {
478             Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
479         };
480         Ok(ret)
481     }
482 }
483
484 pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
485     let k = CString::new(k.as_bytes())?;
486     let v = CString::new(v.as_bytes())?;
487
488     unsafe {
489         let _guard = env_lock();
490         cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ())
491     }
492 }
493
494 pub fn unsetenv(n: &OsStr) -> io::Result<()> {
495     let nbuf = CString::new(n.as_bytes())?;
496
497     unsafe {
498         let _guard = env_lock();
499         cvt(libc::unsetenv(nbuf.as_ptr())).map(|_| ())
500     }
501 }
502
503 pub fn page_size() -> usize {
504     unsafe {
505         libc::sysconf(libc::_SC_PAGESIZE) as usize
506     }
507 }
508
509 pub fn temp_dir() -> PathBuf {
510     crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
511         if cfg!(target_os = "android") {
512             PathBuf::from("/data/local/tmp")
513         } else {
514             PathBuf::from("/tmp")
515         }
516     })
517 }
518
519 pub fn home_dir() -> Option<PathBuf> {
520     return crate::env::var_os("HOME").or_else(|| unsafe {
521         fallback()
522     }).map(PathBuf::from);
523
524     #[cfg(any(target_os = "android",
525               target_os = "ios",
526               target_os = "emscripten",
527               target_os = "redox"))]
528     unsafe fn fallback() -> Option<OsString> { None }
529     #[cfg(not(any(target_os = "android",
530                   target_os = "ios",
531                   target_os = "emscripten",
532                   target_os = "redox")))]
533     unsafe fn fallback() -> Option<OsString> {
534         let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
535             n if n < 0 => 512 as usize,
536             n => n as usize,
537         };
538         let mut buf = Vec::with_capacity(amt);
539         let mut passwd: libc::passwd = mem::zeroed();
540         let mut result = ptr::null_mut();
541         match libc::getpwuid_r(libc::getuid(), &mut passwd, buf.as_mut_ptr(),
542                                buf.capacity(), &mut result) {
543             0 if !result.is_null() => {
544                 let ptr = passwd.pw_dir as *const _;
545                 let bytes = CStr::from_ptr(ptr).to_bytes().to_vec();
546                 Some(OsStringExt::from_vec(bytes))
547             },
548             _ => None,
549         }
550     }
551 }
552
553 pub fn exit(code: i32) -> ! {
554     unsafe { libc::exit(code as c_int) }
555 }
556
557 pub fn getpid() -> u32 {
558     unsafe { libc::getpid() as u32 }
559 }
560
561 pub fn getppid() -> u32 {
562     unsafe { libc::getppid() as u32 }
563 }
564
565 #[cfg(target_env = "gnu")]
566 pub fn glibc_version() -> Option<(usize, usize)> {
567     if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
568         parse_glibc_version(version_str)
569     } else {
570         None
571     }
572 }
573
574 #[cfg(target_env = "gnu")]
575 fn glibc_version_cstr() -> Option<&'static CStr> {
576     weak! {
577         fn gnu_get_libc_version() -> *const libc::c_char
578     }
579     if let Some(f) = gnu_get_libc_version.get() {
580         unsafe { Some(CStr::from_ptr(f())) }
581     } else {
582         None
583     }
584 }
585
586 // Returns Some((major, minor)) if the string is a valid "x.y" version,
587 // ignoring any extra dot-separated parts. Otherwise return None.
588 #[cfg(target_env = "gnu")]
589 fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
590     let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse();
591     match (parsed_ints.next(), parsed_ints.next()) {
592         (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)),
593         _ => None
594     }
595 }
596
597 #[cfg(all(test, target_env = "gnu"))]
598 mod test {
599     use super::*;
600
601     #[test]
602     fn test_glibc_version() {
603         // This mostly just tests that the weak linkage doesn't panic wildly...
604         glibc_version();
605     }
606
607     #[test]
608     fn test_parse_glibc_version() {
609         let cases = [
610             ("0.0", Some((0, 0))),
611             ("01.+2", Some((1, 2))),
612             ("3.4.5.six", Some((3, 4))),
613             ("1", None),
614             ("1.-2", None),
615             ("1.foo", None),
616             ("foo.1", None),
617         ];
618         for &(version_str, parsed) in cases.iter() {
619             assert_eq!(parsed, parse_glibc_version(version_str));
620         }
621     }
622 }