Skip to content

Re-adding execvpe support, conditional on platform #683

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
and nix::Error::UnsupportedOperation}`
([#614](https://github.com/nix-rust/nix/pull/614))
- Added `cfmakeraw`, `cfsetspeed`, and `tcgetsid`. ([#527](https://github.com/nix-rust/nix/pull/527))
- Readded execvpe support, conditional on platform. libc now provides this for Haiku, Linux, and OpenBSD.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to "Added unistd::execvpe for Haiku, Linux, and OpenBSD."

([#683](https://github.com/nix-rust/nix/pull/683))

### Changed
- Changed `ioctl!(write ...)` to take argument by value instead as pointer.
Expand Down
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ exclude = [

[features]
eventfd = []
execvpe = []
preadv_pwritev = []
signalfd = []

[dependencies]
libc = "0.2.25"
libc = { git = "https://github.com/rust-lang/libc" }
bitflags = "0.9"
cfg-if = "0.1.0"
void = "1.0.2"
Expand Down
3 changes: 1 addition & 2 deletions src/pty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ use {Errno, Result, Error, fcntl};

/// Representation of a master/slave pty pair
///
/// This is returned by `openpty`. Note that this type does *not* implement `Drop`, so the user
/// must manually close the file descriptors.
/// This is returned by `openpty`
pub struct OpenptyResult {
pub master: RawFd,
pub slave: RawFd,
Expand Down
16 changes: 11 additions & 5 deletions src/unistd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,7 @@ mod linux {
use {Errno, Result, NixPath};
use super::{Uid, Gid};

#[cfg(feature = "execvpe")]
#[cfg(any(target_os = "haiku", target_os = "linux", target_os = "openbsd"))]
use std::ffi::CString;

pub fn pivot_root<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
Expand Down Expand Up @@ -1644,8 +1644,14 @@ mod linux {
Errno::result(res).map(drop)
}

/// Replaces the current process image with a new one.
/// ([see exec(3)](http://man7.org/linux/man-pages/man3/exec.3.html))
///
/// # Errors
/// An error is returned if the executable file does not exist, cannot be accessed, or
/// could not be executed otherwise.
#[inline]
#[cfg(feature = "execvpe")]
#[cfg(any(target_os = "haiku", target_os = "linux", target_os = "openbsd"))]
pub fn execvpe(filename: &CString, args: &[CString], env: &[CString]) -> Result<()> {
use std::ptr;
use libc::c_char;
Expand All @@ -1656,10 +1662,10 @@ mod linux {
let mut env_p: Vec<*const c_char> = env.iter().map(|s| s.as_ptr()).collect();
env_p.push(ptr::null());

unsafe {
super::ffi::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
let res = unsafe {
libc::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
};

Err(Error::Sys(Errno::last()))
Errno::result(res).map(drop)
}
}
9 changes: 5 additions & 4 deletions test/sys/test_aio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::io::{Write, Read, Seek, SeekFrom};
use std::ops::Deref;
use std::os::unix::io::AsRawFd;
use std::rc::Rc;
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{thread, time};
use tempfile::tempfile;
Expand Down Expand Up @@ -232,6 +233,8 @@ fn test_write() {

lazy_static! {
pub static ref SIGNALED: AtomicBool = AtomicBool::new(false);
// protects access to SIGUSR2 handlers, not just SIGNALED
pub static ref SIGUSR2_MTX: Mutex<()> = Mutex::new(());
}

extern fn sigfunc(_: c_int) {
Expand All @@ -243,8 +246,7 @@ extern fn sigfunc(_: c_int) {
#[test]
#[cfg_attr(any(all(target_env = "musl", target_arch = "x86_64"), target_arch = "mips"), ignore)]
fn test_write_sigev_signal() {
#[allow(unused_variables)]
let m = ::SIGUSR2_MTX.lock().expect("Mutex got poisoned by another test");
let _ = SIGUSR2_MTX.lock().expect("Mutex got poisoned by another test");
let sa = SigAction::new(SigHandler::Handler(sigfunc),
SA_RESETHAND,
SigSet::empty());
Expand Down Expand Up @@ -374,8 +376,7 @@ fn test_lio_listio_nowait() {
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
#[cfg_attr(any(target_arch = "mips", target_env = "musl"), ignore)]
fn test_lio_listio_signal() {
#[allow(unused_variables)]
let m = ::SIGUSR2_MTX.lock().expect("Mutex got poisoned by another test");
let _ = SIGUSR2_MTX.lock().expect("Mutex got poisoned by another test");
const INITIAL: &'static [u8] = b"abcdef123456";
const WBUF: &'static [u8] = b"CDEF";
let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
Expand Down
46 changes: 16 additions & 30 deletions test/sys/test_termios.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::os::unix::prelude::*;
use tempfile::tempfile;

use nix::{Error, fcntl};
use nix::{Error, fcntl, unistd};
use nix::errno::Errno;
use nix::pty::openpty;
use nix::sys::termios::{self, ECHO, OPOST, OCRNL, Termios, tcgetattr};
Expand All @@ -15,27 +14,22 @@ fn write_all(f: RawFd, buf: &[u8]) {
}
}

// Test tcgetattr on a terminal
#[test]
fn test_tcgetattr_pty() {
let pty = openpty(None, None).unwrap();
assert!(termios::tcgetattr(pty.master).is_ok());
close(pty.master).unwrap();
close(pty.slave).unwrap();
}
// Test tcgetattr on something that isn't a terminal
#[test]
fn test_tcgetattr_enotty() {
let file = tempfile().unwrap();
assert_eq!(termios::tcgetattr(file.as_raw_fd()).err(),
Some(Error::Sys(Errno::ENOTTY)));
}

// Test tcgetattr on an invalid file descriptor
#[test]
fn test_tcgetattr_ebadf() {
assert_eq!(termios::tcgetattr(-1).err(),
Some(Error::Sys(Errno::EBADF)));
fn test_tcgetattr() {
for fd in 0..5 {
let termios = termios::tcgetattr(fd);
match unistd::isatty(fd) {
// If `fd` is a TTY, tcgetattr must succeed.
Ok(true) => assert!(termios.is_ok()),
// If it's an invalid file descriptor, tcgetattr should also return
// the same error
Err(Error::Sys(Errno::EBADF)) => {
assert_eq!(termios.err(), Some(Error::Sys(Errno::EBADF)));
},
// Otherwise it should return any error
_ => assert!(termios.is_err())
}
}
}

// Test modifying output flags
Expand All @@ -47,8 +41,6 @@ fn test_output_flags() {
assert!(pty.master > 0);
assert!(pty.slave > 0);
let termios = tcgetattr(pty.master).unwrap();
close(pty.master).unwrap();
close(pty.slave).unwrap();
termios
};

Expand All @@ -72,8 +64,6 @@ fn test_output_flags() {
let mut buf = [0u8; 10];
::read_exact(pty.slave, &mut buf);
let transformed_string = "foofoofoo\n";
close(pty.master).unwrap();
close(pty.slave).unwrap();
assert_eq!(&buf, transformed_string.as_bytes());
}

Expand All @@ -86,8 +76,6 @@ fn test_local_flags() {
assert!(pty.master > 0);
assert!(pty.slave > 0);
let termios = tcgetattr(pty.master).unwrap();
close(pty.master).unwrap();
close(pty.slave).unwrap();
termios
};

Expand All @@ -114,8 +102,6 @@ fn test_local_flags() {
// Try to read from the master, which should not have anything as echoing was disabled.
let mut buf = [0u8; 10];
let read = read(pty.master, &mut buf).unwrap_err();
close(pty.master).unwrap();
close(pty.slave).unwrap();
assert_eq!(read, Error::Sys(Errno::EAGAIN));
}

Expand Down
6 changes: 0 additions & 6 deletions test/sys/test_wait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ use libc::exit;

#[test]
fn test_wait_signal() {
#[allow(unused_variables)]
let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");

match fork() {
Ok(Child) => pause().unwrap_or(()),
Ok(Parent { child }) => {
Expand All @@ -22,9 +19,6 @@ fn test_wait_signal() {

#[test]
fn test_wait_exit() {
#[allow(unused_variables)]
let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");

match fork() {
Ok(Child) => unsafe { exit(12); },
Ok(Parent { child }) => {
Expand Down
12 changes: 0 additions & 12 deletions test/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ mod test_unistd;

use nixtest::assert_size_of;
use std::os::unix::io::RawFd;
use std::sync::Mutex;
use nix::unistd::read;

/// Helper function analogous to std::io::Read::read_exact, but for `RawFD`s
Expand All @@ -38,17 +37,6 @@ fn read_exact(f: RawFd, buf: &mut [u8]) {
}
}

lazy_static! {
/// Any test that changes the process's current working directory must grab
/// this mutex
pub static ref CWD_MTX: Mutex<()> = Mutex::new(());
/// Any test that creates child processes must grab this mutex, regardless
/// of what it does with those children.
pub static ref FORK_MTX: Mutex<()> = Mutex::new(());
/// Any test that registers a SIGUSR2 handler must grab this mutex
pub static ref SIGUSR2_MTX: Mutex<()> = Mutex::new(());
}

#[test]
pub fn test_size_of_long() {
// This test is mostly here to ensure that 32bit CI is correctly
Expand Down
54 changes: 35 additions & 19 deletions test/test_mq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,43 @@ use nix::Error::Sys;

#[test]
fn test_mq_send_and_receive() {

const MSG_SIZE: c_long = 32;
let attr = MqAttr::new(0, 10, MSG_SIZE, 0);
let mq_name= &CString::new(b"/a_nix_test_queue".as_ref()).unwrap();

let mqd0 = mq_open(mq_name, O_CREAT | O_WRONLY,
S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH,
Some(&attr)).unwrap();
let msg_to_send = "msg_1";
mq_send(mqd0, msg_to_send.as_bytes(), 1).unwrap();

let mqd1 = mq_open(mq_name, O_CREAT | O_RDONLY,
S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH,
Some(&attr)).unwrap();
let mut buf = [0u8; 32];
let mut prio = 0u32;
let len = mq_receive(mqd1, &mut buf, &mut prio).unwrap();
assert!(prio == 1);

mq_close(mqd1).unwrap();
mq_close(mqd0).unwrap();
assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap());
let mq_name_in_parent = &CString::new(b"/a_nix_test_queue".as_ref()).unwrap();
let mqd_in_parent = mq_open(mq_name_in_parent, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, Some(&attr)).unwrap();
let msg_to_send = "msg_1".as_bytes();

mq_send(mqd_in_parent, msg_to_send, 1).unwrap();

let (reader, writer) = pipe().unwrap();

let pid = fork();
match pid {
Ok(Child) => {
let mq_name_in_child = &CString::new(b"/a_nix_test_queue".as_ref()).unwrap();
let mqd_in_child = mq_open(mq_name_in_child, O_CREAT | O_RDONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, Some(&attr)).unwrap();
let mut buf = [0u8; 32];
let mut prio = 0u32;
mq_receive(mqd_in_child, &mut buf, &mut prio).unwrap();
assert!(prio == 1);
write(writer, &buf).unwrap(); // pipe result to parent process. Otherwise cargo does not report test failures correctly
mq_close(mqd_in_child).unwrap();
}
Ok(Parent { child }) => {
mq_close(mqd_in_parent).unwrap();

// Wait for the child to exit.
waitpid(child, None).unwrap();
// Read 1024 bytes.
let mut read_buf = [0u8; 32];
read(reader, &mut read_buf).unwrap();
let message_str = str::from_utf8(&read_buf).unwrap();
assert_eq!(&message_str[.. message_str.char_indices().nth(5).unwrap().0], "msg_1");
},
// panic, fork should never fail unless there is a serious problem with the OS
Err(_) => panic!("Error: Fork Failed")
}
}


Expand Down
Loading