summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/unistd.rs25
-rw-r--r--test/sys/test_termios.rs2
-rw-r--r--test/test_unistd.rs82
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");