diff options
-rw-r--r-- | src/unistd.rs | 25 | ||||
-rw-r--r-- | test/sys/test_termios.rs | 2 | ||||
-rw-r--r-- | test/test_unistd.rs | 82 |
3 files changed, 72 insertions, 37 deletions
diff --git a/src/unistd.rs b/src/unistd.rs index 354e46db..a4e37412 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -29,6 +29,9 @@ mod ffi { // execute program // doc: http://man7.org/linux/man-pages/man2/execve.2.html pub fn execve(filename: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int; + // doc: http://man7.org/linux/man-pages/man3/exec.3.html + #[cfg(any(target_os = "linux", target_os = "android"))] + pub fn execvpe(filename: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int; // run the current process in the background // doc: http://man7.org/linux/man-pages/man3/daemon.3.html @@ -312,9 +315,12 @@ pub fn chroot<P: ?Sized + NixPath>(path: &P) -> Result<()> { #[cfg(any(target_os = "linux", target_os = "android"))] mod linux { + use std::ptr; use sys::syscall::{syscall, SYSPIVOTROOT}; use errno::Errno; use {Error, Result, NixPath}; + use std::ffi::CString; + use libc::c_char; pub fn pivot_root<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( new_root: &P1, put_old: &P2) -> Result<()> { @@ -332,4 +338,23 @@ mod linux { Ok(()) } + + #[inline] + pub fn execvpe(filename: &CString, args: &[CString], env: &[CString]) -> Result<()> { + let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect(); + args_p.push(ptr::null()); + + let mut env_p: Vec<*const c_char> = env.iter().map(|s| s.as_ptr()).collect(); + env_p.push(ptr::null()); + + let res = unsafe { + super::ffi::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) + }; + + if res != 0 { + return Err(Error::Sys(Errno::last())); + } + + unreachable!() + } } diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs index fd6dbcd6..a41304d7 100644 --- a/test/sys/test_termios.rs +++ b/test/sys/test_termios.rs @@ -12,7 +12,7 @@ fn test_tcgetattr() { // If it's an invalid file descriptor, tcgetattr should also return // the same error Err(Error::Sys(Errno::EBADF)) => { - assert!(termios.err() == Some(Error::Sys(Errno::EBADF))); + assert_eq!(termios.err(), Some(Error::Sys(Errno::EBADF))); }, // Otherwise it should return any error _ => assert!(termios.is_err()) diff --git a/test/test_unistd.rs b/test/test_unistd.rs index f6c0c55c..5c0076c9 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -29,44 +29,54 @@ fn test_fork_and_waitpid() { } } +macro_rules! execve_test_factory( + ($test_name:ident, $syscall:ident, $unix_sh:expr, $android_sh:expr) => ( + #[test] + fn $test_name() { + // The `exec`d process will write to `writer`, and we'll read that + // data from `reader`. + let (reader, writer) = pipe().unwrap(); -#[test] -fn test_execve() { - // The `exec`d process will write to `writer`, and we'll read that - // data from `reader`. - let (reader, writer) = pipe().unwrap(); - - match fork().unwrap() { - Child => { - #[cfg(not(target_os = "android"))] - const SH_PATH: &'static [u8] = b"/bin/sh"; + match fork().unwrap() { + Child => { + #[cfg(not(target_os = "android"))] + const SH_PATH: &'static [u8] = $unix_sh; - #[cfg(target_os = "android")] - const SH_PATH: &'static [u8] = b"/system/bin/sh"; + #[cfg(target_os = "android")] + const SH_PATH: &'static [u8] = $android_sh; - // Close stdout. - close(1).unwrap(); - // Make `writer` be the stdout of the new process. - dup(writer).unwrap(); - // exec! - execve(&CString::new(SH_PATH).unwrap(), - &[CString::new(b"".as_ref()).unwrap(), - CString::new(b"-c".as_ref()).unwrap(), - CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz".as_ref()).unwrap()], - &[CString::new(b"foo=bar".as_ref()).unwrap(), - CString::new(b"baz=quux".as_ref()).unwrap()]).unwrap(); - }, - Parent(child_pid) => { - // Wait for the child to exit. - waitpid(child_pid, None).unwrap(); - // Read 1024 bytes. - let mut buf = [0u8; 1024]; - read(reader, &mut buf).unwrap(); - // It should contain the things we printed using `/bin/sh`. - let string = String::from_utf8_lossy(&buf); - assert!(string.contains("nix!!!")); - assert!(string.contains("foo=bar")); - assert!(string.contains("baz=quux")); + // Close stdout. + close(1).unwrap(); + // Make `writer` be the stdout of the new process. + dup(writer).unwrap(); + // exec! + $syscall( + &CString::new(SH_PATH).unwrap(), + &[CString::new(b"".as_ref()).unwrap(), + CString::new(b"-c".as_ref()).unwrap(), + CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz" + .as_ref()).unwrap()], + &[CString::new(b"foo=bar".as_ref()).unwrap(), + CString::new(b"baz=quux".as_ref()).unwrap()]).unwrap(); + }, + Parent(child_pid) => { + // Wait for the child to exit. + waitpid(child_pid, None).unwrap(); + // Read 1024 bytes. + let mut buf = [0u8; 1024]; + read(reader, &mut buf).unwrap(); + // It should contain the things we printed using `/bin/sh`. + let string = String::from_utf8_lossy(&buf); + assert!(string.contains("nix!!!")); + assert!(string.contains("foo=bar")); + assert!(string.contains("baz=quux")); + } } } -} + ) +); + +execve_test_factory!(test_execve, execve, b"/bin/sh", b"/system/bin/sh"); + +#[cfg(any(target_os = "linux", target_os = "android"))] +execve_test_factory!(test_execvpe, execvpe, b"sh", b"sh"); |