From 8d2af4a5458f19fbf028b5a4012d389482327ccc Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sat, 11 Aug 2018 16:45:18 -0600 Subject: [PATCH] Update to new system calls and enviromental variables --- .gitmodules | 2 +- src/libstd/sys/redox/mod.rs | 23 ++++++ src/libstd/sys/redox/os.rs | 101 +++++++++++++++++-------- src/libstd/sys/redox/process.rs | 41 ++++++---- src/libstd/sys/redox/syscall/call.rs | 13 +--- src/libstd/sys/redox/syscall/flag.rs | 44 ++++++++++- src/libstd/sys/redox/syscall/number.rs | 2 +- 7 files changed, 168 insertions(+), 58 deletions(-) diff --git a/.gitmodules b/.gitmodules index 038237aa179a9..b5f60e6b6770f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,7 +10,7 @@ url = https://github.com/rust-lang/rust-installer.git [submodule "src/liblibc"] path = src/liblibc - url = https://github.com/rust-lang/libc.git + url = https://gitlab.redox-os.org/redox-os/liblibc.git [submodule "src/doc/nomicon"] path = src/doc/nomicon url = https://github.com/rust-lang-nursery/nomicon.git diff --git a/src/libstd/sys/redox/mod.rs b/src/libstd/sys/redox/mod.rs index f943257c68733..f899439c3f707 100644 --- a/src/libstd/sys/redox/mod.rs +++ b/src/libstd/sys/redox/mod.rs @@ -75,6 +75,29 @@ pub fn cvt(result: Result) -> io::Result { result.map_err(|err| io::Error::from_raw_os_error(err.errno)) } +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt_libc(t: T) -> io::Result { + if t.is_minus_one() { + Err(io::Error::last_os_error()) + } else { + Ok(t) + } +} + /// On Redox, use an illegal instruction to abort pub unsafe fn abort_internal() -> ! { ::core::intrinsics::abort(); diff --git a/src/libstd/sys/redox/os.rs b/src/libstd/sys/redox/os.rs index 5822216779b6e..84eb56615b60d 100644 --- a/src/libstd/sys/redox/os.rs +++ b/src/libstd/sys/redox/os.rs @@ -12,10 +12,12 @@ #![allow(unused_imports)] // lots of cfg code here +use libc::{self, c_char}; + use os::unix::prelude::*; use error::Error as StdError; -use ffi::{OsString, OsStr}; +use ffi::{CStr, CString, OsStr, OsString}; use fmt; use io::{self, Read, Write}; use iter; @@ -27,7 +29,7 @@ use ptr; use slice; use str; use sys_common::mutex::Mutex; -use sys::{cvt, fd, syscall}; +use sys::{cvt, cvt_libc, fd, syscall}; use vec; extern { @@ -129,6 +131,8 @@ pub fn current_exe() -> io::Result { Ok(PathBuf::from(path)) } +pub static ENV_LOCK: Mutex = Mutex::new(); + pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, _dont_send_or_sync_me: PhantomData<*mut ()>, @@ -140,52 +144,83 @@ impl Iterator for Env { fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } +pub unsafe fn environ() -> *mut *const *const c_char { + extern { static mut environ: *const *const c_char; } + &mut environ +} + /// Returns a vector of (variable, value) byte-vector pairs for all the /// environment variables of the current process. pub fn env() -> Env { - let mut variables: Vec<(OsString, OsString)> = Vec::new(); - if let Ok(mut file) = ::fs::File::open("env:") { - let mut string = String::new(); - if file.read_to_string(&mut string).is_ok() { - for line in string.lines() { - let mut parts = line.splitn(2, '='); - if let Some(name) = parts.next() { - let value = parts.next().unwrap_or(""); - variables.push((OsString::from(name.to_string()), - OsString::from(value.to_string()))); - } + unsafe { + let _guard = ENV_LOCK.lock(); + let mut environ = *environ(); + if environ == ptr::null() { + panic!("os::env() failure getting env string from OS: {}", + io::Error::last_os_error()); + } + let mut result = Vec::new(); + while *environ != ptr::null() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); } + environ = environ.offset(1); + } + return Env { + iter: result.into_iter(), + _dont_send_or_sync_me: PhantomData, + } + } + + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + // Strategy (copied from glibc): Variable name and value are separated + // by an ASCII equals sign '='. Since a variable name must not be + // empty, allow variable names starting with an equals sign. Skip all + // malformed lines. + if input.is_empty() { + return None; } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p+1..].to_vec()), + )) } - Env { iter: variables.into_iter(), _dont_send_or_sync_me: PhantomData } } -pub fn getenv(key: &OsStr) -> io::Result> { - if ! key.is_empty() { - if let Ok(mut file) = ::fs::File::open(&("env:".to_owned() + key.to_str().unwrap())) { - let mut string = String::new(); - file.read_to_string(&mut string)?; - Ok(Some(OsString::from(string))) +pub fn getenv(k: &OsStr) -> io::Result> { + // environment variables with a nul byte can't be set, so their value is + // always None as well + let k = CString::new(k.as_bytes())?; + unsafe { + let _guard = ENV_LOCK.lock(); + let s = libc::getenv(k.as_ptr()) as *const libc::c_char; + let ret = if s.is_null() { + None } else { - Ok(None) - } - } else { - Ok(None) + Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) + }; + Ok(ret) } } -pub fn setenv(key: &OsStr, value: &OsStr) -> io::Result<()> { - if ! key.is_empty() { - let mut file = ::fs::File::create(&("env:".to_owned() + key.to_str().unwrap()))?; - file.write_all(value.as_bytes())?; - file.set_len(value.len() as u64)?; +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let k = CString::new(k.as_bytes())?; + let v = CString::new(v.as_bytes())?; + + unsafe { + let _guard = ENV_LOCK.lock(); + cvt_libc(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ()) } - Ok(()) } -pub fn unsetenv(key: &OsStr) -> io::Result<()> { - ::fs::remove_file(&("env:".to_owned() + key.to_str().unwrap()))?; - Ok(()) +pub fn unsetenv(n: &OsStr) -> io::Result<()> { + let nbuf = CString::new(n.as_bytes())?; + + unsafe { + let _guard = ENV_LOCK.lock(); + cvt_libc(libc::unsetenv(nbuf.as_ptr())).map(|_| ()) + } } pub fn page_size() -> usize { diff --git a/src/libstd/sys/redox/process.rs b/src/libstd/sys/redox/process.rs index 2037616e6ac7c..f85834dff5d05 100644 --- a/src/libstd/sys/redox/process.rs +++ b/src/libstd/sys/redox/process.rs @@ -9,15 +9,17 @@ // except according to those terms. use env::{split_paths}; -use ffi::OsStr; +use ffi::{CStr, OsStr}; use os::unix::ffi::OsStrExt; use fmt; use io::{self, Error, ErrorKind}; use iter; use libc::{EXIT_SUCCESS, EXIT_FAILURE}; use path::{Path, PathBuf}; +use ptr; use sys::fd::FileDesc; use sys::fs::{File, OpenOptions}; +use sys::os::{ENV_LOCK, environ}; use sys::pipe::{self, AnonPipe}; use sys::{cvt, syscall}; use sys_common::process::{CommandEnv, DefaultEnvKey}; @@ -297,12 +299,6 @@ impl Command { t!(callback()); } - let args: Vec<[usize; 2]> = iter::once( - [self.program.as_ptr() as usize, self.program.len()] - ).chain( - self.args.iter().map(|arg| [arg.as_ptr() as usize, arg.len()]) - ).collect(); - self.env.apply(); let program = if self.program.contains(':') || self.program.contains('/') { @@ -321,14 +317,33 @@ impl Command { None }; - if let Some(program) = program { - if let Err(err) = syscall::execve(program.as_os_str().as_bytes(), &args) { - io::Error::from_raw_os_error(err.errno as i32) - } else { - panic!("return from exec without err"); + let fd = if let Some(program) = program { + t!(cvt(syscall::open(program.as_os_str().as_bytes(), syscall::O_RDONLY))) + } else { + return io::Error::from_raw_os_error(syscall::ENOENT); + }; + + let mut args: Vec<[usize; 2]> = iter::once( + [self.program.as_ptr() as usize, self.program.len()] + ).chain( + self.args.iter().map(|arg| [arg.as_ptr() as usize, arg.len()]) + ).collect(); + + let mut vars: Vec<[usize; 2]> = Vec::new(); + unsafe { + let _guard = ENV_LOCK.lock(); + let mut environ = *environ(); + while *environ != ptr::null() { + let var = CStr::from_ptr(*environ).to_bytes(); + vars.push([var.as_ptr() as usize, var.len()]); + environ = environ.offset(1); } + } + + if let Err(err) = syscall::fexec(fd, &args, &vars) { + io::Error::from_raw_os_error(err.errno as i32) } else { - io::Error::from_raw_os_error(syscall::ENOENT) + panic!("return from exec without err"); } } diff --git a/src/libstd/sys/redox/syscall/call.rs b/src/libstd/sys/redox/syscall/call.rs index f9a8bd61ac800..0188d684d3a2d 100644 --- a/src/libstd/sys/redox/syscall/call.rs +++ b/src/libstd/sys/redox/syscall/call.rs @@ -82,12 +82,6 @@ pub fn dup2(fd: usize, newfd: usize, buf: &[u8]) -> Result { unsafe { syscall4(SYS_DUP2, fd, newfd, buf.as_ptr() as usize, buf.len()) } } -/// Replace the current process with a new executable -pub fn execve>(path: T, args: &[[usize; 2]]) -> Result { - unsafe { syscall4(SYS_EXECVE, path.as_ref().as_ptr() as usize, - path.as_ref().len(), args.as_ptr() as usize, args.len()) } -} - /// Exit the current process pub fn exit(status: usize) -> Result { unsafe { syscall1(SYS_EXIT, status) } @@ -110,9 +104,10 @@ pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result { unsafe { syscall3(SYS_FCNTL, fd, cmd, arg) } } -/// Register a file for event-based I/O -pub fn fevent(fd: usize, flags: usize) -> Result { - unsafe { syscall2(SYS_FEVENT, fd, flags) } +/// Replace the current process with a new executable +pub fn fexec>(fd: usize, args: &[[usize; 2]], vars: &[[usize; 2]]) -> Result { + unsafe { syscall5(SYS_FEXEC, fd, args.as_ptr() as usize, args.len(), + vars.as_ptr() as usize, vars.len()) } } /// Map a file into memory diff --git a/src/libstd/sys/redox/syscall/flag.rs b/src/libstd/sys/redox/syscall/flag.rs index 0f61b9fa77b52..0f1a2c2315973 100644 --- a/src/libstd/sys/redox/syscall/flag.rs +++ b/src/libstd/sys/redox/syscall/flag.rs @@ -113,4 +113,46 @@ pub const SA_RESTART: usize = 0x10000000; pub const SA_NODEFER: usize = 0x40000000; pub const SA_RESETHAND: usize = 0x80000000; -pub const WNOHANG: usize = 1; +pub const WNOHANG: usize = 0x01; +pub const WUNTRACED: usize = 0x02; +pub const WCONTINUED: usize = 0x08; + +/// True if status indicates the child is stopped. +pub fn wifstopped(status: usize) -> bool { + (status & 0xff) == 0x7f +} + +/// If wifstopped(status), the signal that stopped the child. +pub fn wstopsig(status: usize) -> usize { + (status >> 8) & 0xff +} + +/// True if status indicates the child continued after a stop. +pub fn wifcontinued(status: usize) -> bool { + status == 0xffff +} + +/// True if STATUS indicates termination by a signal. +pub fn wifsignaled(status: usize) -> bool { + ((status & 0x7f) + 1) as i8 >= 2 +} + +/// If wifsignaled(status), the terminating signal. +pub fn wtermsig(status: usize) -> usize { + status & 0x7f +} + +/// True if status indicates normal termination. +pub fn wifexited(status: usize) -> bool { + wtermsig(status) == 0 +} + +/// If wifexited(status), the exit status. +pub fn wexitstatus(status: usize) -> usize { + (status >> 8) & 0xff +} + +/// True if status indicates a core dump was created. +pub fn wcoredump(status: usize) -> bool { + (status & 0x80) != 0 +} diff --git a/src/libstd/sys/redox/syscall/number.rs b/src/libstd/sys/redox/syscall/number.rs index 45cb40e390840..1e187565a675c 100644 --- a/src/libstd/sys/redox/syscall/number.rs +++ b/src/libstd/sys/redox/syscall/number.rs @@ -36,6 +36,7 @@ pub const SYS_FCHMOD: usize = SYS_CLASS_FILE | 94; pub const SYS_FCHOWN: usize = SYS_CLASS_FILE | 207; pub const SYS_FCNTL: usize = SYS_CLASS_FILE | 55; pub const SYS_FEVENT: usize = SYS_CLASS_FILE | 927; +pub const SYS_FEXEC: usize = SYS_CLASS_FILE | 11; pub const SYS_FMAP: usize = SYS_CLASS_FILE | 90; pub const SYS_FUNMAP: usize = SYS_CLASS_FILE | 91; pub const SYS_FPATH: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 928; @@ -50,7 +51,6 @@ pub const SYS_BRK: usize = 45; pub const SYS_CHDIR: usize = 12; pub const SYS_CLOCK_GETTIME: usize = 265; pub const SYS_CLONE: usize = 120; -pub const SYS_EXECVE: usize = 11; pub const SYS_EXIT: usize = 1; pub const SYS_FUTEX: usize = 240; pub const SYS_GETCWD: usize = 183;